zeitwerk 2.6.4 → 2.6.5

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: 8c7bbcbb7fa03b5ef6389335805145cabf2a4fdd203c15b3c6ecdf68f229e499
4
- data.tar.gz: 3581c99a8d4431eeeed3b6139aca5363f9c13d136dddb22aa5c88b55d2ff2dac
3
+ metadata.gz: 6811a58f4992b66a3625b1b931f070463619e806c0e044611df1f706a0cb1477
4
+ data.tar.gz: 07dd0a9133025560ad914dca656d0ed1044cc791d6e09586dbd03c0a72929751
5
5
  SHA512:
6
- metadata.gz: 3575d2cd414fe45f4c42ce286fbf9cbf648ed0cf5c3643db82167023adde590058cbe88c2348fe2c9f6f8d56f04858ef49441115544dca88b18b48f2eb36d886
7
- data.tar.gz: 4d9fc1a1c72df9233e742f22ad2a0bd636301e55cd9078e856cdfb9a8b2727345d86f278e592fb0294959e9aad92895619e69133050afa943192a61477bad64d
6
+ metadata.gz: 0a360aad989a229b0a315d6d38690401f18ef55b45e8ae30e05db81eb85f9954f8ec1e4fa738b774175c06736f44bcecf2dcfbca74b7d79a049d59071489a7b9
7
+ data.tar.gz: 851ecb2490e716aac571f140f51bedbaf1f1eb99761d1c98a9afe1982099bddc9455efedcd243ff770b7d08c616dd48efce20a7e2378e41ef2844e197cc085f2
data/README.md CHANGED
@@ -213,7 +213,7 @@ serializers/user_serializer.rb -> UserSerializer
213
213
  <a id="markdown-custom-root-namespaces" name="custom-root-namespaces"></a>
214
214
  #### Custom root namespaces
215
215
 
216
- While `Object` is by far the most common root namespace, you can associate a different one to a particular root directory. The method `push_dir` accepts a class or module object in the optional `namespace` keyword argument.
216
+ While `Object` is by far the most common root namespace, you can associate a different one to a particular root directory. The method `push_dir` accepts a non-anonymous class or module object in the optional `namespace` keyword argument.
217
217
 
218
218
  For example, given:
219
219
 
@@ -449,6 +449,8 @@ In gems, the method needs to be invoked after the main namespace has been define
449
449
 
450
450
  Eager loading is synchronized and idempotent.
451
451
 
452
+ Attempting to eager load without previously calling `setup` raises `Zeitwerk::SetupRequired`.
453
+
452
454
  <a id="markdown-eager-load-exclusions" name="eager-load-exclusions"></a>
453
455
  #### Eager load exclusions
454
456
 
@@ -492,6 +494,8 @@ The method checks if a regular eager load was already executed, in which case it
492
494
 
493
495
  Nested root directories which are descendants of the argument are skipped. Those subtrees are considered to be conceptually apart.
494
496
 
497
+ Attempting to eager load a directory without previously calling `setup` raises `Zeitwerk::SetupRequired`.
498
+
495
499
  <a id="markdown-eager-load-namespaces" name="eager-load-namespaces"></a>
496
500
  #### Eager load namespaces
497
501
 
@@ -527,6 +531,8 @@ The method checks if a regular eager load was already executed, in which case it
527
531
 
528
532
  If root directories are assigned to custom namespaces, the method behaves as you'd expect, according to the namespacing relationship between the custom namespace and the argument.
529
533
 
534
+ Attempting to eager load a namespace without previously calling `setup` raises `Zeitwerk::SetupRequired`.
535
+
530
536
  <a id="markdown-eager-load-namespaces-shared-by-several-loaders" name="eager-load-namespaces-shared-by-several-loaders"></a>
531
537
  #### Eager load namespaces shared by several loaders
532
538
 
@@ -540,6 +546,8 @@ This may be handy, for example, if a framework supports plugins and a shared nam
540
546
 
541
547
  Please, note that loaders only eager load namespaces they manage, as documented above. Therefore, this method does not allow you to eager load namespaces not managed by Zeitwerk loaders.
542
548
 
549
+ This method does not require that all registered loaders have `setup` already invoked, since that is out of your control. If there's any in that state, it is simply skipped.
550
+
543
551
  <a id="markdown-global-eager-load" name="global-eager-load"></a>
544
552
  #### Global eager load
545
553
 
@@ -555,6 +563,8 @@ Note that thanks to idempotence `Zeitwerk::Loader.eager_load_all` won't eager lo
555
563
 
556
564
  This method does not accept the `force` flag, since in general it wouldn't be a good idea to force eager loading in 3rd party code.
557
565
 
566
+ This method does not require that all registered loaders have `setup` already invoked, since that is out of your control. If there's any in that state, it is simply skipped.
567
+
558
568
  <a id="markdown-loading-individual-files" name="loading-individual-files"></a>
559
569
  ### Loading individual files
560
570
 
@@ -591,7 +601,7 @@ loader.reload
591
601
 
592
602
  There is no way to undo this, either you want to reload or you don't.
593
603
 
594
- Enabling reloading after setup raises `Zeitwerk::Error`. Attempting to reload without having it enabled raises `Zeitwerk::ReloadingDisabledError`.
604
+ Enabling reloading after setup raises `Zeitwerk::Error`. Attempting to reload without having it enabled raises `Zeitwerk::ReloadingDisabledError`. Attempting to reload without previously calling `setup` raises `Zeitwerk::SetupRequired`.
595
605
 
596
606
  Generally speaking, reloading is useful while developing running services like web applications. Gems that implement regular libraries, so to speak, or services running in testing or production environments, won't normally have a use case for reloading. If reloading is not enabled, Zeitwerk is able to use less memory.
597
607
 
@@ -12,4 +12,10 @@ module Zeitwerk
12
12
 
13
13
  class NameError < ::NameError
14
14
  end
15
+
16
+ class SetupRequired < Error
17
+ def initialize
18
+ super("please, finish your configuration and call Zeitwerk::Loader#setup once all is ready")
19
+ end
20
+ end
15
21
  end
@@ -11,28 +11,28 @@ module Zeitwerk
11
11
  module ExplicitNamespace # :nodoc: all
12
12
  class << self
13
13
  include RealModName
14
+ extend Internal
14
15
 
15
16
  # Maps constant paths that correspond to explicit namespaces according to
16
17
  # the file system, to the loader responsible for them.
17
18
  #
18
- # @private
19
19
  # @sig Hash[String, Zeitwerk::Loader]
20
20
  attr_reader :cpaths
21
+ private :cpaths
21
22
 
22
- # @private
23
23
  # @sig Mutex
24
24
  attr_reader :mutex
25
+ private :mutex
25
26
 
26
- # @private
27
27
  # @sig TracePoint
28
28
  attr_reader :tracer
29
+ private :tracer
29
30
 
30
31
  # Asserts `cpath` corresponds to an explicit namespace for which `loader`
31
32
  # is responsible.
32
33
  #
33
- # @private
34
34
  # @sig (String, Zeitwerk::Loader) -> void
35
- def register(cpath, loader)
35
+ internal def register(cpath, loader)
36
36
  mutex.synchronize do
37
37
  cpaths[cpath] = loader
38
38
  # We check enabled? because, looking at the C source code, enabling an
@@ -41,24 +41,21 @@ module Zeitwerk
41
41
  end
42
42
  end
43
43
 
44
- # @private
45
44
  # @sig (Zeitwerk::Loader) -> void
46
- def unregister_loader(loader)
45
+ internal def unregister_loader(loader)
47
46
  cpaths.delete_if { |_cpath, l| l == loader }
48
47
  disable_tracer_if_unneeded
49
48
  end
50
49
 
51
- private
52
-
53
50
  # @sig () -> void
54
- def disable_tracer_if_unneeded
51
+ private def disable_tracer_if_unneeded
55
52
  mutex.synchronize do
56
53
  tracer.disable if cpaths.empty?
57
54
  end
58
55
  end
59
56
 
60
57
  # @sig (TracePoint) -> void
61
- def tracepoint_class_callback(event)
58
+ private def tracepoint_class_callback(event)
62
59
  # If the class is a singleton class, we won't do anything with it so we
63
60
  # can bail out immediately. This is several orders of magnitude faster
64
61
  # than accessing its name.
@@ -5,6 +5,7 @@ require "securerandom"
5
5
 
6
6
  module Zeitwerk::Loader::Config
7
7
  extend Zeitwerk::Internal
8
+ include Zeitwerk::RealModName
8
9
 
9
10
  # @sig #camelize
10
11
  attr_accessor :inflector
@@ -12,14 +13,15 @@ module Zeitwerk::Loader::Config
12
13
  # @sig #call | #debug | nil
13
14
  attr_accessor :logger
14
15
 
15
- # Absolute paths of the root directories. Stored in a hash to preserve order,
16
- # easily handle duplicates, have a fast lookup needed for detecting nested
17
- # paths, and store namespaces as values.
16
+ # Absolute paths of the root directories, mapped to their respective root namespaces:
18
17
  #
19
18
  # "/Users/fxn/blog/app/channels" => Object,
20
19
  # "/Users/fxn/blog/app/adapters" => ActiveJob::QueueAdapters,
21
20
  # ...
22
21
  #
22
+ # Stored in a hash to preserve order, easily handle duplicates, and have a
23
+ # fast lookup by directory.
24
+ #
23
25
  # This is a private collection maintained by the loader. The public
24
26
  # interface for it is `push_dir` and `dirs`.
25
27
  #
@@ -112,6 +114,10 @@ module Zeitwerk::Loader::Config
112
114
  raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module object, should be"
113
115
  end
114
116
 
117
+ unless real_mod_name(namespace)
118
+ raise Zeitwerk::Error, "root namespaces cannot be anonymous"
119
+ end
120
+
115
121
  abspath = File.expand_path(path)
116
122
  if dir?(abspath)
117
123
  raise_if_conflicting_directory(abspath)
@@ -283,17 +289,22 @@ module Zeitwerk::Loader::Config
283
289
  return false if ignored_paths.empty?
284
290
 
285
291
  walk_up(abspath) do |abspath|
286
- return true if ignored_paths.member?(abspath)
292
+ return true if ignored_path?(abspath)
287
293
  return false if roots.key?(abspath)
288
294
  end
289
295
 
290
296
  false
291
297
  end
292
298
 
299
+ # @sig (String) -> bool
300
+ private def ignored_path?(abspath)
301
+ ignored_paths.member?(abspath)
302
+ end
303
+
293
304
  # @sig () -> Array[String]
294
305
  private def actual_roots
295
306
  roots.reject do |root_dir, _root_namespace|
296
- !dir?(root_dir) || ignored_paths.member?(root_dir)
307
+ !dir?(root_dir) || ignored_path?(root_dir)
297
308
  end
298
309
  end
299
310
 
@@ -9,6 +9,7 @@ module Zeitwerk::Loader::EagerLoad
9
9
  def eager_load(force: false)
10
10
  mutex.synchronize do
11
11
  break if @eager_loaded
12
+ raise Zeitwerk::SetupRequired unless @setup
12
13
 
13
14
  log("eager load start") if logger
14
15
 
@@ -29,6 +30,8 @@ module Zeitwerk::Loader::EagerLoad
29
30
 
30
31
  # @sig (String | Pathname) -> void
31
32
  def eager_load_dir(path)
33
+ raise Zeitwerk::SetupRequired unless @setup
34
+
32
35
  abspath = File.expand_path(path)
33
36
 
34
37
  raise Zeitwerk::Error.new("#{abspath} is not a directory") unless dir?(abspath)
@@ -37,7 +40,7 @@ module Zeitwerk::Loader::EagerLoad
37
40
 
38
41
  root_namespace = nil
39
42
  walk_up(abspath) do |dir|
40
- return if ignored_paths.member?(dir)
43
+ return if ignored_path?(dir)
41
44
  return if eager_load_exclusions.member?(dir)
42
45
 
43
46
  break if root_namespace = roots[dir]
@@ -67,6 +70,8 @@ module Zeitwerk::Loader::EagerLoad
67
70
 
68
71
  # @sig (Module) -> void
69
72
  def eager_load_namespace(mod)
73
+ raise Zeitwerk::SetupRequired unless @setup
74
+
70
75
  unless mod.is_a?(Module)
71
76
  raise Zeitwerk::Error, "#{mod.inspect} is not a class or module object"
72
77
  end
@@ -111,7 +116,7 @@ module Zeitwerk::Loader::EagerLoad
111
116
 
112
117
  raise Zeitwerk::Error.new("#{abspath} does not exist") unless File.exist?(abspath)
113
118
  raise Zeitwerk::Error.new("#{abspath} is not a Ruby file") if dir?(abspath) || !ruby?(abspath)
114
- raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_paths.member?(abspath)
119
+ raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_path?(abspath)
115
120
 
116
121
  basename = File.basename(abspath, ".rb")
117
122
  base_cname = inflector.camelize(basename, abspath).to_sym
@@ -120,7 +125,7 @@ module Zeitwerk::Loader::EagerLoad
120
125
  cnames = []
121
126
 
122
127
  walk_up(File.dirname(abspath)) do |dir|
123
- raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_paths.member?(dir)
128
+ raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_path?(dir)
124
129
 
125
130
  break if root_namespace = roots[dir]
126
131
 
@@ -1,12 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk::Loader::Helpers
4
- private
5
-
6
4
  # --- Logging -----------------------------------------------------------------------------------
7
5
 
8
6
  # @sig (String) -> void
9
- def log(message)
7
+ private def log(message)
10
8
  method_name = logger.respond_to?(:debug) ? :debug : :call
11
9
  logger.send(method_name, "Zeitwerk@#{tag}: #{message}")
12
10
  end
@@ -14,7 +12,7 @@ module Zeitwerk::Loader::Helpers
14
12
  # --- Files and directories ---------------------------------------------------------------------
15
13
 
16
14
  # @sig (String) { (String, String) -> void } -> void
17
- def ls(dir)
15
+ private def ls(dir)
18
16
  children = Dir.children(dir)
19
17
 
20
18
  # The order in which a directory is listed depends on the file system.
@@ -28,7 +26,7 @@ module Zeitwerk::Loader::Helpers
28
26
  next if hidden?(basename)
29
27
 
30
28
  abspath = File.join(dir, basename)
31
- next if ignored_paths.member?(abspath)
29
+ next if ignored_path?(abspath)
32
30
 
33
31
  if dir?(abspath)
34
32
  next if roots.key?(abspath)
@@ -44,7 +42,7 @@ module Zeitwerk::Loader::Helpers
44
42
  end
45
43
 
46
44
  # @sig (String) -> bool
47
- def has_at_least_one_ruby_file?(dir)
45
+ private def has_at_least_one_ruby_file?(dir)
48
46
  to_visit = [dir]
49
47
 
50
48
  while dir = to_visit.shift
@@ -61,22 +59,22 @@ module Zeitwerk::Loader::Helpers
61
59
  end
62
60
 
63
61
  # @sig (String) -> bool
64
- def ruby?(path)
62
+ private def ruby?(path)
65
63
  path.end_with?(".rb")
66
64
  end
67
65
 
68
66
  # @sig (String) -> bool
69
- def dir?(path)
67
+ private def dir?(path)
70
68
  File.directory?(path)
71
69
  end
72
70
 
73
71
  # @sig (String) -> bool
74
- def hidden?(basename)
72
+ private def hidden?(basename)
75
73
  basename.start_with?(".")
76
74
  end
77
75
 
78
76
  # @sig (String) { (String) -> void } -> void
79
- def walk_up(abspath)
77
+ private def walk_up(abspath)
80
78
  loop do
81
79
  yield abspath
82
80
  abspath, basename = File.split(abspath)
@@ -104,11 +102,11 @@ module Zeitwerk::Loader::Helpers
104
102
  #
105
103
  # @sig (Module, Symbol) -> String?
106
104
  if method(:autoload?).arity == 1
107
- def strict_autoload_path(parent, cname)
105
+ private def strict_autoload_path(parent, cname)
108
106
  parent.autoload?(cname) if cdef?(parent, cname)
109
107
  end
110
108
  else
111
- def strict_autoload_path(parent, cname)
109
+ private def strict_autoload_path(parent, cname)
112
110
  parent.autoload?(cname, false)
113
111
  end
114
112
  end
@@ -117,23 +115,23 @@ module Zeitwerk::Loader::Helpers
117
115
  if Symbol.method_defined?(:name)
118
116
  # Symbol#name was introduced in Ruby 3.0. It returns always the same
119
117
  # frozen object, so we may save a few string allocations.
120
- def cpath(parent, cname)
118
+ private def cpath(parent, cname)
121
119
  Object == parent ? cname.name : "#{real_mod_name(parent)}::#{cname.name}"
122
120
  end
123
121
  else
124
- def cpath(parent, cname)
122
+ private def cpath(parent, cname)
125
123
  Object == parent ? cname.to_s : "#{real_mod_name(parent)}::#{cname}"
126
124
  end
127
125
  end
128
126
 
129
127
  # @sig (Module, Symbol) -> bool
130
- def cdef?(parent, cname)
128
+ private def cdef?(parent, cname)
131
129
  parent.const_defined?(cname, false)
132
130
  end
133
131
 
134
132
  # @raise [NameError]
135
133
  # @sig (Module, Symbol) -> Object
136
- def cget(parent, cname)
134
+ private def cget(parent, cname)
137
135
  parent.const_get(cname, false)
138
136
  end
139
137
  end
@@ -109,7 +109,7 @@ module Zeitwerk
109
109
  Registry.register_loader(self)
110
110
  end
111
111
 
112
- # Sets autoloads in the root namespace.
112
+ # Sets autoloads in the root namespaces.
113
113
  #
114
114
  # @sig () -> void
115
115
  def setup
@@ -140,6 +140,8 @@ module Zeitwerk
140
140
  # @sig () -> void
141
141
  def unload
142
142
  mutex.synchronize do
143
+ raise SetupRequired unless @setup
144
+
143
145
  # We are going to keep track of the files that were required by our
144
146
  # autoloads to later remove them from $LOADED_FEATURES, thus making them
145
147
  # loadable by Kernel#require again.
@@ -199,7 +201,7 @@ module Zeitwerk
199
201
  shadowed_files.clear
200
202
 
201
203
  Registry.on_unload(self)
202
- ExplicitNamespace.unregister_loader(self)
204
+ ExplicitNamespace.__unregister_loader(self)
203
205
 
204
206
  @setup = false
205
207
  @eager_loaded = false
@@ -216,6 +218,7 @@ module Zeitwerk
216
218
  # @sig () -> void
217
219
  def reload
218
220
  raise ReloadingDisabledError unless reloading_enabled?
221
+ raise SetupRequired unless @setup
219
222
 
220
223
  unload
221
224
  recompute_ignored_paths
@@ -245,7 +248,7 @@ module Zeitwerk
245
248
  # @sig () -> void
246
249
  def unregister
247
250
  Registry.unregister_loader(self)
248
- ExplicitNamespace.unregister_loader(self)
251
+ ExplicitNamespace.__unregister_loader(self)
249
252
  end
250
253
 
251
254
  # The return value of this predicate is only meaningful if the loader has
@@ -283,19 +286,31 @@ module Zeitwerk
283
286
  Registry.loader_for_gem(called_from, warn_on_extra_files: warn_on_extra_files)
284
287
  end
285
288
 
286
- # Broadcasts `eager_load` to all loaders.
289
+ # Broadcasts `eager_load` to all loaders. Those that have not been setup
290
+ # are skipped.
287
291
  #
288
292
  # @sig () -> void
289
293
  def eager_load_all
290
- Registry.loaders.each(&:eager_load)
294
+ Registry.loaders.each do |loader|
295
+ begin
296
+ loader.eager_load
297
+ rescue SetupRequired
298
+ # This is fine, we eager load what can be eager loaded.
299
+ end
300
+ end
291
301
  end
292
302
 
293
- # Broadcasts `eager_load_namespace` to all loaders.
303
+ # Broadcasts `eager_load_namespace` to all loaders. Those that have not
304
+ # been setup are skipped.
294
305
  #
295
306
  # @sig (Module) -> void
296
307
  def eager_load_namespace(mod)
297
308
  Registry.loaders.each do |loader|
298
- loader.eager_load_namespace(mod)
309
+ begin
310
+ loader.eager_load_namespace(mod)
311
+ rescue SetupRequired
312
+ # This is fine, we eager load what can be eager loaded.
313
+ end
299
314
  end
300
315
  end
301
316
 
@@ -435,7 +450,7 @@ module Zeitwerk
435
450
 
436
451
  # @sig (String) -> void
437
452
  def register_explicit_namespace(cpath)
438
- ExplicitNamespace.register(cpath, self)
453
+ ExplicitNamespace.__register(cpath, self)
439
454
  end
440
455
 
441
456
  # @sig (String) -> void
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "2.6.4"
4
+ VERSION = "2.6.5"
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.6.4
4
+ version: 2.6.5
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-11-01 00:00:00.000000000 Z
11
+ date: 2022-11-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Zeitwerk implements constant autoloading with Ruby semantics. Each gem