homeASCIIcasts

279: Understanding The Asset Pipeline 

(view original Railscast)

Other translations: Ja Es Fr

The asset pipeline is one of the biggest new features in Rails 3.1 but it can also be the most confusing. In this episode we’ll attempt to demystify it a little by showing how it manages your Rails applications’ assets. If you’re completely unfamiliar with it a good place to start is the a Rails Guide on the asset pipeline as this covers a lot of its features.

If you’ve written any Rails 3.1 applications you’ll probably know that if you visit http://localhost:3000/assets/application.js you’ll get a file containing all of your application’s JavaScript. But how does this work?

There’s nothing special about the application.js file; any file that is put under the /app/assets/javascripts directory is just as accessible. If, for example, we create a file in that directory called greeting.txt we can view it in a browser in a similar way at http://localhost:3000/assets/greeting.txt. Even though the file is under /app/assets/javascripts the URL we access it from is /assets/greeting.txt. This applies no matter what subdirectory under /app/assets we put the file in. We can even create a new directory and put the file in there and it’ll still be available at the same URL. If we do create a new directory however, we’ll need to restart the server before any files we put in there are accessible.

The /app/assets directory isn’t the only place that we can add assets. If we create an assets directory under /lib any files we add there will be accessible from there as if they were in the main /app/assets directory. This also applies to any files under a /vendor/assets directory.

If you have assets that are not really specific to the current application then assets directories under either /lib or /vendor are ideal places for them. If our app uses a jQuery plugin then the /vendor/assets directory is a good place for its JavaScript files as these are something that’s maintained by someone else. For assets that we do maintain but which aren’t specific to our application the /lib directory is a good place.

At its most basic the asset pipeline is a list of loadpaths. We can see this list by running the console and viewing Rails.application.config.assets.paths. We’ll view the output as YAML to make it easier to read.

> y Rails.application.config.assets.paths
--- 
- /Users/eifion/store/app/assets/images
- /Users/eifion/store/app/assets/javascripts
- /Users/eifion/store/app/assets/stylesheets
- /Users/eifion/store/lib/assets/greeting.txt
- /Users/eifion/store/vendor/assets/stylesheets
- /Users/eifion/.rvm/gems/ruby-1.9.2-p180@railspre/gems/jquery-rails-1.0.13/vendor/assets/javascripts

The output shows every directory under the app/assets directory and also those under /lib/assets and /vendor/assets. There’s an interesting directory at the end of the list which comes from the jquery-rails gem that we’ve included in our application. We can look at its contents with the bundle open command.

$ bundle open jquery-rails

This opens the gem with the text editor defined by our shell’s BUNDLER_EDITOR or EDITOR environment variables. If we look at the gem’s files we’ll see an vendor/asset/javascripts directory containing a number of jQuery files that we can load through the asset pipeline.

The file and folder structure of the jquery-rails gem.

As you might expect we can access any of these files in a browser under the assets path as the directory they’re in is in the asset pipeline’s loadpath.

The jquery.js file is accessible under the assets folder.

This is interesting because it means that Ruby gems are no longer just about managing Ruby code. We can use them to manage Javascript and any other assets in them as well. It’s likely that we’ll soon see more JavaScript libraries being distributed as Ruby gems so that they can have all of the benefits of Bundler and the dependency management it provides.

Managing Assets With Sprockets

Let’s go back to our application’s application.js file now and take a look at it.

/app/assets/javascripts/application.js

// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery
//= require jquery_ujs
//= require_tree .

The file only contains comments but some of these are significant. This type of file is known as a manifest and it’s managed internally by Sprockets. When a request comes in for this file Sprockets looks at the manifest and compiles together every file that is mentioned in it and includes their contents before any code in this file.

The loadpath works here as well. We have require jquery in this file (the extension .js is optional and can be left off). Sprockets will search the loadpath for this file and, in this case, load it from the jquery-rails engine’s vendor/asset/javascripts directory.

We can add any JavaScript file that’s in the loadpath here. So if we add require jquery-ui to the file the gem’s jquery-ui.js file will be included. This applies to CoffeeScript files too; if we include require home the /app/assets/javascripts/home.js.coffee file will be parsed and included.

Including that home file isn’t necessary, however, as at the bottom of the file we have require_tree . and the dot here represents the current directory. This means that every JavaScript or CoffeeScript file in that directory and its subdirectories will be included.

If we want to exclude certain files from the tree we can do so. Let’s say that we have some admin pages on the site and some JavaScript files that should only be included when we’re viewing one of those pages. By default these files will be included on all of the site’s pages.

If we want to see the files that are included we can add a debug_assets=1 parameter to the URL. This will stop the JavaScript files from being combined and when we view the page’s source we’ll see all of the files that Sprockets would include, including the file in the admin directory.

The JavaScript files are not combined if we add the debug_assets parameter to the query string.

There are a couple of ways that we can get around this problem. We could use require_directory instead of require_tree as this will only load the files in the current directory and not in subdirectories. If we want more control over the included files we can require them separately instead of including the whole directory. Alternatively we could move the JavaScript files that we want to be included on all pages into a public subdirectory. We can then use require_tree ./public to include just those files.

You might be wondering what commands can be passed into the Sprockets manifest. There isn’t a good source of documentation yet but within the directive_processor.rb file in the source code are comments that explain how it works and the various commands that can be passed in.

Preprocessing

The asset pipeline also handles preprocessing. To show how this works we’ll create a file called greeting.txt in a new /app/assets/anything directory. As it stands this is just a static text file but we can add another extension to the file name and specify a processor, for example .erb. We can now add ERB code to this file and it will be processed.

/app/assets/anything/greeting.txt.erb

hello world <%= 1 + 1 %>

If we look at this file in a browser we’ll see that the ERB code has been processed. Note that we don’t include the preprocessor extension in the URL.

The erb code is preprocessed before being sent to the browser.

This is basically how SASS and CoffeeScript work. When a file has an .scss extension this is treated as a preprocessor extension and the file will be passed through the SASS processor. We can even chain the extensions and create a file with, for example, an .scss.erb extension. This file would first be passed through the ERB processor and then the SASS processor.

The preprocessor is very configurable. We can add our own processors or swap out the existing ones. This is all handled by the Tilt gem and there’s more information on how it works and the extensions you can use there.

Differences in Production Mode

That’s it for our quick walkthrough of the asset pipeline’s features. There are some differences in how the pipeline works in production mode and we’ll spend the rest of the episode covering these. First, we’ll start up the server in production mode.

$ rails s -e production

If we visit our application’s homepage now and view the source we’ll see that our assets are delivered differently.

<link href="/assets/application-412fe22651f4486c51e54176003a9f57.css" media="screen" rel="stylesheet" type="text/css" />
  <script src="/assets/application-3e3a5167191afa70c7b72440eee7dd40.js" type="text/javascript"></script>

The filenames now include a hash and this is done for caching purposes. This works much better than the old method of adding a querystring that Rails 3.0 uses as it actually changes the filename. Also, if we look at the file itself we’ll see that the JavaScript is minified, saving bandwidth.

These assets are automatically cached and served by the Rack Cache middleware so they’re pretty fast. If we want to have the webserver itself handle serving and hosting the assets instead we can precompile them by running

$ rake assets:precompile

This will precompile the assets into the /public directory so that they’re easy accessible by the web server.

That’s it for this episode on the asset pipeline. Don’t forget to checkout the Rails Guide for more information on it.