![Abstract tooth wheels](https://uploads.sitepoint.com/wp-content/uploads/2013/12/Fotolia_51801972_Subscription_XXL-256x300.jpg)
Web applications are becoming faster and faster by each passing day. One of the most important features to keep users coming back is speed. If a web application is slow and takes too much time to load, then it will lose it’s users. If a web application is fast then its users will increase. Developing a slow web application is not an option nowadays.
There are many factors that affect web application performance and an important one is loading assets such as images, CSS files, JavaScript files etc. Every web application contains some images and CSS files to make it look pretty, along with some JavaScript files to handle user interaction and behavior. If assets are loading faster, the web application ought to perform better. There are many strategies to make assets load fast such as minifying, compressing (gzipping), caching etc.
There are a few frameworks that have built-in functionality to perform the aforementioned operations on assets. We (Ruby Community) love Rails and Rails has built-in support for performing different operations on assets as of version 3.1. Thanks to @sstephenson and @joshpeek for the great effort in developing and maintaining Sprockets. Sprockets is a Ruby Gem that touts itself as a “Rack-based asset packaging system”. Sprockets can be used with any Rack-based web framework such as Sinatra, Padrino etc. Sprockets comes built-in as of Rails 3.1 and is considered as an integral part of Rails.
To learn about Sprockets there is an excellent Rails Guide available. I encourage you to read the Rails Guide on Sprockets before proceeding with this article. In this article, we will be looking at how the asset pipeline works in Rails and the precompilation of assets work.
rake assets:precompile
We use rake assets:precompile
to precompile our assets before pushing code to production. This command precompiles assets and places them under the public/assets
directory in our Rails application. Let’s begin our journey by looking at the internals of the Rails Asset Pipeline. This article is based on Rails 3.2.15
and Sprocket 2.2.2
. If you haven’t installed Rails 3.2.15
then install that first.
I’ am using RVM to manage my gems. After you have installed Rails 3.2.15
it’s time to get to the real stuff. As I told you, we will be reading the internals of the Rails asset pipeline, so it would be awesome if you open the Rails gem directory and Sprockets directory in your favorite editor. I will provide the full GitHub path to different code sections, so you can also view the code on GitHub.
Let’s create a test Rails application first. We will be using this application to see and adjust various configuration options and to see our precompiled assets. To create a Rails application we use following command:
rails new asset_pipeline_test
You can use any name you like in-place of asset_pipeline_test
. cd
into the newly created Rails application and issue the following command to find the path to the Rails gem directory (I am assuming you have installed the necessary gems by issuing the bundle install
command).
bundle show rails
This command will return the path of the Rails gem directory. Run the same command, changing rails
to sprockets
to get the path to the Sprockets gem directory.
Now I have a surprise for you. Go ahead and check the contents of the Sprockets gem directory by navigating to the path from running those commands. You will see different files in the Sprockets gem directory.
Now navigate to the Rails gem directory and you might be surprised that this directory is empty :-). I was a bit shocked by seeing an empty directory. We all know that each gem has a directory and all of it’s files reside there. If this is true, then where are Rails gem files??? Rails 3 was a major refactoring and things are now much more organized as compared to Rails 2.x. The Rails gem files are in the railties-3.2.15
directory so instead of using rails-3.2.15
we will be using the railties-3.2.15
directory to inspect different parts of Rails.
Also open actionpack-3.2.15
in your favorite editor, because it has a directory sprockets
which contains necessary code to run Sprockets from Rails.
Sprockets::Environment
According to Sprockets, we need an instance of the Sprockets::Environment
class to access and serve assets from your application. Let’s see where this is defined in the code.
Here is the code responsible for creating an instance of Sprockets::Environment
for our Rails application. You might be wondering why this code is in railtie.rb
. The answer to this question is very simple.
As I mentioned earlier, Rails 3 was a major refactoring (thanks to the Rails core team for their awesome efforts). Rails::Railtie
is the core of the Rails framework and provides several hooks to extend Rails and/or modify the initialization process. Railtie provides us with several hooks to perform our desired functionality. We want a Sprockets::Environment
instance available to our application before any other code is executed, so we extend our Sprockets::Railtie
class from Rails::Railtie
. In the code block on line:23, we are assigning the instance of Sprocket::Environment
to app.assets
. app
is an instance of our Rails app and you are familiar with it in the form of Rails.application
.
We have two versions of assets. app.assets
and config.assets
. config
is an instance of app.config
which was assigned to config
in line:18. config.assets
in the Sprockets::Railtie
‘s context or Rails.application.config.assets
context is a configuration object that is used to hold various configuration options for Rails.application.assets
.
In review, Rails.application.assets
is an instance of Sprockets::Environment
and Rails.application.config.assets
is a configuration object.
Rails.application.config.assets
is defined here. As you can see, it contains configuration options that we use to set up out different environments from different files from our Rails application.
Now, we know where the instance of Sprockets::Environment
is created and what is the difference between Rails.application.assets
and Rails.application.config.assets
. Let’s deep dive into the internals of Sprockets.
Types of Assets
There are following three types of assets as per Sprockets
- Bundled Assets
- Processed Assets
- Static Assets
Static Asset
We will be looking in more detail about Bundled
and Processed Assets
in Part 2. Here, we’re discussing Static Assets
. Static assets are assets that don’t need any extra processing. They are just created with a new name. Images, for example, are Static assets. Let’s take a look how Static assets are precompiled and saved.
Navigate here and you will see the method build_asset
. build_asset
is used to build an asset (duh). logical_path
is the name of asset i.e. rails.png
and pathname
is something like /home/ubuntu/Desktop/work/asset_pipeline_article/app/assets/images/rails.png
(your path will be different based on the root
of your application).
At line:381 you will see following line
StaticAsset.new(index, logical_path, pathname)
StaticAsset
is defined in the Sprockets gem under the lib/sprockets
directory. StaticAsset
is a subclass of Asset
. Asset
is the base class for all types of assets.
Navigate here to see the initialize
method for the Asset
class. In the build_asset
method, we have seen that an instance of StaticAsset
is being created. When an instance of StaticAsset
is created, the initialize
method of Asset
is called. For StaticAsset
, a digest is created and a new file is saved with new name which contains file_name-digest.file_ext
. The digest is created based on the contents of file.
Here, the file is saved. This method is responsible for saving assets to disk, and is pretty self-explanatory.
Run rake assets:precompile
from the command-line from the root of your Rails application. Look at the public/assets
directory and you’ll see the assets of your Rails application. Our interest at this point is rails.png
.
Notice that there are two versions of rails.png
: One named rails.png
name and another named rails-a3386665c05a2d82f711a4aaa72d247c.png
. You might be wondering how two files are created because as you can see in the write_to
method that only one filename
parameter is passed. How does Sprockets
creates two files? This question will be answered in Part 2 of this article.
You might also be wondering where and how this digest
gets created. In the constructor of the Asset
class you will see following line:
@digest = environment.file_digest(pathname).hexdigest
environment
is an instance of Index
defined in index.rb
in the Sprockets gem under the lib/sprockets
directory. It is sub-class of Base
defined in base.rb
in the lib/sprockets
directory. file_digest
is defined in base.rb
. Navigate to the file_digest
method in base.rb
and on line 242 the digest
method is called.
In the digest
method, you can see that if @digest
exists, it is used and dupped copy is returned or new digest is created and it’s dupped version is returned. digest_class
returns, by default, Digest::MD5
. However, we can assign some other class such as Digest::SHA1
too. Digest::MD5
is assigned to digest_class
attribute here. The new digest is created in digest
method by using following line:
digest_class.new.update(VERSION).update(version.to_s)
Here VERSION
is Sprockets::VERSION
and version.to_s
is production-1.0
.
Based on this information, let’s use our console to see if we can generate same digests that Sprockets generates for rails.png
. Type rails console
at the root of your Rails application.
To generate a digest for rails.png
, we should have the pathname of rails.png
. As already mentioned, the pathname is the absolute path of the asset. In my case,the pathname of rails.png
is /home/ubuntu/Desktop/work/asset_pipeline_article/app/assets/images/rails.png
.
Type following commands in console:
digest = Digest::MD5.new
digest.update(Sprockets::VERSION).update('production-1.0')
digest.file("/home/ubuntu/Desktop/work/asset_pipeline_article/app/assets/images/rails.png").hexdigest
digest.file
method returns the digest for the file based on its content. This will give us, in my case, a3386665c05a2d82f711a4aaa72d247c
for rails.png
, confirming the same digest that Sprockets generated. Digest is same for default rails.png
across all Rails applications as long as Sprockets::VERSION
and version.to_s
are same.
In this article, we discovered how Rails the asset pipeline works at a basic level and how static assets are precompiled, along wiht how a digest is created. The next part will contain an in-depth coverage of the Rails asset pipeline, covering things like how paths are assigned, how paths are resolved, how bundled assets and processed assets work, and many other concepts.
I hope you learned something about the Asset Pipeline today. Be on the look out for Part 2!
Frequently Asked Questions (FAQs) about Asset Precompiling in Ruby on Rails
What is the purpose of asset precompiling in Ruby on Rails?
Asset precompiling in Ruby on Rails is a process that combines and minifies your CSS and JavaScript files to reduce the number of requests a browser needs to make, and the size of those requests, when a user visits your site. This can significantly improve the performance of your site, especially for users with slower internet connections or on mobile devices.
How does the asset pipeline work in Ruby on Rails?
The asset pipeline in Ruby on Rails is a framework that allows you to write your assets in a higher-level language, such as CoffeeScript or Sass, and then compiles them into plain CSS or JavaScript that can be served to the browser. It also combines all your CSS and JavaScript files into a single file each, which reduces the number of HTTP requests the browser needs to make.
How can I precompile assets in the development environment?
By default, Rails does not precompile assets in the development environment. However, you can force it to do so by running the command rake assets:precompile
. This will precompile your assets and place them in the public/assets
directory. Note that this can slow down your development process, as every change to your assets will require recompiling them.
How does asset precompiling work on Heroku?
When you deploy your Rails application to Heroku, it automatically precompiles your assets during the slug compilation phase. If you have precompiled your assets locally, you should ensure that they are not included in your git repository, as this can cause conflicts with the assets precompiled by Heroku.
What are the benefits of precompiling assets?
Precompiling assets can significantly improve the performance of your Rails application. By combining and minifying your CSS and JavaScript files, it reduces the number of HTTP requests the browser needs to make, and the size of those requests. This can result in faster page load times, especially for users with slower internet connections or on mobile devices.
How can I troubleshoot issues with asset precompiling?
If you’re having issues with asset precompiling, there are a few things you can check. First, ensure that your assets are being included in the asset pipeline by checking your app/assets
directory. If they’re not there, they won’t be precompiled. Second, check your config/environments/production.rb
file to ensure that asset precompiling is enabled.
Can I customize the asset precompiling process?
Yes, you can customize the asset precompiling process in Rails. You can specify which files to include or exclude from precompiling, change the output directory for precompiled assets, and more. These settings can be configured in your config/application.rb
or config/environments/production.rb
files.
What is the difference between asset precompiling and asset minification?
Asset precompiling and asset minification are two steps in the process of optimizing your assets for production. Precompiling involves combining your CSS and JavaScript files into a single file each, while minification involves removing unnecessary characters (like whitespace and comments) from your assets to reduce their size.
How can I precompile assets for a specific environment?
You can precompile assets for a specific environment by running the command rake assets:precompile RAILS_ENV=your_environment
. Replace your_environment
with the name of the environment you want to precompile assets for.
What happens if I don’t precompile my assets?
If you don’t precompile your assets, Rails will serve them in their original, unoptimized form. This can result in slower page load times, as the browser will need to make more HTTP requests, and those requests will be larger. It can also result in compatibility issues, as some browsers may not support the higher-level languages (like CoffeeScript or Sass) that you’re using in your assets.
Imran Latif is a Web Developer and Ruby on Rails and JavaScript enthusiast from Pakistan. He is a passionate programmer and always keeps on learning new tools and technologies. During his free time he watches tech videos and reads tech articles to increase his knowledge.