zeitwerk 2.5.0.beta2 → 2.5.0.beta6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +125 -12
- data/lib/zeitwerk/autoloads.rb +2 -0
- data/lib/zeitwerk/error.rb +2 -0
- data/lib/zeitwerk/explicit_namespace.rb +2 -0
- data/lib/zeitwerk/kernel.rb +1 -1
- data/lib/zeitwerk/loader/callbacks.rb +10 -5
- data/lib/zeitwerk/loader/config.rb +23 -10
- data/lib/zeitwerk/loader/helpers.rb +2 -0
- data/lib/zeitwerk/loader.rb +33 -13
- data/lib/zeitwerk/real_mod_name.rb +2 -0
- 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: 74d74bec1f4963cc5d04f037e14723408ae5d1f8c4df16f31f2901402e3230ed
|
4
|
+
data.tar.gz: 3ebd7f860583cc7a560360b2e766193dc0c74c5cd902c7cb632a614e32dc68e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0bec92696285efb5ad1f76094b71a4de5a38a0665b493737c945623042b4d744be2c58f4006ed5c8c4abe0ccb70854c94ba63c3b7eeef1a5ae47086a956668d
|
7
|
+
data.tar.gz: a1392b3b73dcd85769f793aeca9a9d10cb50c70259d88761574c1e62e201b24f32d7122eda587af6017dea93fca27ce9463e5f0dae7b98523d3edba2c13802eb
|
data/README.md
CHANGED
@@ -19,19 +19,24 @@
|
|
19
19
|
- [Implicit namespaces](#implicit-namespaces)
|
20
20
|
- [Explicit namespaces](#explicit-namespaces)
|
21
21
|
- [Collapsing directories](#collapsing-directories)
|
22
|
+
- [Testing compliance](#testing-compliance)
|
22
23
|
- [Usage](#usage)
|
23
24
|
- [Setup](#setup)
|
24
25
|
- [Generic](#generic)
|
25
26
|
- [for_gem](#for_gem)
|
26
27
|
- [Autoloading](#autoloading)
|
27
28
|
- [Eager loading](#eager-loading)
|
29
|
+
- [Eager load exclusions](#eager-load-exclusions)
|
30
|
+
- [Global eager load](#global-eager-load)
|
28
31
|
- [Reloading](#reloading)
|
29
32
|
- [Inflection](#inflection)
|
30
33
|
- [Zeitwerk::Inflector](#zeitwerkinflector)
|
31
34
|
- [Zeitwerk::GemInflector](#zeitwerkgeminflector)
|
32
35
|
- [Custom inflector](#custom-inflector)
|
33
|
-
- [
|
34
|
-
|
36
|
+
- [Callbacks](#callbacks)
|
37
|
+
- [The on_setup callback](#the-on_setup-callback)
|
38
|
+
- [The on_load callback](#the-on_load-callback)
|
39
|
+
- [The on_unload callback](#the-on_unload-callback)
|
35
40
|
- [Technical details](#technical-details)
|
36
41
|
- [Logging](#logging)
|
37
42
|
- [Loader tag](#loader-tag)
|
@@ -40,15 +45,19 @@
|
|
40
45
|
- [Use case: The adapter pattern](#use-case-the-adapter-pattern)
|
41
46
|
- [Use case: Test files mixed with implementation files](#use-case-test-files-mixed-with-implementation-files)
|
42
47
|
- [Edge cases](#edge-cases)
|
48
|
+
- [Beware of circular dependencies](#beware-of-circular-dependencies)
|
43
49
|
- [Reopening third-party namespaces](#reopening-third-party-namespaces)
|
44
50
|
- [Rules of thumb](#rules-of-thumb)
|
45
51
|
- [Debuggers](#debuggers)
|
52
|
+
- [debug.rb](#debugrb)
|
46
53
|
- [Break](#break)
|
47
54
|
- [Byebug](#byebug)
|
48
55
|
- [Pronunciation](#pronunciation)
|
49
56
|
- [Supported Ruby versions](#supported-ruby-versions)
|
50
57
|
- [Testing](#testing)
|
51
58
|
- [Motivation](#motivation)
|
59
|
+
- [Kernel#require is brittle](#kernelrequire-is-brittle)
|
60
|
+
- [Rails autoloading was brittle](#rails-autoloading-was-brittle)
|
52
61
|
- [Thanks](#thanks)
|
53
62
|
- [License](#license)
|
54
63
|
|
@@ -157,6 +166,16 @@ class HttpCrawler
|
|
157
166
|
end
|
158
167
|
```
|
159
168
|
|
169
|
+
The first example needs a custom [inflection](https://github.com/fxn/zeitwerk#inflection) rule:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
loader.inflector.inflect("max_retries" => "MAX_RETRIES")
|
173
|
+
```
|
174
|
+
|
175
|
+
Otherwise, Zeitwerk would expect the file to define `MaxRetries`.
|
176
|
+
|
177
|
+
In the second example, no custom rule is needed.
|
178
|
+
|
160
179
|
<a id="markdown-root-directories-and-root-namespaces" name="root-directories-and-root-namespaces"></a>
|
161
180
|
### Root directories and root namespaces
|
162
181
|
|
@@ -275,6 +294,23 @@ To illustrate usage of glob patterns, if `actions` in the example above is part
|
|
275
294
|
loader.collapse("#{__dir__}/*/actions")
|
276
295
|
```
|
277
296
|
|
297
|
+
<a id="markdown-testing-compliance" name="testing-compliance"></a>
|
298
|
+
### Testing compliance
|
299
|
+
|
300
|
+
When a managed file is loaded, Zeitwerk verifies the expected constant is defined. If it is not, `Zeitwerk::NameError` is raised.
|
301
|
+
|
302
|
+
So, an easy way to ensure compliance in the test suite is to eager load the project:
|
303
|
+
|
304
|
+
```ruby
|
305
|
+
begin
|
306
|
+
loader.eager_load(force: true)
|
307
|
+
rescue Zeitwerk::NameError => e
|
308
|
+
flunk e.message
|
309
|
+
else
|
310
|
+
assert true
|
311
|
+
end
|
312
|
+
```
|
313
|
+
|
278
314
|
<a id="markdown-usage" name="usage"></a>
|
279
315
|
## Usage
|
280
316
|
|
@@ -377,7 +413,16 @@ Zeitwerk instances are able to eager load their managed files:
|
|
377
413
|
loader.eager_load
|
378
414
|
```
|
379
415
|
|
380
|
-
That skips [ignored files and directories](#ignoring-parts-of-the-project)
|
416
|
+
That skips [ignored files and directories](#ignoring-parts-of-the-project).
|
417
|
+
|
418
|
+
In gems, the method needs to be invoked after the main namespace has been defined, as shown in [Synopsis](https://github.com/fxn/zeitwerk#synopsis).
|
419
|
+
|
420
|
+
Eager loading is synchronized and idempotent.
|
421
|
+
|
422
|
+
<a id="markdown-eager-load-exclusions" name="eager-load-exclusions"></a>
|
423
|
+
#### Eager load exclusions
|
424
|
+
|
425
|
+
You can tell Zeitwerk that certain files or directories are autoloadable, but should not be eager loaded:
|
381
426
|
|
382
427
|
```ruby
|
383
428
|
db_adapters = "#{__dir__}/my_gem/db_adapters"
|
@@ -386,13 +431,20 @@ loader.setup
|
|
386
431
|
loader.eager_load # won't eager load the database adapters
|
387
432
|
```
|
388
433
|
|
389
|
-
|
434
|
+
However, that can be overridden with `force`:
|
390
435
|
|
391
|
-
|
436
|
+
```ruby
|
437
|
+
loader.eager_load(force: true) # database adapters are eager loaded
|
438
|
+
```
|
439
|
+
|
440
|
+
Which may be handy if the project eager loads in the test suite to [ensure project layour compliance](#testing-compliance).
|
392
441
|
|
393
|
-
|
442
|
+
The `force` flag does not affect ignored files and directories, those are still ignored.
|
394
443
|
|
395
|
-
|
444
|
+
<a id="markdown-global-eager-load" name="global-eager-load"></a>
|
445
|
+
#### Global eager load
|
446
|
+
|
447
|
+
If you want to eager load yourself and all dependencies that use Zeitwerk, you can broadcast the `eager_load` call to all instances:
|
396
448
|
|
397
449
|
```ruby
|
398
450
|
Zeitwerk::Loader.eager_load_all
|
@@ -402,6 +454,8 @@ This may be handy in top-level services, like web applications.
|
|
402
454
|
|
403
455
|
Note that thanks to idempotence `Zeitwerk::Loader.eager_load_all` won't eager load twice if any of the instances already eager loaded.
|
404
456
|
|
457
|
+
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.
|
458
|
+
|
405
459
|
<a id="markdown-reloading" name="reloading"></a>
|
406
460
|
### Reloading
|
407
461
|
|
@@ -551,8 +605,26 @@ class MyGem::Inflector < Zeitwerk::GemInflector
|
|
551
605
|
end
|
552
606
|
```
|
553
607
|
|
608
|
+
<a id="markdown-callbacks" name="callbacks"></a>
|
609
|
+
### Callbacks
|
610
|
+
|
611
|
+
<a id="markdown-the-on_setup-callback" name="the-on_setup-callback"></a>
|
612
|
+
#### The on_setup callback
|
613
|
+
|
614
|
+
The `on_setup` callback is fired on setup and on each reload:
|
615
|
+
|
616
|
+
```ruby
|
617
|
+
loader.on_setup do
|
618
|
+
# Ready to autoload here.
|
619
|
+
end
|
620
|
+
```
|
621
|
+
|
622
|
+
Multiple `on_setup` callbacks are supported, and they run in order of definition.
|
623
|
+
|
624
|
+
If `setup` was already executed, the callback is fired immediately.
|
625
|
+
|
554
626
|
<a id="markdown-the-on_load-callback" name="the-on_load-callback"></a>
|
555
|
-
|
627
|
+
#### The on_load callback
|
556
628
|
|
557
629
|
The usual place to run something when a file is loaded is the file itself. However, sometimes you'd like to be called, and this is possible with the `on_load` callback.
|
558
630
|
|
@@ -610,8 +682,10 @@ There are use cases for this last catch-all callback, but they are rare. If you
|
|
610
682
|
|
611
683
|
If both types of callbacks are defined, the specific ones run first.
|
612
684
|
|
685
|
+
Since `on_load` callbacks are executed right after files are loaded, even if the loading context seems to be far away, in practice **the block is subject to [circular dependencies](#beware-of-circular-dependencies)**. As a rule of thumb, as far as loading order and its interdependencies is concerned, you have to program as if the block was executed at the bottom of the file just loaded.
|
686
|
+
|
613
687
|
<a id="markdown-the-on_unload-callback" name="the-on_unload-callback"></a>
|
614
|
-
|
688
|
+
#### The on_unload callback
|
615
689
|
|
616
690
|
When reloading is enabled, you may occasionally need to execute something before a certain autoloaded class or module is unloaded. The `on_unload` callback allows you to do that.
|
617
691
|
|
@@ -822,6 +896,26 @@ Trip = Struct.new { ... } # NOT SUPPORTED
|
|
822
896
|
|
823
897
|
This only affects explicit namespaces, those idioms work well for any other ordinary class or module.
|
824
898
|
|
899
|
+
<a id="markdown-beware-of-circular-dependencies" name="beware-of-circular-dependencies"></a>
|
900
|
+
### Beware of circular dependencies
|
901
|
+
|
902
|
+
In Ruby, you can't have certain top-level circular dependencies. Take for example:
|
903
|
+
|
904
|
+
```ruby
|
905
|
+
# c.rb
|
906
|
+
class C < D
|
907
|
+
end
|
908
|
+
|
909
|
+
# d.rb
|
910
|
+
class D
|
911
|
+
C
|
912
|
+
end
|
913
|
+
```
|
914
|
+
|
915
|
+
In order to define `C`, you need to load `D`. However, the body of `D` refers to `C`.
|
916
|
+
|
917
|
+
Circular dependencies like those do not work in plain Ruby, and therefore do not work in projects managed by Zeitwerk either.
|
918
|
+
|
825
919
|
<a id="markdown-reopening-third-party-namespaces" name="reopening-third-party-namespaces"></a>
|
826
920
|
### Reopening third-party namespaces
|
827
921
|
|
@@ -876,6 +970,11 @@ With that, when Zeitwerk scans the file system and reaches the gem directories `
|
|
876
970
|
<a id="markdown-debuggers" name="debuggers"></a>
|
877
971
|
### Debuggers
|
878
972
|
|
973
|
+
<a id="markdown-debugrb" name="debugrb"></a>
|
974
|
+
#### debug.rb
|
975
|
+
|
976
|
+
The new [debug.rb](https://github.com/ruby/debug) gem and Zeitwerk seem to be compatible, as far as I can tell. This is the new debugger that is going to ship with Ruby 3.1.
|
977
|
+
|
879
978
|
<a id="markdown-break" name="break"></a>
|
880
979
|
#### Break
|
881
980
|
|
@@ -894,7 +993,11 @@ Zeitwerk and [Byebug](https://github.com/deivid-rodriguez/byebug) are incompatib
|
|
894
993
|
<a id="markdown-supported-ruby-versions" name="supported-ruby-versions"></a>
|
895
994
|
## Supported Ruby versions
|
896
995
|
|
897
|
-
Zeitwerk works with
|
996
|
+
Zeitwerk works with CRuby 2.5 and above.
|
997
|
+
|
998
|
+
On TruffleRuby all is good except for thread-safety. Right now, in TruffleRuby `Module#autoload` does not block threads accessing a constant that is being autoloaded. CRuby prevents such access to avoid concurrent threads from seeing partial evaluations of the corresponding file. Zeitwerk inherits autoloading thread-safety from this property. This is not an issue if your project gets eager loaded, or if you lazy load in single-threaded environments. (See https://github.com/oracle/truffleruby/issues/2431.)
|
999
|
+
|
1000
|
+
JRuby 9.3.0.0 is almost there. As of this writing, the test suite of Zeitwerk passes on JRuby except for three tests. (See https://github.com/jruby/jruby/issues/6781.)
|
898
1001
|
|
899
1002
|
<a id="markdown-testing" name="testing"></a>
|
900
1003
|
## Testing
|
@@ -925,9 +1028,19 @@ and run `bin/test`.
|
|
925
1028
|
<a id="markdown-motivation" name="motivation"></a>
|
926
1029
|
## Motivation
|
927
1030
|
|
928
|
-
|
1031
|
+
<a id="markdown-kernelrequire-is-brittle" name="kernelrequire-is-brittle"></a>
|
1032
|
+
### Kernel#require is brittle
|
1033
|
+
|
1034
|
+
Since `require` has global side-effects, and there is no static way to verify that you have issued the `require` calls for code that your file depends on, in practice it is very easy to forget some. That introduces bugs that depend on the load order.
|
1035
|
+
|
1036
|
+
Also, if the project has namespaces, setting things up and getting client code to load things in a consistent way needs discipline. For example, `require "foo/bar"` may define `Foo`, instead of reopen it. That may be a broken window, giving place to superclass mismatches or partially-defined namespaces.
|
1037
|
+
|
1038
|
+
With Zeitwerk, you just name things following conventions and done. Things are available everywhere, and descend is always orderly. Without effort and without broken windows.
|
1039
|
+
|
1040
|
+
<a id="markdown-rails-autoloading-was-brittle" name="rails-autoloading-was-brittle"></a>
|
1041
|
+
### Rails autoloading was brittle
|
929
1042
|
|
930
|
-
|
1043
|
+
Autoloading in Rails was based on `const_missing` up to Rails 5. That callback lacks fundamental information like the nesting or the resolution algorithm being used. Because of that, Rails autoloading was not able to match Ruby's semantics, and that introduced a [series of issues](https://guides.rubyonrails.org/v5.2/autoloading_and_reloading_constants.html#common-gotchas). Zeitwerk is based on a different technique and fixed Rails autoloading starting with Rails 6.
|
931
1044
|
|
932
1045
|
<a id="markdown-thanks" name="thanks"></a>
|
933
1046
|
## Thanks
|
data/lib/zeitwerk/autoloads.rb
CHANGED
data/lib/zeitwerk/error.rb
CHANGED
data/lib/zeitwerk/kernel.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Kernel
|
4
4
|
module_function
|
5
5
|
|
6
|
-
# We are going to decorate
|
6
|
+
# We are going to decorate Kernel#require with two goals.
|
7
7
|
#
|
8
8
|
# First, by intercepting Kernel#require calls, we are able to autovivify
|
9
9
|
# modules on required directories, and also do internal housekeeping when
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zeitwerk::Loader::Callbacks
|
2
4
|
include Zeitwerk::RealModName
|
3
5
|
|
@@ -9,16 +11,19 @@ module Zeitwerk::Loader::Callbacks
|
|
9
11
|
cref = autoloads.delete(file)
|
10
12
|
cpath = cpath(*cref)
|
11
13
|
|
14
|
+
# If reloading is enabled, we need to put this constant for unloading
|
15
|
+
# regardless of what cdef? says. In Ruby < 3.1 the internal state is not
|
16
|
+
# fully cleared. Module#constants still includes it, and you need to
|
17
|
+
# remove_const. See https://github.com/ruby/ruby/pull/4715.
|
12
18
|
to_unload[cpath] = [file, cref] if reloading_enabled?
|
13
19
|
Zeitwerk::Registry.unregister_autoload(file)
|
14
20
|
|
15
|
-
if
|
16
|
-
log("constant #{cpath} loaded from file #{file}")
|
17
|
-
|
21
|
+
if cdef?(*cref)
|
22
|
+
log("constant #{cpath} loaded from file #{file}") if logger
|
23
|
+
run_on_load_callbacks(cpath, cget(*cref), file) unless on_load_callbacks.empty?
|
24
|
+
else
|
18
25
|
raise Zeitwerk::NameError.new("expected file #{file} to define constant #{cpath}, but didn't", cref.last)
|
19
26
|
end
|
20
|
-
|
21
|
-
run_on_load_callbacks(cpath, cget(*cref), file) unless on_load_callbacks.empty?
|
22
27
|
end
|
23
28
|
|
24
29
|
# Invoked from our decorated Kernel#require when a managed directory is
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "set"
|
2
4
|
require "securerandom"
|
3
5
|
|
@@ -54,6 +56,12 @@ module Zeitwerk::Loader::Config
|
|
54
56
|
# @sig Set[String]
|
55
57
|
attr_reader :eager_load_exclusions
|
56
58
|
|
59
|
+
# User-oriented callbacks to be fired on setup and on reload.
|
60
|
+
#
|
61
|
+
# @private
|
62
|
+
# @sig Array[{ () -> void }]
|
63
|
+
attr_reader :on_setup_callbacks
|
64
|
+
|
57
65
|
# User-oriented callbacks to be fired when a constant is loaded.
|
58
66
|
#
|
59
67
|
# @private
|
@@ -81,6 +89,7 @@ module Zeitwerk::Loader::Config
|
|
81
89
|
@collapse_dirs = Set.new
|
82
90
|
@eager_load_exclusions = Set.new
|
83
91
|
@reloading_enabled = false
|
92
|
+
@on_setup_callbacks = []
|
84
93
|
@on_load_callbacks = {}
|
85
94
|
@on_unload_callbacks = {}
|
86
95
|
@logger = self.class.default_logger
|
@@ -188,6 +197,17 @@ module Zeitwerk::Loader::Config
|
|
188
197
|
end
|
189
198
|
end
|
190
199
|
|
200
|
+
# Configure a block to be called after setup and on each reload.
|
201
|
+
# If setup was already done, the block runs immediately.
|
202
|
+
#
|
203
|
+
# @sig () { () -> void } -> void
|
204
|
+
def on_setup(&block)
|
205
|
+
mutex.synchronize do
|
206
|
+
on_setup_callbacks << block
|
207
|
+
block.call if @setup
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
191
211
|
# Configure a block to be invoked once a certain constant path is loaded.
|
192
212
|
# Supports multiple callbacks, and if there are many, they are executed in
|
193
213
|
# the order in which they were defined.
|
@@ -247,17 +267,10 @@ module Zeitwerk::Loader::Config
|
|
247
267
|
|
248
268
|
# @private
|
249
269
|
# @sig (String) -> bool
|
250
|
-
def
|
251
|
-
|
252
|
-
|
253
|
-
return false if dir.start_with?(ignored_path + "/")
|
270
|
+
def ignores?(abspath)
|
271
|
+
ignored_paths.any? do |ignored_path|
|
272
|
+
ignored_path == abspath || (dir?(ignored_path) && abspath.start_with?(ignored_path + "/"))
|
254
273
|
end
|
255
|
-
|
256
|
-
root_dirs.each_key do |root_dir|
|
257
|
-
return true if root_dir.start_with?(dir) || dir.start_with?(root_dir + "/")
|
258
|
-
end
|
259
|
-
|
260
|
-
false
|
261
274
|
end
|
262
275
|
|
263
276
|
private
|
data/lib/zeitwerk/loader.rb
CHANGED
@@ -110,6 +110,8 @@ module Zeitwerk
|
|
110
110
|
set_autoloads_in_dir(root_dir, namespace)
|
111
111
|
end
|
112
112
|
|
113
|
+
on_setup_callbacks.each(&:call)
|
114
|
+
|
113
115
|
@setup = true
|
114
116
|
end
|
115
117
|
end
|
@@ -143,7 +145,7 @@ module Zeitwerk
|
|
143
145
|
# Could happen if loaded with require_relative. That is unsupported,
|
144
146
|
# and the constant path would escape unloadable_cpath? This is just
|
145
147
|
# defensive code to clean things up as much as we are able to.
|
146
|
-
unload_cref(parent, cname)
|
148
|
+
unload_cref(parent, cname)
|
147
149
|
unloaded_files.add(abspath) if ruby?(abspath)
|
148
150
|
end
|
149
151
|
end
|
@@ -154,7 +156,7 @@ module Zeitwerk
|
|
154
156
|
run_on_unload_callbacks(cpath, value, abspath)
|
155
157
|
end
|
156
158
|
|
157
|
-
unload_cref(parent, cname)
|
159
|
+
unload_cref(parent, cname)
|
158
160
|
unloaded_files.add(abspath) if ruby?(abspath)
|
159
161
|
end
|
160
162
|
|
@@ -208,25 +210,28 @@ module Zeitwerk
|
|
208
210
|
# Eager loads all files in the root directories, recursively. Files do not
|
209
211
|
# need to be in `$LOAD_PATH`, absolute file names are used. Ignored files
|
210
212
|
# are not eager loaded. You can opt-out specifically in specific files and
|
211
|
-
# directories with `do_not_eager_load
|
213
|
+
# directories with `do_not_eager_load`, and that can be overridden passing
|
214
|
+
# `force: true`.
|
212
215
|
#
|
213
|
-
# @sig () -> void
|
214
|
-
def eager_load
|
216
|
+
# @sig (true | false) -> void
|
217
|
+
def eager_load(force: false)
|
215
218
|
mutex.synchronize do
|
216
219
|
break if @eager_loaded
|
217
220
|
|
218
221
|
log("eager load start") if logger
|
219
222
|
|
223
|
+
honour_exclusions = !force
|
224
|
+
|
220
225
|
queue = []
|
221
226
|
actual_root_dirs.each do |root_dir, namespace|
|
222
|
-
queue << [namespace, root_dir] unless excluded_from_eager_load?(root_dir)
|
227
|
+
queue << [namespace, root_dir] unless honour_exclusions && excluded_from_eager_load?(root_dir)
|
223
228
|
end
|
224
229
|
|
225
230
|
while to_eager_load = queue.shift
|
226
231
|
namespace, dir = to_eager_load
|
227
232
|
|
228
233
|
ls(dir) do |basename, abspath|
|
229
|
-
next if excluded_from_eager_load?(abspath)
|
234
|
+
next if honour_exclusions && excluded_from_eager_load?(abspath)
|
230
235
|
|
231
236
|
if ruby?(abspath)
|
232
237
|
if cref = autoloads.cref_for(abspath)
|
@@ -454,12 +459,21 @@ module Zeitwerk
|
|
454
459
|
def raise_if_conflicting_directory(dir)
|
455
460
|
self.class.mutex.synchronize do
|
456
461
|
Registry.loaders.each do |loader|
|
457
|
-
if loader
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
462
|
+
next if loader == self
|
463
|
+
next if loader.ignores?(dir)
|
464
|
+
|
465
|
+
dir = dir + "/"
|
466
|
+
loader.root_dirs.each do |root_dir, _namespace|
|
467
|
+
next if ignores?(root_dir)
|
468
|
+
|
469
|
+
root_dir = root_dir + "/"
|
470
|
+
if dir.start_with?(root_dir) || root_dir.start_with?(dir)
|
471
|
+
require "pp" # Needed for pretty_inspect, even in Ruby 2.5.
|
472
|
+
raise Error,
|
473
|
+
"loader\n\n#{pretty_inspect}\n\nwants to manage directory #{dir.chop}," \
|
474
|
+
" which is already managed by\n\n#{loader.pretty_inspect}\n"
|
475
|
+
EOS
|
476
|
+
end
|
463
477
|
end
|
464
478
|
end
|
465
479
|
end
|
@@ -480,7 +494,13 @@ module Zeitwerk
|
|
480
494
|
|
481
495
|
# @sig (Module, Symbol) -> void
|
482
496
|
def unload_cref(parent, cname)
|
497
|
+
# Let's optimistically remove_const. The way we use it, this is going to
|
498
|
+
# succeed always if all is good.
|
483
499
|
parent.__send__(:remove_const, cname)
|
500
|
+
rescue ::NameError
|
501
|
+
# There are a few edge scenarios in which this may happen. If the constant
|
502
|
+
# is gone, that is OK, anyway.
|
503
|
+
else
|
484
504
|
log("#{cpath(parent, cname)} unloaded") if logger
|
485
505
|
end
|
486
506
|
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.5.0.
|
4
|
+
version: 2.5.0.beta6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Xavier Noria
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|