zeitwerk 2.6.17 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +0 -41
- data/lib/zeitwerk/core_ext/module.rb +12 -0
- data/lib/zeitwerk/cref.rb +5 -39
- data/lib/zeitwerk/explicit_namespace.rb +49 -58
- data/lib/zeitwerk/loader/callbacks.rb +2 -1
- data/lib/zeitwerk/loader/helpers.rb +1 -3
- data/lib/zeitwerk/loader.rb +8 -6
- data/lib/zeitwerk/real_mod_name.rb +2 -8
- data/lib/zeitwerk/registry.rb +5 -2
- data/lib/zeitwerk/version.rb +1 -1
- data/lib/zeitwerk.rb +3 -1
- metadata +6 -5
- /data/lib/zeitwerk/{kernel.rb → core_ext/kernel.rb} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79a3e282ee7602f7d40c75c1299b24de4de890ee90a22cd37ed9c7a660936074
|
4
|
+
data.tar.gz: bd60115bc776137770e16ee527e4a07e815af547d1d5de851039e0187d50eff8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebdab4575a6e35591d1603ed11dc31f77eeffb15c8270f54719eb0850f3641efa968bebd8f02c32330ca768b8211d9aeb972fd41e0efac710faa023a0d62a841
|
7
|
+
data.tar.gz: 6d58d0256f35d6ac6445cba6619943aa4b3974905eb28588284f14aadd27304b1f11fc46d4f0905074c8888e52eabc03e312054930b702c875856d6c0a6a0694
|
data/README.md
CHANGED
@@ -54,7 +54,6 @@
|
|
54
54
|
- [Use case: The adapter pattern](#use-case-the-adapter-pattern)
|
55
55
|
- [Use case: Test files mixed with implementation files](#use-case-test-files-mixed-with-implementation-files)
|
56
56
|
- [Shadowed files](#shadowed-files)
|
57
|
-
- [Edge cases](#edge-cases)
|
58
57
|
- [Beware of circular dependencies](#beware-of-circular-dependencies)
|
59
58
|
- [Reopening third-party namespaces](#reopening-third-party-namespaces)
|
60
59
|
- [Introspection](#introspection)
|
@@ -63,7 +62,6 @@
|
|
63
62
|
- [`Zeitwerk::Loader#all_expected_cpaths`](#zeitwerkloaderall_expected_cpaths)
|
64
63
|
- [Encodings](#encodings)
|
65
64
|
- [Rules of thumb](#rules-of-thumb)
|
66
|
-
- [Debuggers](#debuggers)
|
67
65
|
- [Pronunciation](#pronunciation)
|
68
66
|
- [Supported Ruby versions](#supported-ruby-versions)
|
69
67
|
- [Testing](#testing)
|
@@ -1178,36 +1176,6 @@ file #{file} is ignored because #{constant_path} is already defined
|
|
1178
1176
|
|
1179
1177
|
Shadowing only applies to Ruby files, namespace definition can be spread over multiple directories. And you can also reopen third-party namespaces if done [orderly](#reopening-third-party-namespaces).
|
1180
1178
|
|
1181
|
-
<a id="markdown-edge-cases" name="edge-cases"></a>
|
1182
|
-
### Edge cases
|
1183
|
-
|
1184
|
-
[Explicit namespaces](#explicit-namespaces) like `Trip` here:
|
1185
|
-
|
1186
|
-
```ruby
|
1187
|
-
# trip.rb
|
1188
|
-
class Trip
|
1189
|
-
include Geolocation
|
1190
|
-
end
|
1191
|
-
|
1192
|
-
# trip/geolocation.rb
|
1193
|
-
module Trip::Geolocation
|
1194
|
-
...
|
1195
|
-
end
|
1196
|
-
```
|
1197
|
-
|
1198
|
-
have to be defined with the `class`/`module` keywords, as in the example above.
|
1199
|
-
|
1200
|
-
For technical reasons, raw constant assignment is not supported:
|
1201
|
-
|
1202
|
-
```ruby
|
1203
|
-
# trip.rb
|
1204
|
-
Trip = Class { ...} # NOT SUPPORTED
|
1205
|
-
Trip = Struct.new { ... } # NOT SUPPORTED
|
1206
|
-
Trip = Data.define { ... } # NOT SUPPORTED
|
1207
|
-
```
|
1208
|
-
|
1209
|
-
This only affects explicit namespaces, those idioms work well for any other ordinary class or module.
|
1210
|
-
|
1211
1179
|
<a id="markdown-beware-of-circular-dependencies" name="beware-of-circular-dependencies"></a>
|
1212
1180
|
### Beware of circular dependencies
|
1213
1181
|
|
@@ -1406,15 +1374,6 @@ The test suite passes on Windows with codepage `Windows-1252` if all the involve
|
|
1406
1374
|
|
1407
1375
|
6. In a given process, ideally, there should be at most one loader with reloading enabled. Technically, you can have more, but it may get tricky if one refers to constants managed by the other one. Do that only if you know what you are doing.
|
1408
1376
|
|
1409
|
-
<a id="markdown-debuggers" name="debuggers"></a>
|
1410
|
-
### Debuggers
|
1411
|
-
|
1412
|
-
Zeitwerk and [debug.rb](https://github.com/ruby/debug) are fully compatible if CRuby is ≥ 3.1 (see [ruby/debug#558](https://github.com/ruby/debug/pull/558)).
|
1413
|
-
|
1414
|
-
[Byebug](https://github.com/deivid-rodriguez/byebug) is compatible except for an edge case explained in [deivid-rodriguez/byebug#564](https://github.com/deivid-rodriguez/byebug/issues/564). Prior to CRuby 3.1, `debug.rb` has a similar edge incompatibility.
|
1415
|
-
|
1416
|
-
[Break](https://github.com/gsamokovarov/break) is fully compatible.
|
1417
|
-
|
1418
1377
|
<a id="markdown-pronunciation" name="pronunciation"></a>
|
1419
1378
|
## Pronunciation
|
1420
1379
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zeitwerk::ConstAdded
|
4
|
+
def const_added(cname)
|
5
|
+
if loader = Zeitwerk::ExplicitNamespace.__loader_for(self, cname)
|
6
|
+
loader.on_namespace_loaded(const_get(cname, false))
|
7
|
+
end
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
Module.prepend(self)
|
12
|
+
end
|
data/lib/zeitwerk/cref.rb
CHANGED
@@ -26,48 +26,14 @@ class Zeitwerk::Cref
|
|
26
26
|
@path = nil
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
#
|
33
|
-
# @sig () -> String
|
34
|
-
def path
|
35
|
-
@path ||= Object.equal?(@mod) ? @cname.name : "#{real_mod_name(@mod)}::#{@cname.name}"
|
36
|
-
end
|
37
|
-
else
|
38
|
-
# @sig () -> String
|
39
|
-
def path
|
40
|
-
@path ||= Object.equal?(@mod) ? @cname.to_s : "#{real_mod_name(@mod)}::#{@cname}"
|
41
|
-
end
|
29
|
+
# @sig () -> String
|
30
|
+
def path
|
31
|
+
@path ||= Object.equal?(@mod) ? @cname.name : "#{real_mod_name(@mod)}::#{@cname.name}".freeze
|
42
32
|
end
|
43
33
|
|
44
|
-
# The autoload? predicate takes into account the ancestor chain of the
|
45
|
-
# receiver, like const_defined? and other methods in the constants API do.
|
46
|
-
#
|
47
|
-
# For example, given
|
48
|
-
#
|
49
|
-
# class A
|
50
|
-
# autoload :X, "x.rb"
|
51
|
-
# end
|
52
|
-
#
|
53
|
-
# class B < A
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
# B.autoload?(:X) returns "x.rb".
|
57
|
-
#
|
58
|
-
# We need a way to retrieve it ignoring ancestors.
|
59
|
-
#
|
60
34
|
# @sig () -> String?
|
61
|
-
|
62
|
-
|
63
|
-
def autoload?
|
64
|
-
@mod.autoload?(@cname) if self.defined?
|
65
|
-
end
|
66
|
-
else
|
67
|
-
# @sig () -> String?
|
68
|
-
def autoload?
|
69
|
-
@mod.autoload?(@cname, false)
|
70
|
-
end
|
35
|
+
def autoload?
|
36
|
+
@mod.autoload?(@cname, false)
|
71
37
|
end
|
72
38
|
|
73
39
|
# @sig (String) -> bool
|
@@ -1,93 +1,84 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Zeitwerk
|
4
|
-
# Centralizes the logic
|
5
|
-
#
|
6
|
-
# after the constant has been defined.
|
4
|
+
# Centralizes the logic needed to descend into matching subdirectories right
|
5
|
+
# after the constant for an explicit namespace has been defined.
|
7
6
|
#
|
8
7
|
# The implementation assumes an explicit namespace is managed by one loader.
|
9
8
|
# Loaders that reopen namespaces owned by other projects are responsible for
|
10
9
|
# loading their constant before setup. This is documented.
|
11
10
|
module ExplicitNamespace # :nodoc: all
|
11
|
+
# Maps cpaths of explicit namespaces with their corresponding loader.
|
12
|
+
# Entries are added as the namespaces are found, and removed as they are
|
13
|
+
# autoloaded.
|
14
|
+
#
|
15
|
+
# @sig Hash[String => Zeitwerk::Loader]
|
16
|
+
@cpaths = {}
|
17
|
+
|
12
18
|
class << self
|
13
19
|
include RealModName
|
14
20
|
extend Internal
|
15
21
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# @sig Hash[String, Zeitwerk::Loader]
|
20
|
-
attr_reader :cpaths
|
21
|
-
private :cpaths
|
22
|
-
|
23
|
-
# @sig Mutex
|
24
|
-
attr_reader :mutex
|
25
|
-
private :mutex
|
26
|
-
|
27
|
-
# @sig TracePoint
|
28
|
-
attr_reader :tracer
|
29
|
-
private :tracer
|
30
|
-
|
31
|
-
# Asserts `cpath` corresponds to an explicit namespace for which `loader`
|
32
|
-
# is responsible.
|
22
|
+
# Registers `cpath` as being the constant path of an explicit namespace
|
23
|
+
# managed by `loader`.
|
33
24
|
#
|
34
25
|
# @sig (String, Zeitwerk::Loader) -> void
|
35
26
|
internal def register(cpath, loader)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
27
|
+
@cpaths[cpath] = loader
|
28
|
+
end
|
29
|
+
|
30
|
+
# @sig (String) -> Zeitwerk::Loader?
|
31
|
+
internal def loader_for(mod, cname)
|
32
|
+
cpath = mod.equal?(Object) ? cname.name : "#{real_mod_name(mod)}::#{cname}"
|
33
|
+
@cpaths.delete(cpath)
|
42
34
|
end
|
43
35
|
|
44
36
|
# @sig (Zeitwerk::Loader) -> void
|
45
37
|
internal def unregister_loader(loader)
|
46
|
-
cpaths.delete_if {
|
47
|
-
disable_tracer_if_unneeded
|
38
|
+
@cpaths.delete_if { _2.equal?(loader) }
|
48
39
|
end
|
49
40
|
|
50
41
|
# This is an internal method only used by the test suite.
|
51
42
|
#
|
52
|
-
# @sig (String) ->
|
43
|
+
# @sig (String) -> Zeitwerk::Loader?
|
53
44
|
internal def registered?(cpath)
|
54
|
-
cpaths
|
45
|
+
@cpaths[cpath]
|
55
46
|
end
|
56
47
|
|
48
|
+
# This is an internal method only used by the test suite.
|
49
|
+
#
|
57
50
|
# @sig () -> void
|
58
|
-
|
59
|
-
|
60
|
-
tracer.disable if cpaths.empty?
|
61
|
-
end
|
51
|
+
internal def clear
|
52
|
+
@cpaths.clear
|
62
53
|
end
|
63
54
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
# than accessing its name.
|
69
|
-
return if event.self.singleton_class?
|
55
|
+
module Synchronized
|
56
|
+
extend Internal
|
57
|
+
|
58
|
+
MUTEX = Mutex.new
|
70
59
|
|
71
|
-
|
72
|
-
|
73
|
-
# event on Class.new or Module.new, so that would incur in an extra call
|
74
|
-
# for nothing.
|
75
|
-
#
|
76
|
-
# On the other hand, if we were called, cpaths is not empty. Otherwise
|
77
|
-
# the tracer is disabled. So we do need to go ahead with the hash code
|
78
|
-
# computation and delete call.
|
79
|
-
if loader = cpaths.delete(real_mod_name(event.self))
|
80
|
-
loader.on_namespace_loaded(event.self)
|
81
|
-
disable_tracer_if_unneeded
|
60
|
+
internal def register(...)
|
61
|
+
MUTEX.synchronize { super }
|
82
62
|
end
|
83
|
-
end
|
84
|
-
end
|
85
63
|
|
86
|
-
|
87
|
-
|
64
|
+
internal def loader_for(...)
|
65
|
+
MUTEX.synchronize { super }
|
66
|
+
end
|
67
|
+
|
68
|
+
internal def unregister_loader(...)
|
69
|
+
MUTEX.synchronize { super }
|
70
|
+
end
|
88
71
|
|
89
|
-
|
90
|
-
|
91
|
-
|
72
|
+
internal def registered?(...)
|
73
|
+
MUTEX.synchronize { super }
|
74
|
+
end
|
75
|
+
|
76
|
+
internal def clear
|
77
|
+
MUTEX.synchronize { super }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
prepend Synchronized unless RUBY_ENGINE == "ruby"
|
82
|
+
end
|
92
83
|
end
|
93
84
|
end
|
@@ -6,6 +6,7 @@ module Zeitwerk::Loader::Callbacks
|
|
6
6
|
|
7
7
|
# Invoked from our decorated Kernel#require when a managed file is autoloaded.
|
8
8
|
#
|
9
|
+
# @raise [Zeitwerk::NameError]
|
9
10
|
# @sig (String) -> void
|
10
11
|
internal def on_file_autoloaded(file)
|
11
12
|
cref = autoloads.delete(file)
|
@@ -72,7 +73,7 @@ module Zeitwerk::Loader::Callbacks
|
|
72
73
|
end
|
73
74
|
|
74
75
|
# Invoked when a class or module is created or reopened, either from the
|
75
|
-
#
|
76
|
+
# const_added or from module autovivification. If the namespace has matching
|
76
77
|
# subdirectories, we descend into them now.
|
77
78
|
#
|
78
79
|
# @private
|
@@ -57,9 +57,7 @@ module Zeitwerk::Loader::Helpers
|
|
57
57
|
to_visit = [dir]
|
58
58
|
|
59
59
|
while (dir = to_visit.shift)
|
60
|
-
|
61
|
-
|
62
|
-
children.each do |basename|
|
60
|
+
Dir.each_child(dir) do |basename|
|
63
61
|
next if hidden?(basename)
|
64
62
|
|
65
63
|
abspath = File.join(dir, basename)
|
data/lib/zeitwerk/loader.rb
CHANGED
@@ -469,7 +469,7 @@ module Zeitwerk
|
|
469
469
|
# Registering is idempotent, and we have to keep the autoload pointing
|
470
470
|
# to the file. This may run again if more directories are found later
|
471
471
|
# on, no big deal.
|
472
|
-
register_explicit_namespace(cref
|
472
|
+
register_explicit_namespace(cref)
|
473
473
|
end
|
474
474
|
# If the existing autoload points to a file, it has to be preserved, if
|
475
475
|
# not, it is fine as it is. In either case, we do not need to override.
|
@@ -515,8 +515,10 @@ module Zeitwerk
|
|
515
515
|
|
516
516
|
log("earlier autoload for #{cref.path} discarded, it is actually an explicit namespace defined in #{file}") if logger
|
517
517
|
|
518
|
+
# Order matters: When Module#const_added is triggered by the autoload, we
|
519
|
+
# don't want the namespace to be registered yet.
|
518
520
|
define_autoload(cref, file)
|
519
|
-
register_explicit_namespace(cref
|
521
|
+
register_explicit_namespace(cref)
|
520
522
|
end
|
521
523
|
|
522
524
|
# @sig (Module, Symbol, String) -> void
|
@@ -545,13 +547,13 @@ module Zeitwerk
|
|
545
547
|
if autoload_path = cref.autoload?
|
546
548
|
autoload_path if autoloads.key?(autoload_path)
|
547
549
|
else
|
548
|
-
Registry.inception?(cref.path)
|
550
|
+
Registry.inception?(cref.path, self)
|
549
551
|
end
|
550
552
|
end
|
551
553
|
|
552
|
-
# @sig (
|
553
|
-
private def register_explicit_namespace(
|
554
|
-
ExplicitNamespace.__register(
|
554
|
+
# @sig (Zeitwerk::Cref) -> void
|
555
|
+
private def register_explicit_namespace(cref)
|
556
|
+
ExplicitNamespace.__register(cref.path, self)
|
555
557
|
end
|
556
558
|
|
557
559
|
# @sig (String) -> void
|
@@ -10,13 +10,7 @@ module Zeitwerk::RealModName
|
|
10
10
|
# The name method can be overridden, hence the indirection in this method.
|
11
11
|
#
|
12
12
|
# @sig (Module) -> String?
|
13
|
-
|
14
|
-
|
15
|
-
UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
|
16
|
-
end
|
17
|
-
else
|
18
|
-
def real_mod_name(mod)
|
19
|
-
UNBOUND_METHOD_MODULE_NAME.bind(mod).call
|
20
|
-
end
|
13
|
+
def real_mod_name(mod)
|
14
|
+
UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
|
21
15
|
end
|
22
16
|
end
|
data/lib/zeitwerk/registry.rb
CHANGED
@@ -110,9 +110,12 @@ module Zeitwerk
|
|
110
110
|
|
111
111
|
# @private
|
112
112
|
# @sig (String) -> String?
|
113
|
-
def inception?(cpath)
|
113
|
+
def inception?(cpath, registered_by_loader=nil)
|
114
114
|
if pair = inceptions[cpath]
|
115
|
-
pair
|
115
|
+
abspath, loader = pair
|
116
|
+
if registered_by_loader.nil? || registered_by_loader.equal?(loader)
|
117
|
+
abspath
|
118
|
+
end
|
116
119
|
end
|
117
120
|
end
|
118
121
|
|
data/lib/zeitwerk/version.rb
CHANGED
data/lib/zeitwerk.rb
CHANGED
@@ -11,10 +11,12 @@ module Zeitwerk
|
|
11
11
|
require_relative "zeitwerk/inflector"
|
12
12
|
require_relative "zeitwerk/gem_inflector"
|
13
13
|
require_relative "zeitwerk/null_inflector"
|
14
|
-
require_relative "zeitwerk/kernel"
|
15
14
|
require_relative "zeitwerk/error"
|
16
15
|
require_relative "zeitwerk/version"
|
17
16
|
|
17
|
+
require_relative "zeitwerk/core_ext/kernel"
|
18
|
+
require_relative "zeitwerk/core_ext/module"
|
19
|
+
|
18
20
|
# This is a dangerous method.
|
19
21
|
#
|
20
22
|
# @experimental
|
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.7.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: 2024-
|
11
|
+
date: 2024-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|
@@ -23,6 +23,8 @@ files:
|
|
23
23
|
- MIT-LICENSE
|
24
24
|
- README.md
|
25
25
|
- lib/zeitwerk.rb
|
26
|
+
- lib/zeitwerk/core_ext/kernel.rb
|
27
|
+
- lib/zeitwerk/core_ext/module.rb
|
26
28
|
- lib/zeitwerk/cref.rb
|
27
29
|
- lib/zeitwerk/error.rb
|
28
30
|
- lib/zeitwerk/explicit_namespace.rb
|
@@ -30,7 +32,6 @@ files:
|
|
30
32
|
- lib/zeitwerk/gem_loader.rb
|
31
33
|
- lib/zeitwerk/inflector.rb
|
32
34
|
- lib/zeitwerk/internal.rb
|
33
|
-
- lib/zeitwerk/kernel.rb
|
34
35
|
- lib/zeitwerk/loader.rb
|
35
36
|
- lib/zeitwerk/loader/callbacks.rb
|
36
37
|
- lib/zeitwerk/loader/config.rb
|
@@ -56,14 +57,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
57
|
requirements:
|
57
58
|
- - ">="
|
58
59
|
- !ruby/object:Gem::Version
|
59
|
-
version: '2
|
60
|
+
version: '3.2'
|
60
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
62
|
requirements:
|
62
63
|
- - ">="
|
63
64
|
- !ruby/object:Gem::Version
|
64
65
|
version: '0'
|
65
66
|
requirements: []
|
66
|
-
rubygems_version: 3.5.
|
67
|
+
rubygems_version: 3.5.11
|
67
68
|
signing_key:
|
68
69
|
specification_version: 4
|
69
70
|
summary: Efficient and thread-safe constant autoloader
|
File without changes
|