sajad torkamani

What is autoloading?

In a standard Ruby application, you need to manually require any dependencies that a particular file uses. For example, a PostsController that inherited from a ApplicationController and that used a Post model would need two require statements like so:

# Boring require statements...
require "application_controller"
require "post"

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end

But Rails makes your life easier by allowing you to omit the require statements so that your code becomes something like this:

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end

In idiomatic Rails apps, you typically only use require statements to load stuff from the Ruby standard library, Ruby gems, and the lib directory.

How does Rails implement autoloading

Rails uses Zeitwerk loaders

Rails implements autoloading by managing a couple of Zeitwerk loaders for you. Zeitwerk is an efficient code loader for Ruby that autoloads code based on a conventional file structure.

The file structure typically requires that directory and file names be based on the name of the modules and classes that they define. So, something like this:

lib/my_gem.rb         -> MyGem
lib/my_gem/foo.rb     -> MyGem::Foo
lib/my_gem/bar_baz.rb -> MyGem::BarBaz
lib/my_gem/woo/zoo.rb -> MyGem::Woo::Zoo

Rails’ file naming requirements

File names must match the class or module that they define. For example:

  • app/helpers/users_helper.rb should define UsersHelper.
  • app/controllers/admin/payments_controller.rb should define Admin::PaymentsController.

As you can see from the above examples, a file should define a constant that is the camelized version of the file name. For example:

"users_controller".camelize # => UsersController

Rails’ autoload paths

Rails manages a list of directories whose contents are autoloaded as autoload paths. You can view your app’s autoload paths by running the below in the Rails console.

puts ActiveSupport::Dependencies.autoload_paths

Example output:

/Users/sajad/sites/rails/quotes/app/channels
/Users/sajad/sites/rails/quotes/app/controllers                  
/Users/sajad/sites/rails/quotes/app/controllers/concerns
/Users/sajad/sites/rails/quotes/app/helpers
/Users/sajad/sites/rails/quotes/app/jobs
/Users/sajad/sites/rails/quotes/app/mailers
/Users/sajad/sites/rails/quotes/app/models
/Users/sajad/sites/rails/quotes/app/models/concerns
/Users/sajad/sites/rails/quotes/app/policies
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/sentry-rails-5.1.1/app/jobs
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/controllers
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/helpers
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/mailers
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/actionmailbox-7.0.2.2/app/controllers
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/actionmailbox-7.0.2.2/app/jobs
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/actionmailbox-7.0.2.2/app/models
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/activestorage-7.0.2.2/app/controllers
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/activestorage-7.0.2.2/app/controllers/concerns
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/activestorage-7.0.2.2/app/jobs
/Users/sajad/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/activestorage-7.0.2.2/app/models
/Users/sajad/sites/rails/quotes/spec/mailers/previews

As you can see, Rails adds a couple of directories (e.g., app/models, app/controllers, app/helpers).

It also adds any custom directory under app/ to the autoload path. This means if you have a directory named app/services, that directory will automatically be part of the autoload path. Other Rails engines such as Devise can also register their own directories.

In the Zeitwork docs, autoload paths are called root directories.

Configure autoload paths

The list of default autoload paths can be extended by appending to config.autoload_paths in config/application.rb or config/environments/*.rb.

module AcmeApplication
  class Application < Rails::Application
    config.autoload_paths << "#{root}/extras"
  end
end

Rails’s autoload once paths

You may have code that you want autoloaded but not reloaded. In this case, you can extend config.autoload_once_paths in config/application.rb or config/environments/*.rb:

module MyApplication
  class Application < Rails::Application
    config.autoload_once_paths << "#{root}/app/serializers"
  end
end

Sources

Tagged: Rails