zeitwerk 2.6.0 → 2.6.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +351 -47
- data/lib/zeitwerk/error.rb +6 -0
- data/lib/zeitwerk/explicit_namespace.rb +14 -10
- data/lib/zeitwerk/gem_inflector.rb +2 -2
- data/lib/zeitwerk/gem_loader.rb +12 -8
- data/lib/zeitwerk/internal.rb +12 -0
- data/lib/zeitwerk/kernel.rb +6 -3
- data/lib/zeitwerk/loader/callbacks.rb +25 -17
- data/lib/zeitwerk/loader/config.rb +95 -51
- data/lib/zeitwerk/loader/eager_load.rb +234 -0
- data/lib/zeitwerk/loader/helpers.rb +74 -16
- data/lib/zeitwerk/loader.rb +196 -135
- data/lib/zeitwerk/registry.rb +2 -2
- data/lib/zeitwerk/version.rb +1 -1
- data/lib/zeitwerk.rb +1 -0
- metadata +5 -3
data/lib/zeitwerk/loader.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "monitor"
|
3
4
|
require "set"
|
4
5
|
|
5
6
|
module Zeitwerk
|
@@ -7,11 +8,15 @@ module Zeitwerk
|
|
7
8
|
require_relative "loader/helpers"
|
8
9
|
require_relative "loader/callbacks"
|
9
10
|
require_relative "loader/config"
|
11
|
+
require_relative "loader/eager_load"
|
12
|
+
|
13
|
+
extend Internal
|
10
14
|
|
11
15
|
include RealModName
|
12
16
|
include Callbacks
|
13
17
|
include Helpers
|
14
18
|
include Config
|
19
|
+
include EagerLoad
|
15
20
|
|
16
21
|
MUTEX = Mutex.new
|
17
22
|
private_constant :MUTEX
|
@@ -24,9 +29,9 @@ module Zeitwerk
|
|
24
29
|
# "/Users/fxn/blog/app/models/hotel/pricing.rb" => [Hotel, :Pricing]
|
25
30
|
# ...
|
26
31
|
#
|
27
|
-
# @private
|
28
32
|
# @sig Hash[String, [Module, Symbol]]
|
29
33
|
attr_reader :autoloads
|
34
|
+
internal :autoloads
|
30
35
|
|
31
36
|
# We keep track of autoloaded directories to remove them from the registry
|
32
37
|
# at the end of eager loading.
|
@@ -34,9 +39,9 @@ module Zeitwerk
|
|
34
39
|
# Files are removed as they are autoloaded, but directories need to wait due
|
35
40
|
# to concurrency (see why in Zeitwerk::Loader::Callbacks#on_dir_autoloaded).
|
36
41
|
#
|
37
|
-
# @private
|
38
42
|
# @sig Array[String]
|
39
43
|
attr_reader :autoloaded_dirs
|
44
|
+
internal :autoloaded_dirs
|
40
45
|
|
41
46
|
# Stores metadata needed for unloading. Its entries look like this:
|
42
47
|
#
|
@@ -50,11 +55,11 @@ module Zeitwerk
|
|
50
55
|
# If reloading is enabled, this hash is filled as constants are autoloaded
|
51
56
|
# or eager loaded. Otherwise, the collection remains empty.
|
52
57
|
#
|
53
|
-
# @private
|
54
58
|
# @sig Hash[String, [String, [Module, Symbol]]]
|
55
59
|
attr_reader :to_unload
|
60
|
+
internal :to_unload
|
56
61
|
|
57
|
-
# Maps constant paths
|
62
|
+
# Maps namespace constant paths to their respective directories.
|
58
63
|
#
|
59
64
|
# For example, given this mapping:
|
60
65
|
#
|
@@ -64,21 +69,32 @@ module Zeitwerk
|
|
64
69
|
# ...
|
65
70
|
# ]
|
66
71
|
#
|
67
|
-
# when `Admin` gets defined we know that it plays the role of a namespace
|
68
|
-
# that its children are spread over those directories. We'll visit them
|
69
|
-
# up the corresponding autoloads.
|
72
|
+
# when `Admin` gets defined we know that it plays the role of a namespace
|
73
|
+
# and that its children are spread over those directories. We'll visit them
|
74
|
+
# to set up the corresponding autoloads.
|
70
75
|
#
|
71
|
-
# @private
|
72
76
|
# @sig Hash[String, Array[String]]
|
73
|
-
attr_reader :
|
77
|
+
attr_reader :namespace_dirs
|
78
|
+
internal :namespace_dirs
|
79
|
+
|
80
|
+
# A shadowed file is a file managed by this loader that is ignored when
|
81
|
+
# setting autoloads because its matching constant is already taken.
|
82
|
+
#
|
83
|
+
# This private set is populated as we descend. For example, if the loader
|
84
|
+
# has only scanned the top-level, `shadowed_files` does not have shadowed
|
85
|
+
# files that may exist deep in the project tree yet.
|
86
|
+
#
|
87
|
+
# @sig Set[String]
|
88
|
+
attr_reader :shadowed_files
|
89
|
+
internal :shadowed_files
|
74
90
|
|
75
|
-
# @private
|
76
91
|
# @sig Mutex
|
77
92
|
attr_reader :mutex
|
93
|
+
private :mutex
|
78
94
|
|
79
|
-
# @
|
80
|
-
|
81
|
-
|
95
|
+
# @sig Monitor
|
96
|
+
attr_reader :dirs_autoload_monitor
|
97
|
+
private :dirs_autoload_monitor
|
82
98
|
|
83
99
|
def initialize
|
84
100
|
super
|
@@ -86,24 +102,26 @@ module Zeitwerk
|
|
86
102
|
@autoloads = {}
|
87
103
|
@autoloaded_dirs = []
|
88
104
|
@to_unload = {}
|
89
|
-
@
|
90
|
-
@
|
91
|
-
@mutex2 = Mutex.new
|
105
|
+
@namespace_dirs = Hash.new { |h, cpath| h[cpath] = [] }
|
106
|
+
@shadowed_files = Set.new
|
92
107
|
@setup = false
|
93
108
|
@eager_loaded = false
|
94
109
|
|
110
|
+
@mutex = Mutex.new
|
111
|
+
@dirs_autoload_monitor = Monitor.new
|
112
|
+
|
95
113
|
Registry.register_loader(self)
|
96
114
|
end
|
97
115
|
|
98
|
-
# Sets autoloads in the root
|
116
|
+
# Sets autoloads in the root namespaces.
|
99
117
|
#
|
100
118
|
# @sig () -> void
|
101
119
|
def setup
|
102
120
|
mutex.synchronize do
|
103
121
|
break if @setup
|
104
122
|
|
105
|
-
|
106
|
-
|
123
|
+
actual_roots.each do |root_dir, root_namespace|
|
124
|
+
define_autoloads_for_dir(root_dir, root_namespace)
|
107
125
|
end
|
108
126
|
|
109
127
|
on_setup_callbacks.each(&:call)
|
@@ -120,12 +138,14 @@ module Zeitwerk
|
|
120
138
|
# unload them.
|
121
139
|
#
|
122
140
|
# This method is public but undocumented. Main interface is `reload`, which
|
123
|
-
# means `unload` + `setup`. This one is
|
141
|
+
# means `unload` + `setup`. This one is available to be used together with
|
124
142
|
# `unregister`, which is undocumented too.
|
125
143
|
#
|
126
144
|
# @sig () -> void
|
127
145
|
def unload
|
128
146
|
mutex.synchronize do
|
147
|
+
raise SetupRequired unless @setup
|
148
|
+
|
129
149
|
# We are going to keep track of the files that were required by our
|
130
150
|
# autoloads to later remove them from $LOADED_FEATURES, thus making them
|
131
151
|
# loadable by Kernel#require again.
|
@@ -181,10 +201,11 @@ module Zeitwerk
|
|
181
201
|
autoloads.clear
|
182
202
|
autoloaded_dirs.clear
|
183
203
|
to_unload.clear
|
184
|
-
|
204
|
+
namespace_dirs.clear
|
205
|
+
shadowed_files.clear
|
185
206
|
|
186
207
|
Registry.on_unload(self)
|
187
|
-
ExplicitNamespace.
|
208
|
+
ExplicitNamespace.__unregister_loader(self)
|
188
209
|
|
189
210
|
@setup = false
|
190
211
|
@eager_loaded = false
|
@@ -201,6 +222,7 @@ module Zeitwerk
|
|
201
222
|
# @sig () -> void
|
202
223
|
def reload
|
203
224
|
raise ReloadingDisabledError unless reloading_enabled?
|
225
|
+
raise SetupRequired unless @setup
|
204
226
|
|
205
227
|
unload
|
206
228
|
recompute_ignored_paths
|
@@ -208,57 +230,53 @@ module Zeitwerk
|
|
208
230
|
setup
|
209
231
|
end
|
210
232
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
# directories with `do_not_eager_load`, and that can be overridden passing
|
215
|
-
# `force: true`.
|
216
|
-
#
|
217
|
-
# @sig (true | false) -> void
|
218
|
-
def eager_load(force: false)
|
219
|
-
mutex.synchronize do
|
220
|
-
break if @eager_loaded
|
233
|
+
# @sig (String | Pathname) -> String?
|
234
|
+
def cpath_expected_at(path)
|
235
|
+
abspath = File.expand_path(path)
|
221
236
|
|
222
|
-
|
237
|
+
raise Zeitwerk::Error.new("#{abspath} does not exist") unless File.exist?(abspath)
|
223
238
|
|
224
|
-
|
239
|
+
return unless dir?(abspath) || ruby?(abspath)
|
240
|
+
return if ignored_path?(abspath)
|
225
241
|
|
226
|
-
|
227
|
-
actual_root_dirs.each do |root_dir, namespace|
|
228
|
-
queue << [namespace, root_dir] unless honour_exclusions && excluded_from_eager_load?(root_dir)
|
229
|
-
end
|
242
|
+
paths = []
|
230
243
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
ls(dir) do |basename, abspath|
|
235
|
-
next if honour_exclusions && excluded_from_eager_load?(abspath)
|
236
|
-
|
237
|
-
if ruby?(abspath)
|
238
|
-
if cref = autoloads[abspath]
|
239
|
-
cget(*cref)
|
240
|
-
end
|
241
|
-
elsif !root_dirs.key?(abspath)
|
242
|
-
if collapse?(abspath)
|
243
|
-
queue << [namespace, abspath]
|
244
|
-
else
|
245
|
-
cname = inflector.camelize(basename, abspath)
|
246
|
-
queue << [cget(namespace, cname), abspath]
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
244
|
+
if ruby?(abspath)
|
245
|
+
basename = File.basename(abspath, ".rb")
|
246
|
+
return if hidden?(basename)
|
251
247
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
248
|
+
paths << [basename, abspath]
|
249
|
+
walk_up_from = File.dirname(abspath)
|
250
|
+
else
|
251
|
+
walk_up_from = abspath
|
252
|
+
end
|
253
|
+
|
254
|
+
root_namespace = nil
|
256
255
|
|
257
|
-
|
256
|
+
walk_up(walk_up_from) do |dir|
|
257
|
+
break if root_namespace = roots[dir]
|
258
|
+
return if ignored_path?(dir)
|
258
259
|
|
259
|
-
|
260
|
+
basename = File.basename(dir)
|
261
|
+
return if hidden?(basename)
|
262
|
+
|
263
|
+
paths << [basename, abspath] unless collapse?(dir)
|
264
|
+
end
|
265
|
+
|
266
|
+
return unless root_namespace
|
267
|
+
|
268
|
+
if paths.empty?
|
269
|
+
real_mod_name(root_namespace)
|
270
|
+
else
|
271
|
+
cnames = paths.reverse_each.map { |b, a| cname_for(b, a) }
|
272
|
+
|
273
|
+
if root_namespace == Object
|
274
|
+
cnames.join("::")
|
275
|
+
else
|
276
|
+
"#{real_mod_name(root_namespace)}::#{cnames.join("::")}"
|
260
277
|
end
|
261
278
|
end
|
279
|
+
end
|
262
280
|
|
263
281
|
# Says if the given constant path would be unloaded on reload. This
|
264
282
|
# predicate returns `false` if reloading is disabled.
|
@@ -282,18 +300,29 @@ module Zeitwerk
|
|
282
300
|
# @sig () -> void
|
283
301
|
def unregister
|
284
302
|
Registry.unregister_loader(self)
|
285
|
-
ExplicitNamespace.
|
303
|
+
ExplicitNamespace.__unregister_loader(self)
|
304
|
+
end
|
305
|
+
|
306
|
+
# The return value of this predicate is only meaningful if the loader has
|
307
|
+
# scanned the file. This is the case in the spots where we use it.
|
308
|
+
#
|
309
|
+
# @sig (String) -> Boolean
|
310
|
+
internal def shadowed_file?(file)
|
311
|
+
shadowed_files.member?(file)
|
286
312
|
end
|
287
313
|
|
288
314
|
# --- Class methods ---------------------------------------------------------------------------
|
289
315
|
|
290
316
|
class << self
|
317
|
+
include RealModName
|
318
|
+
|
291
319
|
# @sig #call | #debug | nil
|
292
320
|
attr_accessor :default_logger
|
293
321
|
|
294
322
|
# This is a shortcut for
|
295
323
|
#
|
296
324
|
# require "zeitwerk"
|
325
|
+
#
|
297
326
|
# loader = Zeitwerk::Loader.new
|
298
327
|
# loader.tag = File.basename(__FILE__, ".rb")
|
299
328
|
# loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
|
@@ -308,14 +337,64 @@ module Zeitwerk
|
|
308
337
|
# @sig (bool) -> Zeitwerk::GemLoader
|
309
338
|
def for_gem(warn_on_extra_files: true)
|
310
339
|
called_from = caller_locations(1, 1).first.path
|
311
|
-
Registry.loader_for_gem(called_from, warn_on_extra_files: warn_on_extra_files)
|
340
|
+
Registry.loader_for_gem(called_from, namespace: Object, warn_on_extra_files: warn_on_extra_files)
|
341
|
+
end
|
342
|
+
|
343
|
+
# This is a shortcut for
|
344
|
+
#
|
345
|
+
# require "zeitwerk"
|
346
|
+
#
|
347
|
+
# loader = Zeitwerk::Loader.new
|
348
|
+
# loader.tag = namespace.name + "-" + File.basename(__FILE__, ".rb")
|
349
|
+
# loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
|
350
|
+
# loader.push_dir(__dir__, namespace: namespace)
|
351
|
+
#
|
352
|
+
# except that this method returns the same object in subsequent calls from
|
353
|
+
# the same file, in the unlikely case the gem wants to be able to reload.
|
354
|
+
#
|
355
|
+
# This method returns a subclass of Zeitwerk::Loader, but the exact type
|
356
|
+
# is private, client code can only rely on the interface.
|
357
|
+
#
|
358
|
+
# @sig (bool) -> Zeitwerk::GemLoader
|
359
|
+
def for_gem_extension(namespace)
|
360
|
+
unless namespace.is_a?(Module) # Note that Class < Module.
|
361
|
+
raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module object, should be"
|
362
|
+
end
|
363
|
+
|
364
|
+
unless real_mod_name(namespace)
|
365
|
+
raise Zeitwerk::Error, "extending anonymous namespaces is unsupported"
|
366
|
+
end
|
367
|
+
|
368
|
+
called_from = caller_locations(1, 1).first.path
|
369
|
+
Registry.loader_for_gem(called_from, namespace: namespace, warn_on_extra_files: false)
|
312
370
|
end
|
313
371
|
|
314
|
-
# Broadcasts `eager_load` to all loaders.
|
372
|
+
# Broadcasts `eager_load` to all loaders. Those that have not been setup
|
373
|
+
# are skipped.
|
315
374
|
#
|
316
375
|
# @sig () -> void
|
317
376
|
def eager_load_all
|
318
|
-
Registry.loaders.each
|
377
|
+
Registry.loaders.each do |loader|
|
378
|
+
begin
|
379
|
+
loader.eager_load
|
380
|
+
rescue SetupRequired
|
381
|
+
# This is fine, we eager load what can be eager loaded.
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# Broadcasts `eager_load_namespace` to all loaders. Those that have not
|
387
|
+
# been setup are skipped.
|
388
|
+
#
|
389
|
+
# @sig (Module) -> void
|
390
|
+
def eager_load_namespace(mod)
|
391
|
+
Registry.loaders.each do |loader|
|
392
|
+
begin
|
393
|
+
loader.eager_load_namespace(mod)
|
394
|
+
rescue SetupRequired
|
395
|
+
# This is fine, we eager load what can be eager loaded.
|
396
|
+
end
|
397
|
+
end
|
319
398
|
end
|
320
399
|
|
321
400
|
# Returns an array with the absolute paths of the root directories of all
|
@@ -327,77 +406,58 @@ module Zeitwerk
|
|
327
406
|
end
|
328
407
|
end
|
329
408
|
|
330
|
-
private # -------------------------------------------------------------------------------------
|
331
|
-
|
332
409
|
# @sig (String, Module) -> void
|
333
|
-
def
|
410
|
+
private def define_autoloads_for_dir(dir, parent)
|
334
411
|
ls(dir) do |basename, abspath|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
412
|
+
if ruby?(basename)
|
413
|
+
basename.delete_suffix!(".rb")
|
414
|
+
autoload_file(parent, cname_for(basename, abspath), abspath)
|
415
|
+
else
|
416
|
+
if collapse?(abspath)
|
417
|
+
define_autoloads_for_dir(abspath, parent)
|
340
418
|
else
|
341
|
-
|
342
|
-
# `app/models`, but both of them are root directories.
|
343
|
-
#
|
344
|
-
# To resolve the ambiguity file name -> constant path this introduces,
|
345
|
-
# the `app/models/concerns` directory is totally ignored as a namespace,
|
346
|
-
# it counts only as root. The guard checks that.
|
347
|
-
unless root_dir?(abspath)
|
348
|
-
cname = inflector.camelize(basename, abspath).to_sym
|
349
|
-
if collapse?(abspath)
|
350
|
-
set_autoloads_in_dir(abspath, parent)
|
351
|
-
else
|
352
|
-
autoload_subdir(parent, cname, abspath)
|
353
|
-
end
|
354
|
-
end
|
419
|
+
autoload_subdir(parent, cname_for(basename, abspath), abspath)
|
355
420
|
end
|
356
|
-
rescue ::NameError => error
|
357
|
-
path_type = ruby?(abspath) ? "file" : "directory"
|
358
|
-
|
359
|
-
raise NameError.new(<<~MESSAGE, error.name)
|
360
|
-
#{error.message} inferred by #{inflector.class} from #{path_type}
|
361
|
-
|
362
|
-
#{abspath}
|
363
|
-
|
364
|
-
Possible ways to address this:
|
365
|
-
|
366
|
-
* Tell Zeitwerk to ignore this particular #{path_type}.
|
367
|
-
* Tell Zeitwerk to ignore one of its parent directories.
|
368
|
-
* Rename the #{path_type} to comply with the naming conventions.
|
369
|
-
* Modify the inflector to handle this case.
|
370
|
-
MESSAGE
|
371
421
|
end
|
372
422
|
end
|
373
423
|
end
|
374
424
|
|
375
425
|
# @sig (Module, Symbol, String) -> void
|
376
|
-
def autoload_subdir(parent, cname, subdir)
|
426
|
+
private def autoload_subdir(parent, cname, subdir)
|
377
427
|
if autoload_path = autoload_path_set_by_me_for?(parent, cname)
|
378
428
|
cpath = cpath(parent, cname)
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
429
|
+
if ruby?(autoload_path)
|
430
|
+
# Scanning visited a Ruby file first, and now a directory for the same
|
431
|
+
# constant has been found. This means we are dealing with an explicit
|
432
|
+
# namespace whose definition was seen first.
|
433
|
+
#
|
434
|
+
# Registering is idempotent, and we have to keep the autoload pointing
|
435
|
+
# to the file. This may run again if more directories are found later
|
436
|
+
# on, no big deal.
|
437
|
+
register_explicit_namespace(cpath)
|
438
|
+
end
|
439
|
+
# If the existing autoload points to a file, it has to be preserved, if
|
440
|
+
# not, it is fine as it is. In either case, we do not need to override.
|
441
|
+
# Just remember the subdirectory conforms this namespace.
|
442
|
+
namespace_dirs[cpath] << subdir
|
384
443
|
elsif !cdef?(parent, cname)
|
385
444
|
# First time we find this namespace, set an autoload for it.
|
386
|
-
|
387
|
-
|
445
|
+
namespace_dirs[cpath(parent, cname)] << subdir
|
446
|
+
define_autoload(parent, cname, subdir)
|
388
447
|
else
|
389
448
|
# For whatever reason the constant that corresponds to this namespace has
|
390
449
|
# already been defined, we have to recurse.
|
391
450
|
log("the namespace #{cpath(parent, cname)} already exists, descending into #{subdir}") if logger
|
392
|
-
|
451
|
+
define_autoloads_for_dir(subdir, cget(parent, cname))
|
393
452
|
end
|
394
453
|
end
|
395
454
|
|
396
455
|
# @sig (Module, Symbol, String) -> void
|
397
|
-
def autoload_file(parent, cname, file)
|
456
|
+
private def autoload_file(parent, cname, file)
|
398
457
|
if autoload_path = strict_autoload_path(parent, cname) || Registry.inception?(cpath(parent, cname))
|
399
458
|
# First autoload for a Ruby file wins, just ignore subsequent ones.
|
400
459
|
if ruby?(autoload_path)
|
460
|
+
shadowed_files << file
|
401
461
|
log("file #{file} is ignored because #{autoload_path} has precedence") if logger
|
402
462
|
else
|
403
463
|
promote_namespace_from_implicit_to_explicit(
|
@@ -408,9 +468,10 @@ module Zeitwerk
|
|
408
468
|
)
|
409
469
|
end
|
410
470
|
elsif cdef?(parent, cname)
|
471
|
+
shadowed_files << file
|
411
472
|
log("file #{file} is ignored because #{cpath(parent, cname)} is already defined") if logger
|
412
473
|
else
|
413
|
-
|
474
|
+
define_autoload(parent, cname, file)
|
414
475
|
end
|
415
476
|
end
|
416
477
|
|
@@ -418,18 +479,18 @@ module Zeitwerk
|
|
418
479
|
# the file where we've found the namespace is explicitly defined.
|
419
480
|
#
|
420
481
|
# @sig (dir: String, file: String, parent: Module, cname: Symbol) -> void
|
421
|
-
def promote_namespace_from_implicit_to_explicit(dir:, file:, parent:, cname:)
|
482
|
+
private def promote_namespace_from_implicit_to_explicit(dir:, file:, parent:, cname:)
|
422
483
|
autoloads.delete(dir)
|
423
484
|
Registry.unregister_autoload(dir)
|
424
485
|
|
425
486
|
log("earlier autoload for #{cpath(parent, cname)} discarded, it is actually an explicit namespace defined in #{file}") if logger
|
426
487
|
|
427
|
-
|
488
|
+
define_autoload(parent, cname, file)
|
428
489
|
register_explicit_namespace(cpath(parent, cname))
|
429
490
|
end
|
430
491
|
|
431
492
|
# @sig (Module, Symbol, String) -> void
|
432
|
-
def
|
493
|
+
private def define_autoload(parent, cname, abspath)
|
433
494
|
parent.autoload(cname, abspath)
|
434
495
|
|
435
496
|
if logger
|
@@ -450,7 +511,7 @@ module Zeitwerk
|
|
450
511
|
end
|
451
512
|
|
452
513
|
# @sig (Module, Symbol) -> String?
|
453
|
-
def autoload_path_set_by_me_for?(parent, cname)
|
514
|
+
private def autoload_path_set_by_me_for?(parent, cname)
|
454
515
|
if autoload_path = strict_autoload_path(parent, cname)
|
455
516
|
autoload_path if autoloads.key?(autoload_path)
|
456
517
|
else
|
@@ -459,28 +520,28 @@ module Zeitwerk
|
|
459
520
|
end
|
460
521
|
|
461
522
|
# @sig (String) -> void
|
462
|
-
def register_explicit_namespace(cpath)
|
463
|
-
ExplicitNamespace.
|
523
|
+
private def register_explicit_namespace(cpath)
|
524
|
+
ExplicitNamespace.__register(cpath, self)
|
464
525
|
end
|
465
526
|
|
466
527
|
# @sig (String) -> void
|
467
|
-
def raise_if_conflicting_directory(dir)
|
528
|
+
private def raise_if_conflicting_directory(dir)
|
468
529
|
MUTEX.synchronize do
|
530
|
+
dir_slash = dir + "/"
|
531
|
+
|
469
532
|
Registry.loaders.each do |loader|
|
470
533
|
next if loader == self
|
471
|
-
next if loader.
|
534
|
+
next if loader.__ignores?(dir)
|
472
535
|
|
473
|
-
|
474
|
-
loader.root_dirs.each do |root_dir, _namespace|
|
536
|
+
loader.__roots.each_key do |root_dir|
|
475
537
|
next if ignores?(root_dir)
|
476
538
|
|
477
|
-
|
478
|
-
if
|
539
|
+
root_dir_slash = root_dir + "/"
|
540
|
+
if dir_slash.start_with?(root_dir_slash) || root_dir_slash.start_with?(dir_slash)
|
479
541
|
require "pp" # Needed for pretty_inspect, even in Ruby 2.5.
|
480
542
|
raise Error,
|
481
|
-
"loader\n\n#{pretty_inspect}\n\nwants to manage directory #{dir
|
543
|
+
"loader\n\n#{pretty_inspect}\n\nwants to manage directory #{dir}," \
|
482
544
|
" which is already managed by\n\n#{loader.pretty_inspect}\n"
|
483
|
-
EOS
|
484
545
|
end
|
485
546
|
end
|
486
547
|
end
|
@@ -488,23 +549,23 @@ module Zeitwerk
|
|
488
549
|
end
|
489
550
|
|
490
551
|
# @sig (String, Object, String) -> void
|
491
|
-
def run_on_unload_callbacks(cpath, value, abspath)
|
552
|
+
private def run_on_unload_callbacks(cpath, value, abspath)
|
492
553
|
# Order matters. If present, run the most specific one.
|
493
554
|
on_unload_callbacks[cpath]&.each { |c| c.call(value, abspath) }
|
494
555
|
on_unload_callbacks[:ANY]&.each { |c| c.call(cpath, value, abspath) }
|
495
556
|
end
|
496
557
|
|
497
558
|
# @sig (Module, Symbol) -> void
|
498
|
-
def unload_autoload(parent, cname)
|
499
|
-
parent
|
559
|
+
private def unload_autoload(parent, cname)
|
560
|
+
crem(parent, cname)
|
500
561
|
log("autoload for #{cpath(parent, cname)} removed") if logger
|
501
562
|
end
|
502
563
|
|
503
564
|
# @sig (Module, Symbol) -> void
|
504
|
-
def unload_cref(parent, cname)
|
565
|
+
private def unload_cref(parent, cname)
|
505
566
|
# Let's optimistically remove_const. The way we use it, this is going to
|
506
567
|
# succeed always if all is good.
|
507
|
-
parent
|
568
|
+
crem(parent, cname)
|
508
569
|
rescue ::NameError
|
509
570
|
# There are a few edge scenarios in which this may happen. If the constant
|
510
571
|
# is gone, that is OK, anyway.
|
data/lib/zeitwerk/registry.rb
CHANGED
@@ -86,8 +86,8 @@ module Zeitwerk
|
|
86
86
|
#
|
87
87
|
# @private
|
88
88
|
# @sig (String) -> Zeitwerk::Loader
|
89
|
-
def loader_for_gem(root_file, warn_on_extra_files:)
|
90
|
-
gem_loaders_by_root_file[root_file] ||= GemLoader.
|
89
|
+
def loader_for_gem(root_file, namespace:, warn_on_extra_files:)
|
90
|
+
gem_loaders_by_root_file[root_file] ||= GemLoader.__new(root_file, namespace: namespace, warn_on_extra_files: warn_on_extra_files)
|
91
91
|
end
|
92
92
|
|
93
93
|
# @private
|
data/lib/zeitwerk/version.rb
CHANGED
data/lib/zeitwerk.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zeitwerk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.6.
|
4
|
+
version: 2.6.12
|
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: 2023-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|
@@ -28,10 +28,12 @@ files:
|
|
28
28
|
- lib/zeitwerk/gem_inflector.rb
|
29
29
|
- lib/zeitwerk/gem_loader.rb
|
30
30
|
- lib/zeitwerk/inflector.rb
|
31
|
+
- lib/zeitwerk/internal.rb
|
31
32
|
- lib/zeitwerk/kernel.rb
|
32
33
|
- lib/zeitwerk/loader.rb
|
33
34
|
- lib/zeitwerk/loader/callbacks.rb
|
34
35
|
- lib/zeitwerk/loader/config.rb
|
36
|
+
- lib/zeitwerk/loader/eager_load.rb
|
35
37
|
- lib/zeitwerk/loader/helpers.rb
|
36
38
|
- lib/zeitwerk/real_mod_name.rb
|
37
39
|
- lib/zeitwerk/registry.rb
|
@@ -59,7 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
61
|
- !ruby/object:Gem::Version
|
60
62
|
version: '0'
|
61
63
|
requirements: []
|
62
|
-
rubygems_version: 3.
|
64
|
+
rubygems_version: 3.4.16
|
63
65
|
signing_key:
|
64
66
|
specification_version: 4
|
65
67
|
summary: Efficient and thread-safe constant autoloader
|