Asset Magic Tricks

June 21, 2013 by Alex Coco

What if you didn’t have to copy over assets between projects every time you wanted consistency? I learned a neat little trick that lets you package assets away into a Gem that can be bundled with any number of Rails applications.

I first realized this was possible when I tried the twitter-bootstrap-rails gem. This gem lets you use Twitter’s Bootstrap without needing to copy over or generate any files into your assets directory. Updating bootstrap across applications becomes as simple as updating the gem version. The files appear to be magically loaded and are somehow found when requiring them from a manifest file.

The way this works is with Rails Engines. Engines are essentially small Rails applications that get loaded within (and sometimes mounted to) your main application. Making your gem a Rails Engine is quite simple.

Creating the Gem

As an example, we’ll write a simple gem that will contain some styles to make h1 tags look nicer. We’ll call it pretty_h1.

First, start off by generating the new gem:

$ bundle gem pretty_h1

To set up your gem as an Engine, you’ll need to do two things. First, create the file /lib/pretty_h1/engine.rb and fill it with the following:

module PrettyH1
  class Engine < ::Rails::Engine
  end
end

Second, you need to require the file we just created by adding require 'pretty_h1/engine' to /lib/pretty_h1.rb.

Now that the gem has been set up to behave like a Rails Engine, it can take advantage of the fact that in an Engine, the /vendor directory is loaded automatically. Lets add a stylesheet in /vendor/assets/stylesheets/pretty_h1.css.

h1 {
  font-family: 'Helvetica Neue', Helvetica, sans-serif;
  font-size: 48px;
  font-weight: normal;
}

The last step before we can use our new gem is to complete the Gemspec. Don’t forget to add Rails as a dependency by adding spec.add_dependency 'rails', '>= 3.1' to your gemspec.

Using the Gem

To use the gem we’ll need to add it to a Rails application’s Gemfile. Either releasing the gem and get from RubyGems or use path to specify the path to the gem on the filesystem. Once it’s in our Gemfile, bundle it by running:

$ bundle install

Our app now has access to all the assets in our gem’s /vendor/assets/ directory! We can require the pretty_h1.css file from our Rails application’s application.css by adding a require in the manifest section of application.css:

 *= require 'pretty_h1'

After refreshing the page, you’ll notice that the style from our pretty_h1 gem has been applied to the h1 tags.

More Magic

This magic doesn’t stop at assets. Rails Engines are powerful and let you add a lot of functionality to an application. A great example of Rails Engines being put to good use is plataformatec’s devise.

Devise makes use of an Engine’s ability to add a gem’s files to the load path so that they become available to the application without copying them over. More specifically, devise adds models, controllers, and views to the load path. This allows developers to make use of ready-made chunks of applications in their own apps.