Patrick Ward words, code, and music

How I Use Compass With Rails 3.1

For one of my ongoing projects, I’m using the latest version of Rails (3.1.0.rc6 as of this post) along with the Compass CSS framework. At first, this turned out to be an adventure in maintaining a calm composure. But, my experience of the Rails Asset Pipeline coupled with the Compass framework is proving to be worth the effort.

My First Approach

My first approach to the problem was to keep them completely separate. I generated a Compass project outside of the asset pipeline folders and set it’s config parameters to generate CSS files within the normal app/assets/stylesheets folder under rails. The setup was essentially like this:

    RAILS_ROOT
      + compass/
        + sass/
          style.scss
          _layout_partial.scss
          _some_other_partial.scss
          _etc...
      + app/
        + assets/
          + stylesheets/
            application.css
            style.css

Using Guard or compass watch on the command line I would essentially pre-compile the Compass CSS files. Using the default Rails application.css file, Sprockets would then pick up the pre-compiled style.css file for use within the Rails framework.

While this worked, it was hackish and required additional steps to complete. Thankfully, the Rails, sass-rails, and Compass projects have all come a long way in the past few months.

My New Approach

I’ve since abandoned the idea of using Compass outside of the asset pipeline and now use it coupled with Sprockets own require system. In addition to the latest versions of the Compass and Rails projects, an understanding of how Sprockets and the asset pipeline work is essential to making this work.

Use the right version of Compass

To begin with, I found it necessary to use the git version of the rails31 branch of Compass, which seems to have some fixes for using it with Rails 3.1 conventions. Here’s a summary of the key Gemfile adjustments I’ve made recently to get things kickstarted.

gem 'rails', '3.1.0.rc6'

group :assets do
  gem 'sass-rails', "~> 3.1.0.rc"
  gem 'coffee-rails', "~> 3.1.0.rc"
  gem 'uglifier'
  gem 'compass', :git => "https://github.com/chriseppstein/compass.git", :branch => "rails31"
end

Sprockets vs Sass vs Compass

At this point, I needed to understand the key differences betweeen Sprockets, Sass, and Compass.

Sprockets is essentially a pre-processor and rails uses it to stitch together your CSS files into a single, monolithic stylesheet. While it recognizes and precompiles (or filters) Sass files, it’s require system does not understand the concept of Sass partials or mixins through the use of imports and includes. So, you have to separate the logic used in Sass from the logic used in Sprockets. There is a linear process that you must be aware of when working with the two that looks something like this:

  1. Rails starts up the asset pipeline (or sprockets), which begins to read in the “required” files you specified
  2. When it finds a .sass or .scss file in it’s require path, it passes that file off to Sass for rendering. (This is true for any file that requires additional processing, such as .erb files.)
  3. Sass then uses it’s own rendering engine to import, include, and process partials, variables, etc. This is separate and outside the scope of what Sprockets does. So, if you want to reference Sass variables or mixins in different files, you need to ensure that the proper Sass @import calls have been made in those files.
  4. Sprockets then stitches all of the rendered files it finds back into it’s own process using the separate logic of its require system.

The key distiction here is that Sprockets does not render Sass files, it filters them through Sass before including them back into it’s own require system. They are separate and distinct in their knowledge of each other.

For Compass, remember that, at heart, it is a collection of Sass mixins. When used this way, you can create a folder structure that keeps Sass and Sprockets logically and visually separated.

Separating Sass from Sprockets

What helped me was physically separating the Sass based code from the Sprocket requires. This is not the standard way that the Rails 3.1 generators expect you to use the asset pipeline, but it made more sense to me for several reasons:

  1. I tend to organize my Compass/Sass files in heirarchical order which depends on a liberal use of @import and @include for mixins
  2. I felt I had more control over the order of requires and the process that Sprockets uses to include files.
  3. I liked the visual and physical separation of CSS code

The folder structure for my most recent project ended up looking similar to the following:

    RAILS_ROOT
      + app/
        + assets/
          + stylesheets/
            application.css
            + application/
              application.css.scss
              _base.scss
              _buttons.scss
              ...
              _layout.scss
              _menu.scss
              _users.scss
              etc...
            + aristo/
              jquery.ui.all.css.scss
              _base.scss
              _button.scss
              etc..

This enabled me to separate key areas of the application’s CSS and use the Compass framework, along with the full capabilities of Sass, without worrying about how Sprockets was going to pull in each of my files.

Removing require_tree

Rails puts a few magical requires into the default application.css, which at first seem perfectly reasonable, until you start using it the way I’m describing here. Then, it becomes a bit of a hassle.

So, my first order of business was to remove the require_tree directive from my application.css file. That directive requires all stylesheets from the current directory at the location of the directive. Since I am trying to have more control over how stylesheets are ordered in my application, that directive doesn’t help me. So, out it goes and in go more specific requires based on my folder structure above.

Adding application.css.scss

I like to structure my Compass files using a primary css file that pulls in additional partials as needed. This led me to creating the application.css.scss file underneath the application subdirectory: application/application.css.scss

Rather than use Rails generated scss files (e.g. users.css.scss for a users_controller), I opted to use partials underneath the application directory and pull them into the main application.css.scss file instead. This gives me greater control over the order of files that are imported and let’s me use Compass and Sass the way that I’m used to using them. In fact, when Rails generates the users.css.scss file, I just delete it before I make a commit to git.

As you can see, the application.css.scss file becomes a mid-level processor for my Compass structured css:

// Framework
@import 'compass/reset';
@import 'compass/css3';
@import 'susy';
// Application
@import 'base';
@import 'defaults';
@import 'layout';
// etc...
// Controllers
@import 'users';

I can now use Compass and Sass the way I like to without worrying about how the Rails asset pipeline is going to see them.

But, What About the application.css file?

Let’s not forget that we’re still using Sprockets. As I mentioned earlier, I take out the require_tree directive and replace it with specific requires for the Sass files I’m interested in including into my manifest.

In my case I’m adding both an application css file along with a jQuery UI file I converted to Sass for use in my application.

I keep the require_self directive, which leaves my application.css file looking similar to the following:

/*
 *= require_self
 *= require "aristo/jquery.ui.all"
 *= require "application/application"
 *= require "some_additional_css_file"
*/

At this point, I can add additional plain css files, Sass files, vendor files, etc. all within the boundaries of how my application expects them to be ordered (i.e. required). An example of that is the use of my customized jquery.ui.all file that I’m requiring outside of my Compass framework.

Summary

This approach allows me to use the best of Compass and Sass along with the flexibility (and Rails oriented way) of the asset pipeline (or Sprockets), but without having to sacrifice the tried and true method that Compass users are used to.

Condensed into a few easy steps, the process is:

  1. Use the rails31 branch of the Compass gem in your Gemfile.
  2. Separate your application’s Compass/Sass files from Sprockets (e.g. I use a folder structure)
  3. Remove the Rails generated css.scss files and use partials with Compass instead
  4. Remove the require_tree directive from application.css
  5. Add your additional Sass or CSS files using regular require directives in application.css