zeitwerk 2.2.0 → 2.3.0
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 +103 -21
- data/lib/zeitwerk/inflector.rb +1 -1
- data/lib/zeitwerk/loader/callbacks.rb +1 -1
- data/lib/zeitwerk/loader.rb +68 -19
- data/lib/zeitwerk/real_mod_name.rb +8 -2
- 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: b8fd1828e8e92937af8b1e46033442f517c65c07a443a9a265b67c03f22b8b86
|
|
4
|
+
data.tar.gz: 4e04076d6ac82d1bff9bf3db8604ca7e826afad753fb1268116cd115f15d301b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cacd0d2c43a566a5e3be7dfb98585e9598a9fda8e930bb0f97a96180db332d6303fb1cfbf850f85dd822071968fc624262b94c27db85ad3e1236f1e243df64c2
|
|
7
|
+
data.tar.gz: 0f9c90d543eae10edade4777f63c02a0f5ef30ecfdfe752fc9f11e5f6e3c835d2b893b0e8074e4cd068859e41dcf6e5374527c73499876deab3a9974cd0d2bf4
|
data/README.md
CHANGED
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
- [File structure](#file-structure)
|
|
13
13
|
- [Implicit namespaces](#implicit-namespaces)
|
|
14
14
|
- [Explicit namespaces](#explicit-namespaces)
|
|
15
|
+
- [Collapsing directories](#collapsing-directories)
|
|
15
16
|
- [Nested root directories](#nested-root-directories)
|
|
16
17
|
- [Usage](#usage)
|
|
17
18
|
- [Setup](#setup)
|
|
18
|
-
- [
|
|
19
|
+
- [Autoloading](#autoloading)
|
|
19
20
|
- [Eager loading](#eager-loading)
|
|
21
|
+
- [Reloading](#reloading)
|
|
20
22
|
- [Inflection](#inflection)
|
|
21
23
|
- [Zeitwerk::Inflector](#zeitwerkinflector)
|
|
22
24
|
- [Zeitwerk::GemInflector](#zeitwerkgeminflector)
|
|
@@ -32,6 +34,7 @@
|
|
|
32
34
|
- [Autoloading, explicit namespaces, and debuggers](#autoloading-explicit-namespaces-and-debuggers)
|
|
33
35
|
- [Pronunciation](#pronunciation)
|
|
34
36
|
- [Supported Ruby versions](#supported-ruby-versions)
|
|
37
|
+
- [Testing](#testing)
|
|
35
38
|
- [Motivation](#motivation)
|
|
36
39
|
- [Thanks](#thanks)
|
|
37
40
|
- [License](#license)
|
|
@@ -163,6 +166,32 @@ end
|
|
|
163
166
|
|
|
164
167
|
An explicit namespace must be managed by one single loader. Loaders that reopen namespaces owned by other projects are responsible for loading their constants before setup.
|
|
165
168
|
|
|
169
|
+
<a id="markdown-collapsing-directories" name="collapsing-directories"></a>
|
|
170
|
+
### Collapsing directories
|
|
171
|
+
|
|
172
|
+
Say some directories in a project exist for organizational purposes only, and you prefer not to have them as namespaces. For example, the `actions` subdirectory in the next example is not meant to represent a namespace, it is there only to group all actions related to bookings:
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
booking.rb -> Booking
|
|
176
|
+
booking/actions/create.rb -> Booking::Create
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
To make it work that way, configure Zeitwerk to collapse said directory:
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
loader.collapse("booking/actions")
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
This method accepts an arbitrary number of strings or `Pathname` objects, and also an array of them.
|
|
186
|
+
|
|
187
|
+
You can pass directories and glob patterns. Glob patterns are expanded when they are added, and again on each reload.
|
|
188
|
+
|
|
189
|
+
To illustrate usage of glob patterns, if `actions` in the example above is part of a standardized structure, you could use a wildcard:
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
loader.collapse("*/actions")
|
|
193
|
+
```
|
|
194
|
+
|
|
166
195
|
<a id="markdown-nested-root-directories" name="nested-root-directories"></a>
|
|
167
196
|
### Nested root directories
|
|
168
197
|
|
|
@@ -218,33 +247,28 @@ end
|
|
|
218
247
|
|
|
219
248
|
Zeitwerk works internally only with absolute paths to avoid costly file searches in `$LOAD_PATH`. Indeed, the root directories do not even need to belong to `$LOAD_PATH`, everything just works by design if they don't.
|
|
220
249
|
|
|
221
|
-
<a id="markdown-
|
|
222
|
-
###
|
|
223
|
-
|
|
224
|
-
Zeitwerk is able to reload code, but you need to enable this feature:
|
|
225
|
-
|
|
226
|
-
```ruby
|
|
227
|
-
loader = Zeitwerk::Loader.new
|
|
228
|
-
loader.push_dir(...)
|
|
229
|
-
loader.enable_reloading # you need to opt-in before setup
|
|
230
|
-
loader.setup
|
|
231
|
-
...
|
|
232
|
-
loader.reload
|
|
233
|
-
```
|
|
250
|
+
<a id="markdown-autoloading" name="autoloading"></a>
|
|
251
|
+
### Autoloading
|
|
234
252
|
|
|
235
|
-
|
|
253
|
+
After `setup`, you are able to reference classes and modules from the project without issuing `require` calls for them. They are all available everywhere, autoloading loads them on demand. This works even if the reference to the class or module is first hit in client code, outside your project.
|
|
236
254
|
|
|
237
|
-
|
|
255
|
+
Let's revisit the example above:
|
|
238
256
|
|
|
239
|
-
|
|
257
|
+
```ruby
|
|
258
|
+
# lib/my_gem.rb (main file)
|
|
240
259
|
|
|
241
|
-
|
|
260
|
+
require "zeitwerk"
|
|
261
|
+
loader = Zeitwerk::Loader.for_gem
|
|
262
|
+
loader.setup
|
|
242
263
|
|
|
243
|
-
|
|
264
|
+
module MyGem
|
|
265
|
+
include MyLogger # (*)
|
|
266
|
+
end
|
|
267
|
+
```
|
|
244
268
|
|
|
245
|
-
|
|
269
|
+
That works, and there is no `require "my_gem/my_logger"`. When `(*)` is reached, Zeitwerk seamlessly autoloads `MyGem::MyLogger`.
|
|
246
270
|
|
|
247
|
-
|
|
271
|
+
If autoloading a file does not define the expected class or module, Zeitwerk raises `Zeitwerk::NameError`, which is a subclass of `NameError`.
|
|
248
272
|
|
|
249
273
|
<a id="markdown-eager-loading" name="eager-loading"></a>
|
|
250
274
|
### Eager loading
|
|
@@ -268,6 +292,8 @@ In gems, the method needs to be invoked after the main namespace has been define
|
|
|
268
292
|
|
|
269
293
|
Eager loading is synchronized and idempotent.
|
|
270
294
|
|
|
295
|
+
If eager loading a file does not define the expected class or module, Zeitwerk raises `Zeitwerk::NameError`, which is a subclass of `NameError`.
|
|
296
|
+
|
|
271
297
|
If you want to eager load yourself and all dependencies using Zeitwerk, you can broadcast the `eager_load` call to all instances:
|
|
272
298
|
|
|
273
299
|
```ruby
|
|
@@ -278,6 +304,34 @@ This may be handy in top-level services, like web applications.
|
|
|
278
304
|
|
|
279
305
|
Note that thanks to idempotence `Zeitwerk::Loader.eager_load_all` won't eager load twice if any of the instances already eager loaded.
|
|
280
306
|
|
|
307
|
+
<a id="markdown-reloading" name="reloading"></a>
|
|
308
|
+
### Reloading
|
|
309
|
+
|
|
310
|
+
Zeitwerk is able to reload code, but you need to enable this feature:
|
|
311
|
+
|
|
312
|
+
```ruby
|
|
313
|
+
loader = Zeitwerk::Loader.new
|
|
314
|
+
loader.push_dir(...)
|
|
315
|
+
loader.enable_reloading # you need to opt-in before setup
|
|
316
|
+
loader.setup
|
|
317
|
+
...
|
|
318
|
+
loader.reload
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
There is no way to undo this, either you want to reload or you don't.
|
|
322
|
+
|
|
323
|
+
Enabling reloading after setup raises `Zeitwerk::Error`. Attempting to reload without having it enabled raises `Zeitwerk::ReloadingDisabledError`.
|
|
324
|
+
|
|
325
|
+
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.
|
|
326
|
+
|
|
327
|
+
Reloading removes the currently loaded classes and modules and resets the loader so that it will pick whatever is in the file system now.
|
|
328
|
+
|
|
329
|
+
It is important to highlight that this is an instance method. Don't worry about project dependencies managed by Zeitwerk, their loaders are independent.
|
|
330
|
+
|
|
331
|
+
In order for reloading to be thread-safe, you need to implement some coordination. For example, a web framework that serves each request with its own thread may have a globally accessible RW lock. When a request comes in, the framework acquires the lock for reading at the beginning, and the code in the framework that calls `loader.reload` needs to acquire the lock for writing.
|
|
332
|
+
|
|
333
|
+
On reloading, client code has to update anything that would otherwise be storing a stale object. For example, if the routing layer of a web framework stores controller class objects or instances in internal structures, on reload it has to refresh them somehow, possibly reevaluating routes.
|
|
334
|
+
|
|
281
335
|
<a id="markdown-inflection" name="inflection"></a>
|
|
282
336
|
### Inflection
|
|
283
337
|
|
|
@@ -599,6 +653,32 @@ As a workaround, you can eager load. Zeitwerk tries hard to succeed or fail cons
|
|
|
599
653
|
|
|
600
654
|
Zeitwerk works with MRI 2.4.4 and above.
|
|
601
655
|
|
|
656
|
+
<a id="markdown-testing" name="testing"></a>
|
|
657
|
+
## Testing
|
|
658
|
+
|
|
659
|
+
In order to run the test suite of Zeitwerk, `cd` into the project root and execute
|
|
660
|
+
|
|
661
|
+
```
|
|
662
|
+
bin/test
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
To run one particular suite, pass its file name as an argument:
|
|
666
|
+
|
|
667
|
+
```
|
|
668
|
+
bin/test test/lib/zeitwerk/test_eager_load.rb
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
Furthermore, the project has a development dependency on [`minitest-focus`](https://github.com/seattlerb/minitest-focus). To run an individual test mark it with `focus`:
|
|
672
|
+
|
|
673
|
+
```ruby
|
|
674
|
+
focus
|
|
675
|
+
test "capitalizes the first letter" do
|
|
676
|
+
assert_equal "User", camelize("user")
|
|
677
|
+
end
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
and run `bin/test`.
|
|
681
|
+
|
|
602
682
|
<a id="markdown-motivation" name="motivation"></a>
|
|
603
683
|
## Motivation
|
|
604
684
|
|
|
@@ -613,6 +693,8 @@ I'd like to thank [@matthewd](https://github.com/matthewd) for the discussions w
|
|
|
613
693
|
|
|
614
694
|
Also, would like to thank [@Shopify](https://github.com/Shopify), [@rafaelfranca](https://github.com/rafaelfranca), and [@dylanahsmith](https://github.com/dylanahsmith), for sharing [this PoC](https://github.com/Shopify/autoload_reloader). The technique Zeitwerk uses to support explicit namespaces was copied from that project.
|
|
615
695
|
|
|
696
|
+
Jean Boussier ([@casperisfine](https://github.com/casperisfine), [@byroot](https://github.com/byroot)) deserves special mention. Jean migrated autoloading in Shopify when Zeitwerk integration in Rails was yet unreleased. His work and positive attitude have been outstanding, and thanks to his feedback the interface and performance of Zeitwerk are way, way better. Kudos man ❤️.
|
|
697
|
+
|
|
616
698
|
Finally, many thanks to [@schurig](https://github.com/schurig) for recording an [audio file](http://share.hashref.com/zeitwerk/zeitwerk_pronunciation.mp3) with the pronunciation of "Zeitwerk" in perfect German. 💯
|
|
617
699
|
|
|
618
700
|
<a id="markdown-license" name="license"></a>
|
data/lib/zeitwerk/inflector.rb
CHANGED
|
@@ -28,7 +28,7 @@ module Zeitwerk
|
|
|
28
28
|
#
|
|
29
29
|
# inflector.camelize("html_parser", abspath) # => "HTMLParser"
|
|
30
30
|
# inflector.camelize("mysql_adapter", abspath) # => "MySQLAdapter"
|
|
31
|
-
# inflector.camelize("users_controller", abspath) # => "
|
|
31
|
+
# inflector.camelize("users_controller", abspath) # => "UsersController"
|
|
32
32
|
#
|
|
33
33
|
# @param inflections [{String => String}]
|
|
34
34
|
# @return [void]
|
|
@@ -14,7 +14,7 @@ module Zeitwerk::Loader::Callbacks
|
|
|
14
14
|
if logger && cdef?(*cref)
|
|
15
15
|
log("constant #{cpath(*cref)} loaded from file #{file}")
|
|
16
16
|
elsif !cdef?(*cref)
|
|
17
|
-
raise NameError
|
|
17
|
+
raise Zeitwerk::NameError.new("expected file #{file} to define constant #{cpath(*cref)}, but didn't", cref.last)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
data/lib/zeitwerk/loader.rb
CHANGED
|
@@ -39,7 +39,7 @@ module Zeitwerk
|
|
|
39
39
|
# @return [<String>]
|
|
40
40
|
attr_reader :preloads
|
|
41
41
|
|
|
42
|
-
# Absolute paths of files, directories,
|
|
42
|
+
# Absolute paths of files, directories, or glob patterns to be totally
|
|
43
43
|
# ignored.
|
|
44
44
|
#
|
|
45
45
|
# @private
|
|
@@ -54,6 +54,19 @@ module Zeitwerk
|
|
|
54
54
|
# @return [Set<String>]
|
|
55
55
|
attr_reader :ignored_paths
|
|
56
56
|
|
|
57
|
+
# Absolute paths of directories or glob patterns to be collapsed.
|
|
58
|
+
#
|
|
59
|
+
# @private
|
|
60
|
+
# @return [Set<String>]
|
|
61
|
+
attr_reader :collapse_glob_patterns
|
|
62
|
+
|
|
63
|
+
# The actual collection of absolute directory names at the time the collapse
|
|
64
|
+
# glob patterns were expanded. Computed on setup, and recomputed on reload.
|
|
65
|
+
#
|
|
66
|
+
# @private
|
|
67
|
+
# @return [Set<String>]
|
|
68
|
+
attr_reader :collapse_dirs
|
|
69
|
+
|
|
57
70
|
# Maps real absolute paths for which an autoload has been set ---and not
|
|
58
71
|
# executed--- to their corresponding parent class or module and constant
|
|
59
72
|
# name.
|
|
@@ -131,15 +144,17 @@ module Zeitwerk
|
|
|
131
144
|
@inflector = Inflector.new
|
|
132
145
|
@logger = self.class.default_logger
|
|
133
146
|
|
|
134
|
-
@root_dirs
|
|
135
|
-
@preloads
|
|
136
|
-
@ignored_glob_patterns
|
|
137
|
-
@ignored_paths
|
|
138
|
-
@
|
|
139
|
-
@
|
|
140
|
-
@
|
|
141
|
-
@
|
|
142
|
-
@
|
|
147
|
+
@root_dirs = {}
|
|
148
|
+
@preloads = []
|
|
149
|
+
@ignored_glob_patterns = Set.new
|
|
150
|
+
@ignored_paths = Set.new
|
|
151
|
+
@collapse_glob_patterns = Set.new
|
|
152
|
+
@collapse_dirs = Set.new
|
|
153
|
+
@autoloads = {}
|
|
154
|
+
@autoloaded_dirs = []
|
|
155
|
+
@to_unload = {}
|
|
156
|
+
@lazy_subdirs = {}
|
|
157
|
+
@eager_load_exclusions = Set.new
|
|
143
158
|
|
|
144
159
|
# TODO: find a better name for these mutexes.
|
|
145
160
|
@mutex = Mutex.new
|
|
@@ -154,6 +169,7 @@ module Zeitwerk
|
|
|
154
169
|
|
|
155
170
|
# Sets a tag for the loader, useful for logging.
|
|
156
171
|
#
|
|
172
|
+
# @param tag [#to_s]
|
|
157
173
|
# @return [void]
|
|
158
174
|
def tag=(tag)
|
|
159
175
|
@tag = tag.to_s
|
|
@@ -233,6 +249,18 @@ module Zeitwerk
|
|
|
233
249
|
end
|
|
234
250
|
end
|
|
235
251
|
|
|
252
|
+
# Configure directories or glob patterns to be collapsed.
|
|
253
|
+
#
|
|
254
|
+
# @param paths [<String, Pathname, <String, Pathname>>]
|
|
255
|
+
# @return [void]
|
|
256
|
+
def collapse(*glob_patterns)
|
|
257
|
+
glob_patterns = expand_paths(glob_patterns)
|
|
258
|
+
mutex.synchronize do
|
|
259
|
+
collapse_glob_patterns.merge(glob_patterns)
|
|
260
|
+
collapse_dirs.merge(expand_glob_patterns(glob_patterns))
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
236
264
|
# Sets autoloads in the root namespace and preloads files, if any.
|
|
237
265
|
#
|
|
238
266
|
# @return [void]
|
|
@@ -306,7 +334,8 @@ module Zeitwerk
|
|
|
306
334
|
Registry.on_unload(self)
|
|
307
335
|
ExplicitNamespace.unregister(self)
|
|
308
336
|
|
|
309
|
-
@setup
|
|
337
|
+
@setup = false
|
|
338
|
+
@eager_loaded = false
|
|
310
339
|
end
|
|
311
340
|
end
|
|
312
341
|
|
|
@@ -322,6 +351,7 @@ module Zeitwerk
|
|
|
322
351
|
if reloading_enabled?
|
|
323
352
|
unload
|
|
324
353
|
recompute_ignored_paths
|
|
354
|
+
recompute_collapse_dirs
|
|
325
355
|
setup
|
|
326
356
|
else
|
|
327
357
|
raise ReloadingDisabledError, "can't reload, please call loader.enable_reloading before setup"
|
|
@@ -351,8 +381,12 @@ module Zeitwerk
|
|
|
351
381
|
cref[0].const_get(cref[1], false)
|
|
352
382
|
end
|
|
353
383
|
elsif dir?(abspath) && !root_dirs.key?(abspath)
|
|
354
|
-
|
|
355
|
-
|
|
384
|
+
if collapse_dirs.member?(abspath)
|
|
385
|
+
queue << [namespace, abspath]
|
|
386
|
+
else
|
|
387
|
+
cname = inflector.camelize(basename, abspath)
|
|
388
|
+
queue << [namespace.const_get(cname, false), abspath]
|
|
389
|
+
end
|
|
356
390
|
end
|
|
357
391
|
end
|
|
358
392
|
end
|
|
@@ -476,7 +510,7 @@ module Zeitwerk
|
|
|
476
510
|
ls(dir) do |basename, abspath|
|
|
477
511
|
begin
|
|
478
512
|
if ruby?(basename)
|
|
479
|
-
basename
|
|
513
|
+
basename[-3..-1] = ''
|
|
480
514
|
cname = inflector.camelize(basename, abspath).to_sym
|
|
481
515
|
autoload_file(parent, cname, abspath)
|
|
482
516
|
elsif dir?(abspath)
|
|
@@ -488,13 +522,17 @@ module Zeitwerk
|
|
|
488
522
|
# it counts only as root. The guard checks that.
|
|
489
523
|
unless root_dirs.key?(abspath)
|
|
490
524
|
cname = inflector.camelize(basename, abspath).to_sym
|
|
491
|
-
|
|
525
|
+
if collapse_dirs.member?(abspath)
|
|
526
|
+
set_autoloads_in_dir(abspath, parent)
|
|
527
|
+
else
|
|
528
|
+
autoload_subdir(parent, cname, abspath)
|
|
529
|
+
end
|
|
492
530
|
end
|
|
493
531
|
end
|
|
494
532
|
rescue ::NameError => error
|
|
495
533
|
path_type = ruby?(abspath) ? "file" : "directory"
|
|
496
534
|
|
|
497
|
-
raise NameError,
|
|
535
|
+
raise NameError.new(<<~MESSAGE, error.name)
|
|
498
536
|
#{error.message} inferred by #{inflector.class} from #{path_type}
|
|
499
537
|
|
|
500
538
|
#{abspath}
|
|
@@ -558,7 +596,7 @@ module Zeitwerk
|
|
|
558
596
|
end
|
|
559
597
|
|
|
560
598
|
# @param dir [String] directory that would have autovivified a module
|
|
561
|
-
# @param file [String] the file where the namespace is
|
|
599
|
+
# @param file [String] the file where the namespace is explicitly defined
|
|
562
600
|
# @param parent [Module]
|
|
563
601
|
# @param cname [Symbol]
|
|
564
602
|
# @return [void]
|
|
@@ -623,8 +661,14 @@ module Zeitwerk
|
|
|
623
661
|
# @param parent [Module]
|
|
624
662
|
# @param cname [Symbol]
|
|
625
663
|
# @return [String, nil]
|
|
626
|
-
|
|
627
|
-
|
|
664
|
+
if method(:autoload?).arity == 1
|
|
665
|
+
def strict_autoload_path(parent, cname)
|
|
666
|
+
parent.autoload?(cname) if cdef?(parent, cname)
|
|
667
|
+
end
|
|
668
|
+
else
|
|
669
|
+
def strict_autoload_path(parent, cname)
|
|
670
|
+
parent.autoload?(cname, false)
|
|
671
|
+
end
|
|
628
672
|
end
|
|
629
673
|
|
|
630
674
|
# This method is called this way because I prefer `preload` to be the method
|
|
@@ -711,6 +755,11 @@ module Zeitwerk
|
|
|
711
755
|
ignored_paths.replace(expand_glob_patterns(ignored_glob_patterns))
|
|
712
756
|
end
|
|
713
757
|
|
|
758
|
+
# @return [void]
|
|
759
|
+
def recompute_collapse_dirs
|
|
760
|
+
collapse_dirs.replace(expand_glob_patterns(collapse_glob_patterns))
|
|
761
|
+
end
|
|
762
|
+
|
|
714
763
|
# @param message [String]
|
|
715
764
|
# @return [void]
|
|
716
765
|
def log(message)
|
|
@@ -9,7 +9,13 @@ module Zeitwerk::RealModName
|
|
|
9
9
|
#
|
|
10
10
|
# @param mod [Class, Module]
|
|
11
11
|
# @return [String, nil]
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if UnboundMethod.method_defined?(:bind_call)
|
|
13
|
+
def real_mod_name(mod)
|
|
14
|
+
UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
|
|
15
|
+
end
|
|
16
|
+
else
|
|
17
|
+
def real_mod_name(mod)
|
|
18
|
+
UNBOUND_METHOD_MODULE_NAME.bind(mod).call
|
|
19
|
+
end
|
|
14
20
|
end
|
|
15
21
|
end
|
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
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Xavier Noria
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-03-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |2
|
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|