zeitwerk 2.4.0 → 2.4.1
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 +3 -3
- data/lib/zeitwerk/explicit_namespace.rb +9 -8
- data/lib/zeitwerk/gem_inflector.rb +2 -4
- data/lib/zeitwerk/inflector.rb +3 -6
- data/lib/zeitwerk/kernel.rb +1 -2
- data/lib/zeitwerk/loader.rb +69 -106
- data/lib/zeitwerk/loader/callbacks.rb +3 -6
- data/lib/zeitwerk/real_mod_name.rb +1 -2
- data/lib/zeitwerk/registry.rb +12 -23
- data/lib/zeitwerk/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0c408b8f6001e150b16b1e06b1b05d31bcbf4f0144535c995093c822065be3d
|
4
|
+
data.tar.gz: af26de0ecd68b8ebc1836ea431d59c6d7e10d79937c402e3529fc4275631c950
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 012c9a70cd31973a7251f46f62c102bf11155a6ea90dcdfa62bb2cd1859fca07cfed268dd130528fc55eab4ea0d440347e7ed0cb78f54dca861f96f56014541d
|
7
|
+
data.tar.gz: 7243c0f9b6c39dd389f0570fe03f11a8cadb01b74ae5d5efaa9b895ecf3b2717b5eb71e027c555f1cfeb95457dd5f5d8ace4516eb3a77500b05b1c8f973c1a94
|
data/README.md
CHANGED
@@ -151,7 +151,7 @@ require "active_job/queue_adapters"
|
|
151
151
|
loader.push_dir("#{__dir__}/adapters", namespace: ActiveJob::QueueAdapters)
|
152
152
|
```
|
153
153
|
|
154
|
-
your adapter can be stored directly in that directory instead of the canonical
|
154
|
+
your adapter can be stored directly in that directory instead of the canonical `#{__dir__}/active_job/queue_adapters`.
|
155
155
|
|
156
156
|
Please, note that the given namespace must be non-reloadable, though autoloaded constants in that namespace can be. That is, if you associate `app/api` with an existing `Api` module, that module should not be reloadable. However, if the project defines and autoloads the class `Api::V2::Deliveries`, that one can be reloaded.
|
157
157
|
|
@@ -202,7 +202,7 @@ booking/actions/create.rb -> Booking::Create
|
|
202
202
|
To make it work that way, configure Zeitwerk to collapse said directory:
|
203
203
|
|
204
204
|
```ruby
|
205
|
-
loader.collapse("booking/actions")
|
205
|
+
loader.collapse("#{__dir__}/booking/actions")
|
206
206
|
```
|
207
207
|
|
208
208
|
This method accepts an arbitrary number of strings or `Pathname` objects, and also an array of them.
|
@@ -212,7 +212,7 @@ You can pass directories and glob patterns. Glob patterns are expanded when they
|
|
212
212
|
To illustrate usage of glob patterns, if `actions` in the example above is part of a standardized structure, you could use a wildcard:
|
213
213
|
|
214
214
|
```ruby
|
215
|
-
loader.collapse("
|
215
|
+
loader.collapse("#{__dir__}/*/actions")
|
216
216
|
```
|
217
217
|
|
218
218
|
<a id="markdown-nested-root-directories" name="nested-root-directories"></a>
|
@@ -14,24 +14,22 @@ module Zeitwerk
|
|
14
14
|
# the file system, to the loader responsible for them.
|
15
15
|
#
|
16
16
|
# @private
|
17
|
-
# @
|
17
|
+
# @sig Hash[String, Zeitwerk::Loader]
|
18
18
|
attr_reader :cpaths
|
19
19
|
|
20
20
|
# @private
|
21
|
-
# @
|
21
|
+
# @sig Mutex
|
22
22
|
attr_reader :mutex
|
23
23
|
|
24
24
|
# @private
|
25
|
-
# @
|
25
|
+
# @sig TracePoint
|
26
26
|
attr_reader :tracer
|
27
27
|
|
28
28
|
# Asserts `cpath` corresponds to an explicit namespace for which `loader`
|
29
29
|
# is responsible.
|
30
30
|
#
|
31
31
|
# @private
|
32
|
-
# @
|
33
|
-
# @param loader [Zeitwerk::Loader]
|
34
|
-
# @return [void]
|
32
|
+
# @sig (String, Zeitwerk::Loader) -> void
|
35
33
|
def register(cpath, loader)
|
36
34
|
mutex.synchronize do
|
37
35
|
cpaths[cpath] = loader
|
@@ -42,19 +40,22 @@ module Zeitwerk
|
|
42
40
|
end
|
43
41
|
|
44
42
|
# @private
|
45
|
-
# @
|
46
|
-
# @return [void]
|
43
|
+
# @sig (Zeitwerk::Loader) -> void
|
47
44
|
def unregister(loader)
|
48
45
|
cpaths.delete_if { |_cpath, l| l == loader }
|
49
46
|
disable_tracer_if_unneeded
|
50
47
|
end
|
51
48
|
|
49
|
+
private
|
50
|
+
|
51
|
+
# @sig () -> void
|
52
52
|
def disable_tracer_if_unneeded
|
53
53
|
mutex.synchronize do
|
54
54
|
tracer.disable if cpaths.empty?
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
# @sig (TracePoint) -> void
|
58
59
|
def tracepoint_class_callback(event)
|
59
60
|
# If the class is a singleton class, we won't do anything with it so we
|
60
61
|
# can bail out immediately. This is several orders of magnitude faster
|
@@ -2,16 +2,14 @@
|
|
2
2
|
|
3
3
|
module Zeitwerk
|
4
4
|
class GemInflector < Inflector
|
5
|
-
# @
|
5
|
+
# @sig (String) -> void
|
6
6
|
def initialize(root_file)
|
7
7
|
namespace = File.basename(root_file, ".rb")
|
8
8
|
lib_dir = File.dirname(root_file)
|
9
9
|
@version_file = File.join(lib_dir, namespace, "version.rb")
|
10
10
|
end
|
11
11
|
|
12
|
-
# @
|
13
|
-
# @param abspath [String]
|
14
|
-
# @return [String]
|
12
|
+
# @sig (String, String) -> String
|
15
13
|
def camelize(basename, abspath)
|
16
14
|
abspath == @version_file ? "VERSION" : super
|
17
15
|
end
|
data/lib/zeitwerk/inflector.rb
CHANGED
@@ -11,9 +11,7 @@ module Zeitwerk
|
|
11
11
|
#
|
12
12
|
# Takes into account hard-coded mappings configured with `inflect`.
|
13
13
|
#
|
14
|
-
# @
|
15
|
-
# @param _abspath [String]
|
16
|
-
# @return [String]
|
14
|
+
# @sig (String, String) -> String
|
17
15
|
def camelize(basename, _abspath)
|
18
16
|
overrides[basename] || basename.split('_').each(&:capitalize!).join
|
19
17
|
end
|
@@ -30,8 +28,7 @@ module Zeitwerk
|
|
30
28
|
# inflector.camelize("mysql_adapter", abspath) # => "MySQLAdapter"
|
31
29
|
# inflector.camelize("users_controller", abspath) # => "UsersController"
|
32
30
|
#
|
33
|
-
# @
|
34
|
-
# @return [void]
|
31
|
+
# @sig (Hash[String, String]) -> void
|
35
32
|
def inflect(inflections)
|
36
33
|
overrides.merge!(inflections)
|
37
34
|
end
|
@@ -41,7 +38,7 @@ module Zeitwerk
|
|
41
38
|
# Hard-coded basename to constant name user maps that override the default
|
42
39
|
# inflection logic.
|
43
40
|
#
|
44
|
-
# @
|
41
|
+
# @sig () -> Hash[String, String]
|
45
42
|
def overrides
|
46
43
|
@overrides ||= {}
|
47
44
|
end
|
data/lib/zeitwerk/kernel.rb
CHANGED
@@ -19,8 +19,7 @@ module Kernel
|
|
19
19
|
# already existing ancestor chains.
|
20
20
|
alias_method :zeitwerk_original_require, :require
|
21
21
|
|
22
|
-
# @
|
23
|
-
# @return [Boolean]
|
22
|
+
# @sig (String) -> true | false
|
24
23
|
def require(path)
|
25
24
|
if loader = Zeitwerk::Registry.loader_for(path)
|
26
25
|
if path.end_with?(".rb")
|
data/lib/zeitwerk/loader.rb
CHANGED
@@ -9,13 +9,13 @@ module Zeitwerk
|
|
9
9
|
include Callbacks
|
10
10
|
include RealModName
|
11
11
|
|
12
|
-
# @
|
12
|
+
# @sig String
|
13
13
|
attr_reader :tag
|
14
14
|
|
15
|
-
# @
|
15
|
+
# @sig #camelize
|
16
16
|
attr_accessor :inflector
|
17
17
|
|
18
|
-
# @
|
18
|
+
# @sig #call | #debug | nil
|
19
19
|
attr_accessor :logger
|
20
20
|
|
21
21
|
# Absolute paths of the root directories. Stored in a hash to preserve
|
@@ -30,20 +30,20 @@ module Zeitwerk
|
|
30
30
|
# interface for it is `push_dir` and `dirs`.
|
31
31
|
#
|
32
32
|
# @private
|
33
|
-
# @
|
33
|
+
# @sig Hash[String, true]
|
34
34
|
attr_reader :root_dirs
|
35
35
|
|
36
36
|
# Absolute paths of files or directories that have to be preloaded.
|
37
37
|
#
|
38
38
|
# @private
|
39
|
-
# @
|
39
|
+
# @sig Array[String]
|
40
40
|
attr_reader :preloads
|
41
41
|
|
42
42
|
# Absolute paths of files, directories, or glob patterns to be totally
|
43
43
|
# ignored.
|
44
44
|
#
|
45
45
|
# @private
|
46
|
-
# @
|
46
|
+
# @sig Set[String]
|
47
47
|
attr_reader :ignored_glob_patterns
|
48
48
|
|
49
49
|
# The actual collection of absolute file and directory names at the time the
|
@@ -51,20 +51,20 @@ module Zeitwerk
|
|
51
51
|
# reload.
|
52
52
|
#
|
53
53
|
# @private
|
54
|
-
# @
|
54
|
+
# @sig Set[String]
|
55
55
|
attr_reader :ignored_paths
|
56
56
|
|
57
57
|
# Absolute paths of directories or glob patterns to be collapsed.
|
58
58
|
#
|
59
59
|
# @private
|
60
|
-
# @
|
60
|
+
# @sig Set[String]
|
61
61
|
attr_reader :collapse_glob_patterns
|
62
62
|
|
63
63
|
# The actual collection of absolute directory names at the time the collapse
|
64
64
|
# glob patterns were expanded. Computed on setup, and recomputed on reload.
|
65
65
|
#
|
66
66
|
# @private
|
67
|
-
# @
|
67
|
+
# @sig Set[String]
|
68
68
|
attr_reader :collapse_dirs
|
69
69
|
|
70
70
|
# Maps real absolute paths for which an autoload has been set ---and not
|
@@ -76,7 +76,7 @@ module Zeitwerk
|
|
76
76
|
# ...
|
77
77
|
#
|
78
78
|
# @private
|
79
|
-
# @
|
79
|
+
# @sig Hash[String, [Module, Symbol]]
|
80
80
|
attr_reader :autoloads
|
81
81
|
|
82
82
|
# We keep track of autoloaded directories to remove them from the registry
|
@@ -86,7 +86,7 @@ module Zeitwerk
|
|
86
86
|
# to concurrency (see why in Zeitwerk::Loader::Callbacks#on_dir_autoloaded).
|
87
87
|
#
|
88
88
|
# @private
|
89
|
-
# @
|
89
|
+
# @sig Array[String]
|
90
90
|
attr_reader :autoloaded_dirs
|
91
91
|
|
92
92
|
# Stores metadata needed for unloading. Its entries look like this:
|
@@ -102,7 +102,7 @@ module Zeitwerk
|
|
102
102
|
# or eager loaded. Otherwise, the collection remains empty.
|
103
103
|
#
|
104
104
|
# @private
|
105
|
-
# @
|
105
|
+
# @sig Hash[String, [String, [Module, Symbol]]]
|
106
106
|
attr_reader :to_unload
|
107
107
|
|
108
108
|
# Maps constant paths of namespaces to arrays of corresponding directories.
|
@@ -120,21 +120,21 @@ module Zeitwerk
|
|
120
120
|
# up the corresponding autoloads.
|
121
121
|
#
|
122
122
|
# @private
|
123
|
-
# @
|
123
|
+
# @sig Hash[String, Array[String]]
|
124
124
|
attr_reader :lazy_subdirs
|
125
125
|
|
126
126
|
# Absolute paths of files or directories not to be eager loaded.
|
127
127
|
#
|
128
128
|
# @private
|
129
|
-
# @
|
129
|
+
# @sig Set[String]
|
130
130
|
attr_reader :eager_load_exclusions
|
131
131
|
|
132
132
|
# @private
|
133
|
-
# @
|
133
|
+
# @sig Mutex
|
134
134
|
attr_reader :mutex
|
135
135
|
|
136
136
|
# @private
|
137
|
-
# @
|
137
|
+
# @sig Mutex
|
138
138
|
attr_reader :mutex2
|
139
139
|
|
140
140
|
def initialize
|
@@ -170,7 +170,7 @@ module Zeitwerk
|
|
170
170
|
# Sets a tag for the loader, useful for logging.
|
171
171
|
#
|
172
172
|
# @param tag [#to_s]
|
173
|
-
# @
|
173
|
+
# @sig (#to_s) -> void
|
174
174
|
def tag=(tag)
|
175
175
|
@tag = tag.to_s
|
176
176
|
end
|
@@ -178,7 +178,7 @@ module Zeitwerk
|
|
178
178
|
# Absolute paths of the root directories. This is a read-only collection,
|
179
179
|
# please push here via `push_dir`.
|
180
180
|
#
|
181
|
-
# @
|
181
|
+
# @sig () -> Array[String]
|
182
182
|
def dirs
|
183
183
|
root_dirs.keys.freeze
|
184
184
|
end
|
@@ -189,10 +189,8 @@ module Zeitwerk
|
|
189
189
|
# the same process already manages that directory or one of its ascendants
|
190
190
|
# or descendants.
|
191
191
|
#
|
192
|
-
# @param path [<String, Pathname>]
|
193
|
-
# @param namespace [Class, Module]
|
194
192
|
# @raise [Zeitwerk::Error]
|
195
|
-
# @
|
193
|
+
# @sig (String | Pathname, Module) -> void
|
196
194
|
def push_dir(path, namespace: Object)
|
197
195
|
# Note that Class < Module.
|
198
196
|
unless namespace.is_a?(Module)
|
@@ -212,7 +210,7 @@ module Zeitwerk
|
|
212
210
|
# There is no way to undo this, either you want to reload or you don't.
|
213
211
|
#
|
214
212
|
# @raise [Zeitwerk::Error]
|
215
|
-
# @
|
213
|
+
# @sig () -> void
|
216
214
|
def enable_reloading
|
217
215
|
mutex.synchronize do
|
218
216
|
break if @reloading_enabled
|
@@ -225,15 +223,14 @@ module Zeitwerk
|
|
225
223
|
end
|
226
224
|
end
|
227
225
|
|
228
|
-
# @
|
226
|
+
# @sig () -> bool
|
229
227
|
def reloading_enabled?
|
230
228
|
@reloading_enabled
|
231
229
|
end
|
232
230
|
|
233
231
|
# Files or directories to be preloaded instead of lazy loaded.
|
234
232
|
#
|
235
|
-
# @
|
236
|
-
# @return [void]
|
233
|
+
# @sig (*(String | Pathname | Array[String | Pathname])) -> void
|
237
234
|
def preload(*paths)
|
238
235
|
mutex.synchronize do
|
239
236
|
expand_paths(paths).each do |abspath|
|
@@ -245,8 +242,7 @@ module Zeitwerk
|
|
245
242
|
|
246
243
|
# Configure files, directories, or glob patterns to be totally ignored.
|
247
244
|
#
|
248
|
-
# @
|
249
|
-
# @return [void]
|
245
|
+
# @sig (*(String | Pathname | Array[String | Pathname])) -> void
|
250
246
|
def ignore(*glob_patterns)
|
251
247
|
glob_patterns = expand_paths(glob_patterns)
|
252
248
|
mutex.synchronize do
|
@@ -257,8 +253,7 @@ module Zeitwerk
|
|
257
253
|
|
258
254
|
# Configure directories or glob patterns to be collapsed.
|
259
255
|
#
|
260
|
-
# @
|
261
|
-
# @return [void]
|
256
|
+
# @sig (*(String | Pathname | Array[String | Pathname])) -> void
|
262
257
|
def collapse(*glob_patterns)
|
263
258
|
glob_patterns = expand_paths(glob_patterns)
|
264
259
|
mutex.synchronize do
|
@@ -269,7 +264,7 @@ module Zeitwerk
|
|
269
264
|
|
270
265
|
# Sets autoloads in the root namespace and preloads files, if any.
|
271
266
|
#
|
272
|
-
# @
|
267
|
+
# @sig () -> void
|
273
268
|
def setup
|
274
269
|
mutex.synchronize do
|
275
270
|
break if @setup
|
@@ -291,7 +286,7 @@ module Zeitwerk
|
|
291
286
|
# unload them.
|
292
287
|
#
|
293
288
|
# @private
|
294
|
-
# @
|
289
|
+
# @sig () -> void
|
295
290
|
def unload
|
296
291
|
mutex.synchronize do
|
297
292
|
# We are going to keep track of the files that were required by our
|
@@ -354,7 +349,7 @@ module Zeitwerk
|
|
354
349
|
# client code in the README of the project.
|
355
350
|
#
|
356
351
|
# @raise [Zeitwerk::Error]
|
357
|
-
# @
|
352
|
+
# @sig () -> void
|
358
353
|
def reload
|
359
354
|
if reloading_enabled?
|
360
355
|
unload
|
@@ -371,7 +366,7 @@ module Zeitwerk
|
|
371
366
|
# are not eager loaded. You can opt-out specifically in specific files and
|
372
367
|
# directories with `do_not_eager_load`.
|
373
368
|
#
|
374
|
-
# @
|
369
|
+
# @sig () -> void
|
375
370
|
def eager_load
|
376
371
|
mutex.synchronize do
|
377
372
|
break if @eager_loaded
|
@@ -414,8 +409,7 @@ module Zeitwerk
|
|
414
409
|
# Let eager load ignore the given files or directories. The constants
|
415
410
|
# defined in those files are still autoloadable.
|
416
411
|
#
|
417
|
-
# @
|
418
|
-
# @return [void]
|
412
|
+
# @sig (*(String | Pathname | Array[String | Pathname])) -> void
|
419
413
|
def do_not_eager_load(*paths)
|
420
414
|
mutex.synchronize { eager_load_exclusions.merge(expand_paths(paths)) }
|
421
415
|
end
|
@@ -423,8 +417,7 @@ module Zeitwerk
|
|
423
417
|
# Says if the given constant path would be unloaded on reload. This
|
424
418
|
# predicate returns `false` if reloading is disabled.
|
425
419
|
#
|
426
|
-
# @
|
427
|
-
# @return [Boolean]
|
420
|
+
# @sig (String) -> bool
|
428
421
|
def unloadable_cpath?(cpath)
|
429
422
|
to_unload.key?(cpath)
|
430
423
|
end
|
@@ -432,21 +425,20 @@ module Zeitwerk
|
|
432
425
|
# Returns an array with the constant paths that would be unloaded on reload.
|
433
426
|
# This predicate returns an empty array if reloading is disabled.
|
434
427
|
#
|
435
|
-
# @
|
428
|
+
# @sig () -> Array[String]
|
436
429
|
def unloadable_cpaths
|
437
430
|
to_unload.keys.freeze
|
438
431
|
end
|
439
432
|
|
440
433
|
# Logs to `$stdout`, handy shortcut for debugging.
|
441
434
|
#
|
442
|
-
# @
|
435
|
+
# @sig () -> void
|
443
436
|
def log!
|
444
437
|
@logger = ->(msg) { puts msg }
|
445
438
|
end
|
446
439
|
|
447
440
|
# @private
|
448
|
-
# @
|
449
|
-
# @return [Boolean]
|
441
|
+
# @sig (String) -> bool
|
450
442
|
def manages?(dir)
|
451
443
|
dir = dir + "/"
|
452
444
|
ignored_paths.each do |ignored_path|
|
@@ -463,11 +455,11 @@ module Zeitwerk
|
|
463
455
|
# --- Class methods ---------------------------------------------------------------------------
|
464
456
|
|
465
457
|
class << self
|
466
|
-
# @
|
458
|
+
# @sig #call | #debug | nil
|
467
459
|
attr_accessor :default_logger
|
468
460
|
|
469
461
|
# @private
|
470
|
-
# @
|
462
|
+
# @sig Mutex
|
471
463
|
attr_accessor :mutex
|
472
464
|
|
473
465
|
# This is a shortcut for
|
@@ -481,7 +473,7 @@ module Zeitwerk
|
|
481
473
|
# except that this method returns the same object in subsequent calls from
|
482
474
|
# the same file, in the unlikely case the gem wants to be able to reload.
|
483
475
|
#
|
484
|
-
# @
|
476
|
+
# @sig () -> Zeitwerk::Loader
|
485
477
|
def for_gem
|
486
478
|
called_from = caller_locations(1, 1).first.path
|
487
479
|
Registry.loader_for_gem(called_from)
|
@@ -489,7 +481,7 @@ module Zeitwerk
|
|
489
481
|
|
490
482
|
# Broadcasts `eager_load` to all loaders.
|
491
483
|
#
|
492
|
-
# @
|
484
|
+
# @sig () -> void
|
493
485
|
def eager_load_all
|
494
486
|
Registry.loaders.each(&:eager_load)
|
495
487
|
end
|
@@ -497,7 +489,7 @@ module Zeitwerk
|
|
497
489
|
# Returns an array with the absolute paths of the root directories of all
|
498
490
|
# registered loaders. This is a read-only collection.
|
499
491
|
#
|
500
|
-
# @
|
492
|
+
# @sig () -> Array[String]
|
501
493
|
def all_dirs
|
502
494
|
Registry.loaders.flat_map(&:dirs).freeze
|
503
495
|
end
|
@@ -507,16 +499,14 @@ module Zeitwerk
|
|
507
499
|
|
508
500
|
private # -------------------------------------------------------------------------------------
|
509
501
|
|
510
|
-
# @
|
502
|
+
# @sig () -> Array[String]
|
511
503
|
def actual_root_dirs
|
512
504
|
root_dirs.reject do |root_dir, _namespace|
|
513
505
|
!dir?(root_dir) || ignored_paths.member?(root_dir)
|
514
506
|
end
|
515
507
|
end
|
516
508
|
|
517
|
-
# @
|
518
|
-
# @param parent [Module]
|
519
|
-
# @return [void]
|
509
|
+
# @sig (String, Module) -> void
|
520
510
|
def set_autoloads_in_dir(dir, parent)
|
521
511
|
ls(dir) do |basename, abspath|
|
522
512
|
begin
|
@@ -559,10 +549,7 @@ module Zeitwerk
|
|
559
549
|
end
|
560
550
|
end
|
561
551
|
|
562
|
-
# @
|
563
|
-
# @param cname [Symbol]
|
564
|
-
# @param subdir [String]
|
565
|
-
# @return [void]
|
552
|
+
# @sig (Module, Symbol, String) -> void
|
566
553
|
def autoload_subdir(parent, cname, subdir)
|
567
554
|
if autoload_path = autoload_for?(parent, cname)
|
568
555
|
cpath = cpath(parent, cname)
|
@@ -582,10 +569,7 @@ module Zeitwerk
|
|
582
569
|
end
|
583
570
|
end
|
584
571
|
|
585
|
-
# @
|
586
|
-
# @param cname [Symbol]
|
587
|
-
# @param file [String]
|
588
|
-
# @return [void]
|
572
|
+
# @sig (Module, Symbol, String) -> void
|
589
573
|
def autoload_file(parent, cname, file)
|
590
574
|
if autoload_path = autoload_for?(parent, cname)
|
591
575
|
# First autoload for a Ruby file wins, just ignore subsequent ones.
|
@@ -606,11 +590,10 @@ module Zeitwerk
|
|
606
590
|
end
|
607
591
|
end
|
608
592
|
|
609
|
-
#
|
610
|
-
#
|
611
|
-
#
|
612
|
-
# @
|
613
|
-
# @return [void]
|
593
|
+
# `dir` is the directory that would have autovivified a namespace. `file` is
|
594
|
+
# the file where we've found the namespace is explicitly defined.
|
595
|
+
#
|
596
|
+
# @sig (dir: String, file: String, parent: Module, cname: Symbol) -> void
|
614
597
|
def promote_namespace_from_implicit_to_explicit(dir:, file:, parent:, cname:)
|
615
598
|
autoloads.delete(dir)
|
616
599
|
Registry.unregister_autoload(dir)
|
@@ -619,10 +602,7 @@ module Zeitwerk
|
|
619
602
|
register_explicit_namespace(cpath(parent, cname))
|
620
603
|
end
|
621
604
|
|
622
|
-
# @
|
623
|
-
# @param cname [Symbol]
|
624
|
-
# @param abspath [String]
|
625
|
-
# @return [void]
|
605
|
+
# @sig (Module, Symbol, String) -> void
|
626
606
|
def set_autoload(parent, cname, abspath)
|
627
607
|
# $LOADED_FEATURES stores real paths since Ruby 2.4.4. We set and save the
|
628
608
|
# real path to be able to delete it from $LOADED_FEATURES on unload, and to
|
@@ -649,9 +629,7 @@ module Zeitwerk
|
|
649
629
|
end
|
650
630
|
end
|
651
631
|
|
652
|
-
# @
|
653
|
-
# @param cname [Symbol]
|
654
|
-
# @return [String, nil]
|
632
|
+
# @sig (Module, Symbol) -> String?
|
655
633
|
def autoload_for?(parent, cname)
|
656
634
|
strict_autoload_path(parent, cname) || Registry.inception?(cpath(parent, cname))
|
657
635
|
end
|
@@ -672,9 +650,7 @@ module Zeitwerk
|
|
672
650
|
#
|
673
651
|
# We need a way to strictly check in parent ignoring ancestors.
|
674
652
|
#
|
675
|
-
# @
|
676
|
-
# @param cname [Symbol]
|
677
|
-
# @return [String, nil]
|
653
|
+
# @sig (Module, Symbol) -> String?
|
678
654
|
if method(:autoload?).arity == 1
|
679
655
|
def strict_autoload_path(parent, cname)
|
680
656
|
parent.autoload?(cname) if cdef?(parent, cname)
|
@@ -688,15 +664,14 @@ module Zeitwerk
|
|
688
664
|
# This method is called this way because I prefer `preload` to be the method
|
689
665
|
# name to configure preloads in the public interface.
|
690
666
|
#
|
691
|
-
# @
|
667
|
+
# @sig () -> void
|
692
668
|
def do_preload
|
693
669
|
preloads.each do |abspath|
|
694
670
|
do_preload_abspath(abspath)
|
695
671
|
end
|
696
672
|
end
|
697
673
|
|
698
|
-
# @
|
699
|
-
# @return [void]
|
674
|
+
# @sig (String) -> void
|
700
675
|
def do_preload_abspath(abspath)
|
701
676
|
if ruby?(abspath)
|
702
677
|
do_preload_file(abspath)
|
@@ -705,31 +680,25 @@ module Zeitwerk
|
|
705
680
|
end
|
706
681
|
end
|
707
682
|
|
708
|
-
# @
|
709
|
-
# @return [void]
|
683
|
+
# @sig (String) -> void
|
710
684
|
def do_preload_dir(dir)
|
711
685
|
ls(dir) do |_basename, abspath|
|
712
686
|
do_preload_abspath(abspath)
|
713
687
|
end
|
714
688
|
end
|
715
689
|
|
716
|
-
# @
|
717
|
-
# @return [Boolean]
|
690
|
+
# @sig (String) -> bool
|
718
691
|
def do_preload_file(file)
|
719
692
|
log("preloading #{file}") if logger
|
720
693
|
require file
|
721
694
|
end
|
722
695
|
|
723
|
-
# @
|
724
|
-
# @param cname [Symbol]
|
725
|
-
# @return [String]
|
696
|
+
# @sig (Module, Symbol) -> String
|
726
697
|
def cpath(parent, cname)
|
727
698
|
parent.equal?(Object) ? cname.to_s : "#{real_mod_name(parent)}::#{cname}"
|
728
699
|
end
|
729
700
|
|
730
|
-
# @
|
731
|
-
# @yieldparam path [String, String]
|
732
|
-
# @return [void]
|
701
|
+
# @sig (String) { (String, String) -> void } -> void
|
733
702
|
def ls(dir)
|
734
703
|
Dir.foreach(dir) do |basename|
|
735
704
|
next if basename.start_with?(".")
|
@@ -743,57 +712,55 @@ module Zeitwerk
|
|
743
712
|
end
|
744
713
|
end
|
745
714
|
|
746
|
-
# @
|
747
|
-
# @return [Boolean]
|
715
|
+
# @sig (String) -> bool
|
748
716
|
def ruby?(path)
|
749
717
|
path.end_with?(".rb")
|
750
718
|
end
|
751
719
|
|
752
|
-
# @
|
753
|
-
# @return [Boolean]
|
720
|
+
# @sig (String) -> bool
|
754
721
|
def dir?(path)
|
755
722
|
File.directory?(path)
|
756
723
|
end
|
757
724
|
|
758
|
-
# @
|
759
|
-
# @return [<String>]
|
725
|
+
# @sig (String | Pathname | Array[String | Pathname]) -> Array[String]
|
760
726
|
def expand_paths(paths)
|
761
727
|
paths.flatten.map! { |path| File.expand_path(path) }
|
762
728
|
end
|
763
729
|
|
764
|
-
# @
|
765
|
-
# @return [<String>]
|
730
|
+
# @sig (Array[String]) -> Array[String]
|
766
731
|
def expand_glob_patterns(glob_patterns)
|
767
732
|
# Note that Dir.glob works with regular file names just fine. That is,
|
768
733
|
# glob patterns technically need no wildcards.
|
769
734
|
glob_patterns.flat_map { |glob_pattern| Dir.glob(glob_pattern) }
|
770
735
|
end
|
771
736
|
|
772
|
-
# @
|
737
|
+
# @sig () -> void
|
773
738
|
def recompute_ignored_paths
|
774
739
|
ignored_paths.replace(expand_glob_patterns(ignored_glob_patterns))
|
775
740
|
end
|
776
741
|
|
777
|
-
# @
|
742
|
+
# @sig () -> void
|
778
743
|
def recompute_collapse_dirs
|
779
744
|
collapse_dirs.replace(expand_glob_patterns(collapse_glob_patterns))
|
780
745
|
end
|
781
746
|
|
782
|
-
# @
|
783
|
-
# @return [void]
|
747
|
+
# @sig (String) -> void
|
784
748
|
def log(message)
|
785
749
|
method_name = logger.respond_to?(:debug) ? :debug : :call
|
786
750
|
logger.send(method_name, "Zeitwerk@#{tag}: #{message}")
|
787
751
|
end
|
788
752
|
|
753
|
+
# @sig (Module, Symbol) -> bool
|
789
754
|
def cdef?(parent, cname)
|
790
755
|
parent.const_defined?(cname, false)
|
791
756
|
end
|
792
757
|
|
758
|
+
# @sig (String) -> void
|
793
759
|
def register_explicit_namespace(cpath)
|
794
760
|
ExplicitNamespace.register(cpath, self)
|
795
761
|
end
|
796
762
|
|
763
|
+
# @sig (String) -> void
|
797
764
|
def raise_if_conflicting_directory(dir)
|
798
765
|
self.class.mutex.synchronize do
|
799
766
|
Registry.loaders.each do |loader|
|
@@ -808,19 +775,15 @@ module Zeitwerk
|
|
808
775
|
end
|
809
776
|
end
|
810
777
|
|
811
|
-
# @
|
812
|
-
# @param cname [Symbol]
|
813
|
-
# @return [void]
|
778
|
+
# @sig (Module, Symbol) -> void
|
814
779
|
def unload_autoload(parent, cname)
|
815
|
-
parent.
|
780
|
+
parent.__send__(:remove_const, cname)
|
816
781
|
log("autoload for #{cpath(parent, cname)} removed") if logger
|
817
782
|
end
|
818
783
|
|
819
|
-
# @
|
820
|
-
# @param cname [Symbol]
|
821
|
-
# @return [void]
|
784
|
+
# @sig (Module, Symbol) -> void
|
822
785
|
def unload_cref(parent, cname)
|
823
|
-
parent.
|
786
|
+
parent.__send__(:remove_const, cname)
|
824
787
|
log("#{cpath(parent, cname)} unloaded") if logger
|
825
788
|
end
|
826
789
|
end
|
@@ -4,8 +4,7 @@ module Zeitwerk::Loader::Callbacks
|
|
4
4
|
# Invoked from our decorated Kernel#require when a managed file is autoloaded.
|
5
5
|
#
|
6
6
|
# @private
|
7
|
-
# @
|
8
|
-
# @return [void]
|
7
|
+
# @sig (String) -> void
|
9
8
|
def on_file_autoloaded(file)
|
10
9
|
cref = autoloads.delete(file)
|
11
10
|
to_unload[cpath(*cref)] = [file, cref] if reloading_enabled?
|
@@ -22,8 +21,7 @@ module Zeitwerk::Loader::Callbacks
|
|
22
21
|
# autoloaded.
|
23
22
|
#
|
24
23
|
# @private
|
25
|
-
# @
|
26
|
-
# @return [void]
|
24
|
+
# @sig (String) -> void
|
27
25
|
def on_dir_autoloaded(dir)
|
28
26
|
# Module#autoload does not serialize concurrent requires, and we handle
|
29
27
|
# directories ourselves, so the callback needs to account for concurrency.
|
@@ -59,8 +57,7 @@ module Zeitwerk::Loader::Callbacks
|
|
59
57
|
# subdirectories, we descend into them now.
|
60
58
|
#
|
61
59
|
# @private
|
62
|
-
# @
|
63
|
-
# @return [void]
|
60
|
+
# @sig (Module) -> void
|
64
61
|
def on_namespace_loaded(namespace)
|
65
62
|
if subdirs = lazy_subdirs.delete(real_mod_name(namespace))
|
66
63
|
subdirs.each do |subdir|
|
@@ -7,8 +7,7 @@ module Zeitwerk::RealModName
|
|
7
7
|
#
|
8
8
|
# The name method can be overridden, hence the indirection in this method.
|
9
9
|
#
|
10
|
-
# @
|
11
|
-
# @return [String, nil]
|
10
|
+
# @sig (Module) -> String?
|
12
11
|
if UnboundMethod.method_defined?(:bind_call)
|
13
12
|
def real_mod_name(mod)
|
14
13
|
UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
|
data/lib/zeitwerk/registry.rb
CHANGED
@@ -7,14 +7,14 @@ module Zeitwerk
|
|
7
7
|
# them from being garbage collected.
|
8
8
|
#
|
9
9
|
# @private
|
10
|
-
# @
|
10
|
+
# @sig Array[Zeitwerk::Loader]
|
11
11
|
attr_reader :loaders
|
12
12
|
|
13
13
|
# Registers loaders created with `for_gem` to make the method idempotent
|
14
14
|
# in case of reload.
|
15
15
|
#
|
16
16
|
# @private
|
17
|
-
# @
|
17
|
+
# @sig Hash[String, Zeitwerk::Loader]
|
18
18
|
attr_reader :loaders_managing_gems
|
19
19
|
|
20
20
|
# Maps real paths to the loaders responsible for them.
|
@@ -23,7 +23,7 @@ module Zeitwerk
|
|
23
23
|
# invoke callbacks and autovivify modules.
|
24
24
|
#
|
25
25
|
# @private
|
26
|
-
# @
|
26
|
+
# @sig Hash[String, Zeitwerk::Loader]
|
27
27
|
attr_reader :autoloads
|
28
28
|
|
29
29
|
# This hash table addresses an edge case in which an autoload is ignored.
|
@@ -62,14 +62,13 @@ module Zeitwerk
|
|
62
62
|
# end
|
63
63
|
#
|
64
64
|
# @private
|
65
|
-
# @
|
65
|
+
# @sig Hash[String, [String, Zeitwerk::Loader]]
|
66
66
|
attr_reader :inceptions
|
67
67
|
|
68
68
|
# Registers a loader.
|
69
69
|
#
|
70
70
|
# @private
|
71
|
-
# @
|
72
|
-
# @return [void]
|
71
|
+
# @sig (Zeitwerk::Loader) -> void
|
73
72
|
def register_loader(loader)
|
74
73
|
loaders << loader
|
75
74
|
end
|
@@ -78,8 +77,7 @@ module Zeitwerk
|
|
78
77
|
# file. That is how Zeitwerk::Loader.for_gem is idempotent.
|
79
78
|
#
|
80
79
|
# @private
|
81
|
-
# @
|
82
|
-
# @return [Zeitwerk::Loader]
|
80
|
+
# @sig (String) -> Zeitwerk::Loader
|
83
81
|
def loader_for_gem(root_file)
|
84
82
|
loaders_managing_gems[root_file] ||= begin
|
85
83
|
Loader.new.tap do |loader|
|
@@ -91,32 +89,25 @@ module Zeitwerk
|
|
91
89
|
end
|
92
90
|
|
93
91
|
# @private
|
94
|
-
# @
|
95
|
-
# @param realpath [String]
|
96
|
-
# @return [void]
|
92
|
+
# @sig (Zeitwerk::Loader, String) -> String
|
97
93
|
def register_autoload(loader, realpath)
|
98
94
|
autoloads[realpath] = loader
|
99
95
|
end
|
100
96
|
|
101
97
|
# @private
|
102
|
-
# @
|
103
|
-
# @return [void]
|
98
|
+
# @sig (String) -> void
|
104
99
|
def unregister_autoload(realpath)
|
105
100
|
autoloads.delete(realpath)
|
106
101
|
end
|
107
102
|
|
108
103
|
# @private
|
109
|
-
# @
|
110
|
-
# @param realpath [String]
|
111
|
-
# @param loader [Zeitwerk::Loader]
|
112
|
-
# @return [void]
|
104
|
+
# @sig (String, String, Zeitwerk::Loader) -> void
|
113
105
|
def register_inception(cpath, realpath, loader)
|
114
106
|
inceptions[cpath] = [realpath, loader]
|
115
107
|
end
|
116
108
|
|
117
109
|
# @private
|
118
|
-
# @
|
119
|
-
# @return [String, nil]
|
110
|
+
# @sig (String) -> String?
|
120
111
|
def inception?(cpath)
|
121
112
|
if pair = inceptions[cpath]
|
122
113
|
pair.first
|
@@ -124,15 +115,13 @@ module Zeitwerk
|
|
124
115
|
end
|
125
116
|
|
126
117
|
# @private
|
127
|
-
# @
|
128
|
-
# @return [Zeitwerk::Loader, nil]
|
118
|
+
# @sig (String) -> Zeitwerk::Loader?
|
129
119
|
def loader_for(path)
|
130
120
|
autoloads[path]
|
131
121
|
end
|
132
122
|
|
133
123
|
# @private
|
134
|
-
# @
|
135
|
-
# @return [void]
|
124
|
+
# @sig (Zeitwerk::Loader) -> void
|
136
125
|
def on_unload(loader)
|
137
126
|
autoloads.delete_if { |_path, object| object == loader }
|
138
127
|
inceptions.delete_if { |_cpath, (_path, object)| object == loader }
|
data/lib/zeitwerk/version.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.4.
|
4
|
+
version: 2.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Xavier Noria
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|
@@ -41,7 +41,7 @@ metadata:
|
|
41
41
|
changelog_uri: https://github.com/fxn/zeitwerk/blob/master/CHANGELOG.md
|
42
42
|
source_code_uri: https://github.com/fxn/zeitwerk
|
43
43
|
bug_tracker_uri: https://github.com/fxn/zeitwerk/issues
|
44
|
-
post_install_message:
|
44
|
+
post_install_message:
|
45
45
|
rdoc_options: []
|
46
46
|
require_paths:
|
47
47
|
- lib
|
@@ -57,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
57
|
version: '0'
|
58
58
|
requirements: []
|
59
59
|
rubygems_version: 3.1.2
|
60
|
-
signing_key:
|
60
|
+
signing_key:
|
61
61
|
specification_version: 4
|
62
62
|
summary: Efficient and thread-safe constant autoloader
|
63
63
|
test_files: []
|