zeitwerk 2.6.7 → 2.6.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6c7bf12c3a33195c57541b840ca6d7dbc21b19e0d7b91d8933ad37aa88b325d
4
- data.tar.gz: 4b8dd86417dd633b78c02eada9ed6956e8e708397c988237aaddecb9644ba469
3
+ metadata.gz: 418f16f6224359c716a990f72ae916c980a3f388b09ab018c0972b8558530620
4
+ data.tar.gz: '04847c6c8f8f894fe3c2b3c39d175b5854b583d3a02ef5cca27a53cb1b8dafd4'
5
5
  SHA512:
6
- metadata.gz: 4f0bc60d9914a9e815aed501a030f7e9bde7927f750f3836f96ac86a52c0699f4ca69456bd381845428f6b09adccaa085443f39bb0fdaa651437742e21d10af2
7
- data.tar.gz: 3ba55f7bb24725d156cc4043697bfcd6092e2893f1aa71296965c82ae9ed66b79c30f853b535693b6b884d7d3d26cf1b122545e395ba11c92f65552e14116248
6
+ metadata.gz: f86272e84721cf9b8b1e07182b1b60cfe732ef2c266dbe076225d32af52fc9a30c278a14651ddbb3d435636646dd23c948d3fe0f00ed9b8393abdeb34dd40028
7
+ data.tar.gz: 562738b53779310e899c2065c7046844d27daabb5f354972e4c9b65bec3e5c0cdade92d19ed815fe7b0dc01538603daa3e4dd2f0049fe9a7a2e74313c0f040f6
data/README.md CHANGED
@@ -24,6 +24,7 @@
24
24
  - [Setup](#setup)
25
25
  - [Generic](#generic)
26
26
  - [for_gem](#for_gem)
27
+ - [for_gem_extension](#for_gem_extension)
27
28
  - [Autoloading](#autoloading)
28
29
  - [Eager loading](#eager-loading)
29
30
  - [Eager load exclusions](#eager-load-exclusions)
@@ -410,6 +411,65 @@ Otherwise, there's a flag to say the extra stuff is OK:
410
411
  Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
411
412
  ```
412
413
 
414
+ <a id="markdown-for_gem_extension" name="for_gem_extension"></a>
415
+ #### for_gem_extension
416
+
417
+ Let's suppose you are writing a gem to extend `Net::HTTP` with some niche feature. By [convention](https://guides.rubygems.org/name-your-gem/):
418
+
419
+ * The gem should be called `net-http-niche_feature`. That is, hyphens for the extended part, a hyphen, and underscores for yours.
420
+ * The namespace should be `Net::HTTP::NicheFeature`.
421
+ * The entry point should be `lib/net/http/niche_feature.rb`.
422
+ * Optionally, the gem could have a top-level `lib/net-http-niche_feature.rb`, but, if defined, that one should have just a `require` call for the entry point.
423
+
424
+ The top-level file mentioned in the last point is optional. In particular, from
425
+
426
+ ```ruby
427
+ gem "net-http-niche_feature"
428
+ ```
429
+
430
+ if the hyphenated file does not exist, Bundler notes the conventional hyphenated pattern and issues a `require` for `net/http/niche_feature`.
431
+
432
+ Gem extensions following the conventions above have a dedicated loader constructor: `Zeitwerk::Loader.for_gem_extension`.
433
+
434
+ The structure of the gem would be like this:
435
+
436
+ ```ruby
437
+ # lib/net-http-niche_feature.rb (optional)
438
+
439
+ # For technical reasons, this cannot be require_relative.
440
+ require "net/http/niche_feature"
441
+
442
+
443
+ # lib/net/http/niche_feature.rb
444
+
445
+ require "net/http"
446
+ require "zeitwerk"
447
+
448
+ loader = Zeitwerk::Loader.for_gem_extension(Net::HTTP)
449
+ loader.setup
450
+
451
+ module Net::HTTP::NicheFeature
452
+ # Since the setup has been performed, at this point we are already able
453
+ # to reference project constants, in this case Net::HTTP::NicheFeature::MyMixin.
454
+ include MyMixin
455
+ end
456
+
457
+
458
+ # lib/net/http/niche_feature/version.rb
459
+
460
+ module Net::HTTP::NicheFeature
461
+ VERSION = "1.0.0"
462
+ end
463
+ ```
464
+
465
+ `Zeitwerk::Loader.for_gem_extension` expects as argument the namespace being extended, which has to be a non-anonymous class or module object.
466
+
467
+ If it exists, `lib/net/http/niche_feature/version.rb` is expected to define `Net::HTTP::NicheFeature::VERSION`.
468
+
469
+ Due to technical reasons, the entry point of the gem has to be loaded with `Kernel#require`. Loading that file with `Kernel#load` or `Kernel#require_relative` won't generally work. This is important if you load the entry point from the optional hyphenated top-level file.
470
+
471
+ `Zeitwerk::Loader.for_gem_extension` is idempotent when invoked from the same file, to support gems that want to reload (unlikely).
472
+
413
473
  <a id="markdown-autoloading" name="autoloading"></a>
414
474
  ### Autoloading
415
475
 
@@ -5,8 +5,8 @@ module Zeitwerk
5
5
  # @sig (String) -> void
6
6
  def initialize(root_file)
7
7
  namespace = File.basename(root_file, ".rb")
8
- lib_dir = File.dirname(root_file)
9
- @version_file = File.join(lib_dir, namespace, "version.rb")
8
+ root_dir = File.dirname(root_file)
9
+ @version_file = File.join(root_dir, namespace, "version.rb")
10
10
  end
11
11
 
12
12
  # @sig (String, String) -> String
@@ -3,27 +3,31 @@
3
3
  module Zeitwerk
4
4
  # @private
5
5
  class GemLoader < Loader
6
+ include RealModName
7
+
6
8
  # Users should not create instances directly, the public interface is
7
9
  # `Zeitwerk::Loader.for_gem`.
8
10
  private_class_method :new
9
11
 
10
12
  # @private
11
13
  # @sig (String, bool) -> Zeitwerk::GemLoader
12
- def self._new(root_file, warn_on_extra_files:)
13
- new(root_file, warn_on_extra_files: warn_on_extra_files)
14
+ def self.__new(root_file, namespace:, warn_on_extra_files:)
15
+ new(root_file, namespace: namespace, warn_on_extra_files: warn_on_extra_files)
14
16
  end
15
17
 
16
18
  # @sig (String, bool) -> void
17
- def initialize(root_file, warn_on_extra_files:)
19
+ def initialize(root_file, namespace:, warn_on_extra_files:)
18
20
  super()
19
21
 
20
- @tag = File.basename(root_file, ".rb")
22
+ @tag = File.basename(root_file, ".rb")
23
+ @tag = real_mod_name(namespace) + "-" + @tag unless namespace.equal?(Object)
24
+
21
25
  @inflector = GemInflector.new(root_file)
22
26
  @root_file = File.expand_path(root_file)
23
- @lib = File.dirname(root_file)
27
+ @root_dir = File.dirname(root_file)
24
28
  @warn_on_extra_files = warn_on_extra_files
25
29
 
26
- push_dir(@lib)
30
+ push_dir(@root_dir, namespace: namespace)
27
31
  end
28
32
 
29
33
  # @sig () -> void
@@ -38,7 +42,7 @@ module Zeitwerk
38
42
  def warn_on_extra_files
39
43
  expected_namespace_dir = @root_file.delete_suffix(".rb")
40
44
 
41
- ls(@lib) do |basename, abspath|
45
+ ls(@root_dir) do |basename, abspath|
42
46
  next if abspath == @root_file
43
47
  next if abspath == expected_namespace_dir
44
48
 
@@ -109,8 +109,7 @@ module Zeitwerk::Loader::Config
109
109
  # @raise [Zeitwerk::Error]
110
110
  # @sig (String | Pathname, Module) -> void
111
111
  def push_dir(path, namespace: Object)
112
- # Note that Class < Module.
113
- unless namespace.is_a?(Module)
112
+ unless namespace.is_a?(Module) # Note that Class < Module.
114
113
  raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module object, should be"
115
114
  end
116
115
 
@@ -298,9 +297,9 @@ module Zeitwerk::Loader::Config
298
297
  # Common use case.
299
298
  return false if ignored_paths.empty?
300
299
 
301
- walk_up(abspath) do |abspath|
302
- return true if ignored_path?(abspath)
303
- return false if roots.key?(abspath)
300
+ walk_up(abspath) do |path|
301
+ return true if ignored_path?(path)
302
+ return false if roots.key?(path)
304
303
  end
305
304
 
306
305
  false
@@ -328,9 +327,9 @@ module Zeitwerk::Loader::Config
328
327
  # Optimize this common use case.
329
328
  return false if eager_load_exclusions.empty?
330
329
 
331
- walk_up(abspath) do |abspath|
332
- return true if eager_load_exclusions.member?(abspath)
333
- return false if roots.key?(abspath)
330
+ walk_up(abspath) do |path|
331
+ return true if eager_load_exclusions.member?(path)
332
+ return false if roots.key?(path)
334
333
  end
335
334
 
336
335
  false
@@ -264,12 +264,15 @@ module Zeitwerk
264
264
  # --- Class methods ---------------------------------------------------------------------------
265
265
 
266
266
  class << self
267
+ include RealModName
268
+
267
269
  # @sig #call | #debug | nil
268
270
  attr_accessor :default_logger
269
271
 
270
272
  # This is a shortcut for
271
273
  #
272
274
  # require "zeitwerk"
275
+ #
273
276
  # loader = Zeitwerk::Loader.new
274
277
  # loader.tag = File.basename(__FILE__, ".rb")
275
278
  # loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
@@ -284,7 +287,36 @@ module Zeitwerk
284
287
  # @sig (bool) -> Zeitwerk::GemLoader
285
288
  def for_gem(warn_on_extra_files: true)
286
289
  called_from = caller_locations(1, 1).first.path
287
- Registry.loader_for_gem(called_from, warn_on_extra_files: warn_on_extra_files)
290
+ Registry.loader_for_gem(called_from, namespace: Object, warn_on_extra_files: warn_on_extra_files)
291
+ end
292
+
293
+ # This is a shortcut for
294
+ #
295
+ # require "zeitwerk"
296
+ #
297
+ # loader = Zeitwerk::Loader.new
298
+ # loader.tag = namespace.name + "-" + File.basename(__FILE__, ".rb")
299
+ # loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
300
+ # loader.push_dir(__dir__, namespace: namespace)
301
+ #
302
+ # except that this method returns the same object in subsequent calls from
303
+ # the same file, in the unlikely case the gem wants to be able to reload.
304
+ #
305
+ # This method returns a subclass of Zeitwerk::Loader, but the exact type
306
+ # is private, client code can only rely on the interface.
307
+ #
308
+ # @sig (bool) -> Zeitwerk::GemLoader
309
+ def for_gem_extension(namespace)
310
+ unless namespace.is_a?(Module) # Note that Class < Module.
311
+ raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module object, should be"
312
+ end
313
+
314
+ unless real_mod_name(namespace)
315
+ raise Zeitwerk::Error, "extending anonymous namespaces is unsupported"
316
+ end
317
+
318
+ called_from = caller_locations(1, 1).first.path
319
+ Registry.loader_for_gem(called_from, namespace: namespace, warn_on_extra_files: false)
288
320
  end
289
321
 
290
322
  # Broadcasts `eager_load` to all loaders. Those that have not been setup
@@ -86,8 +86,8 @@ module Zeitwerk
86
86
  #
87
87
  # @private
88
88
  # @sig (String) -> Zeitwerk::Loader
89
- def loader_for_gem(root_file, warn_on_extra_files:)
90
- gem_loaders_by_root_file[root_file] ||= GemLoader._new(root_file, warn_on_extra_files: warn_on_extra_files)
89
+ def loader_for_gem(root_file, namespace:, warn_on_extra_files:)
90
+ gem_loaders_by_root_file[root_file] ||= GemLoader.__new(root_file, namespace: namespace, warn_on_extra_files: warn_on_extra_files)
91
91
  end
92
92
 
93
93
  # @private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "2.6.7"
4
+ VERSION = "2.6.8"
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.7
4
+ version: 2.6.8
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-02-10 00:00:00.000000000 Z
11
+ date: 2023-04-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Zeitwerk implements constant autoloading with Ruby semantics. Each gem
@@ -61,7 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
61
  - !ruby/object:Gem::Version
62
62
  version: '0'
63
63
  requirements: []
64
- rubygems_version: 3.4.3
64
+ rubygems_version: 3.4.11
65
65
  signing_key:
66
66
  specification_version: 4
67
67
  summary: Efficient and thread-safe constant autoloader