Ali vs The Rails Asset Pipeline

on Monday, October 13, 2014

Your hero has been missing for some time, but it’s not because he wasn’t busy fighting battles. I was gone in a far away land fighting the Rails Asset Pipeline.

The Asset Pipeline can help make your apps fast and responsive in production environments, but configuring your app to use the Asset Pipeline in production environments is…fun. A lot of people will completely skip over the Asset Pipeline because it can be such a pain in the ass. I think that’s a mistake. Rails is so great because it gives you so many things out of the box for free. So instead of being afraid of the Asset Pipeline, embrace it! It’s actually pretty cool when it works.

Configuring the Asset Pipeline is difficult because it behaves differently in development and production environments. In production enviornments, the Asset Pipeline will do things like minifying JavaScript files and CSS stylesheets to improve perfomance, but I’m not interested in reading minified files when I’m trying to debug something in development. The Asset Pipeline is smart enough to know this and it can give you the things you need to make development easy while also optomizing for production. However, this can lead to confusing bugs when you deploy to production.

To see this confusion in action, let’s say you want to add a search form to a page. The search form would be really cool if it had a search icon to submit the form.

Google Search

There are so many ways you can go about doing this, but I want to do it with CSS. But really I’m going to do it in Sass.

$blue: #28A5C6;

.search-form {
  button {
    background: url('/assets/search.png') $blue no-repeat;
  }
}

I defined a rule for the .search-form class. Any button inside an element with the .search-form class will have the search icon as its background. I put the search icon, search.png in app/assets/images. I’m running everything locally in development, so I’ll get a pretty search form.

A pretty search form

Now, let’s deploy this bad boy to production!

A broken search form

WTF

ASSET PIPELINE!!!

The icon is no where to be found. If we look at the server log, we get a nice error message:

ActionController::RoutingError (No route matches [GET] "/assets/search.png")

Looking back at my button stylesheet rule, I set the background image using CSS’ url function.

background: url('/assets/search.png') $blue no-repeat;

This worked in development, because all assets can be accessed from the assets path. But then why didn’t it work in production? Since I’m using the Asset Pipeline, the app’s assets go through asset precompilation when the app is deployed to production. Asset precompilation is the process by which Rails does all its magic that make apps so fast in production. Part of this process involves creating a cache for assets. Caching is great because we can serve assets faster, and the Asset Pipeline is even better because we don’t have to create caches!

So why can’t I see the search icon in production? When the Asset Pipeline caches assets, it adds a hash to the asset filename. So search.png becomes something like search-14cfc520e5db1343e158a783486eeba3.png. The error message says Rails can’t find search.png, and that’s right because search.png doesn’t exist. Thanfully, Rails can handle this. Instead of using CSS’ url function to include the image, we can use one of Rails’ asset link helpers.

background: image-url('search.png') $blue no-repeat;

The image-url helper is smart enough to serve the regular image in development and the cached image in production. Let’s change that line of code and deploy to production!

A pretty search form

Yay! It worked!

Protip 1: Use Rails’ asset link helpers

Now we’re ready to have some real fun with the asset pipeline! Let’s say I want to change the color of the search icon. To do that, I’ll update the image.

New search icon

Cool, looks good in development. Let’s see what happens when I deploy to production…

A pretty search form

WTF

OMG ASSET PIPELINE!!!

What happened?! I did everything DHH told me to!

When I deployed to production, I had to run asset precompilation again. The Asset Pipeline saw that the image changed, and it created a new version of the cached image. But my stylesheet didn’t change so it’s still referencing the old version of the cached image.

Once again, Rails can handle this! We just need to tell Rails that my stylesheet depends on another asset. At the beginning of the stylesheet, we can add the following line:

//= depend_on_asset 'search.png'

Let’s deploy this change to production and see what happens…

New search icon

Yay! The Asset Pipeline is the best!

Protip 2: Tell Rails when an asset depends on another asset

That last case is tricky and really hard to detect, so let me introduce you to Sprockets Better Errors. Sprockets Better Errors is great because it will complain when you do things in development that won’t work in production. If I had Sprockets Better Errors configured for my development environment, instead of everything working I would’ve seen an ugly exception:

Asset depends on 'search.png' to generate properly but has not declared the
dependency
Please add: `//= depend_on_asset "search.png"` to '.../some_stylesheet.scss'
  (in .../some_stylesheet.scss)

It tells you what the problem is and tells you how to fix it!

Protip 3: Use Sprockets Better Errors

If you follow my three Protips™, you should have a more pleasureable experience with the asset pipeline.

I cannot guranntee you will have a more pleasureable expierence the asset pipeline

This post was inspired by work I did on Baltimore Public Art Commons. Check it out!

Back