zeitwerk 2.1.5 → 2.1.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e89de8ad7897a95ccc14f71e1688eb826c7e01f2c0c9381e09de5dd8815d1974
4
- data.tar.gz: 11bf19ab1b396399afc3110e8122cb783d2a2d64a889fa5ac7e506e65a8cc9be
3
+ metadata.gz: 43750607caaaa5d4fff5bd52204cafb98fbd5109b51a24d9099ba83c6a2a4990
4
+ data.tar.gz: 8ad3251fd93af9e66a9102c13556beff5805a38660d69e4c5de37e2808e76d8f
5
5
  SHA512:
6
- metadata.gz: b9a3e74bde3eec64186834b010f3c047d0b1c849a95c89747c6195b3a5fef974d4b1018cf4fca64a46e27aa5768c543d73b6203af80b170c20dab108544833e0
7
- data.tar.gz: 2055af7e3b74fa7c6c3e0c1e7dc6bb1888736c5d8204106ed0504b83a3cba74f7db2a24a72a0e2afc995cf780d4ebcd5965c74262150bb9c90fc3abf4ae97310
6
+ metadata.gz: e6e87877104078f564686a15b81882a8e20a560a36999e0f8b9916aa520dd46fe1a2b7f65cacdb7b6c62d252472b6ce1370939054f026a335e21652881820814
7
+ data.tar.gz: 3641d412ab4b9176861b9ed9b28ea7964b6a814ebb72804c96967e511562fb87f424c48d7780345be8ccd2c53bdacfe095e3e5173e166a4185bf389740258ca6
data/README.md CHANGED
@@ -338,7 +338,15 @@ This needs to be done before calling `setup`.
338
338
  <a id="markdown-logging" name="logging"></a>
339
339
  ### Logging
340
340
 
341
- Zeitwerk is silent by default, but you can configure a callable as logger:
341
+ Zeitwerk is silent by default, but you can ask loaders to trace their activity. Logging is meant just for troubleshooting, shouldn't normally be enabled.
342
+
343
+ The `log!` mehtod is a quick shortcut to let the loader log to `$stdout`:
344
+
345
+ ```
346
+ loader.log!
347
+ ```
348
+
349
+ If you want more control, a logger can be configured as a callable
342
350
 
343
351
  ```ruby
344
352
  loader.logger = method(:puts)
@@ -53,6 +53,23 @@ module Zeitwerk
53
53
  # @return [Set<String>]
54
54
  attr_reader :ignored_paths
55
55
 
56
+ # A _shadowed file_ is a file managed by this loader that is ignored when
57
+ # setting autoloads because its matching constant is taken. Either the
58
+ # constant is already defined, or there exists an autoload for it.
59
+ #
60
+ # Think $LOAD_PATH and require, only the first occurrence of a given
61
+ # relative name is loaded.
62
+ #
63
+ # This set keeps track of the absolute path of shadowed files, to be able to
64
+ # skip them while eager loading.
65
+ #
66
+ # Note this cannot be implemented with do_not_eager_load because these
67
+ # files are not autoloadable.
68
+ #
69
+ # @private
70
+ # @return [Set<String>]
71
+ attr_reader :shadowed_files
72
+
56
73
  # Maps real absolute paths for which an autoload has been set ---and not
57
74
  # executed--- to their corresponding parent class or module and constant
58
75
  # name.
@@ -138,6 +155,7 @@ module Zeitwerk
138
155
  @autoloaded_dirs = []
139
156
  @to_unload = {}
140
157
  @lazy_subdirs = {}
158
+ @shadowed_files = Set.new
141
159
  @eager_load_exclusions = Set.new
142
160
 
143
161
  # TODO: find a better name for these mutexes.
@@ -306,6 +324,7 @@ module Zeitwerk
306
324
  autoloaded_dirs.clear
307
325
  to_unload.clear
308
326
  lazy_subdirs.clear
327
+ shadowed_files.clear
309
328
 
310
329
  Registry.on_unload(self)
311
330
  ExplicitNamespace.unregister(self)
@@ -343,19 +362,19 @@ module Zeitwerk
343
362
 
344
363
  queue = actual_root_dirs.reject { |dir| eager_load_exclusions.member?(dir) }
345
364
  while dir = queue.shift
346
- const_get_if_autoload(dir)
347
-
348
- each_abspath(dir) do |abspath|
365
+ ls(dir) do |_basename, abspath|
349
366
  next if eager_load_exclusions.member?(abspath)
350
367
 
351
368
  if ruby?(abspath)
352
- const_get_if_autoload(abspath)
369
+ require abspath unless shadowed_files.member?(abspath)
353
370
  elsif dir?(abspath)
354
371
  queue << abspath
355
372
  end
356
373
  end
357
374
  end
358
375
 
376
+ shadowed_files.clear
377
+
359
378
  autoloaded_dirs.each do |autoloaded_dir|
360
379
  Registry.unregister_autoload(autoloaded_dir)
361
380
  end
@@ -391,6 +410,13 @@ module Zeitwerk
391
410
  to_unload.keys.freeze
392
411
  end
393
412
 
413
+ # Logs to `$stdout`, handy shortcut for debugging.
414
+ #
415
+ # @return [void]
416
+ def log!
417
+ @logger = ->(msg) { puts msg }
418
+ end
419
+
394
420
  # --- Class methods ---------------------------------------------------------------------------
395
421
 
396
422
  class << self
@@ -414,7 +440,7 @@ module Zeitwerk
414
440
  #
415
441
  # @return [Zeitwerk::Loader]
416
442
  def for_gem
417
- called_from = caller_locations.first.path
443
+ called_from = caller_locations(1, 1).first.path
418
444
  Registry.loader_for_gem(called_from)
419
445
  end
420
446
 
@@ -449,9 +475,10 @@ module Zeitwerk
449
475
  # @param parent [Module]
450
476
  # @return [void]
451
477
  def set_autoloads_in_dir(dir, parent)
452
- each_abspath(dir) do |abspath|
453
- cname = inflector.camelize(File.basename(abspath, ".rb"), abspath).to_sym
454
- if ruby?(abspath)
478
+ ls(dir) do |basename, abspath|
479
+ if ruby?(basename)
480
+ basename.slice!(-3, 3)
481
+ cname = inflector.camelize(basename, abspath).to_sym
455
482
  autoload_file(parent, cname, abspath)
456
483
  elsif dir?(abspath)
457
484
  # In a Rails application, `app/models/concerns` is a subdirectory of
@@ -460,7 +487,10 @@ module Zeitwerk
460
487
  # To resolve the ambiguity file name -> constant path this introduces,
461
488
  # the `app/models/concerns` directory is totally ignored as a namespace,
462
489
  # it counts only as root. The guard checks that.
463
- autoload_subdir(parent, cname, abspath) unless root_dirs.key?(abspath)
490
+ unless root_dirs.key?(abspath)
491
+ cname = inflector.camelize(basename, abspath).to_sym
492
+ autoload_subdir(parent, cname, abspath)
493
+ end
464
494
  end
465
495
  end
466
496
  end
@@ -496,6 +526,7 @@ module Zeitwerk
496
526
  if autoload_path = autoload_for?(parent, cname)
497
527
  # First autoload for a Ruby file wins, just ignore subsequent ones.
498
528
  if ruby?(autoload_path)
529
+ shadowed_files.add(file)
499
530
  log("file #{file} is ignored because #{autoload_path} has precedence") if logger
500
531
  else
501
532
  promote_namespace_from_implicit_to_explicit(
@@ -506,6 +537,7 @@ module Zeitwerk
506
537
  )
507
538
  end
508
539
  elsif cdef?(parent, cname)
540
+ shadowed_files.add(file)
509
541
  log("file #{file} is ignored because #{cpath(parent, cname)} is already defined") if logger
510
542
  else
511
543
  set_autoload(parent, cname, file)
@@ -605,7 +637,7 @@ module Zeitwerk
605
637
  # @param dir [String]
606
638
  # @return [void]
607
639
  def do_preload_dir(dir)
608
- each_abspath(dir) do |abspath|
640
+ ls(dir) do |_basename, abspath|
609
641
  do_preload_abspath(abspath)
610
642
  end
611
643
  end
@@ -625,13 +657,13 @@ module Zeitwerk
625
657
  end
626
658
 
627
659
  # @param dir [String]
628
- # @yieldparam path [String]
660
+ # @yieldparam path [String, String]
629
661
  # @return [void]
630
- def each_abspath(dir)
631
- Dir.foreach(dir) do |entry|
632
- next if entry.start_with?(".")
633
- abspath = File.join(dir, entry)
634
- yield abspath unless ignored_paths.member?(abspath)
662
+ def ls(dir)
663
+ Dir.foreach(dir) do |basename|
664
+ next if basename.start_with?(".")
665
+ abspath = File.join(dir, basename)
666
+ yield basename, abspath unless ignored_paths.member?(abspath)
635
667
  end
636
668
  end
637
669
 
@@ -664,12 +696,6 @@ module Zeitwerk
664
696
  parent.const_defined?(cname, false)
665
697
  end
666
698
 
667
- def const_get_if_autoload(abspath)
668
- if cref = autoloads[File.realpath(abspath)]
669
- cref[0].const_get(cref[1], false)
670
- end
671
- end
672
-
673
699
  def register_explicit_namespace(cpath)
674
700
  ExplicitNamespace.register(cpath, self)
675
701
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "2.1.5"
4
+ VERSION = "2.1.6"
5
5
  end
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.1.5
4
+ version: 2.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xavier Noria
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-24 00:00:00.000000000 Z
11
+ date: 2019-04-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Zeitwerk implements constant autoloading with Ruby semantics. Each gem