Preparing for Rails 2.0: Controller-based exception handling

    Myles Eftos

    Since Ruby is a pure Object-Oriented Language, exceptions play a big role in the flow of control. Previously, you had the choice of rescuing exceptions at a local level or you could override the rescue_action method in your controller.

    The former method gave you really fine-grained control of what to do in the case of an exception:

    rescue ActiveRecord::RecordInvalid
        render :action => 'new'

    In this case, if the ActiveRecord::RecordInvalid exception is raised (the save! method will raise this if validation fails) Rails will render the ‘new’ action. It became clear, though that adding begin/rescues around the same methods is pretty time consuming and not very DRY – which is where the rescue_action became helpful:

    def rescue_action(exception)
      if exception == ActionView::TemplateError
        render :template => 'errors/404'

    This (rather contrived) example will trap any ActionView::TemplateError and render the 404.erb file in the /app/views/errors directory. You are able to drop that method into any controller (including app_controller), but again, there is a lot of work involved in setting them up, making sure each controller performs the correct action for a given exception, which is why Rails 2.0 introduces rescue_from.

    rescue_from is an attribute of each controller, and allows you to define a method to run when a particular exception is called. So the above example would become:

    rescue_from ActionView::TemplateError, :with => :render_404
    def render_404(exception)
       render :template => 'errors/404'

    or if you prefer to use a inline block:

    rescue_from ActionView::TemplateError do { render :template => 'errors/404' }

    In the current PR release, the rescue_from technique can only catch exceptions of an exact type, so it wouldn’t catch sub-classed exceptions – if you had a custom exception called MyTemplateError that extends ActionView::TemplateError the above code won’t work. The good news is a patch to edge rails fixes this issue, so the release version of Rails 2.0 will work as expected.