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 absolute 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)


120
121
122
123
124
# File 'lib/zeitwerk/registry.rb', line 120

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

.loader_for(path) ⇒ Object



128
129
130
# File 'lib/zeitwerk/registry.rb', line 128

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.



90
91
92
93
94
95
96
97
98
# File 'lib/zeitwerk/registry.rb', line 90

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



134
135
136
137
# File 'lib/zeitwerk/registry.rb', line 134

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

.register_autoload(loader, abspath) ⇒ Object



102
103
104
# File 'lib/zeitwerk/registry.rb', line 102

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

.register_inception(cpath, abspath, loader) ⇒ Object



114
115
116
# File 'lib/zeitwerk/registry.rb', line 114

def register_inception(cpath, abspath, loader)
  inceptions[cpath] = [abspath, 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(abspath) ⇒ Object



108
109
110
# File 'lib/zeitwerk/registry.rb', line 108

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

.unregister_loader(loader) ⇒ Object



78
79
80
81
82
83
# File 'lib/zeitwerk/registry.rb', line 78

def unregister_loader(loader)
  loaders.delete(loader)
  loaders_managing_gems.delete_if { |_, l| l == loader }
  autoloads.delete_if { |_, l| l == loader }
  inceptions.delete_if { |_, (_, l)| l == loader }
end