zeitwerk 2.7.1 → 2.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/zeitwerk/core_ext/module.rb +5 -4
- data/lib/zeitwerk/cref/map.rb +124 -0
- data/lib/zeitwerk/cref.rb +6 -3
- data/lib/zeitwerk/internal.rb +1 -0
- data/lib/zeitwerk/loader/callbacks.rb +14 -16
- data/lib/zeitwerk/loader/config.rb +8 -8
- data/lib/zeitwerk/loader.rb +89 -56
- data/lib/zeitwerk/null_inflector.rb +1 -0
- data/lib/zeitwerk/registry/explicit_namespaces.rb +64 -0
- data/lib/zeitwerk/registry/inceptions.rb +31 -0
- data/lib/zeitwerk/registry.rb +3 -59
- data/lib/zeitwerk/version.rb +1 -1
- data/lib/zeitwerk.rb +0 -1
- metadata +6 -7
- data/lib/zeitwerk/explicit_namespace.rb +0 -113
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a07f90eb2f155582d05f58527ffcbc2f4d76c9a1983260ca8d527becaeb7972
|
4
|
+
data.tar.gz: 65e8dc78ca8e6de674f0fc7d88aad5c9bad0d7687bc9ed26f93d6fa0e6d18e90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7b9d13e3d3d5bf0497ec259bf0817256586e245f7d47c951b8392784f715bc71c20d0ec3c9e465077da6d8e729ca6888fbaaa24820fe4459771e29340ee6d05
|
7
|
+
data.tar.gz: 8b1322d36bc9115a56b6abab6be9549c868e0edd2025fe82dd2c5d0abb082fac8532c82ed03f895d34f2875f27b160f4861185112c4aef2a3129e46569115c0f
|
@@ -1,16 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Zeitwerk::ConstAdded
|
3
|
+
module Zeitwerk::ConstAdded # :nodoc:
|
4
|
+
# @sig (Symbol) -> void
|
4
5
|
def const_added(cname)
|
5
|
-
if loader = Zeitwerk::
|
6
|
+
if loader = Zeitwerk::Registry::ExplicitNamespaces.__loader_for(self, cname)
|
6
7
|
namespace = const_get(cname, false)
|
7
8
|
|
8
9
|
unless namespace.is_a?(Module)
|
9
10
|
cref = Zeitwerk::Cref.new(self, cname)
|
10
|
-
raise Zeitwerk::Error, "#{cref
|
11
|
+
raise Zeitwerk::Error, "#{cref} is expected to be a namespace, should be a class or module (got #{namespace.class})"
|
11
12
|
end
|
12
13
|
|
13
|
-
loader.
|
14
|
+
loader.__on_namespace_loaded(Zeitwerk::Cref.new(self, cname), namespace)
|
14
15
|
end
|
15
16
|
super
|
16
17
|
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This class emulates a hash table whose keys are of type Zeitwerk::Cref.
|
4
|
+
#
|
5
|
+
# It is a synchronized 2-level hash. The keys of the top one, stored in `@map`,
|
6
|
+
# are class and module objects, but their hash code is forced to be their object
|
7
|
+
# IDs (see why below). Then, each one of them stores a hash table keyed on
|
8
|
+
# constant names as symbols. We finally store the values in those.
|
9
|
+
#
|
10
|
+
# For example, if we store values 0, 1, and 2 for the crefs that would
|
11
|
+
# correspond to `M::X`, `M::Y`, and `N::Z`, the map will look like this:
|
12
|
+
#
|
13
|
+
# { M => { X: 0, :Y => 1 }, N => { Z: 2 } }
|
14
|
+
#
|
15
|
+
# This structure is internal, so only the needed interface is implemented.
|
16
|
+
#
|
17
|
+
# Why not use tables that map pairs [Module, Symbol] to their values? Because
|
18
|
+
# class and module objects are not guaranteed to be hashable, the `hash` method
|
19
|
+
# may have been overridden:
|
20
|
+
#
|
21
|
+
# https://github.com/fxn/zeitwerk/issues/188
|
22
|
+
#
|
23
|
+
# We can also use a 1-level hash whose keys are the corresponding class and
|
24
|
+
# module names. In the example above it would be:
|
25
|
+
#
|
26
|
+
# { "M::X" => 0, "M::Y" => 1, "N::Z" => 2 }
|
27
|
+
#
|
28
|
+
# The gem used this approach for several years.
|
29
|
+
#
|
30
|
+
# Another option would be to make crefs hashable. I tried with hash code
|
31
|
+
#
|
32
|
+
# real_mod_hash(mod) ^ cname.hash
|
33
|
+
#
|
34
|
+
# and the matching eql?, but that was about 1.8x slower.
|
35
|
+
#
|
36
|
+
# Finally, I came with this solution which is 1.6x faster than the previous one
|
37
|
+
# based on class and module names, even being synchronized. Also, client code
|
38
|
+
# feels natural, since crefs are central objects in Zeitwerk's implementation.
|
39
|
+
class Zeitwerk::Cref::Map # :nodoc: all
|
40
|
+
def initialize
|
41
|
+
@map = {}
|
42
|
+
@map.compare_by_identity
|
43
|
+
@mutex = Mutex.new
|
44
|
+
end
|
45
|
+
|
46
|
+
# @sig (Zeitwerk::Cref, V) -> V
|
47
|
+
def []=(cref, value)
|
48
|
+
@mutex.synchronize do
|
49
|
+
cnames = (@map[cref.mod] ||= {})
|
50
|
+
cnames[cref.cname] = value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @sig (Zeitwerk::Cref) -> top?
|
55
|
+
def [](cref)
|
56
|
+
@mutex.synchronize do
|
57
|
+
@map[cref.mod]&.[](cref.cname)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @sig (Zeitwerk::Cref, { () -> V }) -> V
|
62
|
+
def get_or_set(cref, &block)
|
63
|
+
@mutex.synchronize do
|
64
|
+
cnames = (@map[cref.mod] ||= {})
|
65
|
+
cnames.fetch(cref.cname) { cnames[cref.cname] = block.call }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @sig (Zeitwerk::Cref) -> top?
|
70
|
+
def delete(cref)
|
71
|
+
delete_mod_cname(cref.mod, cref.cname)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Ad-hoc for loader_for, called from const_added. That is a hot path, I prefer
|
75
|
+
# to not create a cref in every call, since that is global.
|
76
|
+
#
|
77
|
+
# @sig (Module, Symbol) -> top?
|
78
|
+
def delete_mod_cname(mod, cname)
|
79
|
+
@mutex.synchronize do
|
80
|
+
if cnames = @map[mod]
|
81
|
+
value = cnames.delete(cname)
|
82
|
+
@map.delete(mod) if cnames.empty?
|
83
|
+
value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# @sig (top) -> void
|
89
|
+
def delete_by_value(value)
|
90
|
+
@mutex.synchronize do
|
91
|
+
@map.delete_if do |mod, cnames|
|
92
|
+
cnames.delete_if { _2 == value }
|
93
|
+
cnames.empty?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Order of yielded crefs is undefined.
|
99
|
+
#
|
100
|
+
# @sig () { (Zeitwerk::Cref) -> void } -> void
|
101
|
+
def each_key
|
102
|
+
@mutex.synchronize do
|
103
|
+
@map.each do |mod, cnames|
|
104
|
+
cnames.each_key do |cname|
|
105
|
+
yield Zeitwerk::Cref.new(mod, cname)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# @sig () -> void
|
112
|
+
def clear
|
113
|
+
@mutex.synchronize do
|
114
|
+
@map.clear
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# @sig () -> bool
|
119
|
+
def empty? # for tests
|
120
|
+
@mutex.synchronize do
|
121
|
+
@map.empty?
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/zeitwerk/cref.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# This private class encapsulates pairs (mod, cname).
|
4
4
|
#
|
5
5
|
# Objects represent the constant cname in the class or module object mod, and
|
6
|
-
# have API to manage them
|
6
|
+
# have API to manage them. Examples:
|
7
7
|
#
|
8
8
|
# cref.path
|
9
9
|
# cref.set(value)
|
@@ -11,6 +11,8 @@
|
|
11
11
|
#
|
12
12
|
# The constant may or may not exist in mod.
|
13
13
|
class Zeitwerk::Cref
|
14
|
+
require_relative "cref/map"
|
15
|
+
|
14
16
|
include Zeitwerk::RealModName
|
15
17
|
|
16
18
|
# @sig Module
|
@@ -33,6 +35,7 @@ class Zeitwerk::Cref
|
|
33
35
|
def path
|
34
36
|
@path ||= Object.equal?(@mod) ? @cname.name : "#{real_mod_name(@mod)}::#{@cname.name}".freeze
|
35
37
|
end
|
38
|
+
alias to_s path
|
36
39
|
|
37
40
|
# @sig () -> String?
|
38
41
|
def autoload?
|
@@ -49,13 +52,13 @@ class Zeitwerk::Cref
|
|
49
52
|
@mod.const_defined?(@cname, false)
|
50
53
|
end
|
51
54
|
|
52
|
-
# @sig (
|
55
|
+
# @sig (top) -> top
|
53
56
|
def set(value)
|
54
57
|
@mod.const_set(@cname, value)
|
55
58
|
end
|
56
59
|
|
57
60
|
# @raise [NameError]
|
58
|
-
# @sig () ->
|
61
|
+
# @sig () -> top
|
59
62
|
def get
|
60
63
|
@mod.const_get(@cname, false)
|
61
64
|
end
|
data/lib/zeitwerk/internal.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Zeitwerk::Loader::Callbacks
|
4
|
-
include Zeitwerk::RealModName
|
3
|
+
module Zeitwerk::Loader::Callbacks # :nodoc: all
|
5
4
|
extend Zeitwerk::Internal
|
6
5
|
|
7
6
|
# Invoked from our decorated Kernel#require when a managed file is autoloaded.
|
@@ -14,11 +13,11 @@ module Zeitwerk::Loader::Callbacks
|
|
14
13
|
Zeitwerk::Registry.unregister_autoload(file)
|
15
14
|
|
16
15
|
if cref.defined?
|
17
|
-
log("constant #{cref
|
18
|
-
to_unload[
|
16
|
+
log("constant #{cref} loaded from file #{file}") if logger
|
17
|
+
to_unload[file] = cref if reloading_enabled?
|
19
18
|
run_on_load_callbacks(cref.path, cref.get, file) unless on_load_callbacks.empty?
|
20
19
|
else
|
21
|
-
msg = "expected file #{file} to define constant #{cref
|
20
|
+
msg = "expected file #{file} to define constant #{cref}, but didn't"
|
22
21
|
log(msg) if logger
|
23
22
|
|
24
23
|
# Ruby still keeps the autoload defined, but we remove it because the
|
@@ -28,7 +27,7 @@ module Zeitwerk::Loader::Callbacks
|
|
28
27
|
# Since the expected constant was not defined, there is nothing to unload.
|
29
28
|
# However, if the exception is rescued and reloading is enabled, we still
|
30
29
|
# need to deleted the file from $LOADED_FEATURES.
|
31
|
-
to_unload[
|
30
|
+
to_unload[file] = cref if reloading_enabled?
|
32
31
|
|
33
32
|
raise Zeitwerk::NameError.new(msg, cref.cname)
|
34
33
|
end
|
@@ -57,7 +56,7 @@ module Zeitwerk::Loader::Callbacks
|
|
57
56
|
cpath = implicit_namespace.name
|
58
57
|
log("module #{cpath} autovivified from directory #{dir}") if logger
|
59
58
|
|
60
|
-
to_unload[
|
59
|
+
to_unload[dir] = cref if reloading_enabled?
|
61
60
|
|
62
61
|
# We don't unregister `dir` in the registry because concurrent threads
|
63
62
|
# wouldn't find a loader associated to it in Kernel#require and would
|
@@ -65,21 +64,20 @@ module Zeitwerk::Loader::Callbacks
|
|
65
64
|
# these to be able to unregister later if eager loading.
|
66
65
|
autoloaded_dirs << dir
|
67
66
|
|
68
|
-
on_namespace_loaded(implicit_namespace)
|
67
|
+
on_namespace_loaded(cref, implicit_namespace)
|
69
68
|
|
70
69
|
run_on_load_callbacks(cpath, implicit_namespace, dir) unless on_load_callbacks.empty?
|
71
70
|
end
|
72
71
|
end
|
73
72
|
end
|
74
73
|
|
75
|
-
# Invoked when a
|
76
|
-
#
|
77
|
-
#
|
74
|
+
# Invoked when a namespace is created, either from const_added or from module
|
75
|
+
# autovivification. If the namespace has matching subdirectories, we descend
|
76
|
+
# into them now.
|
78
77
|
#
|
79
|
-
# @
|
80
|
-
|
81
|
-
|
82
|
-
if dirs = namespace_dirs.delete(real_mod_name(namespace))
|
78
|
+
# @sig (Zeitwerk::Cref, Module) -> void
|
79
|
+
internal def on_namespace_loaded(cref, namespace)
|
80
|
+
if dirs = namespace_dirs.delete(cref)
|
83
81
|
dirs.each do |dir|
|
84
82
|
define_autoloads_for_dir(dir, namespace)
|
85
83
|
end
|
@@ -88,7 +86,7 @@ module Zeitwerk::Loader::Callbacks
|
|
88
86
|
|
89
87
|
private
|
90
88
|
|
91
|
-
# @sig (String,
|
89
|
+
# @sig (String, top, String) -> void
|
92
90
|
def run_on_load_callbacks(cpath, value, abspath)
|
93
91
|
# Order matters. If present, run the most specific one.
|
94
92
|
callbacks = reloading_enabled? ? on_load_callbacks[cpath] : on_load_callbacks.delete(cpath)
|
@@ -71,15 +71,15 @@ module Zeitwerk::Loader::Config
|
|
71
71
|
|
72
72
|
# User-oriented callbacks to be fired when a constant is loaded.
|
73
73
|
#
|
74
|
-
# @sig Hash[String, Array[{ (
|
75
|
-
# Hash[Symbol, Array[{ (String,
|
74
|
+
# @sig Hash[String, Array[{ (top, String) -> void }]]
|
75
|
+
# Hash[Symbol, Array[{ (String, top, String) -> void }]]
|
76
76
|
attr_reader :on_load_callbacks
|
77
77
|
private :on_load_callbacks
|
78
78
|
|
79
79
|
# User-oriented callbacks to be fired before constants are removed.
|
80
80
|
#
|
81
|
-
# @sig Hash[String, Array[{ (
|
82
|
-
# Hash[Symbol, Array[{ (String,
|
81
|
+
# @sig Hash[String, Array[{ (top, String) -> void }]]
|
82
|
+
# Hash[Symbol, Array[{ (String, top, String) -> void }]]
|
83
83
|
attr_reader :on_unload_callbacks
|
84
84
|
private :on_unload_callbacks
|
85
85
|
|
@@ -247,8 +247,8 @@ module Zeitwerk::Loader::Config
|
|
247
247
|
# end
|
248
248
|
#
|
249
249
|
# @raise [TypeError]
|
250
|
-
# @sig (String) { (
|
251
|
-
# (:ANY) { (String,
|
250
|
+
# @sig (String) { (top, String) -> void } -> void
|
251
|
+
# (:ANY) { (String, top, String) -> void } -> void
|
252
252
|
def on_load(cpath = :ANY, &block)
|
253
253
|
raise TypeError, "on_load only accepts strings" unless cpath.is_a?(String) || cpath == :ANY
|
254
254
|
|
@@ -272,8 +272,8 @@ module Zeitwerk::Loader::Config
|
|
272
272
|
# end
|
273
273
|
#
|
274
274
|
# @raise [TypeError]
|
275
|
-
# @sig (String) { (
|
276
|
-
# (:ANY) { (String,
|
275
|
+
# @sig (String) { (top) -> void } -> void
|
276
|
+
# (:ANY) { (String, top) -> void } -> void
|
277
277
|
def on_unload(cpath = :ANY, &block)
|
278
278
|
raise TypeError, "on_unload only accepts strings" unless cpath.is_a?(String) || cpath == :ANY
|
279
279
|
|
data/lib/zeitwerk/loader.rb
CHANGED
@@ -32,6 +32,30 @@ module Zeitwerk
|
|
32
32
|
attr_reader :autoloads
|
33
33
|
internal :autoloads
|
34
34
|
|
35
|
+
# When the path passed to Module#autoload is in the stack of features being
|
36
|
+
# loaded at the moment, Ruby passes. For example, Module#autoload? returns
|
37
|
+
# `nil` even if the autoload has not been attempted. See
|
38
|
+
#
|
39
|
+
# https://bugs.ruby-lang.org/issues/21035
|
40
|
+
#
|
41
|
+
# We call these "inceptions".
|
42
|
+
#
|
43
|
+
# A common case is the entry point of gems managed by Zeitwerk. Their main
|
44
|
+
# file is normally required and, while doing so, the loader sets an autoload
|
45
|
+
# on the gem namespace. That autoload hits this edge case.
|
46
|
+
#
|
47
|
+
# There is some logic that neeeds to know if an autoload for a given
|
48
|
+
# constant already exists. We check Module#autoload? first, and fallback to
|
49
|
+
# the inceptions just in case.
|
50
|
+
#
|
51
|
+
# This map keeps track of pairs (cref, autoload_path) found by the loader.
|
52
|
+
# The module Zeitwerk::Registry::Inceptions, on the other hand, acts as a
|
53
|
+
# global registry for them.
|
54
|
+
#
|
55
|
+
# @sig Zeitwerk::Cref::Map[String]
|
56
|
+
attr_reader :inceptions
|
57
|
+
internal :inceptions
|
58
|
+
|
35
59
|
# We keep track of autoloaded directories to remove them from the registry
|
36
60
|
# at the end of eager loading.
|
37
61
|
#
|
@@ -42,48 +66,31 @@ module Zeitwerk
|
|
42
66
|
attr_reader :autoloaded_dirs
|
43
67
|
internal :autoloaded_dirs
|
44
68
|
|
45
|
-
#
|
69
|
+
# If reloading is enabled, this collection maps autoload paths to their
|
70
|
+
# autoloaded crefs.
|
46
71
|
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
# #<Zeitwerk::Cref:... @mod=Admin, @cname=:Role, ...>
|
50
|
-
# ]
|
72
|
+
# On unload, the autoload paths are passed to callbacks, files deleted from
|
73
|
+
# $LOADED_FEATURES, and the crefs are deleted.
|
51
74
|
#
|
52
|
-
#
|
53
|
-
# stored in order to be able to delete it from $LOADED_FEATURES, and the
|
54
|
-
# cref is used to remove the constant from the parent class or module.
|
55
|
-
#
|
56
|
-
# If reloading is enabled, this hash is filled as constants are autoloaded
|
57
|
-
# or eager loaded. Otherwise, the collection remains empty.
|
58
|
-
#
|
59
|
-
# @sig Hash[String, [String, Zeitwerk::Cref]]
|
75
|
+
# @sig Hash[String, Zeitwerk::Cref]
|
60
76
|
attr_reader :to_unload
|
61
77
|
internal :to_unload
|
62
78
|
|
63
|
-
# Maps namespace
|
79
|
+
# Maps namespace crefs to the directories that conform the namespace.
|
64
80
|
#
|
65
|
-
#
|
81
|
+
# When these crefs get defined we know their children are spread over those
|
82
|
+
# directories. We'll visit them to set up the corresponding autoloads.
|
66
83
|
#
|
67
|
-
#
|
68
|
-
# "/Users/fxn/blog/app/controllers/admin",
|
69
|
-
# "/Users/fxn/blog/app/models/admin",
|
70
|
-
# ...
|
71
|
-
# ]
|
72
|
-
#
|
73
|
-
# when `Admin` gets defined we know that it plays the role of a namespace
|
74
|
-
# and that its children are spread over those directories. We'll visit them
|
75
|
-
# to set up the corresponding autoloads.
|
76
|
-
#
|
77
|
-
# @sig Hash[String, Array[String]]
|
84
|
+
# @sig Zeitwerk::Cref::Map[String]
|
78
85
|
attr_reader :namespace_dirs
|
79
86
|
internal :namespace_dirs
|
80
87
|
|
81
88
|
# A shadowed file is a file managed by this loader that is ignored when
|
82
89
|
# setting autoloads because its matching constant is already taken.
|
83
90
|
#
|
84
|
-
# This private set is populated as we descend. For example, if the
|
85
|
-
# has only scanned the top-level, `shadowed_files` does not have
|
86
|
-
# files that may exist deep in the project tree
|
91
|
+
# This private set is populated lazily, as we descend. For example, if the
|
92
|
+
# loader has only scanned the top-level, `shadowed_files` does not have the
|
93
|
+
# shadowed files that may exist deep in the project tree.
|
87
94
|
#
|
88
95
|
# @sig Set[String]
|
89
96
|
attr_reader :shadowed_files
|
@@ -101,9 +108,10 @@ module Zeitwerk
|
|
101
108
|
super
|
102
109
|
|
103
110
|
@autoloads = {}
|
111
|
+
@inceptions = Zeitwerk::Cref::Map.new
|
104
112
|
@autoloaded_dirs = []
|
105
113
|
@to_unload = {}
|
106
|
-
@namespace_dirs =
|
114
|
+
@namespace_dirs = Zeitwerk::Cref::Map.new
|
107
115
|
@shadowed_files = Set.new
|
108
116
|
@setup = false
|
109
117
|
@eager_loaded = false
|
@@ -167,7 +175,7 @@ module Zeitwerk
|
|
167
175
|
end
|
168
176
|
end
|
169
177
|
|
170
|
-
to_unload.each do |
|
178
|
+
to_unload.each do |abspath, cref|
|
171
179
|
unless on_unload_callbacks.empty?
|
172
180
|
begin
|
173
181
|
value = cref.get
|
@@ -176,7 +184,7 @@ module Zeitwerk
|
|
176
184
|
# autoload failed to define the expected constant but the user
|
177
185
|
# rescued the exception.
|
178
186
|
else
|
179
|
-
run_on_unload_callbacks(
|
187
|
+
run_on_unload_callbacks(cref, value, abspath)
|
180
188
|
end
|
181
189
|
end
|
182
190
|
|
@@ -205,8 +213,10 @@ module Zeitwerk
|
|
205
213
|
namespace_dirs.clear
|
206
214
|
shadowed_files.clear
|
207
215
|
|
216
|
+
unregister_inceptions
|
217
|
+
unregister_explicit_namespaces
|
218
|
+
|
208
219
|
Registry.on_unload(self)
|
209
|
-
ExplicitNamespace.__unregister_loader(self)
|
210
220
|
|
211
221
|
@setup = false
|
212
222
|
@eager_loaded = false
|
@@ -315,17 +325,23 @@ module Zeitwerk
|
|
315
325
|
# Says if the given constant path would be unloaded on reload. This
|
316
326
|
# predicate returns `false` if reloading is disabled.
|
317
327
|
#
|
328
|
+
# This is an undocumented method that I wrote to help transition from the
|
329
|
+
# classic autoloader in Rails. Its usage was removed from Rails in 7.0.
|
330
|
+
#
|
318
331
|
# @sig (String) -> bool
|
319
332
|
def unloadable_cpath?(cpath)
|
320
|
-
|
333
|
+
unloadable_cpaths.include?(cpath)
|
321
334
|
end
|
322
335
|
|
323
336
|
# Returns an array with the constant paths that would be unloaded on reload.
|
324
337
|
# This predicate returns an empty array if reloading is disabled.
|
325
338
|
#
|
339
|
+
# This is an undocumented method that I wrote to help transition from the
|
340
|
+
# classic autoloader in Rails. Its usage was removed from Rails in 7.0.
|
341
|
+
#
|
326
342
|
# @sig () -> Array[String]
|
327
343
|
def unloadable_cpaths
|
328
|
-
to_unload.
|
344
|
+
to_unload.values.map(&:path)
|
329
345
|
end
|
330
346
|
|
331
347
|
# This is a dangerous method.
|
@@ -333,8 +349,9 @@ module Zeitwerk
|
|
333
349
|
# @experimental
|
334
350
|
# @sig () -> void
|
335
351
|
def unregister
|
352
|
+
unregister_inceptions
|
353
|
+
unregister_explicit_namespaces
|
336
354
|
Registry.unregister_loader(self)
|
337
|
-
ExplicitNamespace.__unregister_loader(self)
|
338
355
|
end
|
339
356
|
|
340
357
|
# The return value of this predicate is only meaningful if the loader has
|
@@ -474,22 +491,22 @@ module Zeitwerk
|
|
474
491
|
# If the existing autoload points to a file, it has to be preserved, if
|
475
492
|
# not, it is fine as it is. In either case, we do not need to override.
|
476
493
|
# Just remember the subdirectory conforms this namespace.
|
477
|
-
namespace_dirs[
|
494
|
+
namespace_dirs.get_or_set(cref) { [] } << subdir
|
478
495
|
elsif !cref.defined?
|
479
496
|
# First time we find this namespace, set an autoload for it.
|
480
|
-
namespace_dirs[
|
497
|
+
namespace_dirs.get_or_set(cref) { [] } << subdir
|
481
498
|
define_autoload(cref, subdir)
|
482
499
|
else
|
483
500
|
# For whatever reason the constant that corresponds to this namespace has
|
484
501
|
# already been defined, we have to recurse.
|
485
|
-
log("the namespace #{cref
|
502
|
+
log("the namespace #{cref} already exists, descending into #{subdir}") if logger
|
486
503
|
define_autoloads_for_dir(subdir, cref.get)
|
487
504
|
end
|
488
505
|
end
|
489
506
|
|
490
507
|
# @sig (Module, Symbol, String) -> void
|
491
508
|
private def autoload_file(cref, file)
|
492
|
-
if autoload_path = cref.autoload? || Registry.
|
509
|
+
if autoload_path = cref.autoload? || Registry::Inceptions.registered?(cref)
|
493
510
|
# First autoload for a Ruby file wins, just ignore subsequent ones.
|
494
511
|
if ruby?(autoload_path)
|
495
512
|
shadowed_files << file
|
@@ -499,7 +516,7 @@ module Zeitwerk
|
|
499
516
|
end
|
500
517
|
elsif cref.defined?
|
501
518
|
shadowed_files << file
|
502
|
-
log("file #{file} is ignored because #{cref
|
519
|
+
log("file #{file} is ignored because #{cref} is already defined") if logger
|
503
520
|
else
|
504
521
|
define_autoload(cref, file)
|
505
522
|
end
|
@@ -513,7 +530,7 @@ module Zeitwerk
|
|
513
530
|
autoloads.delete(dir)
|
514
531
|
Registry.unregister_autoload(dir)
|
515
532
|
|
516
|
-
log("earlier autoload for #{cref
|
533
|
+
log("earlier autoload for #{cref} discarded, it is actually an explicit namespace defined in #{file}") if logger
|
517
534
|
|
518
535
|
# Order matters: When Module#const_added is triggered by the autoload, we
|
519
536
|
# don't want the namespace to be registered yet.
|
@@ -527,19 +544,16 @@ module Zeitwerk
|
|
527
544
|
|
528
545
|
if logger
|
529
546
|
if ruby?(abspath)
|
530
|
-
log("autoload set for #{cref
|
547
|
+
log("autoload set for #{cref}, to be loaded from #{abspath}")
|
531
548
|
else
|
532
|
-
log("autoload set for #{cref
|
549
|
+
log("autoload set for #{cref}, to be autovivified from #{abspath}")
|
533
550
|
end
|
534
551
|
end
|
535
552
|
|
536
553
|
autoloads[abspath] = cref
|
537
554
|
Registry.register_autoload(self, abspath)
|
538
555
|
|
539
|
-
|
540
|
-
unless cref.autoload?
|
541
|
-
Registry.register_inception(cref.path, abspath, self)
|
542
|
-
end
|
556
|
+
register_inception(cref, abspath) unless cref.autoload?
|
543
557
|
end
|
544
558
|
|
545
559
|
# @sig (Module, Symbol) -> String?
|
@@ -547,13 +561,32 @@ module Zeitwerk
|
|
547
561
|
if autoload_path = cref.autoload?
|
548
562
|
autoload_path if autoloads.key?(autoload_path)
|
549
563
|
else
|
550
|
-
|
564
|
+
inceptions[cref]
|
551
565
|
end
|
552
566
|
end
|
553
567
|
|
554
568
|
# @sig (Zeitwerk::Cref) -> void
|
555
569
|
private def register_explicit_namespace(cref)
|
556
|
-
|
570
|
+
Registry::ExplicitNamespaces.__register(cref, self)
|
571
|
+
end
|
572
|
+
|
573
|
+
# @sig () -> void
|
574
|
+
private def unregister_explicit_namespaces
|
575
|
+
Registry::ExplicitNamespaces.__unregister_loader(self)
|
576
|
+
end
|
577
|
+
|
578
|
+
# @sig (Zeitwerk::Cref, String) -> void
|
579
|
+
private def register_inception(cref, abspath)
|
580
|
+
inceptions[cref] = abspath
|
581
|
+
Registry::Inceptions.register(cref, abspath)
|
582
|
+
end
|
583
|
+
|
584
|
+
# @sig () -> void
|
585
|
+
private def unregister_inceptions
|
586
|
+
inceptions.each_key do |cref|
|
587
|
+
Registry::Inceptions.unregister(cref)
|
588
|
+
end
|
589
|
+
inceptions.clear
|
557
590
|
end
|
558
591
|
|
559
592
|
# @sig (String) -> void
|
@@ -580,17 +613,17 @@ module Zeitwerk
|
|
580
613
|
end
|
581
614
|
end
|
582
615
|
|
583
|
-
# @sig (String,
|
584
|
-
private def run_on_unload_callbacks(
|
616
|
+
# @sig (String, top, String) -> void
|
617
|
+
private def run_on_unload_callbacks(cref, value, abspath)
|
585
618
|
# Order matters. If present, run the most specific one.
|
586
|
-
on_unload_callbacks[
|
587
|
-
on_unload_callbacks[:ANY]&.each { |c| c.call(
|
619
|
+
on_unload_callbacks[cref.path]&.each { |c| c.call(value, abspath) }
|
620
|
+
on_unload_callbacks[:ANY]&.each { |c| c.call(cref.path, value, abspath) }
|
588
621
|
end
|
589
622
|
|
590
623
|
# @sig (Module, Symbol) -> void
|
591
624
|
private def unload_autoload(cref)
|
592
625
|
cref.remove
|
593
|
-
log("autoload for #{cref
|
626
|
+
log("autoload for #{cref} removed") if logger
|
594
627
|
end
|
595
628
|
|
596
629
|
# @sig (Module, Symbol) -> void
|
@@ -602,7 +635,7 @@ module Zeitwerk
|
|
602
635
|
# There are a few edge scenarios in which this may happen. If the constant
|
603
636
|
# is gone, that is OK, anyway.
|
604
637
|
else
|
605
|
-
log("#{cref
|
638
|
+
log("#{cref} unloaded") if logger
|
606
639
|
end
|
607
640
|
end
|
608
641
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zeitwerk::Registry
|
4
|
+
# This module is a registry for explicit namespaces.
|
5
|
+
#
|
6
|
+
# When a loader determines that a certain file should define an explicit
|
7
|
+
# namespace, it registers it here, associating its cref with itself.
|
8
|
+
#
|
9
|
+
# If the namespace is autoloaded, our const_added callback retrieves its
|
10
|
+
# loader by calling loader_for. That way, the loader is able to scan the
|
11
|
+
# subdirectories that conform the namespace and set autoloads for their
|
12
|
+
# expected constants just in time.
|
13
|
+
#
|
14
|
+
# Once autoloaded, the namespace is unregistered.
|
15
|
+
#
|
16
|
+
# The implementation assumes an explicit namespace is managed by one loader.
|
17
|
+
# Loaders that reopen namespaces owned by other projects are responsible for
|
18
|
+
# loading their constant before setup. This is documented.
|
19
|
+
module ExplicitNamespaces # :nodoc: all
|
20
|
+
# Maps crefs of explicit namespaces with their corresponding loader.
|
21
|
+
#
|
22
|
+
# Entries are added as the namespaces are found, and removed as they are
|
23
|
+
# autoloaded.
|
24
|
+
#
|
25
|
+
# @sig Zeitwerk::Cref::Map[Zeitwerk::Loader]
|
26
|
+
@loaders = Zeitwerk::Cref::Map.new
|
27
|
+
|
28
|
+
class << self
|
29
|
+
extend Zeitwerk::Internal
|
30
|
+
|
31
|
+
# Registers `cref` as being the constant path of an explicit namespace
|
32
|
+
# managed by `loader`.
|
33
|
+
#
|
34
|
+
# @sig (Zeitwerk::Cref, Zeitwerk::Loader) -> void
|
35
|
+
internal def register(cref, loader)
|
36
|
+
@loaders[cref] = loader
|
37
|
+
end
|
38
|
+
|
39
|
+
# @sig (Module, Symbol) -> Zeitwerk::Loader?
|
40
|
+
internal def loader_for(mod, cname)
|
41
|
+
@loaders.delete_mod_cname(mod, cname)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @sig (Zeitwerk::Loader) -> void
|
45
|
+
internal def unregister_loader(loader)
|
46
|
+
@loaders.delete_by_value(loader)
|
47
|
+
end
|
48
|
+
|
49
|
+
# This is an internal method only used by the test suite.
|
50
|
+
#
|
51
|
+
# @sig (Symbol | String) -> Zeitwerk::Loader?
|
52
|
+
internal def registered?(cref)
|
53
|
+
@loaders[cref]
|
54
|
+
end
|
55
|
+
|
56
|
+
# This is an internal method only used by the test suite.
|
57
|
+
#
|
58
|
+
# @sig () -> void
|
59
|
+
internal def clear
|
60
|
+
@loaders.clear
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Zeitwerk::Registry
|
2
|
+
# Loaders know their own inceptions, but there is a use case in which we need
|
3
|
+
# to know if a given cpath is an inception globally. This is what this
|
4
|
+
# registry is for.
|
5
|
+
module Inceptions # :nodoc: all
|
6
|
+
# @sig Zeitwerk::Cref::Map[String]
|
7
|
+
@inceptions = Zeitwerk::Cref::Map.new
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# @sig (Zeitwerk::Cref, String) -> void
|
11
|
+
def register(cref, autoload_path)
|
12
|
+
@inceptions[cref] = autoload_path
|
13
|
+
end
|
14
|
+
|
15
|
+
# @sig (String) -> String?
|
16
|
+
def registered?(cref)
|
17
|
+
@inceptions[cref]
|
18
|
+
end
|
19
|
+
|
20
|
+
# @sig (String) -> void
|
21
|
+
def unregister(cref)
|
22
|
+
@inceptions.delete(cref)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @sig () -> void
|
26
|
+
def clear # for tests
|
27
|
+
@inceptions.clear
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/zeitwerk/registry.rb
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
module Zeitwerk
|
4
4
|
module Registry # :nodoc: all
|
5
|
+
require_relative "registry/explicit_namespaces"
|
6
|
+
require_relative "registry/inceptions"
|
7
|
+
|
5
8
|
class << self
|
6
9
|
# Keeps track of all loaders. Useful to broadcast messages and to prevent
|
7
10
|
# them from being garbage collected.
|
@@ -25,45 +28,6 @@ module Zeitwerk
|
|
25
28
|
# @sig Hash[String, Zeitwerk::Loader]
|
26
29
|
attr_reader :autoloads
|
27
30
|
|
28
|
-
# This hash table addresses an edge case in which an autoload is ignored.
|
29
|
-
#
|
30
|
-
# For example, let's suppose we want to autoload in a gem like this:
|
31
|
-
#
|
32
|
-
# # lib/my_gem.rb
|
33
|
-
# loader = Zeitwerk::Loader.new
|
34
|
-
# loader.push_dir(__dir__)
|
35
|
-
# loader.setup
|
36
|
-
#
|
37
|
-
# module MyGem
|
38
|
-
# end
|
39
|
-
#
|
40
|
-
# if you require "my_gem", as Bundler would do, this happens while setting
|
41
|
-
# up autoloads:
|
42
|
-
#
|
43
|
-
# 1. Object.autoload?(:MyGem) returns `nil` because the autoload for
|
44
|
-
# the constant is issued by Zeitwerk while the same file is being
|
45
|
-
# required.
|
46
|
-
# 2. The constant `MyGem` is undefined while setup runs.
|
47
|
-
#
|
48
|
-
# Therefore, a directory `lib/my_gem` would autovivify a module according to
|
49
|
-
# the existing information. But that would be wrong.
|
50
|
-
#
|
51
|
-
# To overcome this fundamental limitation, we keep track of the constant
|
52
|
-
# paths that are in this situation ---in the example above, "MyGem"--- and
|
53
|
-
# take this collection into account for the autovivification logic.
|
54
|
-
#
|
55
|
-
# Note that you cannot generally address this by moving the setup code
|
56
|
-
# below the constant definition, because we want libraries to be able to
|
57
|
-
# use managed constants in the module body:
|
58
|
-
#
|
59
|
-
# module MyGem
|
60
|
-
# include MyConcern
|
61
|
-
# end
|
62
|
-
#
|
63
|
-
# @private
|
64
|
-
# @sig Hash[String, [String, Zeitwerk::Loader]]
|
65
|
-
attr_reader :inceptions
|
66
|
-
|
67
31
|
# Registers a loader.
|
68
32
|
#
|
69
33
|
# @private
|
@@ -78,7 +42,6 @@ module Zeitwerk
|
|
78
42
|
loaders.delete(loader)
|
79
43
|
gem_loaders_by_root_file.delete_if { |_, l| l == loader }
|
80
44
|
autoloads.delete_if { |_, l| l == loader }
|
81
|
-
inceptions.delete_if { |_, (_, l)| l == loader }
|
82
45
|
end
|
83
46
|
|
84
47
|
# This method returns always a loader, the same instance for the same root
|
@@ -102,23 +65,6 @@ module Zeitwerk
|
|
102
65
|
autoloads.delete(abspath)
|
103
66
|
end
|
104
67
|
|
105
|
-
# @private
|
106
|
-
# @sig (String, String, Zeitwerk::Loader) -> void
|
107
|
-
def register_inception(cpath, abspath, loader)
|
108
|
-
inceptions[cpath] = [abspath, loader]
|
109
|
-
end
|
110
|
-
|
111
|
-
# @private
|
112
|
-
# @sig (String) -> String?
|
113
|
-
def inception?(cpath, registered_by_loader=nil)
|
114
|
-
if pair = inceptions[cpath]
|
115
|
-
abspath, loader = pair
|
116
|
-
if registered_by_loader.nil? || registered_by_loader.equal?(loader)
|
117
|
-
abspath
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
68
|
# @private
|
123
69
|
# @sig (String) -> Zeitwerk::Loader?
|
124
70
|
def loader_for(path)
|
@@ -129,13 +75,11 @@ module Zeitwerk
|
|
129
75
|
# @sig (Zeitwerk::Loader) -> void
|
130
76
|
def on_unload(loader)
|
131
77
|
autoloads.delete_if { |_path, object| object == loader }
|
132
|
-
inceptions.delete_if { |_cpath, (_path, object)| object == loader }
|
133
78
|
end
|
134
79
|
end
|
135
80
|
|
136
81
|
@loaders = []
|
137
82
|
@gem_loaders_by_root_file = {}
|
138
83
|
@autoloads = {}
|
139
|
-
@inceptions = {}
|
140
84
|
end
|
141
85
|
end
|
data/lib/zeitwerk/version.rb
CHANGED
data/lib/zeitwerk.rb
CHANGED
@@ -7,7 +7,6 @@ module Zeitwerk
|
|
7
7
|
require_relative "zeitwerk/loader"
|
8
8
|
require_relative "zeitwerk/gem_loader"
|
9
9
|
require_relative "zeitwerk/registry"
|
10
|
-
require_relative "zeitwerk/explicit_namespace"
|
11
10
|
require_relative "zeitwerk/inflector"
|
12
11
|
require_relative "zeitwerk/gem_inflector"
|
13
12
|
require_relative "zeitwerk/null_inflector"
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zeitwerk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.7.
|
4
|
+
version: 2.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Xavier Noria
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-02-18 00:00:00.000000000 Z
|
12
11
|
dependencies: []
|
13
12
|
description: |2
|
14
13
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|
@@ -26,8 +25,8 @@ files:
|
|
26
25
|
- lib/zeitwerk/core_ext/kernel.rb
|
27
26
|
- lib/zeitwerk/core_ext/module.rb
|
28
27
|
- lib/zeitwerk/cref.rb
|
28
|
+
- lib/zeitwerk/cref/map.rb
|
29
29
|
- lib/zeitwerk/error.rb
|
30
|
-
- lib/zeitwerk/explicit_namespace.rb
|
31
30
|
- lib/zeitwerk/gem_inflector.rb
|
32
31
|
- lib/zeitwerk/gem_loader.rb
|
33
32
|
- lib/zeitwerk/inflector.rb
|
@@ -40,6 +39,8 @@ files:
|
|
40
39
|
- lib/zeitwerk/null_inflector.rb
|
41
40
|
- lib/zeitwerk/real_mod_name.rb
|
42
41
|
- lib/zeitwerk/registry.rb
|
42
|
+
- lib/zeitwerk/registry/explicit_namespaces.rb
|
43
|
+
- lib/zeitwerk/registry/inceptions.rb
|
43
44
|
- lib/zeitwerk/version.rb
|
44
45
|
homepage: https://github.com/fxn/zeitwerk
|
45
46
|
licenses:
|
@@ -49,7 +50,6 @@ metadata:
|
|
49
50
|
changelog_uri: https://github.com/fxn/zeitwerk/blob/master/CHANGELOG.md
|
50
51
|
source_code_uri: https://github.com/fxn/zeitwerk
|
51
52
|
bug_tracker_uri: https://github.com/fxn/zeitwerk/issues
|
52
|
-
post_install_message:
|
53
53
|
rdoc_options: []
|
54
54
|
require_paths:
|
55
55
|
- lib
|
@@ -64,8 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
64
|
- !ruby/object:Gem::Version
|
65
65
|
version: '0'
|
66
66
|
requirements: []
|
67
|
-
rubygems_version: 3.
|
68
|
-
signing_key:
|
67
|
+
rubygems_version: 3.6.4
|
69
68
|
specification_version: 4
|
70
69
|
summary: Efficient and thread-safe constant autoloader
|
71
70
|
test_files: []
|
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Zeitwerk
|
4
|
-
# This module is essentially a registry for explicit namespaces.
|
5
|
-
#
|
6
|
-
# When a loader determines that a certain file should define an explicit
|
7
|
-
# namespace, it registers it here, associating its cref with itself.
|
8
|
-
#
|
9
|
-
# If the namespace is autoloaded, our const_added callback retrieves its
|
10
|
-
# loader by calling loader_for. That way, the loader is able to scan the
|
11
|
-
# subdirectories that conform the namespace and set autoloads for their
|
12
|
-
# expected constants just in time.
|
13
|
-
#
|
14
|
-
# Once autoloaded, the namespace is unregistered.
|
15
|
-
#
|
16
|
-
# The implementation assumes an explicit namespace is managed by one loader.
|
17
|
-
# Loaders that reopen namespaces owned by other projects are responsible for
|
18
|
-
# loading their constant before setup. This is documented.
|
19
|
-
module ExplicitNamespace # :nodoc: all
|
20
|
-
# Maps cnames or cpaths of explicit namespaces with their corresponding
|
21
|
-
# loader. They are symbols for top-level ones, and strings for nested ones:
|
22
|
-
#
|
23
|
-
# {
|
24
|
-
# :Admin => #<Zeitwerk::Loader:...>,
|
25
|
-
# "Hotel::Pricing" => #<Zeitwerk::Loader:...>
|
26
|
-
# }
|
27
|
-
#
|
28
|
-
# There are two types of keys to make loader_for as fast as possible, since
|
29
|
-
# it is invoked by our const_added for all autoloads and constant actually
|
30
|
-
# added. Globally. With this trick, for top-level constants we do not need
|
31
|
-
# to call Symbol#name and perform a string lookup. Instead, we can directly
|
32
|
-
# perform a fast symbol lookup.
|
33
|
-
#
|
34
|
-
# Entries are added as the namespaces are found, and removed as they are
|
35
|
-
# autoloaded.
|
36
|
-
#
|
37
|
-
# @sig Hash[(Symbol | String) => Zeitwerk::Loader]
|
38
|
-
@loaders = {}
|
39
|
-
|
40
|
-
class << self
|
41
|
-
include RealModName
|
42
|
-
extend Internal
|
43
|
-
|
44
|
-
# Registers `cref` as being the constant path of an explicit namespace
|
45
|
-
# managed by `loader`.
|
46
|
-
#
|
47
|
-
# @sig (String, Zeitwerk::Loader) -> void
|
48
|
-
internal def register(cref, loader)
|
49
|
-
if Object.equal?(cref.mod)
|
50
|
-
@loaders[cref.cname] = loader
|
51
|
-
else
|
52
|
-
@loaders[cref.path] = loader
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# @sig (Module, Symbol) -> Zeitwerk::Loader?
|
57
|
-
internal def loader_for(mod, cname)
|
58
|
-
if Object.equal?(mod)
|
59
|
-
@loaders.delete(cname)
|
60
|
-
else
|
61
|
-
@loaders.delete("#{real_mod_name(mod)}::#{cname}")
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# @sig (Zeitwerk::Loader) -> void
|
66
|
-
internal def unregister_loader(loader)
|
67
|
-
@loaders.delete_if { _2.equal?(loader) }
|
68
|
-
end
|
69
|
-
|
70
|
-
# This is an internal method only used by the test suite.
|
71
|
-
#
|
72
|
-
# @sig (String) -> Zeitwerk::Loader?
|
73
|
-
internal def registered?(cname_or_cpath)
|
74
|
-
@loaders[cname_or_cpath]
|
75
|
-
end
|
76
|
-
|
77
|
-
# This is an internal method only used by the test suite.
|
78
|
-
#
|
79
|
-
# @sig () -> void
|
80
|
-
internal def clear
|
81
|
-
@loaders.clear
|
82
|
-
end
|
83
|
-
|
84
|
-
module Synchronized
|
85
|
-
extend Internal
|
86
|
-
|
87
|
-
MUTEX = Mutex.new
|
88
|
-
|
89
|
-
internal def register(...)
|
90
|
-
MUTEX.synchronize { super }
|
91
|
-
end
|
92
|
-
|
93
|
-
internal def loader_for(...)
|
94
|
-
MUTEX.synchronize { super }
|
95
|
-
end
|
96
|
-
|
97
|
-
internal def unregister_loader(...)
|
98
|
-
MUTEX.synchronize { super }
|
99
|
-
end
|
100
|
-
|
101
|
-
internal def registered?(...)
|
102
|
-
MUTEX.synchronize { super }
|
103
|
-
end
|
104
|
-
|
105
|
-
internal def clear
|
106
|
-
MUTEX.synchronize { super }
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
prepend Synchronized unless RUBY_ENGINE == "ruby"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|