zeitwerk 2.6.13 → 2.6.14

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