Logo Devoh

Asset Pipeline Error Pages

Customizing the error templates in a Rails application is nothing new, but it's not something I've had to do very many times. When the need for customized error pages came up recently, I thought to myself, "Self, it would be really nice if I could use Haml and the asset pipeline for this." Here's how I was able to accomplish it.

Adding HTML to the Asset Pipeline

As our first order of business, there are two things that need to be accomplished within the asset pipeline:

  1. making it aware of HTML templates
  2. adding an engine for Haml template compilation

The first can be accomplished by adding the following lines to config/application.rb.

module RailsApp
  class Application < Rails::Application
    # HTML Assets
    config.assets.paths << Rails.root.join('app/assets/html')
    config.assets.register_mime_type('text/html', '.html')
  end
end

This adds a new asset search path, app/assets/html, where we can keep all our HTML templates, and it also adds the appropriate MIME type for the .html extension.

Teaching the asset pipeline to compile Haml templates is pretty straightforward, too. I already had a config/initializers/haml.rb in place, so I just added this to the end.

# Allow Haml assets in the asset pipeline.
Rails.application.assets.register_engine('.haml', Tilt::HamlTemplate)

Then, create the requisite error pages under app/assets/html. Here's what a 404.html.haml might look like.

!!!
%html{ html_attrs('en_us') }
  %head
    %title RailsApp — 404 Not Found
    %meta{ charset: 'UTF-8' }/
    = stylesheet_link_tag stylesheet_path('error')

  %body
    %header= image_tag('logo.png', alt: 'RailsApp')

    %section
      %header
        %h1
          = image_tag('alert.png', alt: '✗')
          Oops, we couldn’t find that!

      %p The page you’re looking for doesn’t exist.

    %footer
      %small 404 Not Found

There's a small weirdness here, in that we have to use the #stylesheet_path helper to ensure the asset host configuration is used and not just a relative path.

Next, you'll probably want to make sure the HTML templates are precompiled along with the rest of the assets. Adding this line to config/environments/production.rb will pick up all the HTML assets.

RailsApp::Application.configure do
  config.assets.precompile += %w(*.html)
end

Using the Asset Pipeline for Exceptions

Now that the asset pipeline is compiling our HTML assets, the only task left is hooking them into the standard exception handler in Rails.

The default exception handler serves the static HTML files found under public, but our error pages have found a new home under public/assets now. We can reconfigure the ActionDispatch::ShowExceptions middleware by swapping it out in config/initializers/exceptions.rb and giving it the new path.

RailsApp::Application.configure do
  config.middleware.swap(
    ActionDispatch::ShowExceptions,
    ActionDispatch::ShowExceptions,
    ActionDispatch::PublicExceptions.new(
      Rails.root.join('public/assets')
    )
  )
end

In this particular app, the assets are also being pushed to S3 with Asset Sync, so I've also created custom error and maintenance pages for Heroku using the same technique, but referencing the S3 URL instead (serving the assets from the app won't work when the app is down).

So far, this arrangement has worked out great. My only real complaint is that I can't use a layout template to be shared among all the different error pages. But I find the fact that I can use Haml and rely on other asset pipeline resources, like stylesheets and images, is enough of a gain for the additional configuration effort to be worth it.

and tagged with ruby