Last updated

Rails 3: Customized exception handling

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:

1# GET /p/:name
2def show
3  @profile = Profile.find(params[:name])
4end

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:

1# GET /p/:name
2def show
3  @profile = Profile.find(params[:name])
4rescue
5  render :template => 'application/profile_not_found', :status => :not_found
6end

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:

1# GET /p/:name
2def show
3  @profile = Profile.find(params[:name])
4end

Then, in your app/controllers/application_controller.rb add this:

1class ApplicationController < ActionController::Base
2  rescue_from ActiveRecord::RecordNotFound, :with => :rescue_not_found
3
4  protected
5  def rescue_not_found
6    render :template => 'application/not_found', :status => :not_found
7  end
8end

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:

1# Define your own error
2class MyApp::ProfileNotFoundError < StandardError
3end
4
5# GET /p/:name
6def show
7  @profile = Profile.find_by_name(params[:name])
8  raise MyApp::ProfileNotFoundError if @profile.nil?
9end

And add this to your ApplicationController:

1rescue_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:

1rescue_from MyApp::ProfileNotFoundError do |exception|
2  render :nothing => "Profile not found.", :status => 404
3end