zeitwerk 2.6.13 → 2.6.14

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: 5d4ce4eb7136dafe17130fc5d895e525d80ae947f02dd9420b5bc85a4d6f0dfd
4
- data.tar.gz: edcac638955fef258ad36a358b78da6831a715ed46446848e39f9f1d8fb7de0b
3
+ metadata.gz: 06d0e5697b46d13993ad46322296a64f44e05a83087281944378fc79ee8067ba
4
+ data.tar.gz: 00371f27991d326fcbdfbf4e8e0668925a24d81662bc64890b3fbe653ae00243
5
5
  SHA512:
6
- metadata.gz: f0a6e64c56635c9c6a8c9b24bdeb7509e4a7f14489f32aae18b02221c23b95b34096184c78d2d1509130ce75031ed0f3a7751b78e43d9b72122440f07cf980bc
7
- data.tar.gz: 06440147c101a95ac2c8b7dcd43920a3efc3da2f58b902c157730a449d57b6ef74e0d3db7f3d2735be816a74ceb774e2d4075741556c7e2ba7fa00b8130c1723
6
+ metadata.gz: 6a2666faaf75882a06ad5a2b408e1cff593744b15fca61864a20c255a2868af22f8b60a7125c5d61f83374efa5e1a21ee627d17ea0ef8f3586a71efba4f3fa8e
7
+ data.tar.gz: aad3c7421105576216338a3eb521bb44188d9f2a371f69e5a069e4b619ab1d101b1fb9477bc55ef7bf35c50942bd1ddb5c50571392e9e487eedb9a09e5e9a778
data/README.md CHANGED
@@ -60,6 +60,7 @@
60
60
  - [Introspection](#introspection)
61
61
  - [`Zeitwerk::Loader#dirs`](#zeitwerkloaderdirs)
62
62
  - [`Zeitwerk::Loader#cpath_expected_at`](#zeitwerkloadercpath_expected_at)
63
+ - [`Zeitwerk::Loader#all_expected_cpaths`](#zeitwerkloaderall_expected_cpaths)
63
64
  - [Encodings](#encodings)
64
65
  - [Rules of thumb](#rules-of-thumb)
65
66
  - [Debuggers](#debuggers)
@@ -259,7 +260,7 @@ app/controllers/admin/users_controller.rb -> Admin::UsersController
259
260
 
260
261
  and does not have a file called `admin.rb`, Zeitwerk automatically creates an `Admin` module on your behalf the first time `Admin` is used.
261
262
 
262
- To trigger this behavior, the directory must contain non-ignored Ruby files with the `.rb` extension, either directly or recursively. Otherwise, the directory is ignored. This condition is reevaluated during reloads.
263
+ To trigger this behavior, the directory must contain non-ignored Ruby files with the ".rb" extension, either directly or recursively. Otherwise, the directory is ignored. This condition is reevaluated during reloads.
263
264
 
264
265
  <a id="markdown-explicit-namespaces" name="explicit-namespaces"></a>
265
266
  ### Explicit namespaces
@@ -1330,6 +1331,53 @@ loader.cpath_expected_at("8.rb") # => Zeitwerk::NameError
1330
1331
 
1331
1332
  This method does not parse file contents and does not guarantee files define the returned constant path. It just says which is the _expected_ one.
1332
1333
 
1334
+ `Zeitwerk::Loader#cpath_expected_at` is designed to be used with individual paths. If you want to know all the expected constant paths in the project, please use `Zeitwerk::Loader#all_expected_cpaths`, documented next.
1335
+
1336
+ <a id="markdown-zeitwerkloaderall_expected_cpaths" name="zeitwerkloaderall_expected_cpaths"></a>
1337
+ #### `Zeitwerk::Loader#all_expected_cpaths`
1338
+
1339
+ The method `Zeitwerk::Loader#all_expected_cpaths` returns a hash that maps the absolute paths of the files and directories managed by the receiver to their corresponding expected constant paths.
1340
+
1341
+ Ignored files or directories are not included in the result. Directories that do not contain any files with the ".rb" extension (recursively) are also excluded. Additionally, if a directory contains files with the ".rb" extension but all of them are ignored, it is treated as if it contains no ".rb" files.
1342
+
1343
+ For example, if `lib` is the root directory of a gem with the following contents:
1344
+
1345
+ ```
1346
+ lib/my_gem.rb
1347
+ lib/my_gem/version.rb
1348
+ lib/my_gem/ignored.rb
1349
+ lib/my_gem/drivers/unix.rb
1350
+ lib/my_gem/drivers/windows.rb
1351
+ lib/my_gem/collapsed/foo.rb
1352
+ lib/tasks/my_gem.rake
1353
+ ```
1354
+
1355
+ `Zeitwerk::Loader#all_expected_cpaths` would return (maybe in a different order):
1356
+
1357
+ ```ruby
1358
+ {
1359
+ "/.../lib" => "Object",
1360
+ "/.../lib/my_gem.rb" => "MyGem",
1361
+ "/.../lib/my_gem" => "MyGem",
1362
+ "/.../lib/my_gem/version.rb" => "MyGem::VERSION",
1363
+ "/.../lib/my_gem/drivers" => "MyGem::Drivers",
1364
+ "/.../lib/my_gem/drivers/unix.rb" => "MyGem::Drivers::Unix",
1365
+ "/.../lib/my_gem/drivers/windows.rb" => "MyGem::Drivers::Windows",
1366
+ "/.../lib/my_gem/collapsed" => "MyGem"
1367
+ "/.../lib/my_gem/collapsed/foo.rb" => "MyGem::Foo"
1368
+ }
1369
+ ```
1370
+
1371
+ As the names suggest, the previous example assumes `lib/my_gem/ignored.rb` is ignored (so, not present in the returned hash), and `lib/my_gem/collapsed` is a collapsed directory (so the expected namespace at that level is still `MyGem`, this is an edge case).
1372
+
1373
+ Directory paths are guaranteed to not have trailing slashes.
1374
+
1375
+ Note that `lib/tasks` is not present in the hash because it contains no files with the ".rb" extension. The loader does not consider the `lib/tasks` directory to represent a Ruby namespace; therefore, it does not end up in the hash.
1376
+
1377
+ The order of the hash entries is undefined.
1378
+
1379
+ This method does not parse file contents and does not guarantee files define the corresponding constant paths. It just says which are the _expected_ ones.
1380
+
1333
1381
  <a id="markdown-encodings" name="encodings"></a>
1334
1382
  ### Encodings
1335
1383
 
@@ -42,13 +42,12 @@ module Zeitwerk
42
42
  def warn_on_extra_files
43
43
  expected_namespace_dir = @root_file.delete_suffix(".rb")
44
44
 
45
- ls(@root_dir) do |basename, abspath|
45
+ ls(@root_dir) do |basename, abspath, ftype|
46
46
  next if abspath == @root_file
47
47
  next if abspath == expected_namespace_dir
48
48
 
49
49
  basename_without_ext = basename.delete_suffix(".rb")
50
50
  cname = inflector.camelize(basename_without_ext, abspath).to_sym
51
- ftype = dir?(abspath) ? "directory" : "file"
52
51
 
53
52
  warn(<<~EOS)
54
53
  WARNING: Zeitwerk defines the constant #{cname} after the #{ftype}
@@ -164,13 +164,13 @@ module Zeitwerk::Loader::EagerLoad
164
164
  log("eager load directory #{dir} start") if logger
165
165
 
166
166
  queue = [[dir, namespace]]
167
- while to_eager_load = queue.shift
168
- dir, namespace = to_eager_load
167
+ until queue.empty?
168
+ dir, namespace = queue.shift
169
169
 
170
- ls(dir) do |basename, abspath|
170
+ ls(dir) do |basename, abspath, ftype|
171
171
  next if honour_exclusions && eager_load_exclusions.member?(abspath)
172
172
 
173
- if ruby?(abspath)
173
+ if ftype == :file
174
174
  if (cref = autoloads[abspath])
175
175
  cget(*cref)
176
176
  end
@@ -209,9 +209,11 @@ module Zeitwerk::Loader::EagerLoad
209
209
  next_dirs = []
210
210
 
211
211
  suffix.split("::").each do |segment|
212
- while dir = dirs.shift
213
- ls(dir) do |basename, abspath|
214
- next unless dir?(abspath)
212
+ until dirs.empty?
213
+ dir = dirs.shift
214
+
215
+ ls(dir) do |basename, abspath, ftype|
216
+ next unless ftype == :directory
215
217
 
216
218
  if collapse?(abspath)
217
219
  dirs << abspath
@@ -31,13 +31,15 @@ module Zeitwerk::Loader::Helpers
31
31
  if dir?(abspath)
32
32
  next if roots.key?(abspath)
33
33
  next if !has_at_least_one_ruby_file?(abspath)
34
+ ftype = :directory
34
35
  else
35
36
  next unless ruby?(abspath)
37
+ ftype = :file
36
38
  end
37
39
 
38
40
  # We freeze abspath because that saves allocations when passed later to
39
41
  # File methods. See #125.
40
- yield basename, abspath.freeze
42
+ yield basename, abspath.freeze, ftype
41
43
  end
42
44
  end
43
45
 
@@ -45,12 +47,14 @@ module Zeitwerk::Loader::Helpers
45
47
  private def has_at_least_one_ruby_file?(dir)
46
48
  to_visit = [dir]
47
49
 
48
- while dir = to_visit.shift
49
- ls(dir) do |_basename, abspath|
50
- if dir?(abspath)
51
- to_visit << abspath
52
- else
50
+ until to_visit.empty?
51
+ dir = to_visit.shift
52
+
53
+ ls(dir) do |_basename, abspath, ftype|
54
+ if ftype == :file
53
55
  return true
56
+ else
57
+ to_visit << abspath
54
58
  end
55
59
  end
56
60
  end
@@ -230,53 +230,87 @@ module Zeitwerk
230
230
  setup
231
231
  end
232
232
 
233
- # @sig (String | Pathname) -> String?
234
- def cpath_expected_at(path)
235
- abspath = File.expand_path(path)
233
+ # Returns a hash that maps the absolute paths of the managed files and
234
+ # directories to their respective expected constant paths.
235
+ #
236
+ # @sig () -> Hash[String, String]
237
+ def all_expected_cpaths
238
+ result = {}
236
239
 
237
- raise Zeitwerk::Error.new("#{abspath} does not exist") unless File.exist?(abspath)
240
+ actual_roots.each do |root_dir, root_namespace|
241
+ queue = [[root_dir, real_mod_name(root_namespace)]]
238
242
 
239
- return unless dir?(abspath) || ruby?(abspath)
240
- return if ignored_path?(abspath)
243
+ until queue.empty?
244
+ dir, cpath = queue.shift
245
+ result[dir] = cpath
241
246
 
242
- paths = []
247
+ prefix = cpath == "Object" ? "" : cpath + "::"
243
248
 
244
- if ruby?(abspath)
245
- basename = File.basename(abspath, ".rb")
246
- return if hidden?(basename)
249
+ ls(dir) do |basename, abspath, ftype|
250
+ if ftype == :file
251
+ basename.delete_suffix!(".rb")
252
+ result[abspath] = prefix + inflector.camelize(basename, abspath)
253
+ else
254
+ if collapse?(abspath)
255
+ queue << [abspath, cpath]
256
+ else
257
+ queue << [abspath, prefix + inflector.camelize(basename, abspath)]
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
247
263
 
248
- paths << [basename, abspath]
249
- walk_up_from = File.dirname(abspath)
250
- else
251
- walk_up_from = abspath
264
+ result
252
265
  end
253
266
 
254
- root_namespace = nil
267
+ # @sig (String | Pathname) -> String?
268
+ def cpath_expected_at(path)
269
+ abspath = File.expand_path(path)
255
270
 
256
- walk_up(walk_up_from) do |dir|
257
- break if root_namespace = roots[dir]
258
- return if ignored_path?(dir)
271
+ raise Zeitwerk::Error.new("#{abspath} does not exist") unless File.exist?(abspath)
259
272
 
260
- basename = File.basename(dir)
261
- return if hidden?(basename)
273
+ return unless dir?(abspath) || ruby?(abspath)
274
+ return if ignored_path?(abspath)
262
275
 
263
- paths << [basename, abspath] unless collapse?(dir)
264
- end
276
+ paths = []
265
277
 
266
- return unless root_namespace
278
+ if ruby?(abspath)
279
+ basename = File.basename(abspath, ".rb")
280
+ return if hidden?(basename)
267
281
 
268
- if paths.empty?
269
- real_mod_name(root_namespace)
270
- else
271
- cnames = paths.reverse_each.map { |b, a| cname_for(b, a) }
282
+ paths << [basename, abspath]
283
+ walk_up_from = File.dirname(abspath)
284
+ else
285
+ walk_up_from = abspath
286
+ end
272
287
 
273
- if root_namespace == Object
274
- cnames.join("::")
288
+ root_namespace = nil
289
+
290
+ walk_up(walk_up_from) do |dir|
291
+ break if root_namespace = roots[dir]
292
+ return if ignored_path?(dir)
293
+
294
+ basename = File.basename(dir)
295
+ return if hidden?(basename)
296
+
297
+ paths << [basename, abspath] unless collapse?(dir)
298
+ end
299
+
300
+ return unless root_namespace
301
+
302
+ if paths.empty?
303
+ real_mod_name(root_namespace)
275
304
  else
276
- "#{real_mod_name(root_namespace)}::#{cnames.join("::")}"
305
+ cnames = paths.reverse_each.map { |b, a| cname_for(b, a) }
306
+
307
+ if root_namespace == Object
308
+ cnames.join("::")
309
+ else
310
+ "#{real_mod_name(root_namespace)}::#{cnames.join("::")}"
311
+ end
277
312
  end
278
313
  end
279
- end
280
314
 
281
315
  # Says if the given constant path would be unloaded on reload. This
282
316
  # predicate returns `false` if reloading is disabled.
@@ -408,8 +442,8 @@ module Zeitwerk
408
442
 
409
443
  # @sig (String, Module) -> void
410
444
  private def define_autoloads_for_dir(dir, parent)
411
- ls(dir) do |basename, abspath|
412
- if ruby?(basename)
445
+ ls(dir) do |basename, abspath, ftype|
446
+ if ftype == :file
413
447
  basename.delete_suffix!(".rb")
414
448
  autoload_file(parent, cname_for(basename, abspath), abspath)
415
449
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "2.6.13"
4
+ VERSION = "2.6.14"
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.13
4
+ version: 2.6.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xavier Noria
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-06 00:00:00.000000000 Z
11
+ date: 2024-05-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Zeitwerk implements constant autoloading with Ruby semantics. Each gem
@@ -62,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
62
  - !ruby/object:Gem::Version
63
63
  version: '0'
64
64
  requirements: []
65
- rubygems_version: 3.5.5
65
+ rubygems_version: 3.5.7
66
66
  signing_key:
67
67
  specification_version: 4
68
68
  summary: Efficient and thread-safe constant autoloader