How does autoloading work in Rails?
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 defineUsersHelper
.app/controllers/admin/payments_controller.rb
should defineAdmin::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
Thanks for your comment 🙏. Once it's approved, it will appear here.
Leave a comment