zeitwerk 2.6.8 → 2.7.5
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 +193 -64
- data/lib/zeitwerk/{kernel.rb → core_ext/kernel.rb} +6 -10
- data/lib/zeitwerk/core_ext/module.rb +20 -0
- data/lib/zeitwerk/cref/map.rb +159 -0
- data/lib/zeitwerk/cref.rb +69 -0
- data/lib/zeitwerk/error.rb +2 -0
- data/lib/zeitwerk/gem_inflector.rb +2 -2
- data/lib/zeitwerk/gem_loader.rb +6 -7
- data/lib/zeitwerk/inflector.rb +3 -3
- data/lib/zeitwerk/internal.rb +1 -0
- data/lib/zeitwerk/loader/callbacks.rb +44 -40
- data/lib/zeitwerk/loader/config.rb +46 -53
- data/lib/zeitwerk/loader/eager_load.rb +49 -47
- data/lib/zeitwerk/loader/file_system.rb +165 -0
- data/lib/zeitwerk/loader/helpers.rb +28 -125
- data/lib/zeitwerk/loader.rb +287 -190
- data/lib/zeitwerk/null_inflector.rb +6 -0
- data/lib/zeitwerk/real_mod_name.rb +9 -12
- data/lib/zeitwerk/registry/autoloads.rb +38 -0
- data/lib/zeitwerk/registry/explicit_namespaces.rb +61 -0
- data/lib/zeitwerk/registry/inceptions.rb +31 -0
- data/lib/zeitwerk/registry/loaders.rb +33 -0
- data/lib/zeitwerk/registry.rb +42 -91
- data/lib/zeitwerk/version.rb +2 -1
- data/lib/zeitwerk.rb +6 -3
- metadata +15 -10
- data/lib/zeitwerk/explicit_namespace.rb +0 -93
|
@@ -1,143 +1,46 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Zeitwerk::Loader::Helpers
|
|
4
|
-
|
|
4
|
+
CNAME_VALIDATOR = Module.new #: Module
|
|
5
|
+
private_constant :CNAME_VALIDATOR
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
private def
|
|
8
|
-
|
|
9
|
-
logger.send(method_name, "Zeitwerk@#{tag}: #{message}")
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# --- Files and directories ---------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
# @sig (String) { (String, String) -> void } -> void
|
|
15
|
-
private def ls(dir)
|
|
16
|
-
children = Dir.children(dir)
|
|
17
|
-
|
|
18
|
-
# The order in which a directory is listed depends on the file system.
|
|
19
|
-
#
|
|
20
|
-
# Since client code may run in different platforms, it seems convenient to
|
|
21
|
-
# order directory entries. This provides consistent eager loading across
|
|
22
|
-
# platforms, for example.
|
|
23
|
-
children.sort!
|
|
24
|
-
|
|
25
|
-
children.each do |basename|
|
|
26
|
-
next if hidden?(basename)
|
|
27
|
-
|
|
28
|
-
abspath = File.join(dir, basename)
|
|
29
|
-
next if ignored_path?(abspath)
|
|
30
|
-
|
|
31
|
-
if dir?(abspath)
|
|
32
|
-
next if roots.key?(abspath)
|
|
33
|
-
next if !has_at_least_one_ruby_file?(abspath)
|
|
34
|
-
else
|
|
35
|
-
next unless ruby?(abspath)
|
|
36
|
-
end
|
|
7
|
+
#: (String, String) -> Symbol ! Zeitwerk::NameError
|
|
8
|
+
private def cname_for(basename, abspath)
|
|
9
|
+
cname = inflector.camelize(basename, abspath)
|
|
37
10
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
yield basename, abspath.freeze
|
|
11
|
+
unless cname.is_a?(String)
|
|
12
|
+
raise TypeError, "#{inflector.class}#camelize must return a String, received #{cname.inspect}"
|
|
41
13
|
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# @sig (String) -> bool
|
|
45
|
-
private def has_at_least_one_ruby_file?(dir)
|
|
46
|
-
to_visit = [dir]
|
|
47
14
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
to_visit << abspath
|
|
52
|
-
else
|
|
53
|
-
return true
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
15
|
+
if cname.include?("::")
|
|
16
|
+
raise Zeitwerk::NameError.new(<<~MESSAGE, cname)
|
|
17
|
+
wrong constant name #{cname} inferred by #{inflector.class} from
|
|
57
18
|
|
|
58
|
-
|
|
59
|
-
end
|
|
19
|
+
#{abspath}
|
|
60
20
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
path.end_with?(".rb")
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# @sig (String) -> bool
|
|
67
|
-
private def dir?(path)
|
|
68
|
-
File.directory?(path)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# @sig (String) -> bool
|
|
72
|
-
private def hidden?(basename)
|
|
73
|
-
basename.start_with?(".")
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# @sig (String) { (String) -> void } -> void
|
|
77
|
-
private def walk_up(abspath)
|
|
78
|
-
loop do
|
|
79
|
-
yield abspath
|
|
80
|
-
abspath, basename = File.split(abspath)
|
|
81
|
-
break if basename == "/"
|
|
21
|
+
#{inflector.class}#camelize should return a simple constant name without "::"
|
|
22
|
+
MESSAGE
|
|
82
23
|
end
|
|
83
|
-
end
|
|
84
24
|
|
|
85
|
-
|
|
25
|
+
begin
|
|
26
|
+
CNAME_VALIDATOR.const_defined?(cname, false)
|
|
27
|
+
rescue ::NameError => error
|
|
28
|
+
path_type = @fs.rb_extension?(abspath) ? "file" : "directory"
|
|
86
29
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
#
|
|
90
|
-
# For example, given
|
|
91
|
-
#
|
|
92
|
-
# class A
|
|
93
|
-
# autoload :X, "x.rb"
|
|
94
|
-
# end
|
|
95
|
-
#
|
|
96
|
-
# class B < A
|
|
97
|
-
# end
|
|
98
|
-
#
|
|
99
|
-
# B.autoload?(:X) returns "x.rb".
|
|
100
|
-
#
|
|
101
|
-
# We need a way to strictly check in parent ignoring ancestors.
|
|
102
|
-
#
|
|
103
|
-
# @sig (Module, Symbol) -> String?
|
|
104
|
-
if method(:autoload?).arity == 1
|
|
105
|
-
private def strict_autoload_path(parent, cname)
|
|
106
|
-
parent.autoload?(cname) if cdef?(parent, cname)
|
|
107
|
-
end
|
|
108
|
-
else
|
|
109
|
-
private def strict_autoload_path(parent, cname)
|
|
110
|
-
parent.autoload?(cname, false)
|
|
111
|
-
end
|
|
112
|
-
end
|
|
30
|
+
raise Zeitwerk::NameError.new(<<~MESSAGE, error.name)
|
|
31
|
+
#{error.message} inferred by #{inflector.class} from #{path_type}
|
|
113
32
|
|
|
114
|
-
|
|
115
|
-
if Symbol.method_defined?(:name)
|
|
116
|
-
# Symbol#name was introduced in Ruby 3.0. It returns always the same
|
|
117
|
-
# frozen object, so we may save a few string allocations.
|
|
118
|
-
private def cpath(parent, cname)
|
|
119
|
-
Object == parent ? cname.name : "#{real_mod_name(parent)}::#{cname.name}"
|
|
120
|
-
end
|
|
121
|
-
else
|
|
122
|
-
private def cpath(parent, cname)
|
|
123
|
-
Object == parent ? cname.to_s : "#{real_mod_name(parent)}::#{cname}"
|
|
124
|
-
end
|
|
125
|
-
end
|
|
33
|
+
#{abspath}
|
|
126
34
|
|
|
127
|
-
|
|
128
|
-
private def cdef?(parent, cname)
|
|
129
|
-
parent.const_defined?(cname, false)
|
|
130
|
-
end
|
|
35
|
+
Possible ways to address this:
|
|
131
36
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
37
|
+
* Tell Zeitwerk to ignore this particular #{path_type}.
|
|
38
|
+
* Tell Zeitwerk to ignore one of its parent directories.
|
|
39
|
+
* Rename the #{path_type} to comply with the naming conventions.
|
|
40
|
+
* Modify the inflector to handle this case.
|
|
41
|
+
MESSAGE
|
|
42
|
+
end
|
|
137
43
|
|
|
138
|
-
|
|
139
|
-
# @sig (Module, Symbol) -> Object
|
|
140
|
-
private def crem(parent, cname)
|
|
141
|
-
parent.__send__(:remove_const, cname)
|
|
44
|
+
cname.to_sym
|
|
142
45
|
end
|
|
143
46
|
end
|