55 Minutes

Welcome to the 55 Minutes blog.
55 Minutes is a web development consultancy in San Francisco building apps for design-led businesses and startups. On this blog we discuss the technology tools of our trade: Ruby on Rails, Django, Sass, OS X, and more.

Untangling the Rails Asset Pipeline Part 3: Configuration

The asset pipeline feature introduced in Rails 3.1 is not so simple once you start tweaking the settings. This article takes a look at some of the trickier aspects of asset pipeline configuration, along with a practical example of testing production settings without a web server.

You’re reading the third installment of a four-part series on the Rails asset pipeline. The previous entries are Part 1: Caches and Compass and Part 2: Production.

Making sense of the asset pipeline-related settings

By default, Rails scatters the configuration of the asset pipeline in a few different places. Also confusing is that sometimes the term “asset” does not refer to the asset pipeline specifically. Finally, many settings are interrelated. Here’s a quick reference to three of the trickiest:

1. config.assets.compile

config.assets.compile is true in development and test environments, and is what tells Rails to compile (and cache) the contents of app/assets/ on the fly whenever the browser makes a request for a script or stylesheet. It is false in production, meaning assets in production must be precompiled.

Furthermore, it is worth noting that Rails does more than just disable compilation in production: by default, Rails goes one step further and does not even load asset pipeline-related gems in production. This is a nice optimization, but it means that should you ever want to enable asset compilation in production, it is not just a simple matter of setting config.assets.compile=true.

Most applications will have no reason to do so, but if for some reason you do want to enable asset compilation in production, you will need to perform two steps: first set config.assets.compile=true, then alter the Bundler.require section in application.rb:

if defined?(Bundler)
  # If you precompile assets before deploying to production, use this line
  # Bundler.require(*Rails.groups(:assets => %w(development test)))
  # If you want your assets lazily compiled in production, use this line
  Bundler.require(:default, :assets, Rails.env)

2. config.serve_static_assets

config.serve_static_assets predates the asset pipeline; the “assets” in serve_static_assets therefore does not mean exactly the same thing as “assets” in the asset pipeline. Instead, this setting controls whether or not Rails serves the static files located the public/ directory. These might be precompiled asset pipeline “assets”, or they may just be static files you’ve decided to put there, like public/favicon.ico.

When set to true, Rails will install a middleware that checks if each browser request matches a file in the public/ directory. If so, it responds with the matching file, and your routes and controllers will not be used. Since this middleware adds a certain amount of overhead to processing each request, it is important to set config.serve_static_assets=true only if necessary.

This setting is true in development and test environments. In production it is false, because a web server like Nginx will handle serving public/ files.

3. config.assets.precompile

config.assets.precompile controls which assets are precompiled when you run the assets:precompile rake task. By default, Rails only precompiles application.js and application.css (or their coffee, erb, sass, etc. versions). These files are often called application “manifests”, because they will contain a bunch of //=require or @import statements. Rails will compile these referenced files as well.

But scripts and stylesheets not referenced by these manifests will not be compiled! Consider the case where you have factored Internet Explorer-specific styles into a separate ie.css file. It is fairly common to link to this in a conditional comment:

<!--[if IE]>
<%= stylesheet_link_tag "ie" %>

This will work in development, but when you deploy to production it will fail, because ie.css will be skipped during assets:precompile. This is where config.assets.precompile comes in:

config.assets.precompile += ['ie.css']

Regular expressions are also possible, as we explain in Getting Compass to work with Rails 3.1 (and 3.2):

# Precompile *all* assets, except those that start with underscore
config.assets.precompile << /(^[^_\/]|\/[^_])[^\/]*$/

Testing in production without a web server

As explained in the previous article of this series, normally an asset pipeline-enabled application will not work at all in production without a web server. But what if we want to test an application before the web server is set up? Or maybe we want to run our code in production mode locally without a web server.

The solution is to tell Rails to play the role of the web server, which is to say: serve static files from the public/assets directory.

Precompile assets as you would normally. Remember, asset pipeline-related code and gems are disabled in production, so we have to compile ahead of time.

bundle exec rake assets:precompile

Tell Rails to serve static files. In production.rb:

# Serve public/* without a web server
config.serve_static_assets = true

Now start your application in production mode:

rails server -e production

Your application is now up and running sans web server; all CSS and JS will be served by a Rails middleware using the precompiled files in the public/assets directory.

Continue reading part 4 of this series: Untangling the Rails Asset Pipeline, part 4: Troubleshooting.

comments powered by Disqus