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.
@@ -1,143 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk::Loader::Helpers
4
- # --- Logging -----------------------------------------------------------------------------------
4
+ CNAME_VALIDATOR = Module.new #: Module
5
+ private_constant :CNAME_VALIDATOR
5
6
 
6
- # @sig (String) -> void
7
- private def log(message)
8
- method_name = logger.respond_to?(:debug) ? :debug : :call
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
- # We freeze abspath because that saves allocations when passed later to
39
- # File methods. See #125.
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
- while dir = to_visit.shift
49
- ls(dir) do |_basename, abspath|
50
- if dir?(abspath)
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
- false
59
- end
19
+ #{abspath}
60
20
 
61
- # @sig (String) -> bool
62
- private def ruby?(path)
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
- # --- Constants ---------------------------------------------------------------------------------
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
- # The autoload? predicate takes into account the ancestor chain of the
88
- # receiver, like const_defined? and other methods in the constants API do.
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
- # @sig (Module, Symbol) -> String
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
- # @sig (Module, Symbol) -> bool
128
- private def cdef?(parent, cname)
129
- parent.const_defined?(cname, false)
130
- end
35
+ Possible ways to address this:
131
36
 
132
- # @raise [NameError]
133
- # @sig (Module, Symbol) -> Object
134
- private def cget(parent, cname)
135
- parent.const_get(cname, false)
136
- end
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
- # @raise [NameError]
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