Zeitwerk is a thread-safe code loader for Ruby.
Rails makes your constant available upfront (eager load) and on-demand (autoload). To do that, there is an autoloader that handles loading constant defined in conventional file structure and detects constant when you create a new file under app/models/juanito.rb
.
Zeitwerk is the Rails 7 default autoloader, which the autoloader before Rails 7 is called Classic Autoloader.
Classic Autoloader is based on Module#const_missing
+ complex $LOAD_PATH
lookup. When Ruby fails to find a constant, Classic Autoloader will do something to make it up.
Zeitwerk solves this by using (Module#autoload
) that tells Ruby where to find constant to load and Kernel#require
to load files. Zeitwerk scans project root folders/files to load files immediately, and lazily scans into subdirectories.
Official has a HOWTO helping you moving from Classic Autoloader to new Zeitwerk autoloader.
Prepare:
# tmp/a.rb
puts "file tmp/a.rb loaded"
A = "A"
# tmp/b/c.rb
puts Dir.pwd
class B::C = "B::C"
Open a rails console with autoload mode set to Zeitwerk:
irb> loader = Zeitwerk::Loader.new
irb> loader.push_dir("./tmp")
irb> loader.logger = Logger.new(STDOUT)
irb> loader.setup
D, [2023-01-09T10:20:01.726990 #69605] DEBUG -- : Zeitwerk@5bc68e: autoload set for A, to be loaded from /Users/hhh/dev/buildkite/tmp/a.rb
D, [2023-01-09T10:20:01.727325 #69605] DEBUG -- : Zeitwerk@5bc68e: autoload set for B, to be autovivified from /Users/hhh/dev/buildkite/tmp/b
irb> A
file tmp/a.rb loaded
D, [2023-01-09T10:20:13.483262 #69605] DEBUG -- : Zeitwerk@5bc68e: constant A loaded from file /Users/hhh/dev/buildkite/tmp/a.rb
=> "A"
irb> A
=> "A"
To be able to load b/c.rb
, zeitwerk modified the Kernel#require
to be able to load a directory.
file tmp/a.rb loaded
only printed when we actually use the constant A
confirms Zeitwerk first scans the root folder tmp
and did not actually load the file.
...
loader = Zeitwerk::Loader.new
loader.push_dir("./tmp")
loader.logger = Logger.new(STDOUT)
# Add this!
loader.enable_reloading
loader.setup
# call this on every request
loader.reload
Can also eager load by loader.eager_load
.