General rescue throughout controller when id not found - RoR
I have stumbled upon a situation where my application looks for an id that does not exist in the database. An exception is thrown. Of course, this is a pretty standard situation for any web developer.
Thanks to this answer I know that using rescue deals with the situation pretty neatly, like so:
def show @customer = Customer.find(params[:id]) rescue ActiveRecord::RecordNotFound #customer with that id cannot be found redirect_to action: :index #redirect to index page takes place instead of crashing end
In case the customer cannot be found, the user gets redirected to the index page. This works absolutely fine.
Now, this is all nice, but I need to do the same rescue attempts in actions like show, edit, destroy, etc, i.e. every controller method that needs a specific id.
Having said that, here's my question: Isn't there any way to generally tell my controller that if it can't find the id in any of its methods, it shall redirect to the index page (or, generally, perform a specific task)?
You must use rescue_from for this task. See example in the Action Controller Overview Guide
class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found private def record_not_found redirect_to action: :index end end
Rails has a built-in rescue_from class method:
class CustomersController < ApplicationController rescue_from ActiveRecord::RecordNotFound, with: :index ... end
If you're talking about doing this within a single controller (as opposed to doing this globally in every controller) then here are a couple options:
You can use a before_filter to setup your resource:
class CustomerController < ApplicationController before_filter :get_customer, :only => [ :show, :update, :delete ] def show end private def get_customer @customer = ActiveRecord.find(params[:id]) rescue ActiveRecord::RecordNotFound redirect_to :action => :index end end
Or you might use a method instead. I've been moving in this direction rather than using instance variables inside views, and it would also help you solve your problem:
class CustomerController < ApplicationController def show # Uses customer instead of @customer end private def customer @customer ||= Customer.find(params[:id]) rescue ActiveRecord::RecordNotFound redirect_to :action => :index end helper_method :customer end
In certain cases, I would recommend that you use Model.find_by_id(id) as opposed to Model.find(id). Instead of throwing an exception, .find_by_id returns nil. if the record could not be found.
Just make sure to check for nils to avoid NoMethodError!
P.S. For what it's worth, Model.find_by_id(id) is functionally equivalent to Model.where(id: id), which would allow you to build out some additional relations if you want.