Rails is a favored Ruby web framework and a popular choice for building web applications. It has numerous awesome parts, many of which can be used independently of Rails. ActiveSupport, for example, is usable in any Ruby project to get some of the great features that Rails core provides. ActiveRecord is a standalone ORM, providing a sound way of dealing with different databases with a single codebase.
There is another component of Rails that is widely being used outside of Rails: the Asset Pipeline or, technically speaking, Sprockets. Sprockets is a Ruby gem developed by Sam Stephenson that provides support for asset compilation, asset minification, asset serving, and much more. In this article, we will discuss how Rails serves assets using Sprockets.
I would recommend reading How Asset Precompile Works, Part I and How Asset Precompile Works, Part II before proceeding. There are many concepts used here that are discussed in great detail in those articles.
This article is based on
Rails 3.2.17 and
Sprockets 2.2.2. Please install
Rails 3.2.17 first if you haven’t already.
Let’s answer the following questions related to Rails assets:
- How asset tags are inserted?
- What is
/assetsand it’s purpose?
- What is
ActionDispatch::Staticmiddleware used for?
How Asset Tags Are Inserted?
Source code paths have been mentioned both local and online, as appropriate. Two folders are of interest here, which can be discovered with the following commands in a Rails app:
bundle show actionpack bundle show sprockets
Use your editor to open each of these libraries so that you can following along in the source.
As of Rails 3.1, the default
application.js might look like:
//= require jquery //= require jquery_ujs //= require_tree .
actionpack/lib/action_view/helpers directory and
Sprockets is configured according to the Rails environment in
actionpack/lib/sprockets/railtie.rb. Railtie is the core of the Rails framework and provides several hooks to extend Rails and/or modify the initialization process. If we want to modify Rails behavior at any point during or after the initialization process, we can use
Sprockets extends Rails behavior by including the
Sprockets::Helpers::RailsHelper module after
action_view has been loaded. This means that the newly added module will override methods with same name that are already defined in modules under
Now it’s time for a little experiment. Create a new Rails application with a controller and set it’s route.
# creates a new rails application rails new rails_asset_serve # navigate to newly created rails directory cd rails_asset_serve # create a new controller named test with one method index rails generate controller test index
Go ahead and edit
app/views/test/index.html.erb and add some dummy text there. Now edit
config/routes.rb and add following line so that
test controller can be accessed from browser.
root :to => 'test#index'
Run the Rails server, navigate to
application.js, as mentioned above. Now the fun part. Comment out
actionpack/lib/sprockets/railtie.rb:46 and restart your server.
application.js. This is because the default Rails helper is not intelligent, and hence, has skipped dependencies. Sprockets is intelligent enough to resolve dependencies, leading to the links to all our JS source files.
Let’s see how
Sprockets performs this magic.
Don’t forget to uncomment
The Journey Begins
application.js as an example. In
line:7 you can see that the
Sprockets::Helpers::RailsHelper module has included the
ActionView::Helpers::AssetTagHelper is the main module that holds the default Rails helpers. By including them within it’s own module, Sprockets is ensuring that it still has access to all the default Rails helpers.
line:20 you can see
line:26 there is a
collect enumerator called which returns an array of script tags separated. In our case
sources will be pointing to
line:27 you can see an
if statement. If we are in the
development environment then
debug evaluates to
true. The other part (
asset = asset_paths.asset_for(source, 'js')) is a bit interesting. If you have read those two recommended articles that I mentioned above, then you will easily understand what is happening.
assets_paths is a method defined at
line:9 which returns an instance of class
AssetPaths defined at
line:117. There are a bunch of assignments in that method, but the one we are interested in is on
asset_environment is a method whose result is assigned to
asset_environment is defined at
line:113. This method is returning
Rails.application.assets which is an instance of
Sprockets::Environment, the top-level class responsible for everything to do with assets under Sprockets.
line:122, is an instance method which is called to get the desired asset.
asset_environment is responsible for returning an instance of
Sprockets::BundledAsset that points towards the desired asset (
application.js in our example).
 is a shorthand for the
find_asset method and is defined here.
asset_environment returns an instance of
Sprockets::BundledAsset containing all the dependencies of our asset. Iterating over these dependencies is done with the
to_a method, just as
Sprockets is doing on
line:29, there is a call to
super, which is the default Rails helper. Yeah, that’s right. Sprockets has done some clever things. First, it has included its module when
action_view gets loaded resulting in overriding those methods. Then, it included the default Rails helpers in its own modules. By calling
line:29 we are calling it and passing the necessary information and voila! The script tag for each dependency of
application.js is inserted.
Summarizing this process,
Sprockets first overrides the default Rails helpers, intelligently resolves asset dependencies, and then calls the overrided methods again to complete its job.
You might be thinking that the resolving of assets is carried out on each call to our Rails application, but this is not true. When we start/restart the Rails server, all asset resolution happens on the first call for those assets. The assets are cached and reused on subsequent calls until the assets are modified.
production environment, this process is straightforward. Let’s head back to
line:20, where the
When we are in
production environment, the
if statement on
line:27 evaluates to false because
debug is false. The
else case (
line:31 will be executed and call
super by passing the appropriate values. In
production, only one asset tag is inserted because all of it’s dependencies are merged into a single file by Sprockets (when we run
Sprockets this thing is replaced with one method and that is prepend all assets paths with
Rails.application.config.assets.prefix (which is /assets by-default) which is defined here. We all know that every asset in Rails application using
Sprockets have this format
line:94 there is a method
asset_prefix which is returning
We have seen how
Sprockets performs its magic to resolve dependencies and insert the appropriate asset tags. In a forthcoming article, we will answer the remaining two points mentioned at the beginning of this article. Until then, go tell all your friends that you have mastered Sprockets and asset tags!