zeitwerk 2.6.3 → 2.6.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -2
- data/lib/zeitwerk/error.rb +6 -0
- data/lib/zeitwerk/explicit_namespace.rb +8 -11
- data/lib/zeitwerk/internal.rb +12 -0
- data/lib/zeitwerk/loader/config.rb +53 -43
- data/lib/zeitwerk/loader/eager_load.rb +21 -15
- data/lib/zeitwerk/loader/helpers.rb +15 -17
- data/lib/zeitwerk/loader.rb +31 -15
- data/lib/zeitwerk/version.rb +1 -1
- data/lib/zeitwerk.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 220865ab50f0336d05c8b907a08a94a8bf92843ba688c1a9686163a190b0754e
|
4
|
+
data.tar.gz: a9d62662351c60a3b264a68726a84c41fc4b6f92eaae855fb4887e3eb4cf14a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b802eaabb27e6268eafa8d35d4402819551203c5fd2a1692d9a9bae65075bf668f5aecfaf3fa6c763987796b60fd5bde4704aebda4a8bfa1c74a526c27264ab5
|
7
|
+
data.tar.gz: c4a53a60b49e5cb83aee148271b26fee8de15fd072327e52363438b0531d9953961305375ec727cc3a0ff2c021254eb081056350165922bd9f6f8b1e1b3e96f3
|
data/README.md
CHANGED
@@ -213,7 +213,7 @@ serializers/user_serializer.rb -> UserSerializer
|
|
213
213
|
<a id="markdown-custom-root-namespaces" name="custom-root-namespaces"></a>
|
214
214
|
#### Custom root namespaces
|
215
215
|
|
216
|
-
While `Object` is by far the most common root namespace, you can associate a different one to a particular root directory. The method `push_dir` accepts a class or module object in the optional `namespace` keyword argument.
|
216
|
+
While `Object` is by far the most common root namespace, you can associate a different one to a particular root directory. The method `push_dir` accepts a non-anonymous class or module object in the optional `namespace` keyword argument.
|
217
217
|
|
218
218
|
For example, given:
|
219
219
|
|
@@ -449,6 +449,8 @@ In gems, the method needs to be invoked after the main namespace has been define
|
|
449
449
|
|
450
450
|
Eager loading is synchronized and idempotent.
|
451
451
|
|
452
|
+
Attempting to eager load without previously calling `setup` raises `Zeitwerk::SetupRequired`.
|
453
|
+
|
452
454
|
<a id="markdown-eager-load-exclusions" name="eager-load-exclusions"></a>
|
453
455
|
#### Eager load exclusions
|
454
456
|
|
@@ -492,6 +494,8 @@ The method checks if a regular eager load was already executed, in which case it
|
|
492
494
|
|
493
495
|
Nested root directories which are descendants of the argument are skipped. Those subtrees are considered to be conceptually apart.
|
494
496
|
|
497
|
+
Attempting to eager load a directory without previously calling `setup` raises `Zeitwerk::SetupRequired`.
|
498
|
+
|
495
499
|
<a id="markdown-eager-load-namespaces" name="eager-load-namespaces"></a>
|
496
500
|
#### Eager load namespaces
|
497
501
|
|
@@ -527,6 +531,8 @@ The method checks if a regular eager load was already executed, in which case it
|
|
527
531
|
|
528
532
|
If root directories are assigned to custom namespaces, the method behaves as you'd expect, according to the namespacing relationship between the custom namespace and the argument.
|
529
533
|
|
534
|
+
Attempting to eager load a namespace without previously calling `setup` raises `Zeitwerk::SetupRequired`.
|
535
|
+
|
530
536
|
<a id="markdown-eager-load-namespaces-shared-by-several-loaders" name="eager-load-namespaces-shared-by-several-loaders"></a>
|
531
537
|
#### Eager load namespaces shared by several loaders
|
532
538
|
|
@@ -540,6 +546,8 @@ This may be handy, for example, if a framework supports plugins and a shared nam
|
|
540
546
|
|
541
547
|
Please, note that loaders only eager load namespaces they manage, as documented above. Therefore, this method does not allow you to eager load namespaces not managed by Zeitwerk loaders.
|
542
548
|
|
549
|
+
This method does not require that all registered loaders have `setup` already invoked, since that is out of your control. If there's any in that state, it is simply skipped.
|
550
|
+
|
543
551
|
<a id="markdown-global-eager-load" name="global-eager-load"></a>
|
544
552
|
#### Global eager load
|
545
553
|
|
@@ -555,6 +563,8 @@ Note that thanks to idempotence `Zeitwerk::Loader.eager_load_all` won't eager lo
|
|
555
563
|
|
556
564
|
This method does not accept the `force` flag, since in general it wouldn't be a good idea to force eager loading in 3rd party code.
|
557
565
|
|
566
|
+
This method does not require that all registered loaders have `setup` already invoked, since that is out of your control. If there's any in that state, it is simply skipped.
|
567
|
+
|
558
568
|
<a id="markdown-loading-individual-files" name="loading-individual-files"></a>
|
559
569
|
### Loading individual files
|
560
570
|
|
@@ -591,7 +601,7 @@ loader.reload
|
|
591
601
|
|
592
602
|
There is no way to undo this, either you want to reload or you don't.
|
593
603
|
|
594
|
-
Enabling reloading after setup raises `Zeitwerk::Error`. Attempting to reload without having it enabled raises `Zeitwerk::ReloadingDisabledError`.
|
604
|
+
Enabling reloading after setup raises `Zeitwerk::Error`. Attempting to reload without having it enabled raises `Zeitwerk::ReloadingDisabledError`. Attempting to reload without previously calling `setup` raises `Zeitwerk::SetupRequired`.
|
595
605
|
|
596
606
|
Generally speaking, reloading is useful while developing running services like web applications. Gems that implement regular libraries, so to speak, or services running in testing or production environments, won't normally have a use case for reloading. If reloading is not enabled, Zeitwerk is able to use less memory.
|
597
607
|
|
data/lib/zeitwerk/error.rb
CHANGED
@@ -11,28 +11,28 @@ module Zeitwerk
|
|
11
11
|
module ExplicitNamespace # :nodoc: all
|
12
12
|
class << self
|
13
13
|
include RealModName
|
14
|
+
extend Internal
|
14
15
|
|
15
16
|
# Maps constant paths that correspond to explicit namespaces according to
|
16
17
|
# the file system, to the loader responsible for them.
|
17
18
|
#
|
18
|
-
# @private
|
19
19
|
# @sig Hash[String, Zeitwerk::Loader]
|
20
20
|
attr_reader :cpaths
|
21
|
+
private :cpaths
|
21
22
|
|
22
|
-
# @private
|
23
23
|
# @sig Mutex
|
24
24
|
attr_reader :mutex
|
25
|
+
private :mutex
|
25
26
|
|
26
|
-
# @private
|
27
27
|
# @sig TracePoint
|
28
28
|
attr_reader :tracer
|
29
|
+
private :tracer
|
29
30
|
|
30
31
|
# Asserts `cpath` corresponds to an explicit namespace for which `loader`
|
31
32
|
# is responsible.
|
32
33
|
#
|
33
|
-
# @private
|
34
34
|
# @sig (String, Zeitwerk::Loader) -> void
|
35
|
-
def register(cpath, loader)
|
35
|
+
internal def register(cpath, loader)
|
36
36
|
mutex.synchronize do
|
37
37
|
cpaths[cpath] = loader
|
38
38
|
# We check enabled? because, looking at the C source code, enabling an
|
@@ -41,24 +41,21 @@ module Zeitwerk
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
# @private
|
45
44
|
# @sig (Zeitwerk::Loader) -> void
|
46
|
-
def unregister_loader(loader)
|
45
|
+
internal def unregister_loader(loader)
|
47
46
|
cpaths.delete_if { |_cpath, l| l == loader }
|
48
47
|
disable_tracer_if_unneeded
|
49
48
|
end
|
50
49
|
|
51
|
-
private
|
52
|
-
|
53
50
|
# @sig () -> void
|
54
|
-
def disable_tracer_if_unneeded
|
51
|
+
private def disable_tracer_if_unneeded
|
55
52
|
mutex.synchronize do
|
56
53
|
tracer.disable if cpaths.empty?
|
57
54
|
end
|
58
55
|
end
|
59
56
|
|
60
57
|
# @sig (TracePoint) -> void
|
61
|
-
def tracepoint_class_callback(event)
|
58
|
+
private def tracepoint_class_callback(event)
|
62
59
|
# If the class is a singleton class, we won't do anything with it so we
|
63
60
|
# can bail out immediately. This is several orders of magnitude faster
|
64
61
|
# than accessing its name.
|
@@ -4,85 +4,91 @@ require "set"
|
|
4
4
|
require "securerandom"
|
5
5
|
|
6
6
|
module Zeitwerk::Loader::Config
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
extend Zeitwerk::Internal
|
8
|
+
include Zeitwerk::RealModName
|
9
|
+
|
10
|
+
# @sig #camelize
|
11
|
+
attr_accessor :inflector
|
12
|
+
|
13
|
+
# @sig #call | #debug | nil
|
14
|
+
attr_accessor :logger
|
15
|
+
|
16
|
+
# Absolute paths of the root directories, mapped to their respective root namespaces:
|
10
17
|
#
|
11
18
|
# "/Users/fxn/blog/app/channels" => Object,
|
12
19
|
# "/Users/fxn/blog/app/adapters" => ActiveJob::QueueAdapters,
|
13
20
|
# ...
|
14
21
|
#
|
22
|
+
# Stored in a hash to preserve order, easily handle duplicates, and have a
|
23
|
+
# fast lookup by directory.
|
24
|
+
#
|
15
25
|
# This is a private collection maintained by the loader. The public
|
16
26
|
# interface for it is `push_dir` and `dirs`.
|
17
27
|
#
|
18
|
-
# @private
|
19
28
|
# @sig Hash[String, Module]
|
20
|
-
attr_reader :
|
21
|
-
|
22
|
-
# @sig #camelize
|
23
|
-
attr_accessor :inflector
|
29
|
+
attr_reader :roots
|
30
|
+
internal :roots
|
24
31
|
|
25
32
|
# Absolute paths of files, directories, or glob patterns to be totally
|
26
33
|
# ignored.
|
27
34
|
#
|
28
|
-
# @private
|
29
35
|
# @sig Set[String]
|
30
36
|
attr_reader :ignored_glob_patterns
|
37
|
+
private :ignored_glob_patterns
|
31
38
|
|
32
39
|
# The actual collection of absolute file and directory names at the time the
|
33
40
|
# ignored glob patterns were expanded. Computed on setup, and recomputed on
|
34
41
|
# reload.
|
35
42
|
#
|
36
|
-
# @private
|
37
43
|
# @sig Set[String]
|
38
44
|
attr_reader :ignored_paths
|
45
|
+
private :ignored_paths
|
39
46
|
|
40
47
|
# Absolute paths of directories or glob patterns to be collapsed.
|
41
48
|
#
|
42
|
-
# @private
|
43
49
|
# @sig Set[String]
|
44
50
|
attr_reader :collapse_glob_patterns
|
51
|
+
private :collapse_glob_patterns
|
45
52
|
|
46
53
|
# The actual collection of absolute directory names at the time the collapse
|
47
54
|
# glob patterns were expanded. Computed on setup, and recomputed on reload.
|
48
55
|
#
|
49
|
-
# @private
|
50
56
|
# @sig Set[String]
|
51
57
|
attr_reader :collapse_dirs
|
58
|
+
private :collapse_dirs
|
52
59
|
|
53
60
|
# Absolute paths of files or directories not to be eager loaded.
|
54
61
|
#
|
55
|
-
# @private
|
56
62
|
# @sig Set[String]
|
57
63
|
attr_reader :eager_load_exclusions
|
64
|
+
private :eager_load_exclusions
|
58
65
|
|
59
66
|
# User-oriented callbacks to be fired on setup and on reload.
|
60
67
|
#
|
61
|
-
# @private
|
62
68
|
# @sig Array[{ () -> void }]
|
63
69
|
attr_reader :on_setup_callbacks
|
70
|
+
private :on_setup_callbacks
|
64
71
|
|
65
72
|
# User-oriented callbacks to be fired when a constant is loaded.
|
66
73
|
#
|
67
|
-
# @private
|
68
74
|
# @sig Hash[String, Array[{ (Object, String) -> void }]]
|
69
75
|
# Hash[Symbol, Array[{ (String, Object, String) -> void }]]
|
70
76
|
attr_reader :on_load_callbacks
|
77
|
+
private :on_load_callbacks
|
71
78
|
|
72
79
|
# User-oriented callbacks to be fired before constants are removed.
|
73
80
|
#
|
74
|
-
# @private
|
75
81
|
# @sig Hash[String, Array[{ (Object, String) -> void }]]
|
76
82
|
# Hash[Symbol, Array[{ (String, Object, String) -> void }]]
|
77
83
|
attr_reader :on_unload_callbacks
|
78
|
-
|
79
|
-
# @sig #call | #debug | nil
|
80
|
-
attr_accessor :logger
|
84
|
+
private :on_unload_callbacks
|
81
85
|
|
82
86
|
def initialize
|
83
|
-
@initialized_at = Time.now
|
84
|
-
@root_dirs = {}
|
85
87
|
@inflector = Zeitwerk::Inflector.new
|
88
|
+
@logger = self.class.default_logger
|
89
|
+
@tag = SecureRandom.hex(3)
|
90
|
+
@initialized_at = Time.now
|
91
|
+
@roots = {}
|
86
92
|
@ignored_glob_patterns = Set.new
|
87
93
|
@ignored_paths = Set.new
|
88
94
|
@collapse_glob_patterns = Set.new
|
@@ -92,8 +98,6 @@ module Zeitwerk::Loader::Config
|
|
92
98
|
@on_setup_callbacks = []
|
93
99
|
@on_load_callbacks = {}
|
94
100
|
@on_unload_callbacks = {}
|
95
|
-
@logger = self.class.default_logger
|
96
|
-
@tag = SecureRandom.hex(3)
|
97
101
|
end
|
98
102
|
|
99
103
|
# Pushes `path` to the list of root directories.
|
@@ -110,10 +114,14 @@ module Zeitwerk::Loader::Config
|
|
110
114
|
raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module object, should be"
|
111
115
|
end
|
112
116
|
|
117
|
+
unless real_mod_name(namespace)
|
118
|
+
raise Zeitwerk::Error, "root namespaces cannot be anonymous"
|
119
|
+
end
|
120
|
+
|
113
121
|
abspath = File.expand_path(path)
|
114
122
|
if dir?(abspath)
|
115
123
|
raise_if_conflicting_directory(abspath)
|
116
|
-
|
124
|
+
roots[abspath] = namespace
|
117
125
|
else
|
118
126
|
raise Zeitwerk::Error, "the root directory #{abspath} does not exist"
|
119
127
|
end
|
@@ -146,9 +154,9 @@ module Zeitwerk::Loader::Config
|
|
146
154
|
# @sig () -> Array[String] | Hash[String, Module]
|
147
155
|
def dirs(namespaces: false)
|
148
156
|
if namespaces
|
149
|
-
|
157
|
+
roots.clone
|
150
158
|
else
|
151
|
-
|
159
|
+
roots.keys
|
152
160
|
end.freeze
|
153
161
|
end
|
154
162
|
|
@@ -275,71 +283,73 @@ module Zeitwerk::Loader::Config
|
|
275
283
|
# Returns true if the argument has been configured to be ignored, or is a
|
276
284
|
# descendant of an ignored directory.
|
277
285
|
#
|
278
|
-
# @private
|
279
286
|
# @sig (String) -> bool
|
280
|
-
def ignores?(abspath)
|
287
|
+
internal def ignores?(abspath)
|
281
288
|
# Common use case.
|
282
289
|
return false if ignored_paths.empty?
|
283
290
|
|
284
291
|
walk_up(abspath) do |abspath|
|
285
|
-
return true if
|
286
|
-
return false if
|
292
|
+
return true if ignored_path?(abspath)
|
293
|
+
return false if roots.key?(abspath)
|
287
294
|
end
|
288
295
|
|
289
296
|
false
|
290
297
|
end
|
291
298
|
|
292
|
-
|
299
|
+
# @sig (String) -> bool
|
300
|
+
private def ignored_path?(abspath)
|
301
|
+
ignored_paths.member?(abspath)
|
302
|
+
end
|
293
303
|
|
294
304
|
# @sig () -> Array[String]
|
295
|
-
def
|
296
|
-
|
297
|
-
!dir?(root_dir) ||
|
305
|
+
private def actual_roots
|
306
|
+
roots.reject do |root_dir, _root_namespace|
|
307
|
+
!dir?(root_dir) || ignored_path?(root_dir)
|
298
308
|
end
|
299
309
|
end
|
300
310
|
|
301
311
|
# @sig (String) -> bool
|
302
|
-
def root_dir?(dir)
|
303
|
-
|
312
|
+
private def root_dir?(dir)
|
313
|
+
roots.key?(dir)
|
304
314
|
end
|
305
315
|
|
306
316
|
# @sig (String) -> bool
|
307
|
-
def excluded_from_eager_load?(abspath)
|
317
|
+
private def excluded_from_eager_load?(abspath)
|
308
318
|
# Optimize this common use case.
|
309
319
|
return false if eager_load_exclusions.empty?
|
310
320
|
|
311
321
|
walk_up(abspath) do |abspath|
|
312
322
|
return true if eager_load_exclusions.member?(abspath)
|
313
|
-
return false if
|
323
|
+
return false if roots.key?(abspath)
|
314
324
|
end
|
315
325
|
|
316
326
|
false
|
317
327
|
end
|
318
328
|
|
319
329
|
# @sig (String) -> bool
|
320
|
-
def collapse?(dir)
|
330
|
+
private def collapse?(dir)
|
321
331
|
collapse_dirs.member?(dir)
|
322
332
|
end
|
323
333
|
|
324
334
|
# @sig (String | Pathname | Array[String | Pathname]) -> Array[String]
|
325
|
-
def expand_paths(paths)
|
335
|
+
private def expand_paths(paths)
|
326
336
|
paths.flatten.map! { |path| File.expand_path(path) }
|
327
337
|
end
|
328
338
|
|
329
339
|
# @sig (Array[String]) -> Array[String]
|
330
|
-
def expand_glob_patterns(glob_patterns)
|
340
|
+
private def expand_glob_patterns(glob_patterns)
|
331
341
|
# Note that Dir.glob works with regular file names just fine. That is,
|
332
342
|
# glob patterns technically need no wildcards.
|
333
343
|
glob_patterns.flat_map { |glob_pattern| Dir.glob(glob_pattern) }
|
334
344
|
end
|
335
345
|
|
336
346
|
# @sig () -> void
|
337
|
-
def recompute_ignored_paths
|
347
|
+
private def recompute_ignored_paths
|
338
348
|
ignored_paths.replace(expand_glob_patterns(ignored_glob_patterns))
|
339
349
|
end
|
340
350
|
|
341
351
|
# @sig () -> void
|
342
|
-
def recompute_collapse_dirs
|
352
|
+
private def recompute_collapse_dirs
|
343
353
|
collapse_dirs.replace(expand_glob_patterns(collapse_glob_patterns))
|
344
354
|
end
|
345
355
|
end
|
@@ -9,11 +9,12 @@ module Zeitwerk::Loader::EagerLoad
|
|
9
9
|
def eager_load(force: false)
|
10
10
|
mutex.synchronize do
|
11
11
|
break if @eager_loaded
|
12
|
+
raise Zeitwerk::SetupRequired unless @setup
|
12
13
|
|
13
14
|
log("eager load start") if logger
|
14
15
|
|
15
|
-
|
16
|
-
actual_eager_load_dir(root_dir,
|
16
|
+
actual_roots.each do |root_dir, root_namespace|
|
17
|
+
actual_eager_load_dir(root_dir, root_namespace, force: force)
|
17
18
|
end
|
18
19
|
|
19
20
|
autoloaded_dirs.each do |autoloaded_dir|
|
@@ -29,6 +30,8 @@ module Zeitwerk::Loader::EagerLoad
|
|
29
30
|
|
30
31
|
# @sig (String | Pathname) -> void
|
31
32
|
def eager_load_dir(path)
|
33
|
+
raise Zeitwerk::SetupRequired unless @setup
|
34
|
+
|
32
35
|
abspath = File.expand_path(path)
|
33
36
|
|
34
37
|
raise Zeitwerk::Error.new("#{abspath} is not a directory") unless dir?(abspath)
|
@@ -37,10 +40,10 @@ module Zeitwerk::Loader::EagerLoad
|
|
37
40
|
|
38
41
|
root_namespace = nil
|
39
42
|
walk_up(abspath) do |dir|
|
40
|
-
return if
|
43
|
+
return if ignored_path?(dir)
|
41
44
|
return if eager_load_exclusions.member?(dir)
|
42
45
|
|
43
|
-
break if root_namespace =
|
46
|
+
break if root_namespace = roots[dir]
|
44
47
|
|
45
48
|
unless collapse?(dir)
|
46
49
|
basename = File.basename(dir)
|
@@ -67,29 +70,32 @@ module Zeitwerk::Loader::EagerLoad
|
|
67
70
|
|
68
71
|
# @sig (Module) -> void
|
69
72
|
def eager_load_namespace(mod)
|
73
|
+
raise Zeitwerk::SetupRequired unless @setup
|
74
|
+
|
70
75
|
unless mod.is_a?(Module)
|
71
76
|
raise Zeitwerk::Error, "#{mod.inspect} is not a class or module object"
|
72
77
|
end
|
73
78
|
|
74
79
|
return if @eager_loaded
|
75
80
|
|
76
|
-
|
81
|
+
mod_name = real_mod_name(mod)
|
82
|
+
return unless mod_name
|
83
|
+
|
84
|
+
actual_roots.each do |root_dir, root_namespace|
|
77
85
|
if mod.equal?(Object)
|
78
86
|
# A shortcircuiting test depends on the invocation of this method.
|
79
87
|
# Please keep them in sync if refactored.
|
80
88
|
actual_eager_load_dir(root_dir, root_namespace)
|
81
89
|
elsif root_namespace.equal?(Object)
|
82
|
-
eager_load_child_namespace(mod, root_dir, root_namespace)
|
90
|
+
eager_load_child_namespace(mod, mod_name, root_dir, root_namespace)
|
83
91
|
else
|
84
|
-
mod_name = real_mod_name(mod)
|
85
92
|
root_namespace_name = real_mod_name(root_namespace)
|
86
|
-
|
87
93
|
if root_namespace_name.start_with?(mod_name + "::")
|
88
94
|
actual_eager_load_dir(root_dir, root_namespace)
|
89
95
|
elsif mod_name == root_namespace_name
|
90
96
|
actual_eager_load_dir(root_dir, root_namespace)
|
91
97
|
elsif mod_name.start_with?(root_namespace_name + "::")
|
92
|
-
eager_load_child_namespace(mod, root_dir, root_namespace)
|
98
|
+
eager_load_child_namespace(mod, mod_name, root_dir, root_namespace)
|
93
99
|
else
|
94
100
|
# Unrelated constant hierarchies, do nothing.
|
95
101
|
end
|
@@ -110,7 +116,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
110
116
|
|
111
117
|
raise Zeitwerk::Error.new("#{abspath} does not exist") unless File.exist?(abspath)
|
112
118
|
raise Zeitwerk::Error.new("#{abspath} is not a Ruby file") if dir?(abspath) || !ruby?(abspath)
|
113
|
-
raise Zeitwerk::Error.new("#{abspath} is ignored") if
|
119
|
+
raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_path?(abspath)
|
114
120
|
|
115
121
|
basename = File.basename(abspath, ".rb")
|
116
122
|
base_cname = inflector.camelize(basename, abspath).to_sym
|
@@ -119,9 +125,9 @@ module Zeitwerk::Loader::EagerLoad
|
|
119
125
|
cnames = []
|
120
126
|
|
121
127
|
walk_up(File.dirname(abspath)) do |dir|
|
122
|
-
raise Zeitwerk::Error.new("#{abspath} is ignored") if
|
128
|
+
raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_path?(dir)
|
123
129
|
|
124
|
-
break if root_namespace =
|
130
|
+
break if root_namespace = roots[dir]
|
125
131
|
|
126
132
|
unless collapse?(dir)
|
127
133
|
basename = File.basename(dir)
|
@@ -180,8 +186,8 @@ module Zeitwerk::Loader::EagerLoad
|
|
180
186
|
# strict namespace descendendant of `root_namespace`.
|
181
187
|
#
|
182
188
|
# @sig (Module, String, Module, Boolean) -> void
|
183
|
-
private def eager_load_child_namespace(child, root_dir, root_namespace)
|
184
|
-
suffix =
|
189
|
+
private def eager_load_child_namespace(child, child_name, root_dir, root_namespace)
|
190
|
+
suffix = child_name
|
185
191
|
unless root_namespace.equal?(Object)
|
186
192
|
suffix = suffix.delete_prefix(real_mod_name(root_namespace) + "::")
|
187
193
|
end
|
@@ -202,7 +208,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
202
208
|
next unless dir?(abspath)
|
203
209
|
|
204
210
|
if collapse?(abspath)
|
205
|
-
|
211
|
+
dirs << abspath
|
206
212
|
elsif segment == inflector.camelize(basename, abspath)
|
207
213
|
next_dirs << abspath
|
208
214
|
end
|
@@ -1,12 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Zeitwerk::Loader::Helpers
|
4
|
-
private
|
5
|
-
|
6
4
|
# --- Logging -----------------------------------------------------------------------------------
|
7
5
|
|
8
6
|
# @sig (String) -> void
|
9
|
-
def log(message)
|
7
|
+
private def log(message)
|
10
8
|
method_name = logger.respond_to?(:debug) ? :debug : :call
|
11
9
|
logger.send(method_name, "Zeitwerk@#{tag}: #{message}")
|
12
10
|
end
|
@@ -14,7 +12,7 @@ module Zeitwerk::Loader::Helpers
|
|
14
12
|
# --- Files and directories ---------------------------------------------------------------------
|
15
13
|
|
16
14
|
# @sig (String) { (String, String) -> void } -> void
|
17
|
-
def ls(dir)
|
15
|
+
private def ls(dir)
|
18
16
|
children = Dir.children(dir)
|
19
17
|
|
20
18
|
# The order in which a directory is listed depends on the file system.
|
@@ -28,10 +26,10 @@ module Zeitwerk::Loader::Helpers
|
|
28
26
|
next if hidden?(basename)
|
29
27
|
|
30
28
|
abspath = File.join(dir, basename)
|
31
|
-
next if
|
29
|
+
next if ignored_path?(abspath)
|
32
30
|
|
33
31
|
if dir?(abspath)
|
34
|
-
next if
|
32
|
+
next if roots.key?(abspath)
|
35
33
|
next if !has_at_least_one_ruby_file?(abspath)
|
36
34
|
else
|
37
35
|
next unless ruby?(abspath)
|
@@ -44,7 +42,7 @@ module Zeitwerk::Loader::Helpers
|
|
44
42
|
end
|
45
43
|
|
46
44
|
# @sig (String) -> bool
|
47
|
-
def has_at_least_one_ruby_file?(dir)
|
45
|
+
private def has_at_least_one_ruby_file?(dir)
|
48
46
|
to_visit = [dir]
|
49
47
|
|
50
48
|
while dir = to_visit.shift
|
@@ -61,22 +59,22 @@ module Zeitwerk::Loader::Helpers
|
|
61
59
|
end
|
62
60
|
|
63
61
|
# @sig (String) -> bool
|
64
|
-
def ruby?(path)
|
62
|
+
private def ruby?(path)
|
65
63
|
path.end_with?(".rb")
|
66
64
|
end
|
67
65
|
|
68
66
|
# @sig (String) -> bool
|
69
|
-
def dir?(path)
|
67
|
+
private def dir?(path)
|
70
68
|
File.directory?(path)
|
71
69
|
end
|
72
70
|
|
73
71
|
# @sig (String) -> bool
|
74
|
-
def hidden?(basename)
|
72
|
+
private def hidden?(basename)
|
75
73
|
basename.start_with?(".")
|
76
74
|
end
|
77
75
|
|
78
76
|
# @sig (String) { (String) -> void } -> void
|
79
|
-
def walk_up(abspath)
|
77
|
+
private def walk_up(abspath)
|
80
78
|
loop do
|
81
79
|
yield abspath
|
82
80
|
abspath, basename = File.split(abspath)
|
@@ -104,11 +102,11 @@ module Zeitwerk::Loader::Helpers
|
|
104
102
|
#
|
105
103
|
# @sig (Module, Symbol) -> String?
|
106
104
|
if method(:autoload?).arity == 1
|
107
|
-
def strict_autoload_path(parent, cname)
|
105
|
+
private def strict_autoload_path(parent, cname)
|
108
106
|
parent.autoload?(cname) if cdef?(parent, cname)
|
109
107
|
end
|
110
108
|
else
|
111
|
-
def strict_autoload_path(parent, cname)
|
109
|
+
private def strict_autoload_path(parent, cname)
|
112
110
|
parent.autoload?(cname, false)
|
113
111
|
end
|
114
112
|
end
|
@@ -117,23 +115,23 @@ module Zeitwerk::Loader::Helpers
|
|
117
115
|
if Symbol.method_defined?(:name)
|
118
116
|
# Symbol#name was introduced in Ruby 3.0. It returns always the same
|
119
117
|
# frozen object, so we may save a few string allocations.
|
120
|
-
def cpath(parent, cname)
|
118
|
+
private def cpath(parent, cname)
|
121
119
|
Object == parent ? cname.name : "#{real_mod_name(parent)}::#{cname.name}"
|
122
120
|
end
|
123
121
|
else
|
124
|
-
def cpath(parent, cname)
|
122
|
+
private def cpath(parent, cname)
|
125
123
|
Object == parent ? cname.to_s : "#{real_mod_name(parent)}::#{cname}"
|
126
124
|
end
|
127
125
|
end
|
128
126
|
|
129
127
|
# @sig (Module, Symbol) -> bool
|
130
|
-
def cdef?(parent, cname)
|
128
|
+
private def cdef?(parent, cname)
|
131
129
|
parent.const_defined?(cname, false)
|
132
130
|
end
|
133
131
|
|
134
132
|
# @raise [NameError]
|
135
133
|
# @sig (Module, Symbol) -> Object
|
136
|
-
def cget(parent, cname)
|
134
|
+
private def cget(parent, cname)
|
137
135
|
parent.const_get(cname, false)
|
138
136
|
end
|
139
137
|
end
|
data/lib/zeitwerk/loader.rb
CHANGED
@@ -109,15 +109,15 @@ module Zeitwerk
|
|
109
109
|
Registry.register_loader(self)
|
110
110
|
end
|
111
111
|
|
112
|
-
# Sets autoloads in the root
|
112
|
+
# Sets autoloads in the root namespaces.
|
113
113
|
#
|
114
114
|
# @sig () -> void
|
115
115
|
def setup
|
116
116
|
mutex.synchronize do
|
117
117
|
break if @setup
|
118
118
|
|
119
|
-
|
120
|
-
set_autoloads_in_dir(root_dir,
|
119
|
+
actual_roots.each do |root_dir, root_namespace|
|
120
|
+
set_autoloads_in_dir(root_dir, root_namespace)
|
121
121
|
end
|
122
122
|
|
123
123
|
on_setup_callbacks.each(&:call)
|
@@ -140,6 +140,8 @@ module Zeitwerk
|
|
140
140
|
# @sig () -> void
|
141
141
|
def unload
|
142
142
|
mutex.synchronize do
|
143
|
+
raise SetupRequired unless @setup
|
144
|
+
|
143
145
|
# We are going to keep track of the files that were required by our
|
144
146
|
# autoloads to later remove them from $LOADED_FEATURES, thus making them
|
145
147
|
# loadable by Kernel#require again.
|
@@ -199,7 +201,7 @@ module Zeitwerk
|
|
199
201
|
shadowed_files.clear
|
200
202
|
|
201
203
|
Registry.on_unload(self)
|
202
|
-
ExplicitNamespace.
|
204
|
+
ExplicitNamespace.__unregister_loader(self)
|
203
205
|
|
204
206
|
@setup = false
|
205
207
|
@eager_loaded = false
|
@@ -216,6 +218,7 @@ module Zeitwerk
|
|
216
218
|
# @sig () -> void
|
217
219
|
def reload
|
218
220
|
raise ReloadingDisabledError unless reloading_enabled?
|
221
|
+
raise SetupRequired unless @setup
|
219
222
|
|
220
223
|
unload
|
221
224
|
recompute_ignored_paths
|
@@ -245,7 +248,7 @@ module Zeitwerk
|
|
245
248
|
# @sig () -> void
|
246
249
|
def unregister
|
247
250
|
Registry.unregister_loader(self)
|
248
|
-
ExplicitNamespace.
|
251
|
+
ExplicitNamespace.__unregister_loader(self)
|
249
252
|
end
|
250
253
|
|
251
254
|
# The return value of this predicate is only meaningful if the loader has
|
@@ -283,19 +286,31 @@ module Zeitwerk
|
|
283
286
|
Registry.loader_for_gem(called_from, warn_on_extra_files: warn_on_extra_files)
|
284
287
|
end
|
285
288
|
|
286
|
-
# Broadcasts `eager_load` to all loaders.
|
289
|
+
# Broadcasts `eager_load` to all loaders. Those that have not been setup
|
290
|
+
# are skipped.
|
287
291
|
#
|
288
292
|
# @sig () -> void
|
289
293
|
def eager_load_all
|
290
|
-
Registry.loaders.each
|
294
|
+
Registry.loaders.each do |loader|
|
295
|
+
begin
|
296
|
+
loader.eager_load
|
297
|
+
rescue SetupRequired
|
298
|
+
# This is fine, we eager load what can be eager loaded.
|
299
|
+
end
|
300
|
+
end
|
291
301
|
end
|
292
302
|
|
293
|
-
# Broadcasts `eager_load_namespace` to all loaders.
|
303
|
+
# Broadcasts `eager_load_namespace` to all loaders. Those that have not
|
304
|
+
# been setup are skipped.
|
294
305
|
#
|
295
306
|
# @sig (Module) -> void
|
296
307
|
def eager_load_namespace(mod)
|
297
308
|
Registry.loaders.each do |loader|
|
298
|
-
|
309
|
+
begin
|
310
|
+
loader.eager_load_namespace(mod)
|
311
|
+
rescue SetupRequired
|
312
|
+
# This is fine, we eager load what can be eager loaded.
|
313
|
+
end
|
299
314
|
end
|
300
315
|
end
|
301
316
|
|
@@ -435,22 +450,23 @@ module Zeitwerk
|
|
435
450
|
|
436
451
|
# @sig (String) -> void
|
437
452
|
def register_explicit_namespace(cpath)
|
438
|
-
ExplicitNamespace.
|
453
|
+
ExplicitNamespace.__register(cpath, self)
|
439
454
|
end
|
440
455
|
|
441
456
|
# @sig (String) -> void
|
442
457
|
def raise_if_conflicting_directory(dir)
|
443
458
|
MUTEX.synchronize do
|
459
|
+
dir_slash = dir + "/"
|
460
|
+
|
444
461
|
Registry.loaders.each do |loader|
|
445
462
|
next if loader == self
|
446
|
-
next if loader.
|
463
|
+
next if loader.__ignores?(dir)
|
447
464
|
|
448
|
-
|
449
|
-
loader.root_dirs.each do |root_dir, _namespace|
|
465
|
+
loader.__roots.each_key do |root_dir|
|
450
466
|
next if ignores?(root_dir)
|
451
467
|
|
452
|
-
|
453
|
-
if
|
468
|
+
root_dir_slash = root_dir + "/"
|
469
|
+
if dir_slash.start_with?(root_dir_slash) || root_dir_slash.start_with?(dir_slash)
|
454
470
|
require "pp" # Needed for pretty_inspect, even in Ruby 2.5.
|
455
471
|
raise Error,
|
456
472
|
"loader\n\n#{pretty_inspect}\n\nwants to manage directory #{dir}," \
|
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.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Xavier Noria
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|
@@ -28,6 +28,7 @@ files:
|
|
28
28
|
- lib/zeitwerk/gem_inflector.rb
|
29
29
|
- lib/zeitwerk/gem_loader.rb
|
30
30
|
- lib/zeitwerk/inflector.rb
|
31
|
+
- lib/zeitwerk/internal.rb
|
31
32
|
- lib/zeitwerk/kernel.rb
|
32
33
|
- lib/zeitwerk/loader.rb
|
33
34
|
- lib/zeitwerk/loader/callbacks.rb
|