zeitwerk 2.6.11 → 2.6.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0dd43924a88fceee915258e119a02df8fb173199576f52cf18c8298aab091361
4
- data.tar.gz: 6aeeced1ce99e28238ebd1488a543da6ba4fb885e283f999cec30a49db3a0ccf
3
+ metadata.gz: 5d4ce4eb7136dafe17130fc5d895e525d80ae947f02dd9420b5bc85a4d6f0dfd
4
+ data.tar.gz: edcac638955fef258ad36a358b78da6831a715ed46446848e39f9f1d8fb7de0b
5
5
  SHA512:
6
- metadata.gz: ca6b895667327b79fe51d504de760150aeceba5edb64400a6976962c1241bb8061f5c29e4d8584ba888694a0b1b30cf2f149f66f52181089fda262dde541d902
7
- data.tar.gz: 8393b41bda4187ce0195ce5b1192455a2bc574291188b0eba86f31af88fc66e3d441717823ac07407f8c7be4e4a97de06cb5bc1922a133e66ed51cc91f6eb1c7
6
+ metadata.gz: f0a6e64c56635c9c6a8c9b24bdeb7509e4a7f14489f32aae18b02221c23b95b34096184c78d2d1509130ce75031ed0f3a7751b78e43d9b72122440f07cf980bc
7
+ data.tar.gz: 06440147c101a95ac2c8b7dcd43920a3efc3da2f58b902c157730a449d57b6ef74e0d3db7f3d2735be816a74ceb774e2d4075741556c7e2ba7fa00b8130c1723
data/README.md CHANGED
@@ -40,6 +40,7 @@
40
40
  - [Inflection](#inflection)
41
41
  - [Zeitwerk::Inflector](#zeitwerkinflector)
42
42
  - [Zeitwerk::GemInflector](#zeitwerkgeminflector)
43
+ - [Zeitwerk::NullInflector](#zeitwerknullinflector)
43
44
  - [Custom inflector](#custom-inflector)
44
45
  - [Callbacks](#callbacks)
45
46
  - [The on_setup callback](#the-on_setup-callback)
@@ -373,7 +374,7 @@ require "zeitwerk"
373
374
  loader = Zeitwerk::Loader.new
374
375
  loader.tag = File.basename(__FILE__, ".rb")
375
376
  loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
376
- loader.push_dir(__dir__)
377
+ loader.push_dir(File.dirname(__FILE__))
377
378
  ```
378
379
 
379
380
  If the main module references project constants at the top-level, Zeitwerk has to be ready to load them. Their definitions, in turn, may reference other project constants. And this is recursive. Therefore, it is important that the `setup` call happens above the main module definition:
@@ -579,7 +580,7 @@ root_dir2/my_app/routes
579
580
  root_dir3/my_app/routes
580
581
  ```
581
582
 
582
- where `root_directory{1,2,3}` are root directories, eager loading `MyApp::Routes` will eager load the contents of the three corresponding directories.
583
+ where `root_dir{1,2,3}` are root directories, eager loading `MyApp::Routes` will eager load the contents of the three corresponding directories.
583
584
 
584
585
  There might exist external source trees implementing part of the namespace. This happens routinely, because top-level constants are stored in the globally shared `Object`. It happens also when deliberately [reopening third-party namespaces](#reopening-third-party-namespaces). Such external code is not eager loaded, the implementation is carefully scoped to what the receiver manages to avoid side-effects elsewhere.
585
586
 
@@ -774,6 +775,31 @@ This inflector is like the basic one, except it expects `lib/my_gem/version.rb`
774
775
 
775
776
  The inflectors of different loaders are independent of each other. There are no global inflection rules or global configuration that can affect this inflector. It is deterministic.
776
777
 
778
+ <a id="markdown-zeitwerknullinflector" name="zeitwerknullinflector"></a>
779
+ #### Zeitwerk::NullInflector
780
+
781
+ This is an experimental inflector that simply returns its input unchanged.
782
+
783
+ ```ruby
784
+ loader.inflector = Zeitwerk::NullInflector.new
785
+ ```
786
+
787
+ In a project using this inflector, the names of files and directories are equal to the constants they define:
788
+
789
+ ```
790
+ User.rb -> User
791
+ HTMLParser.rb -> HTMLParser
792
+ Admin/Role.rb -> Admin::Role
793
+ ```
794
+
795
+ Point is, you think less. Names that typically need custom configuration like acronyms no longer require your attention. What you see is what you get, simple.
796
+
797
+ This inflector is experimental since Ruby usually goes for snake case in files and directories. But hey, if you fancy giving it a whirl, go for it!
798
+
799
+ The null inflector cannot be used in Rails applications because the `main` autoloader also manages engines. However, you could subclass the default inflector and override `camelize` to return the basename untouched if it starts with an uppercase letter. Generators would not create the expected file names, but you could still experiment to see how far this approach takes you.
800
+
801
+ In case-insensitive file systems, this inflector works as long as directory listings return the expected strings. Zeitwerk lists directories using Ruby APIs like `Dir.children` or `Dir.entries`.
802
+
777
803
  <a id="markdown-custom-inflector" name="custom-inflector"></a>
778
804
  #### Custom inflector
779
805
 
@@ -1006,7 +1032,7 @@ Zeitwerk::Loader.default_logger = method(:puts)
1006
1032
 
1007
1033
  If there is a logger configured, you'll see traces when autoloads are set, files loaded, and modules autovivified. While reloading, removed autoloads and unloaded objects are also traced.
1008
1034
 
1009
- As a curiosity, if your project has namespaces you'll notice in the traces Zeitwerk sets autoloads for _directories_. That's a technique used to be able to descend into subdirectories on demand, avoiding that way unnecessary tree walks.
1035
+ As a curiosity, if your project has namespaces you'll notice in the traces Zeitwerk sets autoloads for _directories_. This allows descending into subdirectories on demand, thus avoiding unnecessary tree walks.
1010
1036
 
1011
1037
  <a id="markdown-loader-tag" name="loader-tag"></a>
1012
1038
  #### Loader tag
@@ -1032,7 +1058,7 @@ Zeitwerk@my_gem: constant MyGem::Foo loaded from ...
1032
1058
  <a id="markdown-ignoring-parts-of-the-project" name="ignoring-parts-of-the-project"></a>
1033
1059
  ### Ignoring parts of the project
1034
1060
 
1035
- Zeitwerk ignores automatically any file or directory whose name starts with a dot, and any files that do not have extension ".rb".
1061
+ Zeitwerk ignores automatically any file or directory whose name starts with a dot, and any files that do not have the extension ".rb".
1036
1062
 
1037
1063
  However, sometimes it might still be convenient to tell Zeitwerk to completely ignore some particular Ruby file or directory. That is possible with `ignore`, which accepts an arbitrary number of strings or `Pathname` objects, and also an array of them.
1038
1064
 
@@ -1325,7 +1351,7 @@ The test suite passes on Windows with codepage `Windows-1252` if all the involve
1325
1351
 
1326
1352
  3. In that line, if two loaders manage files that translate to the same constant in the same namespace, the first one wins, the rest are ignored. Similar to what happens with `require` and `$LOAD_PATH`, only the first occurrence matters.
1327
1353
 
1328
- 4. Projects that reopen a namespace defined by some dependency have to ensure said namespace is loaded before setup. That is, the project has to make sure it reopens, rather than define. This is often accomplished just loading the dependency.
1354
+ 4. Projects that reopen a namespace defined by some dependency have to ensure said namespace is loaded before setup. That is, the project has to make sure it reopens, rather than defines, the namespace. This is often accomplished by loading (e.g., `require`-ing) the dependency.
1329
1355
 
1330
1356
  5. Objects stored in reloadable constants should not be cached in places that are not reloaded. For example, non-reloadable classes should not subclass a reloadable class, or mixin a reloadable module. Otherwise, after reloading, those classes or module objects would become stale. Referring to constants in dynamic places like method calls or lambdas is fine.
1331
1357
 
@@ -14,10 +14,6 @@ module Kernel
14
14
  # should not require anything. But if someone has legacy require calls around,
15
15
  # they will work as expected, and in a compatible way. This feature is by now
16
16
  # EXPERIMENTAL and UNDOCUMENTED.
17
- #
18
- # We cannot decorate with prepend + super because Kernel has already been
19
- # included in Object, and changes in ancestors don't get propagated into
20
- # already existing ancestor chains on Ruby < 3.0.
21
17
  alias_method :zeitwerk_original_require, :require
22
18
  class << self
23
19
  alias_method :zeitwerk_original_require, :require
@@ -28,10 +24,10 @@ module Kernel
28
24
  if loader = Zeitwerk::Registry.loader_for(path)
29
25
  if path.end_with?(".rb")
30
26
  required = zeitwerk_original_require(path)
31
- loader.on_file_autoloaded(path) if required
27
+ loader.__on_file_autoloaded(path) if required
32
28
  required
33
29
  else
34
- loader.on_dir_autoloaded(path)
30
+ loader.__on_dir_autoloaded(path)
35
31
  true
36
32
  end
37
33
  else
@@ -39,7 +35,7 @@ module Kernel
39
35
  if required
40
36
  abspath = $LOADED_FEATURES.last
41
37
  if loader = Zeitwerk::Registry.loader_for(abspath)
42
- loader.on_file_autoloaded(abspath)
38
+ loader.__on_file_autoloaded(abspath)
43
39
  end
44
40
  end
45
41
  required
@@ -2,12 +2,12 @@
2
2
 
3
3
  module Zeitwerk::Loader::Callbacks
4
4
  include Zeitwerk::RealModName
5
+ extend Zeitwerk::Internal
5
6
 
6
7
  # Invoked from our decorated Kernel#require when a managed file is autoloaded.
7
8
  #
8
- # @private
9
9
  # @sig (String) -> void
10
- def on_file_autoloaded(file)
10
+ internal def on_file_autoloaded(file)
11
11
  cref = autoloads.delete(file)
12
12
  cpath = cpath(*cref)
13
13
 
@@ -20,8 +20,16 @@ module Zeitwerk::Loader::Callbacks
20
20
  else
21
21
  msg = "expected file #{file} to define constant #{cpath}, but didn't"
22
22
  log(msg) if logger
23
+
24
+ # Ruby still keeps the autoload defined, but we remove it because the
25
+ # contract in Zeitwerk is more strict.
23
26
  crem(*cref)
27
+
28
+ # Since the expected constant was not defined, there is nothing to unload.
29
+ # However, if the exception is rescued and reloading is enabled, we still
30
+ # need to deleted the file from $LOADED_FEATURES.
24
31
  to_unload[cpath] = [file, cref] if reloading_enabled?
32
+
25
33
  raise Zeitwerk::NameError.new(msg, cref.last)
26
34
  end
27
35
  end
@@ -29,9 +37,8 @@ module Zeitwerk::Loader::Callbacks
29
37
  # Invoked from our decorated Kernel#require when a managed directory is
30
38
  # autoloaded.
31
39
  #
32
- # @private
33
40
  # @sig (String) -> void
34
- def on_dir_autoloaded(dir)
41
+ internal def on_dir_autoloaded(dir)
35
42
  # Module#autoload does not serialize concurrent requires in CRuby < 3.2, and
36
43
  # we handle directories ourselves without going through Kernel#require, so
37
44
  # the callback needs to account for concurrency.
@@ -74,7 +81,7 @@ module Zeitwerk::Loader::Callbacks
74
81
  def on_namespace_loaded(namespace)
75
82
  if dirs = namespace_dirs.delete(real_mod_name(namespace))
76
83
  dirs.each do |dir|
77
- set_autoloads_in_dir(dir, namespace)
84
+ define_autoloads_for_dir(dir, namespace)
78
85
  end
79
86
  end
80
87
  end
@@ -171,7 +171,7 @@ module Zeitwerk::Loader::EagerLoad
171
171
  next if honour_exclusions && eager_load_exclusions.member?(abspath)
172
172
 
173
173
  if ruby?(abspath)
174
- if (cref = autoloads[abspath]) && !shadowed_file?(abspath)
174
+ if (cref = autoloads[abspath])
175
175
  cget(*cref)
176
176
  end
177
177
  else
@@ -121,7 +121,7 @@ module Zeitwerk
121
121
  break if @setup
122
122
 
123
123
  actual_roots.each do |root_dir, root_namespace|
124
- set_autoloads_in_dir(root_dir, root_namespace)
124
+ define_autoloads_for_dir(root_dir, root_namespace)
125
125
  end
126
126
 
127
127
  on_setup_callbacks.each(&:call)
@@ -407,14 +407,14 @@ module Zeitwerk
407
407
  end
408
408
 
409
409
  # @sig (String, Module) -> void
410
- private def set_autoloads_in_dir(dir, parent)
410
+ private def define_autoloads_for_dir(dir, parent)
411
411
  ls(dir) do |basename, abspath|
412
412
  if ruby?(basename)
413
413
  basename.delete_suffix!(".rb")
414
414
  autoload_file(parent, cname_for(basename, abspath), abspath)
415
415
  else
416
416
  if collapse?(abspath)
417
- set_autoloads_in_dir(abspath, parent)
417
+ define_autoloads_for_dir(abspath, parent)
418
418
  else
419
419
  autoload_subdir(parent, cname_for(basename, abspath), abspath)
420
420
  end
@@ -443,12 +443,12 @@ module Zeitwerk
443
443
  elsif !cdef?(parent, cname)
444
444
  # First time we find this namespace, set an autoload for it.
445
445
  namespace_dirs[cpath(parent, cname)] << subdir
446
- set_autoload(parent, cname, subdir)
446
+ define_autoload(parent, cname, subdir)
447
447
  else
448
448
  # For whatever reason the constant that corresponds to this namespace has
449
449
  # already been defined, we have to recurse.
450
450
  log("the namespace #{cpath(parent, cname)} already exists, descending into #{subdir}") if logger
451
- set_autoloads_in_dir(subdir, cget(parent, cname))
451
+ define_autoloads_for_dir(subdir, cget(parent, cname))
452
452
  end
453
453
  end
454
454
 
@@ -471,7 +471,7 @@ module Zeitwerk
471
471
  shadowed_files << file
472
472
  log("file #{file} is ignored because #{cpath(parent, cname)} is already defined") if logger
473
473
  else
474
- set_autoload(parent, cname, file)
474
+ define_autoload(parent, cname, file)
475
475
  end
476
476
  end
477
477
 
@@ -485,12 +485,12 @@ module Zeitwerk
485
485
 
486
486
  log("earlier autoload for #{cpath(parent, cname)} discarded, it is actually an explicit namespace defined in #{file}") if logger
487
487
 
488
- set_autoload(parent, cname, file)
488
+ define_autoload(parent, cname, file)
489
489
  register_explicit_namespace(cpath(parent, cname))
490
490
  end
491
491
 
492
492
  # @sig (Module, Symbol, String) -> void
493
- private def set_autoload(parent, cname, abspath)
493
+ private def define_autoload(parent, cname, abspath)
494
494
  parent.autoload(cname, abspath)
495
495
 
496
496
  if logger
@@ -0,0 +1,5 @@
1
+ class Zeitwerk::NullInflector
2
+ def camelize(basename, _abspath)
3
+ basename
4
+ end
5
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "2.6.11"
4
+ VERSION = "2.6.13"
5
5
  end
data/lib/zeitwerk.rb CHANGED
@@ -9,6 +9,7 @@ module Zeitwerk
9
9
  require_relative "zeitwerk/explicit_namespace"
10
10
  require_relative "zeitwerk/inflector"
11
11
  require_relative "zeitwerk/gem_inflector"
12
+ require_relative "zeitwerk/null_inflector"
12
13
  require_relative "zeitwerk/kernel"
13
14
  require_relative "zeitwerk/error"
14
15
  require_relative "zeitwerk/version"
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.11
4
+ version: 2.6.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xavier Noria
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-02 00:00:00.000000000 Z
11
+ date: 2024-02-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Zeitwerk implements constant autoloading with Ruby semantics. Each gem
@@ -35,6 +35,7 @@ files:
35
35
  - lib/zeitwerk/loader/config.rb
36
36
  - lib/zeitwerk/loader/eager_load.rb
37
37
  - lib/zeitwerk/loader/helpers.rb
38
+ - lib/zeitwerk/null_inflector.rb
38
39
  - lib/zeitwerk/real_mod_name.rb
39
40
  - lib/zeitwerk/registry.rb
40
41
  - lib/zeitwerk/version.rb
@@ -61,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
62
  - !ruby/object:Gem::Version
62
63
  version: '0'
63
64
  requirements: []
64
- rubygems_version: 3.4.16
65
+ rubygems_version: 3.5.5
65
66
  signing_key:
66
67
  specification_version: 4
67
68
  summary: Efficient and thread-safe constant autoloader