Exceptions happen. There’s no way around that. But not all exceptions are created equally.
For instance, a 404 “Not found” error can (and should) be handled correctly in your application.
Let me give you an example of how to handle a ActiveRecord::RecordNotFound
exception. Let’s assume you have an application that could show a user profile:
# GET /p/:name
def show
@profile = Profile.find(params[:name])
end
Now, it may happen that the :name
paramater contains a value that cannot be found in our database, most likely because someone made a typo in the URL.
If Profile#find
cannot get a proper result it will throw ActiveRecord::RecordNotFound
.
Now, instead of showing the user the (by default ugly) 404 page from public/404.html
we want to do something more fancy.
Action-specific exception handling#
Here’s one solution:
# GET /p/:name
def show
@profile = Profile.find(params[:name])
rescue
render :template => 'application/profile_not_found', :status => :not_found
end
You can now create app/views/applicaiton/profile_not_found.html.haml
and give a nice custom error message to your user.
You may try to find some matching profiles to :name
or show a search box.
Global exception handling#
The above example only works for the specific profile show
action. It’s also possible to hanlde exceptions on the application level.
Your show
action still looks like this:
# GET /p/:name
def show
@profile = Profile.find(params[:name])
end
Then, in your app/controllers/application_controller.rb
add this:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :rescue_not_found
protected
def rescue_not_found
render :template => 'application/not_found', :status => :not_found
end
end
Whenever an ActiveRecord::RecordNotFound
exception is thrown (and not handled by the action itself), it will be handled by your ApplicationController
.
Custom exceptions#
It’s possible to throw your own custom exceptions and handle them in different ways. Like this:
# Define your own error
class MyApp::ProfileNotFoundError < StandardError
end
# GET /p/:name
def show
@profile = Profile.find_by_name(params[:name])
raise MyApp::ProfileNotFoundError if @profile.nil?
end
And add this to your ApplicationController
:
rescue_from MyApp::ProfileNotFoundError, :with => :profile_not_found
Optionally, if you don’t want to write that custom profile_not_found
method, you may also supply a block:
rescue_from MyApp::ProfileNotFoundError do |exception|
render :nothing => "Profile not found.", :status => 404
end