zeitwerk 2.6.0 → 2.6.15
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 +430 -51
- data/lib/zeitwerk/error.rb +6 -0
- data/lib/zeitwerk/explicit_namespace.rb +14 -10
- data/lib/zeitwerk/gem_inflector.rb +2 -2
- data/lib/zeitwerk/gem_loader.rb +12 -9
- data/lib/zeitwerk/internal.rb +12 -0
- data/lib/zeitwerk/kernel.rb +6 -7
- data/lib/zeitwerk/loader/callbacks.rb +25 -17
- data/lib/zeitwerk/loader/config.rb +95 -51
- data/lib/zeitwerk/loader/eager_load.rb +234 -0
- data/lib/zeitwerk/loader/helpers.rb +92 -21
- data/lib/zeitwerk/loader.rb +224 -129
- data/lib/zeitwerk/null_inflector.rb +5 -0
- data/lib/zeitwerk/registry.rb +2 -2
- data/lib/zeitwerk/version.rb +1 -1
- data/lib/zeitwerk.rb +2 -0
- metadata +6 -3
data/lib/zeitwerk/error.rb
CHANGED
@@ -11,28 +11,28 @@ module Zeitwerk
|
|
11
11
|
module ExplicitNamespace # :nodoc: all
|
12
12
|
class << self
|
13
13
|
include RealModName
|
14
|
+
extend Internal
|
14
15
|
|
15
16
|
# Maps constant paths that correspond to explicit namespaces according to
|
16
17
|
# the file system, to the loader responsible for them.
|
17
18
|
#
|
18
|
-
# @private
|
19
19
|
# @sig Hash[String, Zeitwerk::Loader]
|
20
20
|
attr_reader :cpaths
|
21
|
+
private :cpaths
|
21
22
|
|
22
|
-
# @private
|
23
23
|
# @sig Mutex
|
24
24
|
attr_reader :mutex
|
25
|
+
private :mutex
|
25
26
|
|
26
|
-
# @private
|
27
27
|
# @sig TracePoint
|
28
28
|
attr_reader :tracer
|
29
|
+
private :tracer
|
29
30
|
|
30
31
|
# Asserts `cpath` corresponds to an explicit namespace for which `loader`
|
31
32
|
# is responsible.
|
32
33
|
#
|
33
|
-
# @private
|
34
34
|
# @sig (String, Zeitwerk::Loader) -> void
|
35
|
-
def register(cpath, loader)
|
35
|
+
internal def register(cpath, loader)
|
36
36
|
mutex.synchronize do
|
37
37
|
cpaths[cpath] = loader
|
38
38
|
# We check enabled? because, looking at the C source code, enabling an
|
@@ -41,24 +41,28 @@ module Zeitwerk
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
# @private
|
45
44
|
# @sig (Zeitwerk::Loader) -> void
|
46
|
-
def unregister_loader(loader)
|
45
|
+
internal def unregister_loader(loader)
|
47
46
|
cpaths.delete_if { |_cpath, l| l == loader }
|
48
47
|
disable_tracer_if_unneeded
|
49
48
|
end
|
50
49
|
|
51
|
-
|
50
|
+
# This is an internal method only used by the test suite.
|
51
|
+
#
|
52
|
+
# @sig (String) -> bool
|
53
|
+
internal def registered?(cpath)
|
54
|
+
cpaths.key?(cpath)
|
55
|
+
end
|
52
56
|
|
53
57
|
# @sig () -> void
|
54
|
-
def disable_tracer_if_unneeded
|
58
|
+
private def disable_tracer_if_unneeded
|
55
59
|
mutex.synchronize do
|
56
60
|
tracer.disable if cpaths.empty?
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
60
64
|
# @sig (TracePoint) -> void
|
61
|
-
def tracepoint_class_callback(event)
|
65
|
+
private def tracepoint_class_callback(event)
|
62
66
|
# If the class is a singleton class, we won't do anything with it so we
|
63
67
|
# can bail out immediately. This is several orders of magnitude faster
|
64
68
|
# than accessing its name.
|
@@ -5,8 +5,8 @@ module Zeitwerk
|
|
5
5
|
# @sig (String) -> void
|
6
6
|
def initialize(root_file)
|
7
7
|
namespace = File.basename(root_file, ".rb")
|
8
|
-
|
9
|
-
@version_file = File.join(
|
8
|
+
root_dir = File.dirname(root_file)
|
9
|
+
@version_file = File.join(root_dir, namespace, "version.rb")
|
10
10
|
end
|
11
11
|
|
12
12
|
# @sig (String, String) -> String
|
data/lib/zeitwerk/gem_loader.rb
CHANGED
@@ -3,27 +3,31 @@
|
|
3
3
|
module Zeitwerk
|
4
4
|
# @private
|
5
5
|
class GemLoader < Loader
|
6
|
+
include RealModName
|
7
|
+
|
6
8
|
# Users should not create instances directly, the public interface is
|
7
9
|
# `Zeitwerk::Loader.for_gem`.
|
8
10
|
private_class_method :new
|
9
11
|
|
10
12
|
# @private
|
11
13
|
# @sig (String, bool) -> Zeitwerk::GemLoader
|
12
|
-
def self.
|
13
|
-
new(root_file, warn_on_extra_files: warn_on_extra_files)
|
14
|
+
def self.__new(root_file, namespace:, warn_on_extra_files:)
|
15
|
+
new(root_file, namespace: namespace, warn_on_extra_files: warn_on_extra_files)
|
14
16
|
end
|
15
17
|
|
16
18
|
# @sig (String, bool) -> void
|
17
|
-
def initialize(root_file, warn_on_extra_files:)
|
19
|
+
def initialize(root_file, namespace:, warn_on_extra_files:)
|
18
20
|
super()
|
19
21
|
|
20
|
-
@tag
|
22
|
+
@tag = File.basename(root_file, ".rb")
|
23
|
+
@tag = real_mod_name(namespace) + "-" + @tag unless namespace.equal?(Object)
|
24
|
+
|
21
25
|
@inflector = GemInflector.new(root_file)
|
22
26
|
@root_file = File.expand_path(root_file)
|
23
|
-
@
|
27
|
+
@root_dir = File.dirname(root_file)
|
24
28
|
@warn_on_extra_files = warn_on_extra_files
|
25
29
|
|
26
|
-
push_dir(@
|
30
|
+
push_dir(@root_dir, namespace: namespace)
|
27
31
|
end
|
28
32
|
|
29
33
|
# @sig () -> void
|
@@ -38,13 +42,12 @@ module Zeitwerk
|
|
38
42
|
def warn_on_extra_files
|
39
43
|
expected_namespace_dir = @root_file.delete_suffix(".rb")
|
40
44
|
|
41
|
-
ls(@
|
45
|
+
ls(@root_dir) do |basename, abspath, ftype|
|
42
46
|
next if abspath == @root_file
|
43
47
|
next if abspath == expected_namespace_dir
|
44
48
|
|
45
49
|
basename_without_ext = basename.delete_suffix(".rb")
|
46
|
-
cname = inflector.camelize(basename_without_ext, abspath)
|
47
|
-
ftype = dir?(abspath) ? "directory" : "file"
|
50
|
+
cname = inflector.camelize(basename_without_ext, abspath).to_sym
|
48
51
|
|
49
52
|
warn(<<~EOS)
|
50
53
|
WARNING: Zeitwerk defines the constant #{cname} after the #{ftype}
|
data/lib/zeitwerk/kernel.rb
CHANGED
@@ -14,21 +14,20 @@ module Kernel
|
|
14
14
|
# should not require anything. But if someone has legacy require calls around,
|
15
15
|
# they will work as expected, and in a compatible way. This feature is by now
|
16
16
|
# EXPERIMENTAL and UNDOCUMENTED.
|
17
|
-
#
|
18
|
-
# We cannot decorate with prepend + super because Kernel has already been
|
19
|
-
# included in Object, and changes in ancestors don't get propagated into
|
20
|
-
# already existing ancestor chains on Ruby < 3.0.
|
21
17
|
alias_method :zeitwerk_original_require, :require
|
18
|
+
class << self
|
19
|
+
alias_method :zeitwerk_original_require, :require
|
20
|
+
end
|
22
21
|
|
23
22
|
# @sig (String) -> true | false
|
24
23
|
def require(path)
|
25
24
|
if loader = Zeitwerk::Registry.loader_for(path)
|
26
25
|
if path.end_with?(".rb")
|
27
26
|
required = zeitwerk_original_require(path)
|
28
|
-
loader.
|
27
|
+
loader.__on_file_autoloaded(path) if required
|
29
28
|
required
|
30
29
|
else
|
31
|
-
loader.
|
30
|
+
loader.__on_dir_autoloaded(path)
|
32
31
|
true
|
33
32
|
end
|
34
33
|
else
|
@@ -36,7 +35,7 @@ module Kernel
|
|
36
35
|
if required
|
37
36
|
abspath = $LOADED_FEATURES.last
|
38
37
|
if loader = Zeitwerk::Registry.loader_for(abspath)
|
39
|
-
loader.
|
38
|
+
loader.__on_file_autoloaded(abspath)
|
40
39
|
end
|
41
40
|
end
|
42
41
|
required
|
@@ -2,38 +2,46 @@
|
|
2
2
|
|
3
3
|
module Zeitwerk::Loader::Callbacks
|
4
4
|
include Zeitwerk::RealModName
|
5
|
+
extend Zeitwerk::Internal
|
5
6
|
|
6
7
|
# Invoked from our decorated Kernel#require when a managed file is autoloaded.
|
7
8
|
#
|
8
|
-
# @private
|
9
9
|
# @sig (String) -> void
|
10
|
-
def on_file_autoloaded(file)
|
10
|
+
internal def on_file_autoloaded(file)
|
11
11
|
cref = autoloads.delete(file)
|
12
12
|
cpath = cpath(*cref)
|
13
13
|
|
14
|
-
# If reloading is enabled, we need to put this constant for unloading
|
15
|
-
# regardless of what cdef? says. In Ruby < 3.1 the internal state is not
|
16
|
-
# fully cleared. Module#constants still includes it, and you need to
|
17
|
-
# remove_const. See https://github.com/ruby/ruby/pull/4715.
|
18
|
-
to_unload[cpath] = [file, cref] if reloading_enabled?
|
19
14
|
Zeitwerk::Registry.unregister_autoload(file)
|
20
15
|
|
21
16
|
if cdef?(*cref)
|
22
17
|
log("constant #{cpath} loaded from file #{file}") if logger
|
18
|
+
to_unload[cpath] = [file, cref] if reloading_enabled?
|
23
19
|
run_on_load_callbacks(cpath, cget(*cref), file) unless on_load_callbacks.empty?
|
24
20
|
else
|
25
|
-
|
21
|
+
msg = "expected file #{file} to define constant #{cpath}, but didn't"
|
22
|
+
log(msg) if logger
|
23
|
+
|
24
|
+
# Ruby still keeps the autoload defined, but we remove it because the
|
25
|
+
# contract in Zeitwerk is more strict.
|
26
|
+
crem(*cref)
|
27
|
+
|
28
|
+
# Since the expected constant was not defined, there is nothing to unload.
|
29
|
+
# However, if the exception is rescued and reloading is enabled, we still
|
30
|
+
# need to deleted the file from $LOADED_FEATURES.
|
31
|
+
to_unload[cpath] = [file, cref] if reloading_enabled?
|
32
|
+
|
33
|
+
raise Zeitwerk::NameError.new(msg, cref.last)
|
26
34
|
end
|
27
35
|
end
|
28
36
|
|
29
37
|
# Invoked from our decorated Kernel#require when a managed directory is
|
30
38
|
# autoloaded.
|
31
39
|
#
|
32
|
-
# @private
|
33
40
|
# @sig (String) -> void
|
34
|
-
def on_dir_autoloaded(dir)
|
35
|
-
# Module#autoload does not serialize concurrent requires, and
|
36
|
-
# directories ourselves
|
41
|
+
internal def on_dir_autoloaded(dir)
|
42
|
+
# Module#autoload does not serialize concurrent requires in CRuby < 3.2, and
|
43
|
+
# we handle directories ourselves without going through Kernel#require, so
|
44
|
+
# the callback needs to account for concurrency.
|
37
45
|
#
|
38
46
|
# Multi-threading would introduce a race condition here in which thread t1
|
39
47
|
# autovivifies the module, and while autoloads for its children are being
|
@@ -42,8 +50,8 @@ module Zeitwerk::Loader::Callbacks
|
|
42
50
|
# Without the mutex and subsequent delete call, t2 would reset the module.
|
43
51
|
# That not only would reassign the constant (undesirable per se) but, worse,
|
44
52
|
# the module object created by t2 wouldn't have any of the autoloads for its
|
45
|
-
# children, since t1 would have correctly deleted its
|
46
|
-
|
53
|
+
# children, since t1 would have correctly deleted its namespace_dirs entry.
|
54
|
+
dirs_autoload_monitor.synchronize do
|
47
55
|
if cref = autoloads.delete(dir)
|
48
56
|
autovivified_module = cref[0].const_set(cref[1], Module.new)
|
49
57
|
cpath = autovivified_module.name
|
@@ -71,9 +79,9 @@ module Zeitwerk::Loader::Callbacks
|
|
71
79
|
# @private
|
72
80
|
# @sig (Module) -> void
|
73
81
|
def on_namespace_loaded(namespace)
|
74
|
-
if
|
75
|
-
|
76
|
-
|
82
|
+
if dirs = namespace_dirs.delete(real_mod_name(namespace))
|
83
|
+
dirs.each do |dir|
|
84
|
+
define_autoloads_for_dir(dir, namespace)
|
77
85
|
end
|
78
86
|
end
|
79
87
|
end
|
@@ -4,85 +4,91 @@ require "set"
|
|
4
4
|
require "securerandom"
|
5
5
|
|
6
6
|
module Zeitwerk::Loader::Config
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
extend Zeitwerk::Internal
|
8
|
+
include Zeitwerk::RealModName
|
9
|
+
|
10
|
+
# @sig #camelize
|
11
|
+
attr_accessor :inflector
|
12
|
+
|
13
|
+
# @sig #call | #debug | nil
|
14
|
+
attr_accessor :logger
|
15
|
+
|
16
|
+
# Absolute paths of the root directories, mapped to their respective root namespaces:
|
10
17
|
#
|
11
|
-
# "/Users/fxn/blog/app/
|
12
|
-
# "/Users/fxn/blog/app/
|
18
|
+
# "/Users/fxn/blog/app/channels" => Object,
|
19
|
+
# "/Users/fxn/blog/app/adapters" => ActiveJob::QueueAdapters,
|
13
20
|
# ...
|
14
21
|
#
|
22
|
+
# Stored in a hash to preserve order, easily handle duplicates, and have a
|
23
|
+
# fast lookup by directory.
|
24
|
+
#
|
15
25
|
# This is a private collection maintained by the loader. The public
|
16
26
|
# interface for it is `push_dir` and `dirs`.
|
17
27
|
#
|
18
|
-
# @
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
# @sig #camelize
|
23
|
-
attr_accessor :inflector
|
28
|
+
# @sig Hash[String, Module]
|
29
|
+
attr_reader :roots
|
30
|
+
internal :roots
|
24
31
|
|
25
32
|
# Absolute paths of files, directories, or glob patterns to be totally
|
26
33
|
# ignored.
|
27
34
|
#
|
28
|
-
# @private
|
29
35
|
# @sig Set[String]
|
30
36
|
attr_reader :ignored_glob_patterns
|
37
|
+
private :ignored_glob_patterns
|
31
38
|
|
32
39
|
# The actual collection of absolute file and directory names at the time the
|
33
40
|
# ignored glob patterns were expanded. Computed on setup, and recomputed on
|
34
41
|
# reload.
|
35
42
|
#
|
36
|
-
# @private
|
37
43
|
# @sig Set[String]
|
38
44
|
attr_reader :ignored_paths
|
45
|
+
private :ignored_paths
|
39
46
|
|
40
47
|
# Absolute paths of directories or glob patterns to be collapsed.
|
41
48
|
#
|
42
|
-
# @private
|
43
49
|
# @sig Set[String]
|
44
50
|
attr_reader :collapse_glob_patterns
|
51
|
+
private :collapse_glob_patterns
|
45
52
|
|
46
53
|
# The actual collection of absolute directory names at the time the collapse
|
47
54
|
# glob patterns were expanded. Computed on setup, and recomputed on reload.
|
48
55
|
#
|
49
|
-
# @private
|
50
56
|
# @sig Set[String]
|
51
57
|
attr_reader :collapse_dirs
|
58
|
+
private :collapse_dirs
|
52
59
|
|
53
60
|
# Absolute paths of files or directories not to be eager loaded.
|
54
61
|
#
|
55
|
-
# @private
|
56
62
|
# @sig Set[String]
|
57
63
|
attr_reader :eager_load_exclusions
|
64
|
+
private :eager_load_exclusions
|
58
65
|
|
59
66
|
# User-oriented callbacks to be fired on setup and on reload.
|
60
67
|
#
|
61
|
-
# @private
|
62
68
|
# @sig Array[{ () -> void }]
|
63
69
|
attr_reader :on_setup_callbacks
|
70
|
+
private :on_setup_callbacks
|
64
71
|
|
65
72
|
# User-oriented callbacks to be fired when a constant is loaded.
|
66
73
|
#
|
67
|
-
# @private
|
68
74
|
# @sig Hash[String, Array[{ (Object, String) -> void }]]
|
69
75
|
# Hash[Symbol, Array[{ (String, Object, String) -> void }]]
|
70
76
|
attr_reader :on_load_callbacks
|
77
|
+
private :on_load_callbacks
|
71
78
|
|
72
79
|
# User-oriented callbacks to be fired before constants are removed.
|
73
80
|
#
|
74
|
-
# @private
|
75
81
|
# @sig Hash[String, Array[{ (Object, String) -> void }]]
|
76
82
|
# Hash[Symbol, Array[{ (String, Object, String) -> void }]]
|
77
83
|
attr_reader :on_unload_callbacks
|
78
|
-
|
79
|
-
# @sig #call | #debug | nil
|
80
|
-
attr_accessor :logger
|
84
|
+
private :on_unload_callbacks
|
81
85
|
|
82
86
|
def initialize
|
83
|
-
@initialized_at = Time.now
|
84
|
-
@root_dirs = {}
|
85
87
|
@inflector = Zeitwerk::Inflector.new
|
88
|
+
@logger = self.class.default_logger
|
89
|
+
@tag = SecureRandom.hex(3)
|
90
|
+
@initialized_at = Time.now
|
91
|
+
@roots = {}
|
86
92
|
@ignored_glob_patterns = Set.new
|
87
93
|
@ignored_paths = Set.new
|
88
94
|
@collapse_glob_patterns = Set.new
|
@@ -92,8 +98,6 @@ module Zeitwerk::Loader::Config
|
|
92
98
|
@on_setup_callbacks = []
|
93
99
|
@on_load_callbacks = {}
|
94
100
|
@on_unload_callbacks = {}
|
95
|
-
@logger = self.class.default_logger
|
96
|
-
@tag = SecureRandom.hex(3)
|
97
101
|
end
|
98
102
|
|
99
103
|
# Pushes `path` to the list of root directories.
|
@@ -105,15 +109,18 @@ module Zeitwerk::Loader::Config
|
|
105
109
|
# @raise [Zeitwerk::Error]
|
106
110
|
# @sig (String | Pathname, Module) -> void
|
107
111
|
def push_dir(path, namespace: Object)
|
108
|
-
# Note that Class < Module.
|
109
|
-
unless namespace.is_a?(Module)
|
112
|
+
unless namespace.is_a?(Module) # Note that Class < Module.
|
110
113
|
raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module object, should be"
|
111
114
|
end
|
112
115
|
|
116
|
+
unless real_mod_name(namespace)
|
117
|
+
raise Zeitwerk::Error, "root namespaces cannot be anonymous"
|
118
|
+
end
|
119
|
+
|
113
120
|
abspath = File.expand_path(path)
|
114
121
|
if dir?(abspath)
|
115
122
|
raise_if_conflicting_directory(abspath)
|
116
|
-
|
123
|
+
roots[abspath] = namespace
|
117
124
|
else
|
118
125
|
raise Zeitwerk::Error, "the root directory #{abspath} does not exist"
|
119
126
|
end
|
@@ -136,12 +143,30 @@ module Zeitwerk::Loader::Config
|
|
136
143
|
@tag = tag.to_s
|
137
144
|
end
|
138
145
|
|
139
|
-
#
|
140
|
-
#
|
146
|
+
# If `namespaces` is falsey (default), returns an array with the absolute
|
147
|
+
# paths of the root directories as strings. If truthy, returns a hash table
|
148
|
+
# instead. Keys are the absolute paths of the root directories as strings,
|
149
|
+
# values are their corresponding namespaces, class or module objects.
|
141
150
|
#
|
142
|
-
#
|
143
|
-
|
144
|
-
|
151
|
+
# If `ignored` is falsey (default), ignored root directories are filtered out.
|
152
|
+
#
|
153
|
+
# These are read-only collections, please add to them with `push_dir`.
|
154
|
+
#
|
155
|
+
# @sig () -> Array[String] | Hash[String, Module]
|
156
|
+
def dirs(namespaces: false, ignored: false)
|
157
|
+
if namespaces
|
158
|
+
if ignored || ignored_paths.empty?
|
159
|
+
roots.clone
|
160
|
+
else
|
161
|
+
roots.reject { |root_dir, _namespace| ignored_path?(root_dir) }
|
162
|
+
end
|
163
|
+
else
|
164
|
+
if ignored || ignored_paths.empty?
|
165
|
+
roots.keys
|
166
|
+
else
|
167
|
+
roots.keys.reject { |root_dir| ignored_path?(root_dir) }
|
168
|
+
end
|
169
|
+
end.freeze
|
145
170
|
end
|
146
171
|
|
147
172
|
# You need to call this method before setup in order to be able to reload.
|
@@ -264,57 +289,76 @@ module Zeitwerk::Loader::Config
|
|
264
289
|
@logger = ->(msg) { puts msg }
|
265
290
|
end
|
266
291
|
|
267
|
-
#
|
292
|
+
# Returns true if the argument has been configured to be ignored, or is a
|
293
|
+
# descendant of an ignored directory.
|
294
|
+
#
|
268
295
|
# @sig (String) -> bool
|
269
|
-
def ignores?(abspath)
|
270
|
-
|
271
|
-
|
296
|
+
internal def ignores?(abspath)
|
297
|
+
# Common use case.
|
298
|
+
return false if ignored_paths.empty?
|
299
|
+
|
300
|
+
walk_up(abspath) do |path|
|
301
|
+
return true if ignored_path?(path)
|
302
|
+
return false if roots.key?(path)
|
272
303
|
end
|
304
|
+
|
305
|
+
false
|
273
306
|
end
|
274
307
|
|
275
|
-
|
308
|
+
# @sig (String) -> bool
|
309
|
+
private def ignored_path?(abspath)
|
310
|
+
ignored_paths.member?(abspath)
|
311
|
+
end
|
276
312
|
|
277
313
|
# @sig () -> Array[String]
|
278
|
-
def
|
279
|
-
|
280
|
-
!dir?(root_dir) ||
|
314
|
+
private def actual_roots
|
315
|
+
roots.reject do |root_dir, _root_namespace|
|
316
|
+
!dir?(root_dir) || ignored_path?(root_dir)
|
281
317
|
end
|
282
318
|
end
|
283
319
|
|
284
320
|
# @sig (String) -> bool
|
285
|
-
def root_dir?(dir)
|
286
|
-
|
321
|
+
private def root_dir?(dir)
|
322
|
+
roots.key?(dir)
|
287
323
|
end
|
288
324
|
|
289
325
|
# @sig (String) -> bool
|
290
|
-
def excluded_from_eager_load?(abspath)
|
291
|
-
|
326
|
+
private def excluded_from_eager_load?(abspath)
|
327
|
+
# Optimize this common use case.
|
328
|
+
return false if eager_load_exclusions.empty?
|
329
|
+
|
330
|
+
walk_up(abspath) do |path|
|
331
|
+
return true if eager_load_exclusions.member?(path)
|
332
|
+
return false if roots.key?(path)
|
333
|
+
end
|
334
|
+
|
335
|
+
false
|
292
336
|
end
|
293
337
|
|
294
338
|
# @sig (String) -> bool
|
295
|
-
def collapse?(dir)
|
339
|
+
private def collapse?(dir)
|
296
340
|
collapse_dirs.member?(dir)
|
297
341
|
end
|
298
342
|
|
299
343
|
# @sig (String | Pathname | Array[String | Pathname]) -> Array[String]
|
300
|
-
def expand_paths(paths)
|
344
|
+
private def expand_paths(paths)
|
301
345
|
paths.flatten.map! { |path| File.expand_path(path) }
|
302
346
|
end
|
303
347
|
|
304
348
|
# @sig (Array[String]) -> Array[String]
|
305
|
-
def expand_glob_patterns(glob_patterns)
|
349
|
+
private def expand_glob_patterns(glob_patterns)
|
306
350
|
# Note that Dir.glob works with regular file names just fine. That is,
|
307
351
|
# glob patterns technically need no wildcards.
|
308
352
|
glob_patterns.flat_map { |glob_pattern| Dir.glob(glob_pattern) }
|
309
353
|
end
|
310
354
|
|
311
355
|
# @sig () -> void
|
312
|
-
def recompute_ignored_paths
|
356
|
+
private def recompute_ignored_paths
|
313
357
|
ignored_paths.replace(expand_glob_patterns(ignored_glob_patterns))
|
314
358
|
end
|
315
359
|
|
316
360
|
# @sig () -> void
|
317
|
-
def recompute_collapse_dirs
|
361
|
+
private def recompute_collapse_dirs
|
318
362
|
collapse_dirs.replace(expand_glob_patterns(collapse_glob_patterns))
|
319
363
|
end
|
320
364
|
end
|