zeitwerk 2.7.0 → 2.7.1

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: 79a3e282ee7602f7d40c75c1299b24de4de890ee90a22cd37ed9c7a660936074
4
- data.tar.gz: bd60115bc776137770e16ee527e4a07e815af547d1d5de851039e0187d50eff8
3
+ metadata.gz: aba46812169c8e26085b099c708093b00a29bfd39e8d8210d29bc99e7df0e7fa
4
+ data.tar.gz: 0fc20386009d52d21a0cb79a64c5d800f7aecfce5a38e8430a10dc5d04c2609d
5
5
  SHA512:
6
- metadata.gz: ebdab4575a6e35591d1603ed11dc31f77eeffb15c8270f54719eb0850f3641efa968bebd8f02c32330ca768b8211d9aeb972fd41e0efac710faa023a0d62a841
7
- data.tar.gz: 6d58d0256f35d6ac6445cba6619943aa4b3974905eb28588284f14aadd27304b1f11fc46d4f0905074c8888e52eabc03e312054930b702c875856d6c0a6a0694
6
+ metadata.gz: 3edac5ad6f940caa70c4cac093bf271b8399b078d7124f450513c963ec5099e9b9082841ceb9893b81f1edf8e34dc642ec811403415fb230211670c63a950766
7
+ data.tar.gz: 47339a8b35dc06108fde9aadd62e83b1ea4e4ef22854c31849069f09ece1115dce07d63ec354fe96fbc599bba411ecff48db6a8b0c8b150ad7ee016787e9d75c
data/README.md CHANGED
@@ -281,6 +281,8 @@ class Hotel < ApplicationRecord
281
281
  end
282
282
  ```
283
283
 
284
+ When autoloaded, Zeitwerk verifies the expected constant (`Hotel` in the example) stores a class or module object. If it doesn't, `Zeitwerk::Error` is raised.
285
+
284
286
  An explicit namespace must be managed by one single loader. Loaders that reopen namespaces owned by other projects are responsible for loading their constants before setup.
285
287
 
286
288
  <a id="markdown-collapsing-directories" name="collapsing-directories"></a>
@@ -1382,9 +1384,12 @@ The test suite passes on Windows with codepage `Windows-1252` if all the involve
1382
1384
  <a id="markdown-supported-ruby-versions" name="supported-ruby-versions"></a>
1383
1385
  ## Supported Ruby versions
1384
1386
 
1385
- Zeitwerk works with CRuby 2.5 and above.
1387
+ Starting with version 2.7, Zeitwerk requires Ruby 3.2 or newer.
1386
1388
 
1387
- On TruffleRuby all is good except for thread-safety. Right now, in TruffleRuby `Module#autoload` does not block threads accessing a constant that is being autoloaded. CRuby prevents such access to avoid concurrent threads from seeing partial evaluations of the corresponding file. Zeitwerk inherits autoloading thread-safety from this property. This is not an issue if your project gets eager loaded, or if you lazy load in single-threaded environments. (See https://github.com/oracle/truffleruby/issues/2431.)
1389
+ Zeitwerk 2.7 requires TruffleRuby 24.1.2+ due to https://github.com/oracle/truffleruby/issues/3683.
1390
+ Alternatively, TruffleRuby users can use a `< 2.7` version constraint for the `zeitwerk` gem.
1391
+ As of this writing, [autoloading is not fully thread-safe yet on TruffleRuby](https://github.com/oracle/truffleruby/issues/2431).
1392
+ If your program is multi-threaded, you need to eager load before threads are created.
1388
1393
 
1389
1394
  JRuby 9.3.0.0 is almost there. As of this writing, the test suite of Zeitwerk passes on JRuby except for three tests. (See https://github.com/jruby/jruby/issues/6781.)
1390
1395
 
@@ -3,7 +3,14 @@
3
3
  module Zeitwerk::ConstAdded
4
4
  def const_added(cname)
5
5
  if loader = Zeitwerk::ExplicitNamespace.__loader_for(self, cname)
6
- loader.on_namespace_loaded(const_get(cname, false))
6
+ namespace = const_get(cname, false)
7
+
8
+ unless namespace.is_a?(Module)
9
+ cref = Zeitwerk::Cref.new(self, cname)
10
+ raise Zeitwerk::Error, "#{cref.path} is expected to be a namespace, should be a class or module (got #{namespace.class})"
11
+ end
12
+
13
+ loader.on_namespace_loaded(namespace)
7
14
  end
8
15
  super
9
16
  end
data/lib/zeitwerk/cref.rb CHANGED
@@ -13,6 +13,9 @@
13
13
  class Zeitwerk::Cref
14
14
  include Zeitwerk::RealModName
15
15
 
16
+ # @sig Module
17
+ attr_reader :mod
18
+
16
19
  # @sig Symbol
17
20
  attr_reader :cname
18
21
 
@@ -1,55 +1,84 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- # Centralizes the logic needed to descend into matching subdirectories right
5
- # after the constant for an explicit namespace has been defined.
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.
6
15
  #
7
16
  # The implementation assumes an explicit namespace is managed by one loader.
8
17
  # Loaders that reopen namespaces owned by other projects are responsible for
9
18
  # loading their constant before setup. This is documented.
10
19
  module ExplicitNamespace # :nodoc: all
11
- # Maps cpaths of explicit namespaces with their corresponding loader.
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
+ #
12
34
  # Entries are added as the namespaces are found, and removed as they are
13
35
  # autoloaded.
14
36
  #
15
- # @sig Hash[String => Zeitwerk::Loader]
16
- @cpaths = {}
37
+ # @sig Hash[(Symbol | String) => Zeitwerk::Loader]
38
+ @loaders = {}
17
39
 
18
40
  class << self
19
41
  include RealModName
20
42
  extend Internal
21
43
 
22
- # Registers `cpath` as being the constant path of an explicit namespace
44
+ # Registers `cref` as being the constant path of an explicit namespace
23
45
  # managed by `loader`.
24
46
  #
25
47
  # @sig (String, Zeitwerk::Loader) -> void
26
- internal def register(cpath, loader)
27
- @cpaths[cpath] = loader
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
28
54
  end
29
55
 
30
- # @sig (String) -> Zeitwerk::Loader?
56
+ # @sig (Module, Symbol) -> Zeitwerk::Loader?
31
57
  internal def loader_for(mod, cname)
32
- cpath = mod.equal?(Object) ? cname.name : "#{real_mod_name(mod)}::#{cname}"
33
- @cpaths.delete(cpath)
58
+ if Object.equal?(mod)
59
+ @loaders.delete(cname)
60
+ else
61
+ @loaders.delete("#{real_mod_name(mod)}::#{cname}")
62
+ end
34
63
  end
35
64
 
36
65
  # @sig (Zeitwerk::Loader) -> void
37
66
  internal def unregister_loader(loader)
38
- @cpaths.delete_if { _2.equal?(loader) }
67
+ @loaders.delete_if { _2.equal?(loader) }
39
68
  end
40
69
 
41
70
  # This is an internal method only used by the test suite.
42
71
  #
43
72
  # @sig (String) -> Zeitwerk::Loader?
44
- internal def registered?(cpath)
45
- @cpaths[cpath]
73
+ internal def registered?(cname_or_cpath)
74
+ @loaders[cname_or_cpath]
46
75
  end
47
76
 
48
77
  # This is an internal method only used by the test suite.
49
78
  #
50
79
  # @sig () -> void
51
80
  internal def clear
52
- @cpaths.clear
81
+ @loaders.clear
53
82
  end
54
83
 
55
84
  module Synchronized
@@ -84,7 +84,7 @@ module Zeitwerk::Loader::EagerLoad
84
84
  return unless mod_name
85
85
 
86
86
  actual_roots.each do |root_dir, root_namespace|
87
- if mod.equal?(Object)
87
+ if Object.equal?(mod)
88
88
  # A shortcircuiting test depends on the invocation of this method.
89
89
  # Please keep them in sync if refactored.
90
90
  actual_eager_load_dir(root_dir, root_namespace)
@@ -553,7 +553,7 @@ module Zeitwerk
553
553
 
554
554
  # @sig (Zeitwerk::Cref) -> void
555
555
  private def register_explicit_namespace(cref)
556
- ExplicitNamespace.__register(cref.path, self)
556
+ ExplicitNamespace.__register(cref, self)
557
557
  end
558
558
 
559
559
  # @sig (String) -> void
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "2.7.0"
4
+ VERSION = "2.7.1"
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.7.0
4
+ version: 2.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xavier Noria
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-11 00:00:00.000000000 Z
11
+ date: 2024-10-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Zeitwerk implements constant autoloading with Ruby semantics. Each gem
@@ -64,7 +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.5.11
67
+ rubygems_version: 3.5.21
68
68
  signing_key:
69
69
  specification_version: 4
70
70
  summary: Efficient and thread-safe constant autoloader