zeitwerk 2.6.7 → 2.6.8

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: 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