zeitwerk 2.7.1 → 2.7.3

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.
@@ -0,0 +1,33 @@
1
+ module Zeitwerk::Registry
2
+ class Loaders # :nodoc:
3
+ #: () -> void
4
+ def initialize
5
+ @loaders = [] #: Array[Zeitwerk::Loader]
6
+ end
7
+
8
+ #: ({ (Zeitwerk::Loader) -> void }) -> void
9
+ def each(&block)
10
+ @loaders.each(&block)
11
+ end
12
+
13
+ #: (Zeitwerk::Loader) -> void
14
+ def register(loader)
15
+ @loaders << loader
16
+ end
17
+
18
+ #: (Zeitwerk::Loader) -> Zeitwerk::Loader?
19
+ def unregister(loader)
20
+ @loaders.delete(loader)
21
+ end
22
+
23
+ #: (Zeitwerk::Loader) -> bool
24
+ def registered?(loader) # for tests
25
+ @loaders.include?(loader)
26
+ end
27
+
28
+ #: () -> void
29
+ def clear # for tests
30
+ @loaders.clear
31
+ end
32
+ end
33
+ end
@@ -2,18 +2,23 @@
2
2
 
3
3
  module Zeitwerk
4
4
  module Registry # :nodoc: all
5
+ require_relative "registry/autoloads"
6
+ require_relative "registry/explicit_namespaces"
7
+ require_relative "registry/inceptions"
8
+ require_relative "registry/loaders"
9
+
5
10
  class << self
6
11
  # Keeps track of all loaders. Useful to broadcast messages and to prevent
7
12
  # them from being garbage collected.
8
13
  #
9
14
  # @private
10
- # @sig Array[Zeitwerk::Loader]
15
+ #: Zeitwerk::Registry::Loaders
11
16
  attr_reader :loaders
12
17
 
13
18
  # Registers gem loaders to let `for_gem` be idempotent in case of reload.
14
19
  #
15
20
  # @private
16
- # @sig Hash[String, Zeitwerk::Loader]
21
+ #: Hash[String, Zeitwerk::Loader]
17
22
  attr_reader :gem_loaders_by_root_file
18
23
 
19
24
  # Maps absolute paths to the loaders responsible for them.
@@ -22,120 +27,37 @@ module Zeitwerk
22
27
  # invoke callbacks and autovivify modules.
23
28
  #
24
29
  # @private
25
- # @sig Hash[String, Zeitwerk::Loader]
30
+ #: Zeitwerk::Registry::Autoloads
26
31
  attr_reader :autoloads
27
32
 
28
- # This hash table addresses an edge case in which an autoload is ignored.
29
- #
30
- # For example, let's suppose we want to autoload in a gem like this:
31
- #
32
- # # lib/my_gem.rb
33
- # loader = Zeitwerk::Loader.new
34
- # loader.push_dir(__dir__)
35
- # loader.setup
36
- #
37
- # module MyGem
38
- # end
39
- #
40
- # if you require "my_gem", as Bundler would do, this happens while setting
41
- # up autoloads:
42
- #
43
- # 1. Object.autoload?(:MyGem) returns `nil` because the autoload for
44
- # the constant is issued by Zeitwerk while the same file is being
45
- # required.
46
- # 2. The constant `MyGem` is undefined while setup runs.
47
- #
48
- # Therefore, a directory `lib/my_gem` would autovivify a module according to
49
- # the existing information. But that would be wrong.
50
- #
51
- # To overcome this fundamental limitation, we keep track of the constant
52
- # paths that are in this situation ---in the example above, "MyGem"--- and
53
- # take this collection into account for the autovivification logic.
54
- #
55
- # Note that you cannot generally address this by moving the setup code
56
- # below the constant definition, because we want libraries to be able to
57
- # use managed constants in the module body:
58
- #
59
- # module MyGem
60
- # include MyConcern
61
- # end
62
- #
63
33
  # @private
64
- # @sig Hash[String, [String, Zeitwerk::Loader]]
65
- attr_reader :inceptions
34
+ #: Zeitwerk::Registry::ExplicitNamespaces
35
+ attr_reader :explicit_namespaces
66
36
 
67
- # Registers a loader.
68
- #
69
37
  # @private
70
- # @sig (Zeitwerk::Loader) -> void
71
- def register_loader(loader)
72
- loaders << loader
73
- end
38
+ #: Zeitwerk::Registry::Inceptions
39
+ attr_reader :inceptions
74
40
 
75
41
  # @private
76
- # @sig (Zeitwerk::Loader) -> void
42
+ #: (Zeitwerk::Loader) -> void
77
43
  def unregister_loader(loader)
78
- loaders.delete(loader)
79
44
  gem_loaders_by_root_file.delete_if { |_, l| l == loader }
80
- autoloads.delete_if { |_, l| l == loader }
81
- inceptions.delete_if { |_, (_, l)| l == loader }
82
45
  end
83
46
 
84
47
  # This method returns always a loader, the same instance for the same root
85
48
  # file. That is how Zeitwerk::Loader.for_gem is idempotent.
86
49
  #
87
50
  # @private
88
- # @sig (String) -> Zeitwerk::Loader
51
+ #: (String, namespace: Module, warn_on_extra_files: boolish) -> Zeitwerk::Loader
89
52
  def loader_for_gem(root_file, namespace:, warn_on_extra_files:)
90
53
  gem_loaders_by_root_file[root_file] ||= GemLoader.__new(root_file, namespace: namespace, warn_on_extra_files: warn_on_extra_files)
91
54
  end
92
-
93
- # @private
94
- # @sig (Zeitwerk::Loader, String) -> String
95
- def register_autoload(loader, abspath)
96
- autoloads[abspath] = loader
97
- end
98
-
99
- # @private
100
- # @sig (String) -> void
101
- def unregister_autoload(abspath)
102
- autoloads.delete(abspath)
103
- end
104
-
105
- # @private
106
- # @sig (String, String, Zeitwerk::Loader) -> void
107
- def register_inception(cpath, abspath, loader)
108
- inceptions[cpath] = [abspath, loader]
109
- end
110
-
111
- # @private
112
- # @sig (String) -> String?
113
- def inception?(cpath, registered_by_loader=nil)
114
- if pair = inceptions[cpath]
115
- abspath, loader = pair
116
- if registered_by_loader.nil? || registered_by_loader.equal?(loader)
117
- abspath
118
- end
119
- end
120
- end
121
-
122
- # @private
123
- # @sig (String) -> Zeitwerk::Loader?
124
- def loader_for(path)
125
- autoloads[path]
126
- end
127
-
128
- # @private
129
- # @sig (Zeitwerk::Loader) -> void
130
- def on_unload(loader)
131
- autoloads.delete_if { |_path, object| object == loader }
132
- inceptions.delete_if { |_cpath, (_path, object)| object == loader }
133
- end
134
55
  end
135
56
 
136
- @loaders = []
57
+ @loaders = Loaders.new
137
58
  @gem_loaders_by_root_file = {}
138
- @autoloads = {}
139
- @inceptions = {}
59
+ @autoloads = Autoloads.new
60
+ @explicit_namespaces = ExplicitNamespaces.new
61
+ @inceptions = Inceptions.new
140
62
  end
141
63
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "2.7.1"
4
+ #: String
5
+ VERSION = "2.7.3"
5
6
  end
data/lib/zeitwerk.rb CHANGED
@@ -7,7 +7,6 @@ module Zeitwerk
7
7
  require_relative "zeitwerk/loader"
8
8
  require_relative "zeitwerk/gem_loader"
9
9
  require_relative "zeitwerk/registry"
10
- require_relative "zeitwerk/explicit_namespace"
11
10
  require_relative "zeitwerk/inflector"
12
11
  require_relative "zeitwerk/gem_inflector"
13
12
  require_relative "zeitwerk/null_inflector"
@@ -20,7 +19,7 @@ module Zeitwerk
20
19
  # This is a dangerous method.
21
20
  #
22
21
  # @experimental
23
- # @sig () -> void
22
+ #: () -> void
24
23
  def self.with_loader
25
24
  loader = Zeitwerk::Loader.new
26
25
  yield loader
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zeitwerk
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.1
4
+ version: 2.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xavier Noria
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-10-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: |2
14
13
  Zeitwerk implements constant autoloading with Ruby semantics. Each gem
@@ -26,8 +25,8 @@ files:
26
25
  - lib/zeitwerk/core_ext/kernel.rb
27
26
  - lib/zeitwerk/core_ext/module.rb
28
27
  - lib/zeitwerk/cref.rb
28
+ - lib/zeitwerk/cref/map.rb
29
29
  - lib/zeitwerk/error.rb
30
- - lib/zeitwerk/explicit_namespace.rb
31
30
  - lib/zeitwerk/gem_inflector.rb
32
31
  - lib/zeitwerk/gem_loader.rb
33
32
  - lib/zeitwerk/inflector.rb
@@ -40,6 +39,10 @@ files:
40
39
  - lib/zeitwerk/null_inflector.rb
41
40
  - lib/zeitwerk/real_mod_name.rb
42
41
  - lib/zeitwerk/registry.rb
42
+ - lib/zeitwerk/registry/autoloads.rb
43
+ - lib/zeitwerk/registry/explicit_namespaces.rb
44
+ - lib/zeitwerk/registry/inceptions.rb
45
+ - lib/zeitwerk/registry/loaders.rb
43
46
  - lib/zeitwerk/version.rb
44
47
  homepage: https://github.com/fxn/zeitwerk
45
48
  licenses:
@@ -49,7 +52,6 @@ metadata:
49
52
  changelog_uri: https://github.com/fxn/zeitwerk/blob/master/CHANGELOG.md
50
53
  source_code_uri: https://github.com/fxn/zeitwerk
51
54
  bug_tracker_uri: https://github.com/fxn/zeitwerk/issues
52
- post_install_message:
53
55
  rdoc_options: []
54
56
  require_paths:
55
57
  - lib
@@ -64,8 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
66
  - !ruby/object:Gem::Version
65
67
  version: '0'
66
68
  requirements: []
67
- rubygems_version: 3.5.21
68
- signing_key:
69
+ rubygems_version: 3.6.9
69
70
  specification_version: 4
70
71
  summary: Efficient and thread-safe constant autoloader
71
72
  test_files: []
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Zeitwerk
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.
15
- #
16
- # The implementation assumes an explicit namespace is managed by one loader.
17
- # Loaders that reopen namespaces owned by other projects are responsible for
18
- # loading their constant before setup. This is documented.
19
- module ExplicitNamespace # :nodoc: all
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
- #
34
- # Entries are added as the namespaces are found, and removed as they are
35
- # autoloaded.
36
- #
37
- # @sig Hash[(Symbol | String) => Zeitwerk::Loader]
38
- @loaders = {}
39
-
40
- class << self
41
- include RealModName
42
- extend Internal
43
-
44
- # Registers `cref` as being the constant path of an explicit namespace
45
- # managed by `loader`.
46
- #
47
- # @sig (String, Zeitwerk::Loader) -> void
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
54
- end
55
-
56
- # @sig (Module, Symbol) -> Zeitwerk::Loader?
57
- internal def loader_for(mod, cname)
58
- if Object.equal?(mod)
59
- @loaders.delete(cname)
60
- else
61
- @loaders.delete("#{real_mod_name(mod)}::#{cname}")
62
- end
63
- end
64
-
65
- # @sig (Zeitwerk::Loader) -> void
66
- internal def unregister_loader(loader)
67
- @loaders.delete_if { _2.equal?(loader) }
68
- end
69
-
70
- # This is an internal method only used by the test suite.
71
- #
72
- # @sig (String) -> Zeitwerk::Loader?
73
- internal def registered?(cname_or_cpath)
74
- @loaders[cname_or_cpath]
75
- end
76
-
77
- # This is an internal method only used by the test suite.
78
- #
79
- # @sig () -> void
80
- internal def clear
81
- @loaders.clear
82
- end
83
-
84
- module Synchronized
85
- extend Internal
86
-
87
- MUTEX = Mutex.new
88
-
89
- internal def register(...)
90
- MUTEX.synchronize { super }
91
- end
92
-
93
- internal def loader_for(...)
94
- MUTEX.synchronize { super }
95
- end
96
-
97
- internal def unregister_loader(...)
98
- MUTEX.synchronize { super }
99
- end
100
-
101
- internal def registered?(...)
102
- MUTEX.synchronize { super }
103
- end
104
-
105
- internal def clear
106
- MUTEX.synchronize { super }
107
- end
108
- end
109
-
110
- prepend Synchronized unless RUBY_ENGINE == "ruby"
111
- end
112
- end
113
- end