11# frozen_string_literal: true
22
33module Zeitwerk ::Loader ::Helpers
4- # --- Logging -----------------------------------------------------------------------------------
4+ CNAME_VALIDATOR = Module . new #: Module
5+ private_constant :CNAME_VALIDATOR
56
67 #: (to_s() -> String) -> void
78 private def log ( message )
89 method_name = logger . respond_to? ( :debug ) ? :debug : :call
910 logger . send ( method_name , "Zeitwerk@#{ tag } : #{ message } " )
1011 end
1112
12- # --- Files and directories ---------------------------------------------------------------------
13-
14- #: (String) { (String, String, Symbol) -> void } -> void
15- private def ls ( dir )
16- children = scan_dir ( dir )
17-
18- # The order in which a directory is listed depends on the file system.
19- #
20- # Since client code may run on different platforms, it seems convenient to
21- # sort directory entries. This provides more deterministic behavior, with
22- # consistent eager loading in particular.
23- children . sort_by! ( &:first )
24-
25- children . each do |basename , abspath , ftype |
26- if ftype == :directory && !has_at_least_one_ruby_file? ( abspath )
27- log ( "directory #{ abspath } is ignored because it has no Ruby files" ) if logger
28- next
29- end
30-
31- yield basename , abspath , ftype
32- end
33- end
34-
35- # Looks for a Ruby file using breadth-first search. This type of search is
36- # important to list as less directories as possible and return fast in the
37- # common case in which there are Ruby files.
38- #
39- #: (String) -> bool
40- private def has_at_least_one_ruby_file? ( dir )
41- to_visit = [ dir ]
42-
43- while ( dir = to_visit . shift )
44- scan_dir ( dir ) do |_ , abspath , ftype |
45- return true if ftype == :file
46- to_visit << abspath
47- end
48- end
49-
50- false
51- end
52-
53- # This is a low-level method to scan directories. It filters out some stuff
54- # the loader is never interested in, and passes the ftype up. The rest of the
55- # library should generally use `ls`.
56- #
57- # Keep an eye on https://bugs.ruby-lang.org/issues/21800.
58- #
59- #: (String) { (String, String, Symbol) -> void } -> void
60- #: (String) -> [[String, String, Symbol]]
61- private def scan_dir ( dir )
62- children = [ ] unless block_given?
63-
64- Dir . each_child ( dir ) do |basename |
65- next if hidden? ( basename )
66-
67- abspath = File . join ( dir , basename )
68- next if ignored_path? ( abspath )
69-
70- ftype = supported_ftype? ( abspath )
71- next unless ftype
72-
73- # Conceptually, root directories start separate trees.
74- next if :directory == ftype && root_dir? ( abspath )
75-
76- # We freeze abspath because that saves allocations when passed later to
77- # File methods. See https://github.com/fxn/zeitwerk/pull/125.
78- if block_given?
79- yield basename , abspath . freeze , ftype
80- else
81- children << [ basename , abspath . freeze , ftype ]
82- end
83- end
84-
85- children unless block_given?
86- end
87-
88- # Encodes the documented conventions.
89- #
90- #: (String) -> Symbol?
91- private def supported_ftype? ( abspath )
92- if ruby? ( abspath )
93- :file # By convention, we can avoid a syscall here.
94- elsif dir? ( abspath )
95- :directory
96- end
97- end
98-
99- #: (String) -> bool
100- private def ruby? ( path )
101- path . end_with? ( ".rb" )
102- end
103-
104- #: (String) -> bool
105- private def dir? ( path )
106- File . directory? ( path )
107- end
108-
109- #: (String) -> bool
110- private def hidden? ( basename )
111- basename . start_with? ( "." )
112- end
113-
114- #: (String) { (String) -> void } -> void
115- private def walk_up ( abspath )
116- loop do
117- yield abspath
118- abspath , basename = File . split ( abspath )
119- break if basename == "/"
120- end
121- end
122-
123- # --- Inflection --------------------------------------------------------------------------------
124-
125- CNAME_VALIDATOR = Module . new #: Module
126- private_constant :CNAME_VALIDATOR
127-
12813 #: (String, String) -> Symbol ! Zeitwerk::NameError
12914 private def cname_for ( basename , abspath )
13015 cname = inflector . camelize ( basename , abspath )
@@ -146,7 +31,7 @@ module Zeitwerk::Loader::Helpers
14631 begin
14732 CNAME_VALIDATOR . const_defined? ( cname , false )
14833 rescue ::NameError => error
149- path_type = ruby ?( abspath ) ? "file" : "directory"
34+ path_type = rb_extension ?( abspath ) ? "file" : "directory"
15035
15136 raise Zeitwerk ::NameError . new ( <<~MESSAGE , error . name )
15237 #{ error . message } inferred by #{ inflector . class } from #{ path_type }
0 commit comments