Autoloading Modules in Ruby and Rails

How Module Autoloading Works in Ruby and Rails

Autoloading allows you to speed up the initialization of your library by lazily loading the code only when you need it. This post explains how the autoload method works in Ruby and how Rails overrides this method to provide its own implementation that follows the Rails naming conventions.

4 min read

Typically, you use require to load libraries and modules your Ruby code needs.

require 'net/http'

The Ruby interpreter loads the required code as soon as it comes across the require method.

However, often you may not want to load all the dependencies as soon as possible. If they take a long time to load, it can slow down your application initialization process. This is really important during local development and also when running tests. You want to load, run, and test the app as quickly as possible, without loading any code that you won't use.

In these cases, to speed up the initialization process, Ruby provides the autoload method that lazily loads the specified modules. In addition, Rails overrides and abundantly uses this method to set up various sub-frameworks like ActiveModel and ActiveStorage, etc.

The first time I came across autoloading, I was pretty baffled, as I hadn't seen something like that in C# and JavaScript. After a little bit of digging, it turned out to be a simple and pretty useful concept.

So, what does the autoload method do, and how does it work?

Let's try to understand Ruby's version first.

Autoloading in Ruby

The autoload method takes two arguments: Module Name (can be either a string or a symbol) and filename (string). It registers the filename to be loaded the first time that module is accessed.

autoload :Payment, '/lib/modules/payment.rb'

What it means is that Rails won't load the payment.rb script until you actually need it. It will only load it when you first use the Payment module in your codebase.

Using autoload allows you to speed up the initialization of your library by lazily loading the modules that your code depends on. It won't load the library or framework code you don't need.

Autoloading in Rails

Ruby on Rails provides its own autoload method via its Active Support framework. It allows you to load modules based on Rails naming conventions, by automatically guessing the filename based on the module name.

module ActiveModel
  extend ActiveSupport::Autoload
  
  # autoload this module from the 
  # active_model/secure_password.rb file
  autoload :SecurePassword
end

In this example, Rails will look for the SecurePassword module in the active_model/secure_password.rb file.

Here's the simplified implementation of this method in Rails.

def autoload(const_name, path)
  unless path
    full = [name, const_name.to_s].compact.join("::")
    path = Inflector.underscore(full)
  end

  super const_name, path
end

If the path is not provided, Rails guesses the path by joining the constant name with the current module name and generating its underscored and lowercase form. Finally, it calls Ruby's autoload method by calling super and passes the module name and the generated path.

For example, consider the following code:

module ActiveModel
  extend ActiveSupport::Autoload

  autoload :API
  autoload :Name, "active_model/naming"
end

The first time you use the API module, Rails will load it from active_model/api.rb file. In contrast, when you need the Name class, it will be loaded from the active_model/naming.rb file.

Autoloading Multiple Modules From the Same Directory

If multiple modules live under a common folder structure, you can also use the autoload_under helper.

module ActionController
  extend ActiveSupport::Autoload

  autoload :API
  autoload :Base

  autoload_under "metal" do
    autoload :Cookies
    audoload :Flash
  end
end

The Cookies and Flash modules will be loaded from action_controller/metal/{cookies/flash}.rb files.

Autoloading Multiple Modules in the Same File

If a single file contains multiple modules that you want to autoload, use the autoload_at helper. For example, the action_view/buffers.rb file contains two modules: OutputBuffer and StreamingBuffer. To autoload them both, Rails uses the following code:

module ActionView
  extend ActiveSupport::Autoload
  
  autoload_at "action_view/buffers" do
    autoload :OutputBuffer
    autoload :StreamingBuffer
  end
end

Eager Load Modules As Necessary

Finally, you can also define a set of modules that need to be eager loaded using the eager_autoload method.

class Cipher
  extend ActiveSupport::Autoload
  
  eager_autoload do
    autoload :Aes256Gcm
  end
end

Then you can eager load the Aes256Gcm module by calling the eager_load! method (notice the ! bang)

Cipher.eager_load!

The original implementation of the eager_load! method looped through the modules marked to be eager loaded and simply required them as follows:

def eager_load!
  @_autoloads.each_value { |file| require file }
end

However, in the latest version of Rails, it's using the const_get method to load the constants, see this PR for more details.

Anyway, that's all for now. If you're curious to learn the difference between load, autoload, require, and require_relative methods, check out this article:

The Difference Between load, autoload, require, and require_relative in Ruby 📖
Loading external files can get tricky in Ruby, but it doesn’t have to be. This post explains the usage of Ruby’s load, require, and require_relative methods, and when to use each.

I hope you liked this article and you learned something new.

As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I look forward to hearing from you.

If you'd like to receive future articles directly in your email, please subscribe to my blog. If you're already a subscriber, thank you.