Zeitwerk

Work In Progress

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.