Module: Zeitwerk::Registry

Defined in:
lib/zeitwerk/registry.rb

Overview

:nodoc: all

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.autoloadsObject (readonly)

Maps real paths to the loaders responsible for them.

This information is used by our decorated ‘Kernel#require` to be able to invoke callbacks and autovivify modules.



27
28
29
# File 'lib/zeitwerk/registry.rb', line 27

def autoloads
  @autoloads
end

.inceptionsObject (readonly)

This hash table addresses an edge case in which an autoload is ignored.

For example, let’s suppose we want to autoload in a gem like this:

# lib/my_gem.rb
loader = Zeitwerk::Loader.new
loader.push_dir(__dir__)
loader.setup

module MyGem
end

if you require “my_gem”, as Bundler would do, this happens while setting up autoloads:

1. Object.autoload?(:MyGem) returns `nil` because the autoload for
   the constant is issued by Zeitwerk while the same file is being
   required.
2. The constant `MyGem` is undefined while setup runs.

Therefore, a directory ‘lib/my_gem` would autovivify a module according to the existing information. But that would be wrong.

To overcome this fundamental limitation, we keep track of the constant paths that are in this situation —in the example above, “MyGem”— and take this collection into account for the autovivification logic.

Note that you cannot generally address this by moving the setup code below the constant definition, because we want libraries to be able to use managed constants in the module body:

module MyGem
  include MyConcern
end


66
67
68
# File 'lib/zeitwerk/registry.rb', line 66

def inceptions
  @inceptions
end

.loadersObject (readonly)

Keeps track of all loaders. Useful to broadcast messages and to prevent them from being garbage collected.



11
12
13
# File 'lib/zeitwerk/registry.rb', line 11

def loaders
  @loaders
end

.loaders_managing_gemsObject (readonly)

Registers loaders created with ‘for_gem` to make the method idempotent in case of reload.



18
19
20
# File 'lib/zeitwerk/registry.rb', line 18

def loaders_managing_gems
  @loaders_managing_gems
end

Class Method Details

.inception?(cpath) ⇒ Boolean

Returns:

  • (Boolean)


111
112
113
114
115
# File 'lib/zeitwerk/registry.rb', line 111

def inception?(cpath)
  if pair = inceptions[cpath]
    pair.first
  end
end

.loader_for(path) ⇒ Object



119
120
121
# File 'lib/zeitwerk/registry.rb', line 119

def loader_for(path)
  autoloads[path]
end

.loader_for_gem(root_file) ⇒ Object

This method returns always a loader, the same instance for the same root file. That is how Zeitwerk::Loader.for_gem is idempotent.



81
82
83
84
85
86
87
88
89
# File 'lib/zeitwerk/registry.rb', line 81

def loader_for_gem(root_file)
  loaders_managing_gems[root_file] ||= begin
    Loader.new.tap do |loader|
      loader.tag = File.basename(root_file, ".rb")
      loader.inflector = GemInflector.new(root_file)
      loader.push_dir(File.dirname(root_file))
    end
  end
end

.on_unload(loader) ⇒ Object



125
126
127
128
# File 'lib/zeitwerk/registry.rb', line 125

def on_unload(loader)
  autoloads.delete_if { |_path, object| object == loader }
  inceptions.delete_if { |_cpath, (_path, object)| object == loader }
end

.register_autoload(loader, realpath) ⇒ Object



93
94
95
# File 'lib/zeitwerk/registry.rb', line 93

def register_autoload(loader, realpath)
  autoloads[realpath] = loader
end

.register_inception(cpath, realpath, loader) ⇒ Object



105
106
107
# File 'lib/zeitwerk/registry.rb', line 105

def register_inception(cpath, realpath, loader)
  inceptions[cpath] = [realpath, loader]
end

.register_loader(loader) ⇒ Object

Registers a loader.



72
73
74
# File 'lib/zeitwerk/registry.rb', line 72

def register_loader(loader)
  loaders << loader
end

.unregister_autoload(realpath) ⇒ Object



99
100
101
# File 'lib/zeitwerk/registry.rb', line 99

def unregister_autoload(realpath)
  autoloads.delete(realpath)
end