zeitwerk 2.6.4 → 2.6.5
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/loader/config.rb +16 -5
- data/lib/zeitwerk/loader/eager_load.rb +8 -3
- data/lib/zeitwerk/loader/helpers.rb +14 -16
- data/lib/zeitwerk/loader.rb +23 -8
- data/lib/zeitwerk/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6811a58f4992b66a3625b1b931f070463619e806c0e044611df1f706a0cb1477
|
4
|
+
data.tar.gz: 07dd0a9133025560ad914dca656d0ed1044cc791d6e09586dbd03c0a72929751
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a360aad989a229b0a315d6d38690401f18ef55b45e8ae30e05db81eb85f9954f8ec1e4fa738b774175c06736f44bcecf2dcfbca74b7d79a049d59071489a7b9
|
7
|
+
data.tar.gz: 851ecb2490e716aac571f140f51bedbaf1f1eb99761d1c98a9afe1982099bddc9455efedcd243ff770b7d08c616dd48efce20a7e2378e41ef2844e197cc085f2
|
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.
|
@@ -5,6 +5,7 @@ require "securerandom"
|
|
5
5
|
|
6
6
|
module Zeitwerk::Loader::Config
|
7
7
|
extend Zeitwerk::Internal
|
8
|
+
include Zeitwerk::RealModName
|
8
9
|
|
9
10
|
# @sig #camelize
|
10
11
|
attr_accessor :inflector
|
@@ -12,14 +13,15 @@ module Zeitwerk::Loader::Config
|
|
12
13
|
# @sig #call | #debug | nil
|
13
14
|
attr_accessor :logger
|
14
15
|
|
15
|
-
# Absolute paths of the root directories
|
16
|
-
# easily handle duplicates, have a fast lookup needed for detecting nested
|
17
|
-
# paths, and store namespaces as values.
|
16
|
+
# Absolute paths of the root directories, mapped to their respective root namespaces:
|
18
17
|
#
|
19
18
|
# "/Users/fxn/blog/app/channels" => Object,
|
20
19
|
# "/Users/fxn/blog/app/adapters" => ActiveJob::QueueAdapters,
|
21
20
|
# ...
|
22
21
|
#
|
22
|
+
# Stored in a hash to preserve order, easily handle duplicates, and have a
|
23
|
+
# fast lookup by directory.
|
24
|
+
#
|
23
25
|
# This is a private collection maintained by the loader. The public
|
24
26
|
# interface for it is `push_dir` and `dirs`.
|
25
27
|
#
|
@@ -112,6 +114,10 @@ module Zeitwerk::Loader::Config
|
|
112
114
|
raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module object, should be"
|
113
115
|
end
|
114
116
|
|
117
|
+
unless real_mod_name(namespace)
|
118
|
+
raise Zeitwerk::Error, "root namespaces cannot be anonymous"
|
119
|
+
end
|
120
|
+
|
115
121
|
abspath = File.expand_path(path)
|
116
122
|
if dir?(abspath)
|
117
123
|
raise_if_conflicting_directory(abspath)
|
@@ -283,17 +289,22 @@ module Zeitwerk::Loader::Config
|
|
283
289
|
return false if ignored_paths.empty?
|
284
290
|
|
285
291
|
walk_up(abspath) do |abspath|
|
286
|
-
return true if
|
292
|
+
return true if ignored_path?(abspath)
|
287
293
|
return false if roots.key?(abspath)
|
288
294
|
end
|
289
295
|
|
290
296
|
false
|
291
297
|
end
|
292
298
|
|
299
|
+
# @sig (String) -> bool
|
300
|
+
private def ignored_path?(abspath)
|
301
|
+
ignored_paths.member?(abspath)
|
302
|
+
end
|
303
|
+
|
293
304
|
# @sig () -> Array[String]
|
294
305
|
private def actual_roots
|
295
306
|
roots.reject do |root_dir, _root_namespace|
|
296
|
-
!dir?(root_dir) ||
|
307
|
+
!dir?(root_dir) || ignored_path?(root_dir)
|
297
308
|
end
|
298
309
|
end
|
299
310
|
|
@@ -9,6 +9,7 @@ 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
|
|
@@ -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,7 +40,7 @@ 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
46
|
break if root_namespace = roots[dir]
|
@@ -67,6 +70,8 @@ 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
|
@@ -111,7 +116,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
111
116
|
|
112
117
|
raise Zeitwerk::Error.new("#{abspath} does not exist") unless File.exist?(abspath)
|
113
118
|
raise Zeitwerk::Error.new("#{abspath} is not a Ruby file") if dir?(abspath) || !ruby?(abspath)
|
114
|
-
raise Zeitwerk::Error.new("#{abspath} is ignored") if
|
119
|
+
raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_path?(abspath)
|
115
120
|
|
116
121
|
basename = File.basename(abspath, ".rb")
|
117
122
|
base_cname = inflector.camelize(basename, abspath).to_sym
|
@@ -120,7 +125,7 @@ module Zeitwerk::Loader::EagerLoad
|
|
120
125
|
cnames = []
|
121
126
|
|
122
127
|
walk_up(File.dirname(abspath)) do |dir|
|
123
|
-
raise Zeitwerk::Error.new("#{abspath} is ignored") if
|
128
|
+
raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_path?(dir)
|
124
129
|
|
125
130
|
break if root_namespace = roots[dir]
|
126
131
|
|
@@ -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,7 +26,7 @@ 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
32
|
next if roots.key?(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,7 +109,7 @@ 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
|
@@ -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,7 +450,7 @@ 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
|
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.6.
|
4
|
+
version: 2.6.5
|
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-
|
11
|
+
date: 2022-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|