zeitwerk 2.6.15 → 2.6.16
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 +1 -1
- data/lib/zeitwerk/cref.rb +99 -0
- data/lib/zeitwerk/loader/callbacks.rb +13 -14
- data/lib/zeitwerk/loader/eager_load.rb +7 -9
- data/lib/zeitwerk/loader/helpers.rb +7 -59
- data/lib/zeitwerk/loader.rb +58 -62
- data/lib/zeitwerk/version.rb +1 -1
- data/lib/zeitwerk.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b69678cbbd525cff55aa4cd08d1f3a785d69cac3e851e3cc2a168ef029163a3
|
4
|
+
data.tar.gz: 9da1ded3c41f766feee609adedc9706d4efb4cfd3701c6944f0e781986d578ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76f28b7d765b0209f489cb23f939143a6d1800e997e9a1722a0482aaf4e942f59eef26ec1e9e98a7e722710ba01c3de706f01a64594688310ca7908edb5c4de1
|
7
|
+
data.tar.gz: 2da1dfd280a4fb987f281094e45ce9c1797323680cd90f586c60660516995262aaeb4d38c55e8c330a5c879f5471567633ffe72298371c33a05b5cb6f3805285
|
data/README.md
CHANGED
@@ -1065,7 +1065,7 @@ However, sometimes it might still be convenient to tell Zeitwerk to completely i
|
|
1065
1065
|
|
1066
1066
|
You can ignore file names, directory names, and glob patterns. Glob patterns are expanded when they are added and again on each reload.
|
1067
1067
|
|
1068
|
-
There is an edge case related to nested root directories. Conceptually, root directories are independent source trees. If you ignore a parent of a nested root directory, the nested root directory is not affected. You need to ignore it
|
1068
|
+
There is an edge case related to nested root directories. Conceptually, root directories are independent source trees. If you ignore a parent of a nested root directory, the nested root directory is not affected. You need to ignore it explicitly if you want it ignored too.
|
1069
1069
|
|
1070
1070
|
Let's see some use cases.
|
1071
1071
|
|
@@ -0,0 +1,99 @@
|
|
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
|
+
if Symbol.method_defined?(:name)
|
30
|
+
# Symbol#name was introduced in Ruby 3.0. It returns always the same
|
31
|
+
# frozen object, so we may save a few string allocations.
|
32
|
+
#
|
33
|
+
# @sig () -> String
|
34
|
+
def path
|
35
|
+
@path ||= Object.equal?(@mod) ? @cname.name : "#{real_mod_name(@mod)}::#{@cname.name}"
|
36
|
+
end
|
37
|
+
else
|
38
|
+
# @sig () -> String
|
39
|
+
def path
|
40
|
+
@path ||= Object.equal?(@mod) ? @cname.to_s : "#{real_mod_name(@mod)}::#{@cname}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# The autoload? predicate takes into account the ancestor chain of the
|
45
|
+
# receiver, like const_defined? and other methods in the constants API do.
|
46
|
+
#
|
47
|
+
# For example, given
|
48
|
+
#
|
49
|
+
# class A
|
50
|
+
# autoload :X, "x.rb"
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# class B < A
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# B.autoload?(:X) returns "x.rb".
|
57
|
+
#
|
58
|
+
# We need a way to retrieve it ignoring ancestors.
|
59
|
+
#
|
60
|
+
# @sig () -> String?
|
61
|
+
if method(:autoload?).arity == 1
|
62
|
+
# @sig () -> String?
|
63
|
+
def autoload?
|
64
|
+
@mod.autoload?(@cname) if self.defined?
|
65
|
+
end
|
66
|
+
else
|
67
|
+
# @sig () -> String?
|
68
|
+
def autoload?
|
69
|
+
@mod.autoload?(@cname, false)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @sig (String) -> bool
|
74
|
+
def autoload(abspath)
|
75
|
+
@mod.autoload(@cname, abspath)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @sig () -> bool
|
79
|
+
def defined?
|
80
|
+
@mod.const_defined?(@cname, false)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @sig (Object) -> Object
|
84
|
+
def set(value)
|
85
|
+
@mod.const_set(@cname, value)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @raise [NameError]
|
89
|
+
# @sig () -> Object
|
90
|
+
def get
|
91
|
+
@mod.const_get(@cname, false)
|
92
|
+
end
|
93
|
+
|
94
|
+
# @raise [NameError]
|
95
|
+
# @sig () -> void
|
96
|
+
def remove
|
97
|
+
@mod.__send__(:remove_const, @cname)
|
98
|
+
end
|
99
|
+
end
|
@@ -8,29 +8,28 @@ module Zeitwerk::Loader::Callbacks
|
|
8
8
|
#
|
9
9
|
# @sig (String) -> void
|
10
10
|
internal def on_file_autoloaded(file)
|
11
|
-
cref
|
12
|
-
cpath = cpath(*cref)
|
11
|
+
cref = autoloads.delete(file)
|
13
12
|
|
14
13
|
Zeitwerk::Registry.unregister_autoload(file)
|
15
14
|
|
16
|
-
if
|
17
|
-
log("constant #{
|
18
|
-
to_unload[
|
19
|
-
run_on_load_callbacks(
|
15
|
+
if cref.defined?
|
16
|
+
log("constant #{cref.path} loaded from file #{file}") if logger
|
17
|
+
to_unload[cref.path] = [file, cref] if reloading_enabled?
|
18
|
+
run_on_load_callbacks(cref.path, cref.get, file) unless on_load_callbacks.empty?
|
20
19
|
else
|
21
|
-
msg = "expected file #{file} to define constant #{
|
20
|
+
msg = "expected file #{file} to define constant #{cref.path}, but didn't"
|
22
21
|
log(msg) if logger
|
23
22
|
|
24
23
|
# Ruby still keeps the autoload defined, but we remove it because the
|
25
24
|
# contract in Zeitwerk is more strict.
|
26
|
-
|
25
|
+
cref.remove
|
27
26
|
|
28
27
|
# Since the expected constant was not defined, there is nothing to unload.
|
29
28
|
# However, if the exception is rescued and reloading is enabled, we still
|
30
29
|
# need to deleted the file from $LOADED_FEATURES.
|
31
|
-
to_unload[
|
30
|
+
to_unload[cref.path] = [file, cref] if reloading_enabled?
|
32
31
|
|
33
|
-
raise Zeitwerk::NameError.new(msg, cref.
|
32
|
+
raise Zeitwerk::NameError.new(msg, cref.cname)
|
34
33
|
end
|
35
34
|
end
|
36
35
|
|
@@ -53,8 +52,8 @@ module Zeitwerk::Loader::Callbacks
|
|
53
52
|
# children, since t1 would have correctly deleted its namespace_dirs entry.
|
54
53
|
dirs_autoload_monitor.synchronize do
|
55
54
|
if cref = autoloads.delete(dir)
|
56
|
-
|
57
|
-
cpath =
|
55
|
+
implicit_namespace = cref.set(Module.new)
|
56
|
+
cpath = implicit_namespace.name
|
58
57
|
log("module #{cpath} autovivified from directory #{dir}") if logger
|
59
58
|
|
60
59
|
to_unload[cpath] = [dir, cref] if reloading_enabled?
|
@@ -65,9 +64,9 @@ module Zeitwerk::Loader::Callbacks
|
|
65
64
|
# these to be able to unregister later if eager loading.
|
66
65
|
autoloaded_dirs << dir
|
67
66
|
|
68
|
-
on_namespace_loaded(
|
67
|
+
on_namespace_loaded(implicit_namespace)
|
69
68
|
|
70
|
-
run_on_load_callbacks(cpath,
|
69
|
+
run_on_load_callbacks(cpath, implicit_namespace, dir) unless on_load_callbacks.empty?
|
71
70
|
end
|
72
71
|
end
|
73
72
|
end
|
@@ -61,8 +61,8 @@ module Zeitwerk::Loader::EagerLoad
|
|
61
61
|
cnames.reverse_each do |cname|
|
62
62
|
# Can happen if there are no Ruby files. This is not an error condition,
|
63
63
|
# the directory is actually managed. Could have Ruby files later.
|
64
|
-
return unless
|
65
|
-
namespace =
|
64
|
+
return unless namespace.const_defined?(cname, false)
|
65
|
+
namespace = namespace.const_get(cname, false)
|
66
66
|
end
|
67
67
|
|
68
68
|
# A shortcircuiting test depends on the invocation of this method. Please
|
@@ -145,12 +145,12 @@ module Zeitwerk::Loader::EagerLoad
|
|
145
145
|
|
146
146
|
namespace = root_namespace
|
147
147
|
cnames.reverse_each do |cname|
|
148
|
-
namespace =
|
148
|
+
namespace = namespace.const_get(cname, false)
|
149
149
|
end
|
150
150
|
|
151
151
|
raise Zeitwerk::Error.new("#{abspath} is shadowed") if shadowed_file?(abspath)
|
152
152
|
|
153
|
-
|
153
|
+
namespace.const_get(base_cname, false)
|
154
154
|
end
|
155
155
|
|
156
156
|
# The caller is responsible for making sure `namespace` is the namespace that
|
@@ -164,22 +164,20 @@ module Zeitwerk::Loader::EagerLoad
|
|
164
164
|
log("eager load directory #{dir} start") if logger
|
165
165
|
|
166
166
|
queue = [[dir, namespace]]
|
167
|
-
|
168
|
-
dir, namespace = queue.shift
|
169
|
-
|
167
|
+
while (dir, namespace = queue.shift)
|
170
168
|
ls(dir) do |basename, abspath, ftype|
|
171
169
|
next if honour_exclusions && eager_load_exclusions.member?(abspath)
|
172
170
|
|
173
171
|
if ftype == :file
|
174
172
|
if (cref = autoloads[abspath])
|
175
|
-
|
173
|
+
cref.get
|
176
174
|
end
|
177
175
|
else
|
178
176
|
if collapse?(abspath)
|
179
177
|
queue << [abspath, namespace]
|
180
178
|
else
|
181
179
|
cname = inflector.camelize(basename, abspath).to_sym
|
182
|
-
queue << [abspath,
|
180
|
+
queue << [abspath, namespace.const_get(cname, false)]
|
183
181
|
end
|
184
182
|
end
|
185
183
|
end
|
@@ -30,7 +30,12 @@ module Zeitwerk::Loader::Helpers
|
|
30
30
|
|
31
31
|
if dir?(abspath)
|
32
32
|
next if roots.key?(abspath)
|
33
|
-
|
33
|
+
|
34
|
+
if !has_at_least_one_ruby_file?(abspath)
|
35
|
+
log("directory #{abspath} is ignored because it has no Ruby files") if logger
|
36
|
+
next
|
37
|
+
end
|
38
|
+
|
34
39
|
ftype = :directory
|
35
40
|
else
|
36
41
|
next unless ruby?(abspath)
|
@@ -95,64 +100,7 @@ module Zeitwerk::Loader::Helpers
|
|
95
100
|
end
|
96
101
|
end
|
97
102
|
|
98
|
-
# ---
|
99
|
-
|
100
|
-
# The autoload? predicate takes into account the ancestor chain of the
|
101
|
-
# receiver, like const_defined? and other methods in the constants API do.
|
102
|
-
#
|
103
|
-
# For example, given
|
104
|
-
#
|
105
|
-
# class A
|
106
|
-
# autoload :X, "x.rb"
|
107
|
-
# end
|
108
|
-
#
|
109
|
-
# class B < A
|
110
|
-
# end
|
111
|
-
#
|
112
|
-
# B.autoload?(:X) returns "x.rb".
|
113
|
-
#
|
114
|
-
# We need a way to strictly check in parent ignoring ancestors.
|
115
|
-
#
|
116
|
-
# @sig (Module, Symbol) -> String?
|
117
|
-
if method(:autoload?).arity == 1
|
118
|
-
private def strict_autoload_path(parent, cname)
|
119
|
-
parent.autoload?(cname) if cdef?(parent, cname)
|
120
|
-
end
|
121
|
-
else
|
122
|
-
private def strict_autoload_path(parent, cname)
|
123
|
-
parent.autoload?(cname, false)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
# @sig (Module, Symbol) -> String
|
128
|
-
if Symbol.method_defined?(:name)
|
129
|
-
# Symbol#name was introduced in Ruby 3.0. It returns always the same
|
130
|
-
# frozen object, so we may save a few string allocations.
|
131
|
-
private def cpath(parent, cname)
|
132
|
-
Object == parent ? cname.name : "#{real_mod_name(parent)}::#{cname.name}"
|
133
|
-
end
|
134
|
-
else
|
135
|
-
private def cpath(parent, cname)
|
136
|
-
Object == parent ? cname.to_s : "#{real_mod_name(parent)}::#{cname}"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
# @sig (Module, Symbol) -> bool
|
141
|
-
private def cdef?(parent, cname)
|
142
|
-
parent.const_defined?(cname, false)
|
143
|
-
end
|
144
|
-
|
145
|
-
# @raise [NameError]
|
146
|
-
# @sig (Module, Symbol) -> Object
|
147
|
-
private def cget(parent, cname)
|
148
|
-
parent.const_get(cname, false)
|
149
|
-
end
|
150
|
-
|
151
|
-
# @raise [NameError]
|
152
|
-
# @sig (Module, Symbol) -> Object
|
153
|
-
private def crem(parent, cname)
|
154
|
-
parent.__send__(:remove_const, cname)
|
155
|
-
end
|
103
|
+
# --- Inflection --------------------------------------------------------------------------------
|
156
104
|
|
157
105
|
CNAME_VALIDATOR = Module.new
|
158
106
|
private_constant :CNAME_VALIDATOR
|
data/lib/zeitwerk/loader.rb
CHANGED
@@ -22,14 +22,13 @@ module Zeitwerk
|
|
22
22
|
private_constant :MUTEX
|
23
23
|
|
24
24
|
# Maps absolute paths for which an autoload has been set ---and not
|
25
|
-
# executed--- to their corresponding
|
26
|
-
# name.
|
25
|
+
# executed--- to their corresponding Zeitwerk::Cref object.
|
27
26
|
#
|
28
|
-
# "/Users/fxn/blog/app/models/user.rb" =>
|
29
|
-
# "/Users/fxn/blog/app/models/hotel/pricing.rb" =>
|
27
|
+
# "/Users/fxn/blog/app/models/user.rb" => #<Zeitwerk::Cref:... @mod=Object, @cname=:User, ...>,
|
28
|
+
# "/Users/fxn/blog/app/models/hotel/pricing.rb" => #<Zeitwerk::Cref:... @mod=Hotel, @cname=:Pricing, ...>,
|
30
29
|
# ...
|
31
30
|
#
|
32
|
-
# @sig Hash[String,
|
31
|
+
# @sig Hash[String, Zeitwerk::Cref]
|
33
32
|
attr_reader :autoloads
|
34
33
|
internal :autoloads
|
35
34
|
|
@@ -45,17 +44,19 @@ module Zeitwerk
|
|
45
44
|
|
46
45
|
# Stores metadata needed for unloading. Its entries look like this:
|
47
46
|
#
|
48
|
-
# "Admin::Role" => [
|
47
|
+
# "Admin::Role" => [
|
48
|
+
# ".../admin/role.rb",
|
49
|
+
# #<Zeitwerk::Cref:... @mod=Admin, @cname=:Role, ...>
|
50
|
+
# ]
|
49
51
|
#
|
50
52
|
# The cpath as key helps implementing unloadable_cpath? The file name is
|
51
53
|
# stored in order to be able to delete it from $LOADED_FEATURES, and the
|
52
|
-
#
|
53
|
-
# or module object.
|
54
|
+
# cref is used to remove the constant from the parent class or module.
|
54
55
|
#
|
55
56
|
# If reloading is enabled, this hash is filled as constants are autoloaded
|
56
57
|
# or eager loaded. Otherwise, the collection remains empty.
|
57
58
|
#
|
58
|
-
# @sig Hash[String, [String,
|
59
|
+
# @sig Hash[String, [String, Zeitwerk::Cref]]
|
59
60
|
attr_reader :to_unload
|
60
61
|
internal :to_unload
|
61
62
|
|
@@ -154,22 +155,22 @@ module Zeitwerk
|
|
154
155
|
# is enough.
|
155
156
|
unloaded_files = Set.new
|
156
157
|
|
157
|
-
autoloads.each do |abspath,
|
158
|
-
if
|
159
|
-
unload_autoload(
|
158
|
+
autoloads.each do |abspath, cref|
|
159
|
+
if cref.autoload?
|
160
|
+
unload_autoload(cref)
|
160
161
|
else
|
161
162
|
# Could happen if loaded with require_relative. That is unsupported,
|
162
163
|
# and the constant path would escape unloadable_cpath? This is just
|
163
164
|
# defensive code to clean things up as much as we are able to.
|
164
|
-
unload_cref(
|
165
|
+
unload_cref(cref)
|
165
166
|
unloaded_files.add(abspath) if ruby?(abspath)
|
166
167
|
end
|
167
168
|
end
|
168
169
|
|
169
|
-
to_unload.each do |cpath, (abspath,
|
170
|
+
to_unload.each do |cpath, (abspath, cref)|
|
170
171
|
unless on_unload_callbacks.empty?
|
171
172
|
begin
|
172
|
-
value =
|
173
|
+
value = cref.get
|
173
174
|
rescue ::NameError
|
174
175
|
# Perhaps the user deleted the constant by hand, or perhaps an
|
175
176
|
# autoload failed to define the expected constant but the user
|
@@ -179,7 +180,7 @@ module Zeitwerk
|
|
179
180
|
end
|
180
181
|
end
|
181
182
|
|
182
|
-
unload_cref(
|
183
|
+
unload_cref(cref)
|
183
184
|
unloaded_files.add(abspath) if ruby?(abspath)
|
184
185
|
end
|
185
186
|
|
@@ -240,8 +241,7 @@ module Zeitwerk
|
|
240
241
|
actual_roots.each do |root_dir, root_namespace|
|
241
242
|
queue = [[root_dir, real_mod_name(root_namespace)]]
|
242
243
|
|
243
|
-
|
244
|
-
dir, cpath = queue.shift
|
244
|
+
while (dir, cpath = queue.shift)
|
245
245
|
result[dir] = cpath
|
246
246
|
|
247
247
|
prefix = cpath == "Object" ? "" : cpath + "::"
|
@@ -445,21 +445,22 @@ module Zeitwerk
|
|
445
445
|
ls(dir) do |basename, abspath, ftype|
|
446
446
|
if ftype == :file
|
447
447
|
basename.delete_suffix!(".rb")
|
448
|
-
|
448
|
+
cref = Cref.new(parent, cname_for(basename, abspath))
|
449
|
+
autoload_file(cref, abspath)
|
449
450
|
else
|
450
451
|
if collapse?(abspath)
|
451
452
|
define_autoloads_for_dir(abspath, parent)
|
452
453
|
else
|
453
|
-
|
454
|
+
cref = Cref.new(parent, cname_for(basename, abspath))
|
455
|
+
autoload_subdir(cref, abspath)
|
454
456
|
end
|
455
457
|
end
|
456
458
|
end
|
457
459
|
end
|
458
460
|
|
459
461
|
# @sig (Module, Symbol, String) -> void
|
460
|
-
private def autoload_subdir(
|
461
|
-
if autoload_path = autoload_path_set_by_me_for?(
|
462
|
-
cpath = cpath(parent, cname)
|
462
|
+
private def autoload_subdir(cref, subdir)
|
463
|
+
if autoload_path = autoload_path_set_by_me_for?(cref)
|
463
464
|
if ruby?(autoload_path)
|
464
465
|
# Scanning visited a Ruby file first, and now a directory for the same
|
465
466
|
# constant has been found. This means we are dealing with an explicit
|
@@ -468,88 +469,83 @@ module Zeitwerk
|
|
468
469
|
# Registering is idempotent, and we have to keep the autoload pointing
|
469
470
|
# to the file. This may run again if more directories are found later
|
470
471
|
# on, no big deal.
|
471
|
-
register_explicit_namespace(
|
472
|
+
register_explicit_namespace(cref.path)
|
472
473
|
end
|
473
474
|
# If the existing autoload points to a file, it has to be preserved, if
|
474
475
|
# not, it is fine as it is. In either case, we do not need to override.
|
475
476
|
# Just remember the subdirectory conforms this namespace.
|
476
|
-
namespace_dirs[
|
477
|
-
elsif !
|
477
|
+
namespace_dirs[cref.path] << subdir
|
478
|
+
elsif !cref.defined?
|
478
479
|
# First time we find this namespace, set an autoload for it.
|
479
|
-
namespace_dirs[
|
480
|
-
define_autoload(
|
480
|
+
namespace_dirs[cref.path] << subdir
|
481
|
+
define_autoload(cref, subdir)
|
481
482
|
else
|
482
483
|
# For whatever reason the constant that corresponds to this namespace has
|
483
484
|
# already been defined, we have to recurse.
|
484
|
-
log("the namespace #{
|
485
|
-
define_autoloads_for_dir(subdir,
|
485
|
+
log("the namespace #{cref.path} already exists, descending into #{subdir}") if logger
|
486
|
+
define_autoloads_for_dir(subdir, cref.get)
|
486
487
|
end
|
487
488
|
end
|
488
489
|
|
489
490
|
# @sig (Module, Symbol, String) -> void
|
490
|
-
private def autoload_file(
|
491
|
-
if autoload_path =
|
491
|
+
private def autoload_file(cref, file)
|
492
|
+
if autoload_path = cref.autoload? || Registry.inception?(cref.path)
|
492
493
|
# First autoload for a Ruby file wins, just ignore subsequent ones.
|
493
494
|
if ruby?(autoload_path)
|
494
495
|
shadowed_files << file
|
495
496
|
log("file #{file} is ignored because #{autoload_path} has precedence") if logger
|
496
497
|
else
|
497
|
-
promote_namespace_from_implicit_to_explicit(
|
498
|
-
dir: autoload_path,
|
499
|
-
file: file,
|
500
|
-
parent: parent,
|
501
|
-
cname: cname
|
502
|
-
)
|
498
|
+
promote_namespace_from_implicit_to_explicit(dir: autoload_path, file: file, cref: cref)
|
503
499
|
end
|
504
|
-
elsif
|
500
|
+
elsif cref.defined?
|
505
501
|
shadowed_files << file
|
506
|
-
log("file #{file} is ignored because #{
|
502
|
+
log("file #{file} is ignored because #{cref.path} is already defined") if logger
|
507
503
|
else
|
508
|
-
define_autoload(
|
504
|
+
define_autoload(cref, file)
|
509
505
|
end
|
510
506
|
end
|
511
507
|
|
512
508
|
# `dir` is the directory that would have autovivified a namespace. `file` is
|
513
509
|
# the file where we've found the namespace is explicitly defined.
|
514
510
|
#
|
515
|
-
# @sig (dir: String, file: String,
|
516
|
-
private def promote_namespace_from_implicit_to_explicit(dir:, file:,
|
511
|
+
# @sig (dir: String, file: String, cref: Zeitwerk::Cref) -> void
|
512
|
+
private def promote_namespace_from_implicit_to_explicit(dir:, file:, cref:)
|
517
513
|
autoloads.delete(dir)
|
518
514
|
Registry.unregister_autoload(dir)
|
519
515
|
|
520
|
-
log("earlier autoload for #{
|
516
|
+
log("earlier autoload for #{cref.path} discarded, it is actually an explicit namespace defined in #{file}") if logger
|
521
517
|
|
522
|
-
define_autoload(
|
523
|
-
register_explicit_namespace(
|
518
|
+
define_autoload(cref, file)
|
519
|
+
register_explicit_namespace(cref.path)
|
524
520
|
end
|
525
521
|
|
526
522
|
# @sig (Module, Symbol, String) -> void
|
527
|
-
private def define_autoload(
|
528
|
-
|
523
|
+
private def define_autoload(cref, abspath)
|
524
|
+
cref.autoload(abspath)
|
529
525
|
|
530
526
|
if logger
|
531
527
|
if ruby?(abspath)
|
532
|
-
log("autoload set for #{
|
528
|
+
log("autoload set for #{cref.path}, to be loaded from #{abspath}")
|
533
529
|
else
|
534
|
-
log("autoload set for #{
|
530
|
+
log("autoload set for #{cref.path}, to be autovivified from #{abspath}")
|
535
531
|
end
|
536
532
|
end
|
537
533
|
|
538
|
-
autoloads[abspath] =
|
534
|
+
autoloads[abspath] = cref
|
539
535
|
Registry.register_autoload(self, abspath)
|
540
536
|
|
541
537
|
# See why in the documentation of Zeitwerk::Registry.inceptions.
|
542
|
-
unless
|
543
|
-
Registry.register_inception(
|
538
|
+
unless cref.autoload?
|
539
|
+
Registry.register_inception(cref.path, abspath, self)
|
544
540
|
end
|
545
541
|
end
|
546
542
|
|
547
543
|
# @sig (Module, Symbol) -> String?
|
548
|
-
private def autoload_path_set_by_me_for?(
|
549
|
-
if autoload_path =
|
544
|
+
private def autoload_path_set_by_me_for?(cref)
|
545
|
+
if autoload_path = cref.autoload?
|
550
546
|
autoload_path if autoloads.key?(autoload_path)
|
551
547
|
else
|
552
|
-
Registry.inception?(
|
548
|
+
Registry.inception?(cref.path)
|
553
549
|
end
|
554
550
|
end
|
555
551
|
|
@@ -590,21 +586,21 @@ module Zeitwerk
|
|
590
586
|
end
|
591
587
|
|
592
588
|
# @sig (Module, Symbol) -> void
|
593
|
-
private def unload_autoload(
|
594
|
-
|
595
|
-
log("autoload for #{
|
589
|
+
private def unload_autoload(cref)
|
590
|
+
cref.remove
|
591
|
+
log("autoload for #{cref.path} removed") if logger
|
596
592
|
end
|
597
593
|
|
598
594
|
# @sig (Module, Symbol) -> void
|
599
|
-
private def unload_cref(
|
595
|
+
private def unload_cref(cref)
|
600
596
|
# Let's optimistically remove_const. The way we use it, this is going to
|
601
597
|
# succeed always if all is good.
|
602
|
-
|
598
|
+
cref.remove
|
603
599
|
rescue ::NameError
|
604
600
|
# There are a few edge scenarios in which this may happen. If the constant
|
605
601
|
# is gone, that is OK, anyway.
|
606
602
|
else
|
607
|
-
log("#{
|
603
|
+
log("#{cref.path} unloaded") if logger
|
608
604
|
end
|
609
605
|
end
|
610
606
|
end
|
data/lib/zeitwerk/version.rb
CHANGED
data/lib/zeitwerk.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zeitwerk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.6.
|
4
|
+
version: 2.6.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Xavier Noria
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|
@@ -23,6 +23,7 @@ files:
|
|
23
23
|
- MIT-LICENSE
|
24
24
|
- README.md
|
25
25
|
- lib/zeitwerk.rb
|
26
|
+
- lib/zeitwerk/cref.rb
|
26
27
|
- lib/zeitwerk/error.rb
|
27
28
|
- lib/zeitwerk/explicit_namespace.rb
|
28
29
|
- lib/zeitwerk/gem_inflector.rb
|
@@ -62,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
63
|
- !ruby/object:Gem::Version
|
63
64
|
version: '0'
|
64
65
|
requirements: []
|
65
|
-
rubygems_version: 3.5.
|
66
|
+
rubygems_version: 3.5.10
|
66
67
|
signing_key:
|
67
68
|
specification_version: 4
|
68
69
|
summary: Efficient and thread-safe constant autoloader
|