zeitwerk 2.7.2 → 2.7.3
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/lib/zeitwerk/core_ext/kernel.rb +3 -3
- data/lib/zeitwerk/core_ext/module.rb +4 -4
- data/lib/zeitwerk/cref/map.rb +62 -27
- data/lib/zeitwerk/cref.rb +14 -16
- data/lib/zeitwerk/error.rb +2 -0
- data/lib/zeitwerk/gem_inflector.rb +2 -2
- data/lib/zeitwerk/gem_loader.rb +4 -4
- data/lib/zeitwerk/inflector.rb +3 -3
- data/lib/zeitwerk/internal.rb +1 -1
- data/lib/zeitwerk/loader/callbacks.rb +7 -9
- data/lib/zeitwerk/loader/config.rb +37 -44
- data/lib/zeitwerk/loader/eager_load.rb +7 -7
- data/lib/zeitwerk/loader/helpers.rb +9 -10
- data/lib/zeitwerk/loader.rb +57 -52
- data/lib/zeitwerk/null_inflector.rb +1 -1
- data/lib/zeitwerk/real_mod_name.rb +7 -4
- data/lib/zeitwerk/registry/autoloads.rb +38 -0
- data/lib/zeitwerk/registry/explicit_namespaces.rb +36 -39
- data/lib/zeitwerk/registry/inceptions.rb +20 -20
- data/lib/zeitwerk/registry/loaders.rb +33 -0
- data/lib/zeitwerk/registry.rb +17 -39
- data/lib/zeitwerk/version.rb +2 -1
- data/lib/zeitwerk.rb +1 -1
- metadata +5 -3
@@ -7,10 +7,10 @@ module Zeitwerk::Loader::Config
|
|
7
7
|
extend Zeitwerk::Internal
|
8
8
|
include Zeitwerk::RealModName
|
9
9
|
|
10
|
-
|
10
|
+
#: camelize(String, String) -> String
|
11
11
|
attr_accessor :inflector
|
12
12
|
|
13
|
-
|
13
|
+
#: call(String) -> void | debug(String) -> void | nil
|
14
14
|
attr_accessor :logger
|
15
15
|
|
16
16
|
# Absolute paths of the root directories, mapped to their respective root namespaces:
|
@@ -25,14 +25,13 @@ module Zeitwerk::Loader::Config
|
|
25
25
|
# This is a private collection maintained by the loader. The public
|
26
26
|
# interface for it is `push_dir` and `dirs`.
|
27
27
|
#
|
28
|
-
|
28
|
+
#: Hash[String, Module]
|
29
29
|
attr_reader :roots
|
30
30
|
internal :roots
|
31
31
|
|
32
|
-
# Absolute paths of files, directories, or glob patterns to be
|
33
|
-
# ignored.
|
32
|
+
# Absolute paths of files, directories, or glob patterns to be ignored.
|
34
33
|
#
|
35
|
-
|
34
|
+
#: Set[String]
|
36
35
|
attr_reader :ignored_glob_patterns
|
37
36
|
private :ignored_glob_patterns
|
38
37
|
|
@@ -40,46 +39,46 @@ module Zeitwerk::Loader::Config
|
|
40
39
|
# ignored glob patterns were expanded. Computed on setup, and recomputed on
|
41
40
|
# reload.
|
42
41
|
#
|
43
|
-
|
42
|
+
#: Set[String]
|
44
43
|
attr_reader :ignored_paths
|
45
44
|
private :ignored_paths
|
46
45
|
|
47
46
|
# Absolute paths of directories or glob patterns to be collapsed.
|
48
47
|
#
|
49
|
-
|
48
|
+
#: Set[String]
|
50
49
|
attr_reader :collapse_glob_patterns
|
51
50
|
private :collapse_glob_patterns
|
52
51
|
|
53
52
|
# The actual collection of absolute directory names at the time the collapse
|
54
53
|
# glob patterns were expanded. Computed on setup, and recomputed on reload.
|
55
54
|
#
|
56
|
-
|
55
|
+
#: Set[String]
|
57
56
|
attr_reader :collapse_dirs
|
58
57
|
private :collapse_dirs
|
59
58
|
|
60
59
|
# Absolute paths of files or directories not to be eager loaded.
|
61
60
|
#
|
62
|
-
|
61
|
+
#: Set[String]
|
63
62
|
attr_reader :eager_load_exclusions
|
64
63
|
private :eager_load_exclusions
|
65
64
|
|
66
65
|
# User-oriented callbacks to be fired on setup and on reload.
|
67
66
|
#
|
68
|
-
|
67
|
+
#: Array[{ () -> void }]
|
69
68
|
attr_reader :on_setup_callbacks
|
70
69
|
private :on_setup_callbacks
|
71
70
|
|
72
71
|
# User-oriented callbacks to be fired when a constant is loaded.
|
73
72
|
#
|
74
|
-
|
75
|
-
|
73
|
+
#: Hash[String, Array[{ (top, String) -> void }]]
|
74
|
+
#| Hash[Symbol, Array[{ (String, top, String) -> void }]]
|
76
75
|
attr_reader :on_load_callbacks
|
77
76
|
private :on_load_callbacks
|
78
77
|
|
79
78
|
# User-oriented callbacks to be fired before constants are removed.
|
80
79
|
#
|
81
|
-
|
82
|
-
|
80
|
+
#: Hash[String, Array[{ (top, String) -> void }]]
|
81
|
+
#| Hash[Symbol, Array[{ (String, top, String) -> void }]]
|
83
82
|
attr_reader :on_unload_callbacks
|
84
83
|
private :on_unload_callbacks
|
85
84
|
|
@@ -106,8 +105,7 @@ module Zeitwerk::Loader::Config
|
|
106
105
|
# the same process already manages that directory or one of its ascendants or
|
107
106
|
# descendants.
|
108
107
|
#
|
109
|
-
|
110
|
-
# @sig (String | Pathname, Module) -> void
|
108
|
+
#: (String | Pathname, namespace: Module) -> void ! Zeitwerk::Error
|
111
109
|
def push_dir(path, namespace: Object)
|
112
110
|
unless namespace.is_a?(Module) # Note that Class < Module.
|
113
111
|
raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module object, should be"
|
@@ -131,14 +129,14 @@ module Zeitwerk::Loader::Config
|
|
131
129
|
# Implemented as a method instead of via attr_reader for symmetry with the
|
132
130
|
# writer below.
|
133
131
|
#
|
134
|
-
|
132
|
+
#: () -> String
|
135
133
|
def tag
|
136
134
|
@tag
|
137
135
|
end
|
138
136
|
|
139
137
|
# Sets a tag for the loader, useful for logging.
|
140
138
|
#
|
141
|
-
|
139
|
+
#: (to_s() -> String) -> void
|
142
140
|
def tag=(tag)
|
143
141
|
@tag = tag.to_s
|
144
142
|
end
|
@@ -152,7 +150,7 @@ module Zeitwerk::Loader::Config
|
|
152
150
|
#
|
153
151
|
# These are read-only collections, please add to them with `push_dir`.
|
154
152
|
#
|
155
|
-
|
153
|
+
#: (?namespaces: boolish, ?ignored: boolish) -> Array[String] | Hash[String, Module]
|
156
154
|
def dirs(namespaces: false, ignored: false)
|
157
155
|
if namespaces
|
158
156
|
if ignored || ignored_paths.empty?
|
@@ -172,8 +170,7 @@ module Zeitwerk::Loader::Config
|
|
172
170
|
# You need to call this method before setup in order to be able to reload.
|
173
171
|
# There is no way to undo this, either you want to reload or you don't.
|
174
172
|
#
|
175
|
-
|
176
|
-
# @sig () -> void
|
173
|
+
#: () -> void ! Zeitwerk::Error
|
177
174
|
def enable_reloading
|
178
175
|
mutex.synchronize do
|
179
176
|
break if @reloading_enabled
|
@@ -186,7 +183,7 @@ module Zeitwerk::Loader::Config
|
|
186
183
|
end
|
187
184
|
end
|
188
185
|
|
189
|
-
|
186
|
+
#: () -> bool
|
190
187
|
def reloading_enabled?
|
191
188
|
@reloading_enabled
|
192
189
|
end
|
@@ -194,14 +191,14 @@ module Zeitwerk::Loader::Config
|
|
194
191
|
# Let eager load ignore the given files or directories. The constants defined
|
195
192
|
# in those files are still autoloadable.
|
196
193
|
#
|
197
|
-
|
194
|
+
#: (*(String | Pathname | Array[String | Pathname])) -> void
|
198
195
|
def do_not_eager_load(*paths)
|
199
196
|
mutex.synchronize { eager_load_exclusions.merge(expand_paths(paths)) }
|
200
197
|
end
|
201
198
|
|
202
199
|
# Configure files, directories, or glob patterns to be totally ignored.
|
203
200
|
#
|
204
|
-
|
201
|
+
#: (*(String | Pathname | Array[String | Pathname])) -> void
|
205
202
|
def ignore(*glob_patterns)
|
206
203
|
glob_patterns = expand_paths(glob_patterns)
|
207
204
|
mutex.synchronize do
|
@@ -212,7 +209,7 @@ module Zeitwerk::Loader::Config
|
|
212
209
|
|
213
210
|
# Configure directories or glob patterns to be collapsed.
|
214
211
|
#
|
215
|
-
|
212
|
+
#: (*(String | Pathname | Array[String | Pathname])) -> void
|
216
213
|
def collapse(*glob_patterns)
|
217
214
|
glob_patterns = expand_paths(glob_patterns)
|
218
215
|
mutex.synchronize do
|
@@ -224,7 +221,7 @@ module Zeitwerk::Loader::Config
|
|
224
221
|
# Configure a block to be called after setup and on each reload.
|
225
222
|
# If setup was already done, the block runs immediately.
|
226
223
|
#
|
227
|
-
|
224
|
+
#: () { () -> void } -> void
|
228
225
|
def on_setup(&block)
|
229
226
|
mutex.synchronize do
|
230
227
|
on_setup_callbacks << block
|
@@ -246,9 +243,7 @@ module Zeitwerk::Loader::Config
|
|
246
243
|
# # ...
|
247
244
|
# end
|
248
245
|
#
|
249
|
-
|
250
|
-
# @sig (String) { (top, String) -> void } -> void
|
251
|
-
# (:ANY) { (String, top, String) -> void } -> void
|
246
|
+
#: (String?) { (top, String) -> void } -> void ! TypeError
|
252
247
|
def on_load(cpath = :ANY, &block)
|
253
248
|
raise TypeError, "on_load only accepts strings" unless cpath.is_a?(String) || cpath == :ANY
|
254
249
|
|
@@ -271,9 +266,7 @@ module Zeitwerk::Loader::Config
|
|
271
266
|
# # ...
|
272
267
|
# end
|
273
268
|
#
|
274
|
-
|
275
|
-
# @sig (String) { (top) -> void } -> void
|
276
|
-
# (:ANY) { (String, top) -> void } -> void
|
269
|
+
#: (String?) { (top, String) -> void } -> void ! TypeError
|
277
270
|
def on_unload(cpath = :ANY, &block)
|
278
271
|
raise TypeError, "on_unload only accepts strings" unless cpath.is_a?(String) || cpath == :ANY
|
279
272
|
|
@@ -284,7 +277,7 @@ module Zeitwerk::Loader::Config
|
|
284
277
|
|
285
278
|
# Logs to `$stdout`, handy shortcut for debugging.
|
286
279
|
#
|
287
|
-
|
280
|
+
#: () -> void
|
288
281
|
def log!
|
289
282
|
@logger = ->(msg) { puts msg }
|
290
283
|
end
|
@@ -292,7 +285,7 @@ module Zeitwerk::Loader::Config
|
|
292
285
|
# Returns true if the argument has been configured to be ignored, or is a
|
293
286
|
# descendant of an ignored directory.
|
294
287
|
#
|
295
|
-
|
288
|
+
#: (String) -> bool
|
296
289
|
internal def ignores?(abspath)
|
297
290
|
# Common use case.
|
298
291
|
return false if ignored_paths.empty?
|
@@ -305,24 +298,24 @@ module Zeitwerk::Loader::Config
|
|
305
298
|
false
|
306
299
|
end
|
307
300
|
|
308
|
-
|
301
|
+
#: (String) -> bool
|
309
302
|
private def ignored_path?(abspath)
|
310
303
|
ignored_paths.member?(abspath)
|
311
304
|
end
|
312
305
|
|
313
|
-
|
306
|
+
#: () -> Array[String]
|
314
307
|
private def actual_roots
|
315
308
|
roots.reject do |root_dir, _root_namespace|
|
316
309
|
!dir?(root_dir) || ignored_path?(root_dir)
|
317
310
|
end
|
318
311
|
end
|
319
312
|
|
320
|
-
|
313
|
+
#: (String) -> bool
|
321
314
|
private def root_dir?(dir)
|
322
315
|
roots.key?(dir)
|
323
316
|
end
|
324
317
|
|
325
|
-
|
318
|
+
#: (String) -> bool
|
326
319
|
private def excluded_from_eager_load?(abspath)
|
327
320
|
# Optimize this common use case.
|
328
321
|
return false if eager_load_exclusions.empty?
|
@@ -335,29 +328,29 @@ module Zeitwerk::Loader::Config
|
|
335
328
|
false
|
336
329
|
end
|
337
330
|
|
338
|
-
|
331
|
+
#: (String) -> bool
|
339
332
|
private def collapse?(dir)
|
340
333
|
collapse_dirs.member?(dir)
|
341
334
|
end
|
342
335
|
|
343
|
-
|
336
|
+
#: (String | Pathname | Array[String | Pathname]) -> Array[String]
|
344
337
|
private def expand_paths(paths)
|
345
338
|
paths.flatten.map! { |path| File.expand_path(path) }
|
346
339
|
end
|
347
340
|
|
348
|
-
|
341
|
+
#: (Array[String]) -> Array[String]
|
349
342
|
private def expand_glob_patterns(glob_patterns)
|
350
343
|
# Note that Dir.glob works with regular file names just fine. That is,
|
351
344
|
# glob patterns technically need no wildcards.
|
352
345
|
glob_patterns.flat_map { |glob_pattern| Dir.glob(glob_pattern) }
|
353
346
|
end
|
354
347
|
|
355
|
-
|
348
|
+
#: () -> void
|
356
349
|
private def recompute_ignored_paths
|
357
350
|
ignored_paths.replace(expand_glob_patterns(ignored_glob_patterns))
|
358
351
|
end
|
359
352
|
|
360
|
-
|
353
|
+
#: () -> void
|
361
354
|
private def recompute_collapse_dirs
|
362
355
|
collapse_dirs.replace(expand_glob_patterns(collapse_glob_patterns))
|
363
356
|
end
|
@@ -5,7 +5,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
5
5
|
# specific files and directories with `do_not_eager_load`, and that can be
|
6
6
|
# overridden passing `force: true`.
|
7
7
|
#
|
8
|
-
|
8
|
+
#: (?force: boolish) -> void
|
9
9
|
def eager_load(force: false)
|
10
10
|
mutex.synchronize do
|
11
11
|
break if @eager_loaded
|
@@ -18,7 +18,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
18
18
|
end
|
19
19
|
|
20
20
|
autoloaded_dirs.each do |autoloaded_dir|
|
21
|
-
Zeitwerk::Registry.
|
21
|
+
Zeitwerk::Registry.autoloads.unregister(autoloaded_dir)
|
22
22
|
end
|
23
23
|
autoloaded_dirs.clear
|
24
24
|
|
@@ -28,7 +28,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
#: (String | Pathname) -> void
|
32
32
|
def eager_load_dir(path)
|
33
33
|
raise Zeitwerk::SetupRequired unless @setup
|
34
34
|
|
@@ -70,7 +70,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
70
70
|
actual_eager_load_dir(abspath, namespace)
|
71
71
|
end
|
72
72
|
|
73
|
-
|
73
|
+
#: (Module) -> void
|
74
74
|
def eager_load_namespace(mod)
|
75
75
|
raise Zeitwerk::SetupRequired unless @setup
|
76
76
|
|
@@ -112,7 +112,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
112
112
|
# The method is implemented as `constantize` for files, in a sense, to be able
|
113
113
|
# to descend orderly and make sure the file is loadable.
|
114
114
|
#
|
115
|
-
|
115
|
+
#: (String | Pathname) -> void
|
116
116
|
def load_file(path)
|
117
117
|
abspath = File.expand_path(path)
|
118
118
|
|
@@ -156,7 +156,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
156
156
|
# The caller is responsible for making sure `namespace` is the namespace that
|
157
157
|
# corresponds to `dir`.
|
158
158
|
#
|
159
|
-
|
159
|
+
#: (String, Module, ?force: boolish) -> void
|
160
160
|
private def actual_eager_load_dir(dir, namespace, force: false)
|
161
161
|
honour_exclusions = !force
|
162
162
|
return if honour_exclusions && excluded_from_eager_load?(dir)
|
@@ -189,7 +189,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
189
189
|
# In order to invoke this method, the caller has to ensure `child` is a
|
190
190
|
# strict namespace descendant of `root_namespace`.
|
191
191
|
#
|
192
|
-
|
192
|
+
#: (Module, String, String, Module) -> void
|
193
193
|
private def eager_load_child_namespace(child, child_name, root_dir, root_namespace)
|
194
194
|
suffix = child_name
|
195
195
|
unless root_namespace.equal?(Object)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Zeitwerk::Loader::Helpers
|
4
4
|
# --- Logging -----------------------------------------------------------------------------------
|
5
5
|
|
6
|
-
|
6
|
+
#: (to_s() -> String) -> void
|
7
7
|
private def log(message)
|
8
8
|
method_name = logger.respond_to?(:debug) ? :debug : :call
|
9
9
|
logger.send(method_name, "Zeitwerk@#{tag}: #{message}")
|
@@ -11,7 +11,7 @@ module Zeitwerk::Loader::Helpers
|
|
11
11
|
|
12
12
|
# --- Files and directories ---------------------------------------------------------------------
|
13
13
|
|
14
|
-
|
14
|
+
#: (String) { (String, String, Symbol) -> void } -> void
|
15
15
|
private def ls(dir)
|
16
16
|
children = Dir.children(dir)
|
17
17
|
|
@@ -52,7 +52,7 @@ module Zeitwerk::Loader::Helpers
|
|
52
52
|
# important to list as less directories as possible and return fast in the
|
53
53
|
# common case in which there are Ruby files.
|
54
54
|
#
|
55
|
-
|
55
|
+
#: (String) -> bool
|
56
56
|
private def has_at_least_one_ruby_file?(dir)
|
57
57
|
to_visit = [dir]
|
58
58
|
|
@@ -74,22 +74,22 @@ module Zeitwerk::Loader::Helpers
|
|
74
74
|
false
|
75
75
|
end
|
76
76
|
|
77
|
-
|
77
|
+
#: (String) -> bool
|
78
78
|
private def ruby?(path)
|
79
79
|
path.end_with?(".rb")
|
80
80
|
end
|
81
81
|
|
82
|
-
|
82
|
+
#: (String) -> bool
|
83
83
|
private def dir?(path)
|
84
84
|
File.directory?(path)
|
85
85
|
end
|
86
86
|
|
87
|
-
|
87
|
+
#: (String) -> bool
|
88
88
|
private def hidden?(basename)
|
89
89
|
basename.start_with?(".")
|
90
90
|
end
|
91
91
|
|
92
|
-
|
92
|
+
#: (String) { (String) -> void } -> void
|
93
93
|
private def walk_up(abspath)
|
94
94
|
loop do
|
95
95
|
yield abspath
|
@@ -100,11 +100,10 @@ module Zeitwerk::Loader::Helpers
|
|
100
100
|
|
101
101
|
# --- Inflection --------------------------------------------------------------------------------
|
102
102
|
|
103
|
-
CNAME_VALIDATOR = Module.new
|
103
|
+
CNAME_VALIDATOR = Module.new #: Module
|
104
104
|
private_constant :CNAME_VALIDATOR
|
105
105
|
|
106
|
-
|
107
|
-
# @sig (String, String) -> Symbol
|
106
|
+
#: (String, String) -> Symbol ! Zeitwerk::NameError
|
108
107
|
private def cname_for(basename, abspath)
|
109
108
|
cname = inflector.camelize(basename, abspath)
|
110
109
|
|