zeitwerk 2.6.6 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +239 -83
- data/lib/zeitwerk/{kernel.rb → core_ext/kernel.rb} +3 -7
- data/lib/zeitwerk/core_ext/module.rb +12 -0
- data/lib/zeitwerk/cref.rb +65 -0
- data/lib/zeitwerk/explicit_namespace.rb +54 -56
- data/lib/zeitwerk/gem_inflector.rb +2 -2
- data/lib/zeitwerk/gem_loader.rb +11 -8
- data/lib/zeitwerk/loader/callbacks.rb +33 -25
- data/lib/zeitwerk/loader/config.rb +20 -11
- data/lib/zeitwerk/loader/eager_load.rb +22 -18
- data/lib/zeitwerk/loader/helpers.rb +60 -51
- data/lib/zeitwerk/loader.rb +215 -113
- data/lib/zeitwerk/null_inflector.rb +5 -0
- data/lib/zeitwerk/real_mod_name.rb +2 -8
- data/lib/zeitwerk/registry.rb +7 -4
- data/lib/zeitwerk/version.rb +1 -1
- data/lib/zeitwerk.rb +5 -1
- metadata +8 -5
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This private class encapsulates pairs (mod, cname).
|
4
|
+
#
|
5
|
+
# Objects represent the constant cname in the class or module object mod, and
|
6
|
+
# have API to manage them that encapsulates the constants API. Examples:
|
7
|
+
#
|
8
|
+
# cref.path
|
9
|
+
# cref.set(value)
|
10
|
+
# cref.get
|
11
|
+
#
|
12
|
+
# The constant may or may not exist in mod.
|
13
|
+
class Zeitwerk::Cref
|
14
|
+
include Zeitwerk::RealModName
|
15
|
+
|
16
|
+
# @sig Symbol
|
17
|
+
attr_reader :cname
|
18
|
+
|
19
|
+
# The type of the first argument is Module because Class < Module, class
|
20
|
+
# objects are also valid.
|
21
|
+
#
|
22
|
+
# @sig (Module, Symbol) -> void
|
23
|
+
def initialize(mod, cname)
|
24
|
+
@mod = mod
|
25
|
+
@cname = cname
|
26
|
+
@path = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# @sig () -> String
|
30
|
+
def path
|
31
|
+
@path ||= Object.equal?(@mod) ? @cname.name : "#{real_mod_name(@mod)}::#{@cname.name}".freeze
|
32
|
+
end
|
33
|
+
|
34
|
+
# @sig () -> String?
|
35
|
+
def autoload?
|
36
|
+
@mod.autoload?(@cname, false)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @sig (String) -> bool
|
40
|
+
def autoload(abspath)
|
41
|
+
@mod.autoload(@cname, abspath)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @sig () -> bool
|
45
|
+
def defined?
|
46
|
+
@mod.const_defined?(@cname, false)
|
47
|
+
end
|
48
|
+
|
49
|
+
# @sig (Object) -> Object
|
50
|
+
def set(value)
|
51
|
+
@mod.const_set(@cname, value)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @raise [NameError]
|
55
|
+
# @sig () -> Object
|
56
|
+
def get
|
57
|
+
@mod.const_get(@cname, false)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @raise [NameError]
|
61
|
+
# @sig () -> void
|
62
|
+
def remove
|
63
|
+
@mod.__send__(:remove_const, @cname)
|
64
|
+
end
|
65
|
+
end
|
@@ -1,86 +1,84 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Zeitwerk
|
4
|
-
# Centralizes the logic
|
5
|
-
#
|
6
|
-
# after the constant has been defined.
|
4
|
+
# Centralizes the logic needed to descend into matching subdirectories right
|
5
|
+
# after the constant for an explicit namespace has been defined.
|
7
6
|
#
|
8
7
|
# The implementation assumes an explicit namespace is managed by one loader.
|
9
8
|
# Loaders that reopen namespaces owned by other projects are responsible for
|
10
9
|
# loading their constant before setup. This is documented.
|
11
10
|
module ExplicitNamespace # :nodoc: all
|
11
|
+
# Maps cpaths of explicit namespaces with their corresponding loader.
|
12
|
+
# Entries are added as the namespaces are found, and removed as they are
|
13
|
+
# autoloaded.
|
14
|
+
#
|
15
|
+
# @sig Hash[String => Zeitwerk::Loader]
|
16
|
+
@cpaths = {}
|
17
|
+
|
12
18
|
class << self
|
13
19
|
include RealModName
|
14
20
|
extend Internal
|
15
21
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# @sig Hash[String, Zeitwerk::Loader]
|
20
|
-
attr_reader :cpaths
|
21
|
-
private :cpaths
|
22
|
-
|
23
|
-
# @sig Mutex
|
24
|
-
attr_reader :mutex
|
25
|
-
private :mutex
|
26
|
-
|
27
|
-
# @sig TracePoint
|
28
|
-
attr_reader :tracer
|
29
|
-
private :tracer
|
30
|
-
|
31
|
-
# Asserts `cpath` corresponds to an explicit namespace for which `loader`
|
32
|
-
# is responsible.
|
22
|
+
# Registers `cpath` as being the constant path of an explicit namespace
|
23
|
+
# managed by `loader`.
|
33
24
|
#
|
34
25
|
# @sig (String, Zeitwerk::Loader) -> void
|
35
26
|
internal def register(cpath, loader)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
27
|
+
@cpaths[cpath] = loader
|
28
|
+
end
|
29
|
+
|
30
|
+
# @sig (String) -> Zeitwerk::Loader?
|
31
|
+
internal def loader_for(mod, cname)
|
32
|
+
cpath = mod.equal?(Object) ? cname.name : "#{real_mod_name(mod)}::#{cname}"
|
33
|
+
@cpaths.delete(cpath)
|
42
34
|
end
|
43
35
|
|
44
36
|
# @sig (Zeitwerk::Loader) -> void
|
45
37
|
internal def unregister_loader(loader)
|
46
|
-
cpaths.delete_if {
|
47
|
-
disable_tracer_if_unneeded
|
38
|
+
@cpaths.delete_if { _2.equal?(loader) }
|
48
39
|
end
|
49
40
|
|
41
|
+
# This is an internal method only used by the test suite.
|
42
|
+
#
|
43
|
+
# @sig (String) -> Zeitwerk::Loader?
|
44
|
+
internal def registered?(cpath)
|
45
|
+
@cpaths[cpath]
|
46
|
+
end
|
47
|
+
|
48
|
+
# This is an internal method only used by the test suite.
|
49
|
+
#
|
50
50
|
# @sig () -> void
|
51
|
-
|
52
|
-
|
53
|
-
tracer.disable if cpaths.empty?
|
54
|
-
end
|
51
|
+
internal def clear
|
52
|
+
@cpaths.clear
|
55
53
|
end
|
56
54
|
|
57
|
-
|
58
|
-
|
59
|
-
# If the class is a singleton class, we won't do anything with it so we
|
60
|
-
# can bail out immediately. This is several orders of magnitude faster
|
61
|
-
# than accessing its name.
|
62
|
-
return if event.self.singleton_class?
|
55
|
+
module Synchronized
|
56
|
+
extend Internal
|
63
57
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
#
|
69
|
-
# On the other hand, if we were called, cpaths is not empty. Otherwise
|
70
|
-
# the tracer is disabled. So we do need to go ahead with the hash code
|
71
|
-
# computation and delete call.
|
72
|
-
if loader = cpaths.delete(real_mod_name(event.self))
|
73
|
-
loader.on_namespace_loaded(event.self)
|
74
|
-
disable_tracer_if_unneeded
|
58
|
+
MUTEX = Mutex.new
|
59
|
+
|
60
|
+
internal def register(...)
|
61
|
+
MUTEX.synchronize { super }
|
75
62
|
end
|
76
|
-
end
|
77
|
-
end
|
78
63
|
|
79
|
-
|
80
|
-
|
64
|
+
internal def loader_for(...)
|
65
|
+
MUTEX.synchronize { super }
|
66
|
+
end
|
67
|
+
|
68
|
+
internal def unregister_loader(...)
|
69
|
+
MUTEX.synchronize { super }
|
70
|
+
end
|
81
71
|
|
82
|
-
|
83
|
-
|
84
|
-
|
72
|
+
internal def registered?(...)
|
73
|
+
MUTEX.synchronize { super }
|
74
|
+
end
|
75
|
+
|
76
|
+
internal def clear
|
77
|
+
MUTEX.synchronize { super }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
prepend Synchronized unless RUBY_ENGINE == "ruby"
|
82
|
+
end
|
85
83
|
end
|
86
84
|
end
|
@@ -5,8 +5,8 @@ module Zeitwerk
|
|
5
5
|
# @sig (String) -> void
|
6
6
|
def initialize(root_file)
|
7
7
|
namespace = File.basename(root_file, ".rb")
|
8
|
-
|
9
|
-
@version_file = File.join(
|
8
|
+
root_dir = File.dirname(root_file)
|
9
|
+
@version_file = File.join(root_dir, namespace, "version.rb")
|
10
10
|
end
|
11
11
|
|
12
12
|
# @sig (String, String) -> String
|
data/lib/zeitwerk/gem_loader.rb
CHANGED
@@ -3,27 +3,31 @@
|
|
3
3
|
module Zeitwerk
|
4
4
|
# @private
|
5
5
|
class GemLoader < Loader
|
6
|
+
include RealModName
|
7
|
+
|
6
8
|
# Users should not create instances directly, the public interface is
|
7
9
|
# `Zeitwerk::Loader.for_gem`.
|
8
10
|
private_class_method :new
|
9
11
|
|
10
12
|
# @private
|
11
13
|
# @sig (String, bool) -> Zeitwerk::GemLoader
|
12
|
-
def self.
|
13
|
-
new(root_file, warn_on_extra_files: warn_on_extra_files)
|
14
|
+
def self.__new(root_file, namespace:, warn_on_extra_files:)
|
15
|
+
new(root_file, namespace: namespace, warn_on_extra_files: warn_on_extra_files)
|
14
16
|
end
|
15
17
|
|
16
18
|
# @sig (String, bool) -> void
|
17
|
-
def initialize(root_file, warn_on_extra_files:)
|
19
|
+
def initialize(root_file, namespace:, warn_on_extra_files:)
|
18
20
|
super()
|
19
21
|
|
20
|
-
@tag
|
22
|
+
@tag = File.basename(root_file, ".rb")
|
23
|
+
@tag = real_mod_name(namespace) + "-" + @tag unless namespace.equal?(Object)
|
24
|
+
|
21
25
|
@inflector = GemInflector.new(root_file)
|
22
26
|
@root_file = File.expand_path(root_file)
|
23
|
-
@
|
27
|
+
@root_dir = File.dirname(root_file)
|
24
28
|
@warn_on_extra_files = warn_on_extra_files
|
25
29
|
|
26
|
-
push_dir(@
|
30
|
+
push_dir(@root_dir, namespace: namespace)
|
27
31
|
end
|
28
32
|
|
29
33
|
# @sig () -> void
|
@@ -38,13 +42,12 @@ module Zeitwerk
|
|
38
42
|
def warn_on_extra_files
|
39
43
|
expected_namespace_dir = @root_file.delete_suffix(".rb")
|
40
44
|
|
41
|
-
ls(@
|
45
|
+
ls(@root_dir) do |basename, abspath, ftype|
|
42
46
|
next if abspath == @root_file
|
43
47
|
next if abspath == expected_namespace_dir
|
44
48
|
|
45
49
|
basename_without_ext = basename.delete_suffix(".rb")
|
46
50
|
cname = inflector.camelize(basename_without_ext, abspath).to_sym
|
47
|
-
ftype = dir?(abspath) ? "directory" : "file"
|
48
51
|
|
49
52
|
warn(<<~EOS)
|
50
53
|
WARNING: Zeitwerk defines the constant #{cname} after the #{ftype}
|
@@ -2,38 +2,46 @@
|
|
2
2
|
|
3
3
|
module Zeitwerk::Loader::Callbacks
|
4
4
|
include Zeitwerk::RealModName
|
5
|
+
extend Zeitwerk::Internal
|
5
6
|
|
6
7
|
# Invoked from our decorated Kernel#require when a managed file is autoloaded.
|
7
8
|
#
|
8
|
-
# @
|
9
|
+
# @raise [Zeitwerk::NameError]
|
9
10
|
# @sig (String) -> void
|
10
|
-
def on_file_autoloaded(file)
|
11
|
-
cref
|
12
|
-
|
13
|
-
|
14
|
-
# If reloading is enabled, we need to put this constant for unloading
|
15
|
-
# regardless of what cdef? says. In Ruby < 3.1 the internal state is not
|
16
|
-
# fully cleared. Module#constants still includes it, and you need to
|
17
|
-
# remove_const. See https://github.com/ruby/ruby/pull/4715.
|
18
|
-
to_unload[cpath] = [file, cref] if reloading_enabled?
|
11
|
+
internal def on_file_autoloaded(file)
|
12
|
+
cref = autoloads.delete(file)
|
13
|
+
|
19
14
|
Zeitwerk::Registry.unregister_autoload(file)
|
20
15
|
|
21
|
-
if
|
22
|
-
log("constant #{
|
23
|
-
|
16
|
+
if cref.defined?
|
17
|
+
log("constant #{cref.path} loaded from file #{file}") if logger
|
18
|
+
to_unload[cref.path] = [file, cref] if reloading_enabled?
|
19
|
+
run_on_load_callbacks(cref.path, cref.get, file) unless on_load_callbacks.empty?
|
24
20
|
else
|
25
|
-
|
21
|
+
msg = "expected file #{file} to define constant #{cref.path}, but didn't"
|
22
|
+
log(msg) if logger
|
23
|
+
|
24
|
+
# Ruby still keeps the autoload defined, but we remove it because the
|
25
|
+
# contract in Zeitwerk is more strict.
|
26
|
+
cref.remove
|
27
|
+
|
28
|
+
# Since the expected constant was not defined, there is nothing to unload.
|
29
|
+
# However, if the exception is rescued and reloading is enabled, we still
|
30
|
+
# need to deleted the file from $LOADED_FEATURES.
|
31
|
+
to_unload[cref.path] = [file, cref] if reloading_enabled?
|
32
|
+
|
33
|
+
raise Zeitwerk::NameError.new(msg, cref.cname)
|
26
34
|
end
|
27
35
|
end
|
28
36
|
|
29
37
|
# Invoked from our decorated Kernel#require when a managed directory is
|
30
38
|
# autoloaded.
|
31
39
|
#
|
32
|
-
# @private
|
33
40
|
# @sig (String) -> void
|
34
|
-
def on_dir_autoloaded(dir)
|
35
|
-
# Module#autoload does not serialize concurrent requires, and
|
36
|
-
# directories ourselves
|
41
|
+
internal def on_dir_autoloaded(dir)
|
42
|
+
# Module#autoload does not serialize concurrent requires in CRuby < 3.2, and
|
43
|
+
# we handle directories ourselves without going through Kernel#require, so
|
44
|
+
# the callback needs to account for concurrency.
|
37
45
|
#
|
38
46
|
# Multi-threading would introduce a race condition here in which thread t1
|
39
47
|
# autovivifies the module, and while autoloads for its children are being
|
@@ -43,10 +51,10 @@ module Zeitwerk::Loader::Callbacks
|
|
43
51
|
# That not only would reassign the constant (undesirable per se) but, worse,
|
44
52
|
# the module object created by t2 wouldn't have any of the autoloads for its
|
45
53
|
# children, since t1 would have correctly deleted its namespace_dirs entry.
|
46
|
-
|
54
|
+
dirs_autoload_monitor.synchronize do
|
47
55
|
if cref = autoloads.delete(dir)
|
48
|
-
|
49
|
-
cpath =
|
56
|
+
implicit_namespace = cref.set(Module.new)
|
57
|
+
cpath = implicit_namespace.name
|
50
58
|
log("module #{cpath} autovivified from directory #{dir}") if logger
|
51
59
|
|
52
60
|
to_unload[cpath] = [dir, cref] if reloading_enabled?
|
@@ -57,15 +65,15 @@ module Zeitwerk::Loader::Callbacks
|
|
57
65
|
# these to be able to unregister later if eager loading.
|
58
66
|
autoloaded_dirs << dir
|
59
67
|
|
60
|
-
on_namespace_loaded(
|
68
|
+
on_namespace_loaded(implicit_namespace)
|
61
69
|
|
62
|
-
run_on_load_callbacks(cpath,
|
70
|
+
run_on_load_callbacks(cpath, implicit_namespace, dir) unless on_load_callbacks.empty?
|
63
71
|
end
|
64
72
|
end
|
65
73
|
end
|
66
74
|
|
67
75
|
# Invoked when a class or module is created or reopened, either from the
|
68
|
-
#
|
76
|
+
# const_added or from module autovivification. If the namespace has matching
|
69
77
|
# subdirectories, we descend into them now.
|
70
78
|
#
|
71
79
|
# @private
|
@@ -73,7 +81,7 @@ module Zeitwerk::Loader::Callbacks
|
|
73
81
|
def on_namespace_loaded(namespace)
|
74
82
|
if dirs = namespace_dirs.delete(real_mod_name(namespace))
|
75
83
|
dirs.each do |dir|
|
76
|
-
|
84
|
+
define_autoloads_for_dir(dir, namespace)
|
77
85
|
end
|
78
86
|
end
|
79
87
|
end
|
@@ -109,8 +109,7 @@ module Zeitwerk::Loader::Config
|
|
109
109
|
# @raise [Zeitwerk::Error]
|
110
110
|
# @sig (String | Pathname, Module) -> void
|
111
111
|
def push_dir(path, namespace: Object)
|
112
|
-
# Note that Class < Module.
|
113
|
-
unless namespace.is_a?(Module)
|
112
|
+
unless namespace.is_a?(Module) # Note that Class < Module.
|
114
113
|
raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module object, should be"
|
115
114
|
end
|
116
115
|
|
@@ -149,14 +148,24 @@ module Zeitwerk::Loader::Config
|
|
149
148
|
# instead. Keys are the absolute paths of the root directories as strings,
|
150
149
|
# values are their corresponding namespaces, class or module objects.
|
151
150
|
#
|
151
|
+
# If `ignored` is falsey (default), ignored root directories are filtered out.
|
152
|
+
#
|
152
153
|
# These are read-only collections, please add to them with `push_dir`.
|
153
154
|
#
|
154
155
|
# @sig () -> Array[String] | Hash[String, Module]
|
155
|
-
def dirs(namespaces: false)
|
156
|
+
def dirs(namespaces: false, ignored: false)
|
156
157
|
if namespaces
|
157
|
-
|
158
|
+
if ignored || ignored_paths.empty?
|
159
|
+
roots.clone
|
160
|
+
else
|
161
|
+
roots.reject { |root_dir, _namespace| ignored_path?(root_dir) }
|
162
|
+
end
|
158
163
|
else
|
159
|
-
|
164
|
+
if ignored || ignored_paths.empty?
|
165
|
+
roots.keys
|
166
|
+
else
|
167
|
+
roots.keys.reject { |root_dir| ignored_path?(root_dir) }
|
168
|
+
end
|
160
169
|
end.freeze
|
161
170
|
end
|
162
171
|
|
@@ -288,9 +297,9 @@ module Zeitwerk::Loader::Config
|
|
288
297
|
# Common use case.
|
289
298
|
return false if ignored_paths.empty?
|
290
299
|
|
291
|
-
walk_up(abspath) do |
|
292
|
-
return true if ignored_path?(
|
293
|
-
return false if roots.key?(
|
300
|
+
walk_up(abspath) do |path|
|
301
|
+
return true if ignored_path?(path)
|
302
|
+
return false if roots.key?(path)
|
294
303
|
end
|
295
304
|
|
296
305
|
false
|
@@ -318,9 +327,9 @@ module Zeitwerk::Loader::Config
|
|
318
327
|
# Optimize this common use case.
|
319
328
|
return false if eager_load_exclusions.empty?
|
320
329
|
|
321
|
-
walk_up(abspath) do |
|
322
|
-
return true if eager_load_exclusions.member?(
|
323
|
-
return false if roots.key?(
|
330
|
+
walk_up(abspath) do |path|
|
331
|
+
return true if eager_load_exclusions.member?(path)
|
332
|
+
return false if roots.key?(path)
|
324
333
|
end
|
325
334
|
|
326
335
|
false
|
@@ -45,8 +45,10 @@ module Zeitwerk::Loader::EagerLoad
|
|
45
45
|
|
46
46
|
break if root_namespace = roots[dir]
|
47
47
|
|
48
|
+
basename = File.basename(dir)
|
49
|
+
return if hidden?(basename)
|
50
|
+
|
48
51
|
unless collapse?(dir)
|
49
|
-
basename = File.basename(dir)
|
50
52
|
cnames << inflector.camelize(basename, dir).to_sym
|
51
53
|
end
|
52
54
|
end
|
@@ -59,8 +61,8 @@ module Zeitwerk::Loader::EagerLoad
|
|
59
61
|
cnames.reverse_each do |cname|
|
60
62
|
# Can happen if there are no Ruby files. This is not an error condition,
|
61
63
|
# the directory is actually managed. Could have Ruby files later.
|
62
|
-
return unless
|
63
|
-
namespace =
|
64
|
+
return unless namespace.const_defined?(cname, false)
|
65
|
+
namespace = namespace.const_get(cname, false)
|
64
66
|
end
|
65
67
|
|
66
68
|
# A shortcircuiting test depends on the invocation of this method. Please
|
@@ -119,6 +121,8 @@ module Zeitwerk::Loader::EagerLoad
|
|
119
121
|
raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_path?(abspath)
|
120
122
|
|
121
123
|
basename = File.basename(abspath, ".rb")
|
124
|
+
raise Zeitwerk::Error.new("#{abspath} is ignored") if hidden?(basename)
|
125
|
+
|
122
126
|
base_cname = inflector.camelize(basename, abspath).to_sym
|
123
127
|
|
124
128
|
root_namespace = nil
|
@@ -129,8 +133,10 @@ module Zeitwerk::Loader::EagerLoad
|
|
129
133
|
|
130
134
|
break if root_namespace = roots[dir]
|
131
135
|
|
136
|
+
basename = File.basename(dir)
|
137
|
+
raise Zeitwerk::Error.new("#{abspath} is ignored") if hidden?(basename)
|
138
|
+
|
132
139
|
unless collapse?(dir)
|
133
|
-
basename = File.basename(dir)
|
134
140
|
cnames << inflector.camelize(basename, dir).to_sym
|
135
141
|
end
|
136
142
|
end
|
@@ -139,12 +145,12 @@ module Zeitwerk::Loader::EagerLoad
|
|
139
145
|
|
140
146
|
namespace = root_namespace
|
141
147
|
cnames.reverse_each do |cname|
|
142
|
-
namespace =
|
148
|
+
namespace = namespace.const_get(cname, false)
|
143
149
|
end
|
144
150
|
|
145
151
|
raise Zeitwerk::Error.new("#{abspath} is shadowed") if shadowed_file?(abspath)
|
146
152
|
|
147
|
-
|
153
|
+
namespace.const_get(base_cname, false)
|
148
154
|
end
|
149
155
|
|
150
156
|
# The caller is responsible for making sure `namespace` is the namespace that
|
@@ -158,22 +164,20 @@ module Zeitwerk::Loader::EagerLoad
|
|
158
164
|
log("eager load directory #{dir} start") if logger
|
159
165
|
|
160
166
|
queue = [[dir, namespace]]
|
161
|
-
while
|
162
|
-
|
163
|
-
|
164
|
-
ls(dir) do |basename, abspath|
|
167
|
+
while (current_dir, namespace = queue.shift)
|
168
|
+
ls(current_dir) do |basename, abspath, ftype|
|
165
169
|
next if honour_exclusions && eager_load_exclusions.member?(abspath)
|
166
170
|
|
167
|
-
if
|
168
|
-
if (cref = autoloads[abspath])
|
169
|
-
|
171
|
+
if ftype == :file
|
172
|
+
if (cref = autoloads[abspath])
|
173
|
+
cref.get
|
170
174
|
end
|
171
175
|
else
|
172
176
|
if collapse?(abspath)
|
173
177
|
queue << [abspath, namespace]
|
174
178
|
else
|
175
179
|
cname = inflector.camelize(basename, abspath).to_sym
|
176
|
-
queue << [abspath,
|
180
|
+
queue << [abspath, namespace.const_get(cname, false)]
|
177
181
|
end
|
178
182
|
end
|
179
183
|
end
|
@@ -183,7 +187,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
183
187
|
end
|
184
188
|
|
185
189
|
# In order to invoke this method, the caller has to ensure `child` is a
|
186
|
-
# strict namespace
|
190
|
+
# strict namespace descendant of `root_namespace`.
|
187
191
|
#
|
188
192
|
# @sig (Module, String, Module, Boolean) -> void
|
189
193
|
private def eager_load_child_namespace(child, child_name, root_dir, root_namespace)
|
@@ -203,9 +207,9 @@ module Zeitwerk::Loader::EagerLoad
|
|
203
207
|
next_dirs = []
|
204
208
|
|
205
209
|
suffix.split("::").each do |segment|
|
206
|
-
while dir = dirs.shift
|
207
|
-
ls(dir) do |basename, abspath|
|
208
|
-
next unless
|
210
|
+
while (dir = dirs.shift)
|
211
|
+
ls(dir) do |basename, abspath, ftype|
|
212
|
+
next unless ftype == :directory
|
209
213
|
|
210
214
|
if collapse?(abspath)
|
211
215
|
dirs << abspath
|