zeitwerk 2.5.0.beta2 → 2.5.0.beta6

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: 64ea17faefd07265c96bd8c49c9a4e14170612f85f868c03df91aa4511e473e3
4
- data.tar.gz: fb98b0f841e49016039d73310d50563e8e6c3bbcf4c16f2d4441b241cb266647
3
+ metadata.gz: 74d74bec1f4963cc5d04f037e14723408ae5d1f8c4df16f31f2901402e3230ed
4
+ data.tar.gz: 3ebd7f860583cc7a560360b2e766193dc0c74c5cd902c7cb632a614e32dc68e7
5
5
  SHA512:
6
- metadata.gz: 3dfdc023489a4b7306c2bbac6f56234ff4a80887820967ff691fd9b13609ea256ba5bca301870d5e5b35d26c09ece01158a984d3093fa63d0de0bd3d9cdff458
7
- data.tar.gz: 4860a04e9e489251ce43e56f37c7213ac3dda7c301f9dff1826d7e5f2567b992bfac35f645dab67a356bb8a4024c29d0fd8431f91896f7a254bf747864dd8ef0
6
+ metadata.gz: f0bec92696285efb5ad1f76094b71a4de5a38a0665b493737c945623042b4d744be2c58f4006ed5c8c4abe0ccb70854c94ba63c3b7eeef1a5ae47086a956668d
7
+ data.tar.gz: a1392b3b73dcd85769f793aeca9a9d10cb50c70259d88761574c1e62e201b24f32d7122eda587af6017dea93fca27ce9463e5f0dae7b98523d3edba2c13802eb
data/README.md CHANGED
@@ -19,19 +19,24 @@
19
19
  - [Implicit namespaces](#implicit-namespaces)
20
20
  - [Explicit namespaces](#explicit-namespaces)
21
21
  - [Collapsing directories](#collapsing-directories)
22
+ - [Testing compliance](#testing-compliance)
22
23
  - [Usage](#usage)
23
24
  - [Setup](#setup)
24
25
  - [Generic](#generic)
25
26
  - [for_gem](#for_gem)
26
27
  - [Autoloading](#autoloading)
27
28
  - [Eager loading](#eager-loading)
29
+ - [Eager load exclusions](#eager-load-exclusions)
30
+ - [Global eager load](#global-eager-load)
28
31
  - [Reloading](#reloading)
29
32
  - [Inflection](#inflection)
30
33
  - [Zeitwerk::Inflector](#zeitwerkinflector)
31
34
  - [Zeitwerk::GemInflector](#zeitwerkgeminflector)
32
35
  - [Custom inflector](#custom-inflector)
33
- - [The on_load callback](#the-on_load-callback)
34
- - [The on_unload callback](#the-on_unload-callback)
36
+ - [Callbacks](#callbacks)
37
+ - [The on_setup callback](#the-on_setup-callback)
38
+ - [The on_load callback](#the-on_load-callback)
39
+ - [The on_unload callback](#the-on_unload-callback)
35
40
  - [Technical details](#technical-details)
36
41
  - [Logging](#logging)
37
42
  - [Loader tag](#loader-tag)
@@ -40,15 +45,19 @@
40
45
  - [Use case: The adapter pattern](#use-case-the-adapter-pattern)
41
46
  - [Use case: Test files mixed with implementation files](#use-case-test-files-mixed-with-implementation-files)
42
47
  - [Edge cases](#edge-cases)
48
+ - [Beware of circular dependencies](#beware-of-circular-dependencies)
43
49
  - [Reopening third-party namespaces](#reopening-third-party-namespaces)
44
50
  - [Rules of thumb](#rules-of-thumb)
45
51
  - [Debuggers](#debuggers)
52
+ - [debug.rb](#debugrb)
46
53
  - [Break](#break)
47
54
  - [Byebug](#byebug)
48
55
  - [Pronunciation](#pronunciation)
49
56
  - [Supported Ruby versions](#supported-ruby-versions)
50
57
  - [Testing](#testing)
51
58
  - [Motivation](#motivation)
59
+ - [Kernel#require is brittle](#kernelrequire-is-brittle)
60
+ - [Rails autoloading was brittle](#rails-autoloading-was-brittle)
52
61
  - [Thanks](#thanks)
53
62
  - [License](#license)
54
63
 
@@ -157,6 +166,16 @@ class HttpCrawler
157
166
  end
158
167
  ```
159
168
 
169
+ The first example needs a custom [inflection](https://github.com/fxn/zeitwerk#inflection) rule:
170
+
171
+ ```ruby
172
+ loader.inflector.inflect("max_retries" => "MAX_RETRIES")
173
+ ```
174
+
175
+ Otherwise, Zeitwerk would expect the file to define `MaxRetries`.
176
+
177
+ In the second example, no custom rule is needed.
178
+
160
179
  <a id="markdown-root-directories-and-root-namespaces" name="root-directories-and-root-namespaces"></a>
161
180
  ### Root directories and root namespaces
162
181
 
@@ -275,6 +294,23 @@ To illustrate usage of glob patterns, if `actions` in the example above is part
275
294
  loader.collapse("#{__dir__}/*/actions")
276
295
  ```
277
296
 
297
+ <a id="markdown-testing-compliance" name="testing-compliance"></a>
298
+ ### Testing compliance
299
+
300
+ When a managed file is loaded, Zeitwerk verifies the expected constant is defined. If it is not, `Zeitwerk::NameError` is raised.
301
+
302
+ So, an easy way to ensure compliance in the test suite is to eager load the project:
303
+
304
+ ```ruby
305
+ begin
306
+ loader.eager_load(force: true)
307
+ rescue Zeitwerk::NameError => e
308
+ flunk e.message
309
+ else
310
+ assert true
311
+ end
312
+ ```
313
+
278
314
  <a id="markdown-usage" name="usage"></a>
279
315
  ## Usage
280
316
 
@@ -377,7 +413,16 @@ Zeitwerk instances are able to eager load their managed files:
377
413
  loader.eager_load
378
414
  ```
379
415
 
380
- That skips [ignored files and directories](#ignoring-parts-of-the-project), and you can also tell Zeitwerk that certain files or directories are autoloadable, but should not be eager loaded:
416
+ That skips [ignored files and directories](#ignoring-parts-of-the-project).
417
+
418
+ In gems, the method needs to be invoked after the main namespace has been defined, as shown in [Synopsis](https://github.com/fxn/zeitwerk#synopsis).
419
+
420
+ Eager loading is synchronized and idempotent.
421
+
422
+ <a id="markdown-eager-load-exclusions" name="eager-load-exclusions"></a>
423
+ #### Eager load exclusions
424
+
425
+ You can tell Zeitwerk that certain files or directories are autoloadable, but should not be eager loaded:
381
426
 
382
427
  ```ruby
383
428
  db_adapters = "#{__dir__}/my_gem/db_adapters"
@@ -386,13 +431,20 @@ loader.setup
386
431
  loader.eager_load # won't eager load the database adapters
387
432
  ```
388
433
 
389
- In gems, the method needs to be invoked after the main namespace has been defined, as shown in [Synopsis](https://github.com/fxn/zeitwerk#synopsis).
434
+ However, that can be overridden with `force`:
390
435
 
391
- Eager loading is synchronized and idempotent.
436
+ ```ruby
437
+ loader.eager_load(force: true) # database adapters are eager loaded
438
+ ```
439
+
440
+ Which may be handy if the project eager loads in the test suite to [ensure project layour compliance](#testing-compliance).
392
441
 
393
- If eager loading a file does not define the expected class or module, Zeitwerk raises `Zeitwerk::NameError`, which is a subclass of `NameError`.
442
+ The `force` flag does not affect ignored files and directories, those are still ignored.
394
443
 
395
- If you want to eager load yourself and all dependencies using Zeitwerk, you can broadcast the `eager_load` call to all instances:
444
+ <a id="markdown-global-eager-load" name="global-eager-load"></a>
445
+ #### Global eager load
446
+
447
+ If you want to eager load yourself and all dependencies that use Zeitwerk, you can broadcast the `eager_load` call to all instances:
396
448
 
397
449
  ```ruby
398
450
  Zeitwerk::Loader.eager_load_all
@@ -402,6 +454,8 @@ This may be handy in top-level services, like web applications.
402
454
 
403
455
  Note that thanks to idempotence `Zeitwerk::Loader.eager_load_all` won't eager load twice if any of the instances already eager loaded.
404
456
 
457
+ 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.
458
+
405
459
  <a id="markdown-reloading" name="reloading"></a>
406
460
  ### Reloading
407
461
 
@@ -551,8 +605,26 @@ class MyGem::Inflector < Zeitwerk::GemInflector
551
605
  end
552
606
  ```
553
607
 
608
+ <a id="markdown-callbacks" name="callbacks"></a>
609
+ ### Callbacks
610
+
611
+ <a id="markdown-the-on_setup-callback" name="the-on_setup-callback"></a>
612
+ #### The on_setup callback
613
+
614
+ The `on_setup` callback is fired on setup and on each reload:
615
+
616
+ ```ruby
617
+ loader.on_setup do
618
+ # Ready to autoload here.
619
+ end
620
+ ```
621
+
622
+ Multiple `on_setup` callbacks are supported, and they run in order of definition.
623
+
624
+ If `setup` was already executed, the callback is fired immediately.
625
+
554
626
  <a id="markdown-the-on_load-callback" name="the-on_load-callback"></a>
555
- ### The on_load callback
627
+ #### The on_load callback
556
628
 
557
629
  The usual place to run something when a file is loaded is the file itself. However, sometimes you'd like to be called, and this is possible with the `on_load` callback.
558
630
 
@@ -610,8 +682,10 @@ There are use cases for this last catch-all callback, but they are rare. If you
610
682
 
611
683
  If both types of callbacks are defined, the specific ones run first.
612
684
 
685
+ Since `on_load` callbacks are executed right after files are loaded, even if the loading context seems to be far away, in practice **the block is subject to [circular dependencies](#beware-of-circular-dependencies)**. As a rule of thumb, as far as loading order and its interdependencies is concerned, you have to program as if the block was executed at the bottom of the file just loaded.
686
+
613
687
  <a id="markdown-the-on_unload-callback" name="the-on_unload-callback"></a>
614
- ### The on_unload callback
688
+ #### The on_unload callback
615
689
 
616
690
  When reloading is enabled, you may occasionally need to execute something before a certain autoloaded class or module is unloaded. The `on_unload` callback allows you to do that.
617
691
 
@@ -822,6 +896,26 @@ Trip = Struct.new { ... } # NOT SUPPORTED
822
896
 
823
897
  This only affects explicit namespaces, those idioms work well for any other ordinary class or module.
824
898
 
899
+ <a id="markdown-beware-of-circular-dependencies" name="beware-of-circular-dependencies"></a>
900
+ ### Beware of circular dependencies
901
+
902
+ In Ruby, you can't have certain top-level circular dependencies. Take for example:
903
+
904
+ ```ruby
905
+ # c.rb
906
+ class C < D
907
+ end
908
+
909
+ # d.rb
910
+ class D
911
+ C
912
+ end
913
+ ```
914
+
915
+ In order to define `C`, you need to load `D`. However, the body of `D` refers to `C`.
916
+
917
+ Circular dependencies like those do not work in plain Ruby, and therefore do not work in projects managed by Zeitwerk either.
918
+
825
919
  <a id="markdown-reopening-third-party-namespaces" name="reopening-third-party-namespaces"></a>
826
920
  ### Reopening third-party namespaces
827
921
 
@@ -876,6 +970,11 @@ With that, when Zeitwerk scans the file system and reaches the gem directories `
876
970
  <a id="markdown-debuggers" name="debuggers"></a>
877
971
  ### Debuggers
878
972
 
973
+ <a id="markdown-debugrb" name="debugrb"></a>
974
+ #### debug.rb
975
+
976
+ The new [debug.rb](https://github.com/ruby/debug) gem and Zeitwerk seem to be compatible, as far as I can tell. This is the new debugger that is going to ship with Ruby 3.1.
977
+
879
978
  <a id="markdown-break" name="break"></a>
880
979
  #### Break
881
980
 
@@ -894,7 +993,11 @@ Zeitwerk and [Byebug](https://github.com/deivid-rodriguez/byebug) are incompatib
894
993
  <a id="markdown-supported-ruby-versions" name="supported-ruby-versions"></a>
895
994
  ## Supported Ruby versions
896
995
 
897
- Zeitwerk works with MRI 2.4.4 and above.
996
+ Zeitwerk works with CRuby 2.5 and above.
997
+
998
+ 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.)
999
+
1000
+ 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.)
898
1001
 
899
1002
  <a id="markdown-testing" name="testing"></a>
900
1003
  ## Testing
@@ -925,9 +1028,19 @@ and run `bin/test`.
925
1028
  <a id="markdown-motivation" name="motivation"></a>
926
1029
  ## Motivation
927
1030
 
928
- Since `require` has global side-effects, and there is no static way to verify that you have issued the `require` calls for code that your file depends on, in practice it is very easy to forget some. That introduces bugs that depend on the load order. Zeitwerk provides a way to forget about `require` in your own code, just name things following conventions and done.
1031
+ <a id="markdown-kernelrequire-is-brittle" name="kernelrequire-is-brittle"></a>
1032
+ ### Kernel#require is brittle
1033
+
1034
+ Since `require` has global side-effects, and there is no static way to verify that you have issued the `require` calls for code that your file depends on, in practice it is very easy to forget some. That introduces bugs that depend on the load order.
1035
+
1036
+ Also, if the project has namespaces, setting things up and getting client code to load things in a consistent way needs discipline. For example, `require "foo/bar"` may define `Foo`, instead of reopen it. That may be a broken window, giving place to superclass mismatches or partially-defined namespaces.
1037
+
1038
+ With Zeitwerk, you just name things following conventions and done. Things are available everywhere, and descend is always orderly. Without effort and without broken windows.
1039
+
1040
+ <a id="markdown-rails-autoloading-was-brittle" name="rails-autoloading-was-brittle"></a>
1041
+ ### Rails autoloading was brittle
929
1042
 
930
- On the other hand, autoloading in Rails is based on `const_missing`, which lacks fundamental information like the nesting and the resolution algorithm that was being used. Because of that, Rails autoloading is not able to match Ruby's semantics and that introduces a series of gotchas. The original goal of this project was to bring a better autoloading mechanism for Rails 6.
1043
+ Autoloading in Rails was based on `const_missing` up to Rails 5. That callback lacks fundamental information like the nesting or the resolution algorithm being used. Because of that, Rails autoloading was not able to match Ruby's semantics, and that introduced a [series of issues](https://guides.rubyonrails.org/v5.2/autoloading_and_reloading_constants.html#common-gotchas). Zeitwerk is based on a different technique and fixed Rails autoloading starting with Rails 6.
931
1044
 
932
1045
  <a id="markdown-thanks" name="thanks"></a>
933
1046
  ## Thanks
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zeitwerk
2
4
  # @private
3
5
  class Autoloads
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zeitwerk
2
4
  class Error < StandardError
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zeitwerk
2
4
  # Centralizes the logic for the trace point used to detect the creation of
3
5
  # explicit namespaces, needed to descend into matching subdirectories right
@@ -3,7 +3,7 @@
3
3
  module Kernel
4
4
  module_function
5
5
 
6
- # We are going to decorate Kerner#require with two goals.
6
+ # We are going to decorate Kernel#require with two goals.
7
7
  #
8
8
  # First, by intercepting Kernel#require calls, we are able to autovivify
9
9
  # modules on required directories, and also do internal housekeeping when
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zeitwerk::Loader::Callbacks
2
4
  include Zeitwerk::RealModName
3
5
 
@@ -9,16 +11,19 @@ module Zeitwerk::Loader::Callbacks
9
11
  cref = autoloads.delete(file)
10
12
  cpath = cpath(*cref)
11
13
 
14
+ # If reloading is enabled, we need to put this constant for unloading
15
+ # regardless of what cdef? says. In Ruby < 3.1 the internal state is not
16
+ # fully cleared. Module#constants still includes it, and you need to
17
+ # remove_const. See https://github.com/ruby/ruby/pull/4715.
12
18
  to_unload[cpath] = [file, cref] if reloading_enabled?
13
19
  Zeitwerk::Registry.unregister_autoload(file)
14
20
 
15
- if logger && cdef?(*cref)
16
- log("constant #{cpath} loaded from file #{file}")
17
- elsif !cdef?(*cref)
21
+ if cdef?(*cref)
22
+ log("constant #{cpath} loaded from file #{file}") if logger
23
+ run_on_load_callbacks(cpath, cget(*cref), file) unless on_load_callbacks.empty?
24
+ else
18
25
  raise Zeitwerk::NameError.new("expected file #{file} to define constant #{cpath}, but didn't", cref.last)
19
26
  end
20
-
21
- run_on_load_callbacks(cpath, cget(*cref), file) unless on_load_callbacks.empty?
22
27
  end
23
28
 
24
29
  # Invoked from our decorated Kernel#require when a managed directory is
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "set"
2
4
  require "securerandom"
3
5
 
@@ -54,6 +56,12 @@ module Zeitwerk::Loader::Config
54
56
  # @sig Set[String]
55
57
  attr_reader :eager_load_exclusions
56
58
 
59
+ # User-oriented callbacks to be fired on setup and on reload.
60
+ #
61
+ # @private
62
+ # @sig Array[{ () -> void }]
63
+ attr_reader :on_setup_callbacks
64
+
57
65
  # User-oriented callbacks to be fired when a constant is loaded.
58
66
  #
59
67
  # @private
@@ -81,6 +89,7 @@ module Zeitwerk::Loader::Config
81
89
  @collapse_dirs = Set.new
82
90
  @eager_load_exclusions = Set.new
83
91
  @reloading_enabled = false
92
+ @on_setup_callbacks = []
84
93
  @on_load_callbacks = {}
85
94
  @on_unload_callbacks = {}
86
95
  @logger = self.class.default_logger
@@ -188,6 +197,17 @@ module Zeitwerk::Loader::Config
188
197
  end
189
198
  end
190
199
 
200
+ # Configure a block to be called after setup and on each reload.
201
+ # If setup was already done, the block runs immediately.
202
+ #
203
+ # @sig () { () -> void } -> void
204
+ def on_setup(&block)
205
+ mutex.synchronize do
206
+ on_setup_callbacks << block
207
+ block.call if @setup
208
+ end
209
+ end
210
+
191
211
  # Configure a block to be invoked once a certain constant path is loaded.
192
212
  # Supports multiple callbacks, and if there are many, they are executed in
193
213
  # the order in which they were defined.
@@ -247,17 +267,10 @@ module Zeitwerk::Loader::Config
247
267
 
248
268
  # @private
249
269
  # @sig (String) -> bool
250
- def manages?(dir)
251
- dir = dir + "/"
252
- ignored_paths.each do |ignored_path|
253
- return false if dir.start_with?(ignored_path + "/")
270
+ def ignores?(abspath)
271
+ ignored_paths.any? do |ignored_path|
272
+ ignored_path == abspath || (dir?(ignored_path) && abspath.start_with?(ignored_path + "/"))
254
273
  end
255
-
256
- root_dirs.each_key do |root_dir|
257
- return true if root_dir.start_with?(dir) || dir.start_with?(root_dir + "/")
258
- end
259
-
260
- false
261
274
  end
262
275
 
263
276
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zeitwerk::Loader::Helpers
2
4
  private
3
5
 
@@ -110,6 +110,8 @@ module Zeitwerk
110
110
  set_autoloads_in_dir(root_dir, namespace)
111
111
  end
112
112
 
113
+ on_setup_callbacks.each(&:call)
114
+
113
115
  @setup = true
114
116
  end
115
117
  end
@@ -143,7 +145,7 @@ module Zeitwerk
143
145
  # Could happen if loaded with require_relative. That is unsupported,
144
146
  # and the constant path would escape unloadable_cpath? This is just
145
147
  # defensive code to clean things up as much as we are able to.
146
- unload_cref(parent, cname) if cdef?(parent, cname)
148
+ unload_cref(parent, cname)
147
149
  unloaded_files.add(abspath) if ruby?(abspath)
148
150
  end
149
151
  end
@@ -154,7 +156,7 @@ module Zeitwerk
154
156
  run_on_unload_callbacks(cpath, value, abspath)
155
157
  end
156
158
 
157
- unload_cref(parent, cname) if cdef?(parent, cname)
159
+ unload_cref(parent, cname)
158
160
  unloaded_files.add(abspath) if ruby?(abspath)
159
161
  end
160
162
 
@@ -208,25 +210,28 @@ module Zeitwerk
208
210
  # Eager loads all files in the root directories, recursively. Files do not
209
211
  # need to be in `$LOAD_PATH`, absolute file names are used. Ignored files
210
212
  # are not eager loaded. You can opt-out specifically in specific files and
211
- # directories with `do_not_eager_load`.
213
+ # directories with `do_not_eager_load`, and that can be overridden passing
214
+ # `force: true`.
212
215
  #
213
- # @sig () -> void
214
- def eager_load
216
+ # @sig (true | false) -> void
217
+ def eager_load(force: false)
215
218
  mutex.synchronize do
216
219
  break if @eager_loaded
217
220
 
218
221
  log("eager load start") if logger
219
222
 
223
+ honour_exclusions = !force
224
+
220
225
  queue = []
221
226
  actual_root_dirs.each do |root_dir, namespace|
222
- queue << [namespace, root_dir] unless excluded_from_eager_load?(root_dir)
227
+ queue << [namespace, root_dir] unless honour_exclusions && excluded_from_eager_load?(root_dir)
223
228
  end
224
229
 
225
230
  while to_eager_load = queue.shift
226
231
  namespace, dir = to_eager_load
227
232
 
228
233
  ls(dir) do |basename, abspath|
229
- next if excluded_from_eager_load?(abspath)
234
+ next if honour_exclusions && excluded_from_eager_load?(abspath)
230
235
 
231
236
  if ruby?(abspath)
232
237
  if cref = autoloads.cref_for(abspath)
@@ -454,12 +459,21 @@ module Zeitwerk
454
459
  def raise_if_conflicting_directory(dir)
455
460
  self.class.mutex.synchronize do
456
461
  Registry.loaders.each do |loader|
457
- if loader != self && loader.manages?(dir)
458
- require "pp"
459
- raise Error,
460
- "loader\n\n#{pretty_inspect}\n\nwants to manage directory #{dir}," \
461
- " which is already managed by\n\n#{loader.pretty_inspect}\n"
462
- EOS
462
+ next if loader == self
463
+ next if loader.ignores?(dir)
464
+
465
+ dir = dir + "/"
466
+ loader.root_dirs.each do |root_dir, _namespace|
467
+ next if ignores?(root_dir)
468
+
469
+ root_dir = root_dir + "/"
470
+ if dir.start_with?(root_dir) || root_dir.start_with?(dir)
471
+ require "pp" # Needed for pretty_inspect, even in Ruby 2.5.
472
+ raise Error,
473
+ "loader\n\n#{pretty_inspect}\n\nwants to manage directory #{dir.chop}," \
474
+ " which is already managed by\n\n#{loader.pretty_inspect}\n"
475
+ EOS
476
+ end
463
477
  end
464
478
  end
465
479
  end
@@ -480,7 +494,13 @@ module Zeitwerk
480
494
 
481
495
  # @sig (Module, Symbol) -> void
482
496
  def unload_cref(parent, cname)
497
+ # Let's optimistically remove_const. The way we use it, this is going to
498
+ # succeed always if all is good.
483
499
  parent.__send__(:remove_const, cname)
500
+ rescue ::NameError
501
+ # There are a few edge scenarios in which this may happen. If the constant
502
+ # is gone, that is OK, anyway.
503
+ else
484
504
  log("#{cpath(parent, cname)} unloaded") if logger
485
505
  end
486
506
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zeitwerk::RealModName
2
4
  UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
3
5
  private_constant :UNBOUND_METHOD_MODULE_NAME
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "2.5.0.beta2"
4
+ VERSION = "2.5.0.beta6"
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.5.0.beta2
4
+ version: 2.5.0.beta6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xavier Noria
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-20 00:00:00.000000000 Z
11
+ date: 2021-10-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Zeitwerk implements constant autoloading with Ruby semantics. Each gem