is the new code loader
that comes with Rails 6 by default.
In addition to providing autoloading,
and reloading capabilities,
it also improves the classical code loader
by being efficient
According to the author of Zeitwerk,
one of the main motivations for writing Zeitwerk
was to keep code DRY
to remove the brittle
Zeitwerk is available as a gem with no additional dependencies. It means any regular Ruby project can use Zeitwerk.
How to use Zeitwerk
Zeitwerk is baked in a Rails 6 project, thanks to the Zeitwerk-Rails integration. For a non-Rails project, adding the following into the project's entry point sets up Zeitwerk.
1 2loader = Zeitwerk::Loader.new 3loader.push_dir(...) 4loader.setup 5
For gem maintainers,
Zeitwerk provides the handy
The following example from Zeitwerk documentation
illustrates the usage of
1#lib/my_gem.rb (main file) 2 3require "zeitwerk" 4loader = Zeitwerk::Loader.for_gem 5loader.setup 6 7module MyGem 8 # Since the setup has been performed, at this point we are already 9 # able to reference project constants, in this case MyGem::MyLogger. 10 include MyLogger 11end 12
How does Zeitwerk work?
Before we look into Zeitwerk's internals, the following section provides a quick refresher on constant-resolution in Ruby and how classical code loader of Rails works.
Ruby's constant resolution looks for a constant in the following places.
- In each entry of Module.nesting
- In each entry of Module.ancestors
It triggers 'constant_missing' callback when it can't find the constant.
Classical Code Loader in Rails
Classical code loader (code loader in Rails version < 6.0) achieves autoloading by overriding Module#const_missing and loads the missing constant without the need for an explicit require call as long as the code follows certain conventions.
- The file should be within a directory in ActiveSupport::Dependencies.autoload_paths
- A file should be named after the class, i.e Admin::RoutesController => admin/routes_controller.rb
Zeitwerk takes an entirely different approach in autoloading by registering constants to be autoloaded by Ruby.
Consider the following configuration in which Zeitwerk manages
lib directory and
1 2loader.push_dir('./lib') 3
Zeitwerk then uses Module.autoload to tell Ruby that "Automobile" can be found in "lib/automobile.rb".
1 2autoload "Automobile", "lib/automobile.rb" 3
Unlike classical loader, Zeitwerk takes module nesting into account while loading constants by leveraging the new Tracepoint API to go look for constants defined in subdirectories when a new class or module is defined.
Let us look at an example to understand this better.
1 2class Automobile 3 # => Tracepoint hook triggers here. 4 # include Engine 5end 6
When the tracepoint hook triggers,
Zeitwerk checks for an
in the same level as automobile.rb
sets up Module.autoload
for that directory
all the files (in this case ./automobile/engine.rb)
within that directory.
Previously in Rails, we had a code loader that was riddled with gotchas and struggled to be thread safe. Zeitwerk does a better job by leveraging the new Ruby standard API and matches Ruby's semantics for constants.