ExceptionHandler Ruby on Rails Gem

ExceptionHandler is an “error pages” gem for Ruby on Rails. Currently at version 0.7.7.0, the gem has been downloaded over 170,000 times and is broadly considered as the “best” dynamic error pages gem for the framework. It works extremely well. The most important thing to realize about ExceptionHandler is that it's basically designed to provide…

ExceptionHandler is an “error pages” gem for Ruby on Rails.

Currently at version 0.7.7.0, the gem has been downloaded over 170,000 times and is broadly considered as the “best” dynamic error pages gem for the framework. It works extremely well.

The most important thing to realize about ExceptionHandler is that it's basically designed to provide a “translation” system for Rails errors, converting them into the appropriate HTTP error that a web browser can read.

You see, whilst Rails may raise exceptions from within its core, these errors are not what you see in your browser. You see the http error that the framework delivers to the web server. This http error is accompanied by an “HTTP Message Body” which is what the browser displays on the page.

In other words, when dealing with Rails errors – if you want to actually show “branded” error pages (with your own layout or another one) – you have to “hack” Rails to deliver those specific pages when errors occur. The pages that are shown have NO bearing on what the user sees in their web browser; they're simply there to provide a “branded” way for them to engage with it.

Whenever Rails raises an exception inside its application – that error (it could be anything such as a database problem or something) is only valid for the Rails application. What Rails does is “translate” that error into one of two types of HTTP error (which a browser can read): 4xx (client error) or 5xx (server error).

Every time you use a “web” application, your computer is sending a request for data on port 80 of a publicly accessible IP address. If the other computer (server) has a “web server” application running on port 80, it will accept the request and respond with HTTP based data (which will typically include HTML code).

The entire “web” is a public directory for the “Internet”, meaning that if you the IP (or equivalent domain name) for a connected computer, you should be able to access it via the “Hypertext Transfer Protocol” . This HTTP protocol is the core of how the “web” works, and why most people get confused when dealing with “errors” in their Rails based applications.

HTTP “errors” are not really errors at all but erroneous responses. Each “error” you see is still an HTML page, displayed with a corresponding HTTP status code.

Since HTTP is a “stateless” protocol, it has to work with what's known as a “request / response” pattern – essentially meaning that every single time you send a new request to a web service, that request is valued as entirely new.

This is opposed to “stateful” processes, which “retain state” between requests; in the sense of native applications or similar. The point is that what you see with HTTP errors is a response to an erroneous request. They're really just status codes which explain that.

To do this properly, you have to be able to “translate” Rails-based errors into the appropriate HTTP error response. This is done by the ActionDispatch :: ShowExceptions middleware – which calls a middleware “hook” (“exceptions_app”) to determine which “HTML” response to show in the body of the erroneous message.

It uses the “rescue_responses” hash to determine which HTTP error code to match any Rails exceptions to. This hash can be extended within Rails, allowing users to map different Rails-based “exceptions” to the appropriate HTTP response code.

Say your application raises an error with ActiveRecord.

If it can not find a particular item in the database – Rails will raise the ActiveRecord :: NotFound exception class. This is not an HTTP-compatible error; it's simply a “Rails” exception. The key is that it has to be “translated” into an error that HTTP-based browsers can understand. This is where ActionDispatch :: ShowExceptions comes in.

ShowExceptions essentially takes the exception object – passes it through the “rescue_responses” hash (to get an appropriate HTTP status code) and – specifically – invokes the “exceptions_app” middleware hook to generate the “HTTP message body” for the response …

wrapper = ExceptionWrapper.new (backtrace_cleaner, exception)

status = wrapper.status_code

request.set_header “action_dispatch.exception”, wrapper.exception

request.set_header “action_dispatch.original_path”, request.path_info

request.path_info = “/ # {status}”

response = @ exceptions_app.call (request.env)

The key thing to realize here is that @exceptions_app is what ExceptionHandler has been built to manage.

Every time Rails raises an exception, the HTTP protocol still remains no matter what. In other words, you're always going to require sending an HTTP status code and message body to the requesting web browser … the difference lies in what you send.

The problem for Rails is that @exceptions_app defaults to the “routes” – which means that it will load 404.HTML or 500.HTML from the / public directory of your application. Whilst this works, the pages are unprofessional and static.

To change the pages, ExceptionHandler overrides the “@exceptions_app” hook with its own controller and views. This means that you're basically able to invoke “dynamic” web pages with Rails.

The way ExceptionHandler works is to use the application's “default” layout (typically “application”) for 4xx errors, and include a completely custom “exception” layout for 5xx errors. The custom layout is generally recommended as 5xx errors denote server issues, which means that if you're using a layout that references the database (as most “application” layouts do), it could cause an infinite loop to occur.

This means that if you have an error – say ActiveRecord can not load a record from the database – Rails will still invoke ActiveDispatch :: ShowExceptions.

However, due to ExceptionHandler overriding the “exceptions_app” hook, rather than the static 404.HTML / 500.HTML pages being called, the controller and views pipeline provided by the gem are invoked instead.