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 +4 -4
- data/README.md +49 -1
- data/lib/zeitwerk/gem_loader.rb +1 -2
- data/lib/zeitwerk/loader/eager_load.rb +9 -7
- data/lib/zeitwerk/loader/helpers.rb +10 -6
- data/lib/zeitwerk/loader.rb +67 -33
- data/lib/zeitwerk/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06d0e5697b46d13993ad46322296a64f44e05a83087281944378fc79ee8067ba
|
4
|
+
data.tar.gz: 00371f27991d326fcbdfbf4e8e0668925a24d81662bc64890b3fbe653ae00243
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
|
data/lib/zeitwerk/gem_loader.rb
CHANGED
@@ -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
|
-
|
168
|
-
dir, namespace =
|
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
|
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
|
-
|
213
|
-
|
214
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
data/lib/zeitwerk/loader.rb
CHANGED
@@ -230,53 +230,87 @@ module Zeitwerk
|
|
230
230
|
setup
|
231
231
|
end
|
232
232
|
|
233
|
-
|
234
|
-
|
235
|
-
|
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
|
-
|
240
|
+
actual_roots.each do |root_dir, root_namespace|
|
241
|
+
queue = [[root_dir, real_mod_name(root_namespace)]]
|
238
242
|
|
239
|
-
|
240
|
-
|
243
|
+
until queue.empty?
|
244
|
+
dir, cpath = queue.shift
|
245
|
+
result[dir] = cpath
|
241
246
|
|
242
|
-
|
247
|
+
prefix = cpath == "Object" ? "" : cpath + "::"
|
243
248
|
|
244
|
-
|
245
|
-
|
246
|
-
|
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
|
-
|
249
|
-
walk_up_from = File.dirname(abspath)
|
250
|
-
else
|
251
|
-
walk_up_from = abspath
|
264
|
+
result
|
252
265
|
end
|
253
266
|
|
254
|
-
|
267
|
+
# @sig (String | Pathname) -> String?
|
268
|
+
def cpath_expected_at(path)
|
269
|
+
abspath = File.expand_path(path)
|
255
270
|
|
256
|
-
|
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
|
-
|
261
|
-
return if
|
273
|
+
return unless dir?(abspath) || ruby?(abspath)
|
274
|
+
return if ignored_path?(abspath)
|
262
275
|
|
263
|
-
paths
|
264
|
-
end
|
276
|
+
paths = []
|
265
277
|
|
266
|
-
|
278
|
+
if ruby?(abspath)
|
279
|
+
basename = File.basename(abspath, ".rb")
|
280
|
+
return if hidden?(basename)
|
267
281
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
282
|
+
paths << [basename, abspath]
|
283
|
+
walk_up_from = File.dirname(abspath)
|
284
|
+
else
|
285
|
+
walk_up_from = abspath
|
286
|
+
end
|
272
287
|
|
273
|
-
|
274
|
-
|
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
|
-
|
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
|
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
|
data/lib/zeitwerk/version.rb
CHANGED
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.
|
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-
|
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.
|
65
|
+
rubygems_version: 3.5.7
|
66
66
|
signing_key:
|
67
67
|
specification_version: 4
|
68
68
|
summary: Efficient and thread-safe constant autoloader
|