DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Jeremy Friesen for The DEV Team

Posted on • Originally published at takeonrules.com on

Application Exception Handling in Rails

A Quick Tour through (Some of the) Layers of Propagation

Our Forem code base has three primary exception handling strategies.

  • Inline
  • Propagate up to Controller
  • Propagate up to Application

Each of these strategies are valid, and things propagate from up from method to controller to application.

Handling Strategies

Inline

Below is an example of a function that handles all exceptions by writing them to the log. If any part of the do_something raises an exception, we’ll capture it, and write it to the log.

Also whatever called my_function will continue processing.

def my_function
  do_something
rescue => e
  logger.error(e)
end

Enter fullscreen mode Exit fullscreen mode

Another variation is to capture a specific exception.

def my_function
  do_something
rescue NoMethodError => e
  logger.error(e)
end

Enter fullscreen mode Exit fullscreen mode

In the above example, the code only handles NoMethodError exceptions. If the do_something method raised a RuntimeError exception, our rescue would not handle that exception.

When specifying the exception, the rescue considers the inheritance of the exception object. The rescue will handle any exception that is a descendant of the NoMethodError class.

Propagate Up to Controller

In Rails, you can add handle exceptions at the controller level. Here’s the code you might see:

class UsersController
  rescue_from ActiveRecord::NotFoundError, with: :not_found

  def show
    @user = User.find(params[:id])
  end

  private

  def not_found
    render "404.html", status: 404
  end
end

Enter fullscreen mode Exit fullscreen mode

See the rescue_from method documentation for more details. Of particular note is the final line in the documentation: β€œExceptions raised inside exception handlers are not propagated up.”

This means if you use a rescue_from, and are looking at things in development, you won’t see the exception in the browser.

Propagate Up to Application Handling

If you don’t use inline nor rescue_from, your exceptions will bubble up to the application. And without any configuration, those visiting your site will see the default Rails exception page.

To handle exceptions at the application level you add them to the following to your application’s ./config/application.rb.

In the below example all β€œPundit::NotAuthorizedError” exceptions will call the not_found method on the controller that handled the request.

config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :not_found

Enter fullscreen mode Exit fullscreen mode

That’s the first piece of the configuration. The second part is to add another piece to the configuration; you want to set config.consider_all_requests_local.

You’ll often see the following in the ./config/environments/production.rb.

config.consider_all_requests_local = false

Enter fullscreen mode Exit fullscreen mode

This means we are configuring Rails to look at the config.action_dispatch.rescue_responses and call the corresponding methods. In other words, don’t show the ugly exceptions to our production users. There’s other configurations to ensure that we show a 500 error page, but that’s outside the scope of this post.

But in the development environment (e.g. ./config/environments/development.rb) that value will often be set to true. Which means, we are telling Rails to ignore the config.action_dispatch.rescue_responses and will render the ugly, though often useful, exception in the browser.

Conclusion

When do I use which one? That depends. The further away from where you encounter the exception the more you have to consider.

First, if you can inline rescue, that’s great. But maybe don’t inline rescue every ActiveRecord::RecordNotFound exception?

My preference is to minimize the use of rescue_from; it is the β€œalways on”. And that means its hiding the call stack; something I find useful in my development work.

Awhile ago, I read Avdi Grimm’s Exceptional Ruby; I highly recommend picking it up and giving it a read to further understand the power and pitfalls of exceptions.

Top comments (0)

🌚 Life is too short to browse without dark mode