How Asset Precompile Works, Part I

Share this article

Abstract tooth wheels

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 LatifImran Latif
View Author

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.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week