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.

Getting Compass to Work With Rails 3.1 (and 3.2)

The asset pipeline introduced in Rails 3.1 promises many benefits, but can be frustrating to implement in practice. Integrating the Compass framework makes it even more complicated. Here’s what has worked for us.

Updated February 3, 2012: Literally days after this article was originally posted, the Compass and Rails landscape changed yet again with the introduction of the compass-rails gem. We’ve revised the instructions below to use the very latest technique.

If you need some background on Compass, refer to our earlier post: Compass Will Change Your Life.

Where is the official Compass documentation?

Official documentation for installing Compass in a Rails 3.1 or 3.2 application can be hard to find. In fact, the instructions on the official Compass site are way out of date.

Luckily the owner of the Compass project posted a Gist on GitHub last September with some tips for installing Compass in Rails 3.1. But as of February 2012 even those instructions have become out of date. Now your best bet is to use the new compass-rails project README on GitHub.

How we install Compass in a new Rails 3.1/3.2 project

We’re documenting in this article the Compass/Rails installation steps that have worked for us. These have been tested in development and production with real apps.

1. Add compass-rails to the Gemfile

The compass-rails gem is the critical “glue” that connects Compass to the Rails asset pipeline. This gem should be placed in the :assets group of your Gemfile. You should not include the compass gem explicitly; you’ll get it automatically via compass-rails. If you use any Compass plugins, like sassy-buttons, add them here too. Run bundle install when you’re done.

group :assets do
  gem 'compass-rails','~> 1.0.0.rc.2'
  gem 'compass-colors'
  gem 'sassy-buttons'
  gem 'sass-rails', '~> 3.2.3'
  # non-compass gems omitted for brevity
end
Gemfile – View Gist

2. Delete application.css and replace with application.scss

The asset pipeline relies heavily on file extensions to work its magic, so it is important to use the name application.scss: the scss extension tells Rails you want to use Compass. Also note that .css and .scss files differ in how they import other stylesheets. In scss files, always use @import. Here’s how your application.scss file should look:

/*
 * application.scss
 *
 * Important! Do *not* use Sprockets "require" syntax.
 * Use @import to include other stylesheets and Compass mixins.
 */

// Include some nice mixins
@import "compass/css3";
@import "sassy-buttons";

// Example: import partial stylesheet named "_users.scss"
@import "users";
application.scss – View Gist

3. Create compass.rb (optional)

The default configuration of Compass will work nicely with Rails. If you want to tweak Compass settings, you’ll need to create a config/compass.rb file in your Rails app. It’s probably a good idea to create this file, even if it is empty, so other developers (and your future self) know where Compass configuration is supposed to go.

Here at 55 Minutes, there is one important setting that we always use with Compass. We use our css3-foundation project, which organizes scss files into subdirectories. Compass won’t allow us to load partial scss files from these directories unless we explicitly add them to the Compass load path. So our configuration looks like this:

# config/compass.rb
additional_import_paths = ["app/assets/stylesheets/basics", "app/assets/stylesheets/shared"]
compass.rb – View Gist

Now we can write @import "grid" and Compass will successfully find the file basics/_grid.scss.

4. Use image-url

The Rails asset pipeline will do some fancy stuff like automatically add cache-busters to all of your images (e.g. logo-small-fd1a40e11357a70159b4342f33ee30e2.png). During development you can’t predict what those cache-busters will be, so how do you reference images in your stylesheets?

The answer is to use the image-url helper. Always use image-url() instead of plain CSS url() in your stylesheets. For example:

.logo
{
  background: image-url("logo-small.png") no-repeat;
}
example.scss – View Gist

5. Follow the standard asset pipeline file organization

Per the asset pipeline guide, your scripts, stylesheets and images should all be placed in the app/assets/ directory. This applies to stylesheets written in scss as well. The directory structure should look like this:

.
├── Gemfile
├── app
│   └── assets
│       ├── images
│       │   └── logo-small.png
│       ├── javascripts
│       │   └── application.js
│       └── stylesheets
│           ├── _users.scss
│           └── application.scss
└── config
    ├── application.rb
    └── compass.rb
tree – View Gist

That should do it. You’re ready to use Compass to develop your app!

But wait, there’s more: deploying to production

A full explanation of Rails deployment is beyond the scope of this article, but there is an important step that relates specifically to Compass and the asset pipeline.

When deploying your app, run bundle exec rake assets:precompile on the Rails app server before you start it up. By default, Rails will not serve stylesheets, scripts or images in production, so these need to be assembled ahead of time. Running the assets:precompile task tells Compass to compile the scss; Rails will also add cache-busters, apply compression, and stage everything into the public/assets directory. Your web server (Nginx, Passenger) will then do the heavy-lifting of serving those out to clients.

Precompilation is tricky, though. One thing that can go wrong is stylesheets other than application.scss will not get precompiled at all. For example, let’s say you have a special 1024.scss file for serving to desktop browsers. By default, that file will not get compiled, leading to catastrophic errors at runtime. You might see:

A ActionView::Template::Error occurred in sessions#new:

1024.css isn't precompiled
.bundle/bundle/gems/actionpack-3.2.0/lib/sprockets/helpers/rails_helper.rb:142:in `digest_for'
digest_for-error – View Gist

Or, if you followed older recommendations for Compass and Rails integration, you might find that the rake assets:precompile task fails with a rather obtuse error. For example, you might see this:

$ bundle exec rake assets:precompile
/Users/mbrictson/.rbenv/versions/1.9.3-p0/bin/ruby script/rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets
rake aborted!
Undefined variable: "$body-text-line-height".
  (in /Users/mbrictson/Work/rails-project/app/assets/stylesheets/basics/_grid.scss)

Tasks: TOP => assets:precompile:primary
(See full trace by running task with --trace)
rake aborted!
Command failed with status (1): [/Users/mbrictson/.rbenv/versions/1.9.3-p0/...]

Tasks: TOP => assets:precompile
(See full trace by running task with --trace)
rake-output – View Gist

To fix these problems and finish your Compass installation, you’ll need to take one more step.

6. Specify config.assets.precompile in application.rb

We need to explicitly tell Rails which scss files to compile, using the config.assets.precompile setting. We can either list each scss file individually (a maintenance headache for large projects), or we can provide a regular expression to match them all.

Be careful with the regular expression, though: if the expression is so broad that it captures partial scss files (i.e those that start with an underscore), Rails will try to compile those files individually, leading to errors. For example, in the error message above, we mistakenly told Rails to precompile _grid.scss, where it blew up.

The solution is to tell Rails to ignore files that begin with the underscore character when running the precompile task, but include everything else. Chris Eppstein’s original Gist suggested a regular expression for this purpose but it was incomplete: his regex ignored underscore files at the root level of the assets directory, but it failed to ignore underscore files in subdirectories (like our basics/_grid.scss).

Here’s a more bulletproof regex. Place this in application.rb:

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

Now rake assets:precompile will do the right thing. You’re all done!

Send us your feedback by commenting on the Gist for this post on GitHub.

comments powered by Disqus