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
2
3
4
# 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:

1
2
3
4
5
6
# 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:

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

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

1
2
3
4
5
6
7
8
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:

1
2
3
4
5
6
7
8
9
# 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:

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

1
2
3
rescue_from MyApp::ProfileNotFoundError do |exception|
  render :nothing => "Profile not found.", :status => 404
end
Did you enjoy this post? Your generosity is much appreciated.