zeitwerk 2.6.0 → 2.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 of namespaces to arrays of corresponding directories.
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 and
68
- # that its children are spread over those directories. We'll visit them to set
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 :lazy_subdirs
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
- # @private
80
- # @sig Mutex
81
- attr_reader :mutex2
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
- @lazy_subdirs = Hash.new { |h, cpath| h[cpath] = [] }
90
- @mutex = Mutex.new
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 namespace.
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
- actual_root_dirs.each do |root_dir, namespace|
106
- set_autoloads_in_dir(root_dir, namespace)
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 avaiable to be used together with
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
- lazy_subdirs.clear
204
+ namespace_dirs.clear
205
+ shadowed_files.clear
185
206
 
186
207
  Registry.on_unload(self)
187
- ExplicitNamespace.unregister_loader(self)
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
- # Eager loads all files in the root directories, recursively. Files do not
212
- # need to be in `$LOAD_PATH`, absolute file names are used. Ignored files
213
- # are not eager loaded. You can opt-out specifically in specific files and
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
- log("eager load start") if logger
237
+ raise Zeitwerk::Error.new("#{abspath} does not exist") unless File.exist?(abspath)
223
238
 
224
- honour_exclusions = !force
239
+ return unless dir?(abspath) || ruby?(abspath)
240
+ return if ignored_path?(abspath)
225
241
 
226
- queue = []
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
- while to_eager_load = queue.shift
232
- namespace, dir = to_eager_load
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
- autoloaded_dirs.each do |autoloaded_dir|
253
- Registry.unregister_autoload(autoloaded_dir)
254
- end
255
- autoloaded_dirs.clear
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
- @eager_loaded = true
256
+ walk_up(walk_up_from) do |dir|
257
+ break if root_namespace = roots[dir]
258
+ return if ignored_path?(dir)
258
259
 
259
- log("eager load end") if logger
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.unregister_loader(self)
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(&:eager_load)
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 set_autoloads_in_dir(dir, parent)
410
+ private def define_autoloads_for_dir(dir, parent)
334
411
  ls(dir) do |basename, abspath|
335
- begin
336
- if ruby?(basename)
337
- basename.delete_suffix!(".rb")
338
- cname = inflector.camelize(basename, abspath).to_sym
339
- autoload_file(parent, cname, abspath)
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
- # In a Rails application, `app/models/concerns` is a subdirectory of
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
- register_explicit_namespace(cpath) if ruby?(autoload_path)
380
- # We do not need to issue another autoload, the existing one is enough
381
- # no matter if it is for a file or a directory. Just remember the
382
- # subdirectory has to be visited if the namespace is used.
383
- lazy_subdirs[cpath] << subdir
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
- lazy_subdirs[cpath(parent, cname)] << subdir
387
- set_autoload(parent, cname, subdir)
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
- set_autoloads_in_dir(subdir, cget(parent, cname))
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
- set_autoload(parent, cname, file)
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
- set_autoload(parent, cname, file)
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 set_autoload(parent, cname, abspath)
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.register(cpath, self)
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.ignores?(dir)
534
+ next if loader.__ignores?(dir)
472
535
 
473
- dir = dir + "/"
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
- root_dir = root_dir + "/"
478
- if dir.start_with?(root_dir) || root_dir.start_with?(dir)
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.chop}," \
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.__send__(:remove_const, cname)
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.__send__(:remove_const, cname)
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.
@@ -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._new(root_file, warn_on_extra_files: warn_on_extra_files)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "2.6.0"
4
+ VERSION = "2.6.12"
5
5
  end
data/lib/zeitwerk.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Zeitwerk
4
4
  require_relative "zeitwerk/real_mod_name"
5
+ require_relative "zeitwerk/internal"
5
6
  require_relative "zeitwerk/loader"
6
7
  require_relative "zeitwerk/gem_loader"
7
8
  require_relative "zeitwerk/registry"
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.0
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: 2022-06-13 00:00:00.000000000 Z
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.3.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