Skip to content

Commit 42d013a

Browse files
committed
[GR-14873] Add support for base keyword to Dir.{[], glob}.
PullRequest: truffleruby/762
2 parents bd034d6 + 6691d49 commit 42d013a

File tree

6 files changed

+68
-68
lines changed

6 files changed

+68
-68
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Compatibility:
3434

3535
* Yield different number of arguments for `Hash#each` and `Hash#each_pair` based
3636
on the block arity like MRI (#1629).
37+
* Add support for the `base` keyword argument to `Dir.{[], glob}`.
3738

3839
# 1.0 RC 14
3940

lib/mri/rubygems/util.rb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,11 @@ def self.traverse_parents(directory, &block)
121121
# returning absolute paths to the matching files.
122122

123123
def self.glob_files_in_dir(glob, base_path)
124-
# TruffleRuby bug doesn't support base:, and gem tests remove RUBY_ENGINE
125-
#if RUBY_VERSION >= "2.5"
126-
# Dir.glob(glob, base: base_path).map! {|f| File.expand_path(f, base_path) }
127-
#else
124+
if RUBY_VERSION >= "2.5"
125+
Dir.glob(glob, base: base_path).map! {|f| File.expand_path(f, base_path) }
126+
else
128127
Dir.glob(File.expand_path(glob, base_path))
129-
#end
128+
end
130129
end
131130

132131
end

spec/tags/core/dir/element_reference_tags.txt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,3 @@ fails:Dir.[] matches non-dotfiles with '*'
44
fails:Dir.[] matches non-dotfiles in the current directory with '**'
55
fails:Dir.[] recursively matches any nondot subdirectories with '**/'
66
fails:Dir.[] matches dot or non-dotfiles with '{,.}*'
7-
fails:Dir.[] :base option passed matches entries only from within the specified directory
8-
fails:Dir.[] :base option passed accepts both relative and absolute paths
9-
fails:Dir.[] :base option passed returns [] if specified path does not exist
10-
fails:Dir.[] :base option passed returns [] if specified path is a file
11-
fails:Dir.[] :base option passed handles '' as current directory path
12-
fails:Dir.[] :base option passed handles nil as current directory path

spec/tags/core/dir/glob_tags.txt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,3 @@ fails:Dir.glob matches non-dotfiles with '*'
44
fails:Dir.glob matches non-dotfiles in the current directory with '**'
55
fails:Dir.glob recursively matches any nondot subdirectories with '**/'
66
fails:Dir.glob matches dot or non-dotfiles with '{,.}*'
7-
fails:Dir.glob :base option passed matches entries only from within the specified directory
8-
fails:Dir.glob :base option passed accepts both relative and absolute paths
9-
fails:Dir.glob :base option passed returns [] if specified path does not exist
10-
fails:Dir.glob :base option passed returns [] if specified path is a file
11-
fails:Dir.glob :base option passed handles '' as current directory path
12-
fails:Dir.glob :base option passed handles nil as current directory path

src/main/ruby/core/dir.rb

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,17 +209,17 @@ def home(user=nil)
209209
PrivateFile.expand_path("~#{user}")
210210
end
211211

212-
def [](*patterns)
212+
def [](*patterns, base: nil)
213213
if patterns.size == 1
214214
pattern = Truffle::Type.coerce_to_path(patterns[0], false)
215215
return [] if pattern.empty?
216216
patterns = glob_split pattern
217217
end
218218

219-
glob patterns
219+
glob patterns, base: base
220220
end
221221

222-
def glob(pattern, flags=0, &block)
222+
def glob(pattern, flags=0, base: nil, &block)
223223
if pattern.kind_of? Array
224224
patterns = pattern
225225
else
@@ -233,10 +233,17 @@ def glob(pattern, flags=0, &block)
233233
matches = []
234234
index = 0
235235

236+
normalized_base = if base.nil?
237+
nil
238+
else
239+
path = Truffle::Type.coerce_to_path(base)
240+
path.empty? ? '.' : path
241+
end
242+
236243
patterns.each do |pat|
237244
pat = Truffle::Type.coerce_to_path pat
238245
enc = Truffle::Type.ascii_compatible_encoding pat
239-
Dir::Glob.glob pat, flags, matches
246+
Dir::Glob.glob normalized_base, pat, flags, matches
240247

241248
total = matches.size
242249
while index < total

src/main/ruby/core/dir_glob.rb

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,7 @@ def separator
4646
end
4747

4848
def path_join(parent, ent)
49-
return ent unless parent
50-
if parent == '/'
51-
"/#{ent}"
52-
else
53-
"#{parent}#{separator}#{ent}"
54-
end
49+
Dir::Glob.path_join(parent, ent, separator)
5550
end
5651
end
5752

@@ -61,12 +56,12 @@ def initialize(nxt, flags, dir)
6156
@dir = dir
6257
end
6358

64-
def call(matches, path)
59+
def call(matches, path, glob_base_dir)
6560
full = path_join(path, @dir)
6661

6762
# Don't check if full exists. It just costs us time
6863
# and the downstream node will be able to check properly.
69-
@next.call matches, full
64+
@next.call matches, full, glob_base_dir
7065
end
7166
end
7267

@@ -76,31 +71,31 @@ def initialize(nxt, flags, name)
7671
@name = name
7772
end
7873

79-
def call(matches, parent)
74+
def call(matches, parent, glob_base_dir)
8075
path = path_join(parent, @name)
8176

82-
if File.exist? path
77+
if File.exist? path_join(glob_base_dir, path)
8378
matches << path
8479
end
8580
end
8681
end
8782

8883
class RootDirectory < Node
89-
def call(matches, path)
90-
@next.call matches, '/'
84+
def call(matches, path, glob_base_dir)
85+
@next.call matches, '/', glob_base_dir
9186
end
9287
end
9388

9489
class RecursiveDirectories < Node
95-
def call(matches, start)
96-
return if !start || !File.exist?(start)
90+
def call(matches, start, glob_base_dir)
91+
return if !start || !File.exist?(path_join(glob_base_dir, start))
9792

9893
# Even though the recursive entry is zero width
9994
# in this case, its left separator is still the
10095
# dominant one, so we fix things up to use it.
10196
switched = @next.dup
10297
switched.separator = @separator
103-
switched.call matches, start
98+
switched.call matches, start, glob_base_dir
10499

105100
stack = [start]
106101

@@ -109,19 +104,19 @@ def call(matches, start)
109104
until stack.empty?
110105
path = stack.pop
111106
begin
112-
dir = Dir.new(path)
107+
dir = Dir.new(path_join(glob_base_dir, path))
113108
rescue Errno::ENOTDIR
114109
next
115110
end
116111

117112
while ent = dir.read
118113
next if ent == '.' || ent == '..'
119114
full = path_join(path, ent)
120-
mode = Truffle::POSIX.truffleposix_lstat_mode(full)
115+
mode = Truffle::POSIX.truffleposix_lstat_mode(path_join(glob_base_dir, full))
121116

122117
if Truffle::StatOperations.directory?(mode) and (allow_dots or ent.getbyte(0) != 46) # ?.
123118
stack << full
124-
@next.call matches, full
119+
@next.call matches, full, glob_base_dir
125120
end
126121
end
127122
dir.close
@@ -130,7 +125,7 @@ def call(matches, start)
130125
end
131126

132127
class StartRecursiveDirectories < Node
133-
def call(matches, start)
128+
def call(matches, start, glob_base_dir)
134129
raise 'invalid usage' if start
135130

136131
# Even though the recursive entry is zero width
@@ -139,38 +134,38 @@ def call(matches, start)
139134
if @separator
140135
switched = @next.dup
141136
switched.separator = @separator
142-
switched.call matches, start
137+
switched.call matches, start, glob_base_dir
143138
else
144-
@next.call matches, start
139+
@next.call matches, start, glob_base_dir
145140
end
146141

147142
stack = []
148143

149144
allow_dots = ((@flags & File::FNM_DOTMATCH) != 0)
150145

151-
dir = Dir.new('.')
146+
dir = Dir.new(path_join(glob_base_dir, '.'))
152147
while ent = dir.read
153148
next if ent == '.' || ent == '..'
154-
mode = Truffle::POSIX.truffleposix_lstat_mode(ent)
149+
mode = Truffle::POSIX.truffleposix_lstat_mode(path_join(glob_base_dir, ent))
155150

156151
if Truffle::StatOperations.directory?(mode) and (allow_dots or ent.getbyte(0) != 46) # ?.
157152
stack << ent
158-
@next.call matches, ent
153+
@next.call matches, ent, glob_base_dir
159154
end
160155
end
161156
dir.close
162157

163158
until stack.empty?
164159
path = stack.pop
165-
dir = Dir.new(path)
160+
dir = Dir.new(path_join(glob_base_dir, path))
166161
while ent = dir.read
167162
next if ent == '.' || ent == '..'
168163
full = path_join(path, ent)
169-
mode = Truffle::POSIX.truffleposix_lstat_mode(full)
164+
mode = Truffle::POSIX.truffleposix_lstat_mode(path_join(glob_base_dir, full))
170165

171166
if Truffle::StatOperations.directory?(mode) and ent.getbyte(0) != 46 # ?.
172167
stack << full
173-
@next.call matches, full
168+
@next.call matches, full, glob_base_dir
174169
end
175170
end
176171
dir.close
@@ -196,16 +191,16 @@ def initialize(nxt, flags, glob)
196191
@glob.gsub! '**', '*'
197192
end
198193

199-
def call(matches, path)
200-
return if path and !File.exist?("#{path}/.")
194+
def call(matches, path, glob_base_dir)
195+
return if path and !File.exist?(path_join(glob_base_dir, "#{path}/."))
201196

202-
dir = Dir.new(path ? path : '.')
197+
dir = Dir.new(path_join(glob_base_dir, path ? path : '.'))
203198
while ent = dir.read
204199
if match? ent
205200
full = path_join(path, ent)
206201

207-
if File.directory? full
208-
@next.call matches, full
202+
if File.directory? path_join(glob_base_dir, full)
203+
@next.call matches, full, glob_base_dir
209204
end
210205
end
211206
end
@@ -214,11 +209,11 @@ def call(matches, path)
214209
end
215210

216211
class EntryMatch < Match
217-
def call(matches, path)
218-
return if path and !File.exist?("#{path}/.")
212+
def call(matches, path, glob_base_dir)
213+
return if path and !File.exist?("#{path_join(glob_base_dir, path)}/.")
219214

220215
begin
221-
dir = Dir.new(path ? path : '.')
216+
dir = Dir.new(path_join(glob_base_dir, path ? path : '.'))
222217
rescue SystemCallError
223218
return
224219
end
@@ -233,8 +228,8 @@ def call(matches, path)
233228
end
234229

235230
class DirectoriesOnly < Node
236-
def call(matches, path)
237-
if path and File.exist?("#{path}/.")
231+
def call(matches, path, glob_base_dir)
232+
if path and File.exist?("#{path_join(glob_base_dir, path)}/.")
238233
matches << "#{path}/"
239234
end
240235
end
@@ -322,21 +317,31 @@ def self.single_compile(glob, flags=0)
322317
last
323318
end
324319

325-
def self.run(node, all_matches=[])
320+
def self.run(node, all_matches, glob_base_dir)
326321
if ConstantEntry === node
327-
node.call all_matches, nil
322+
node.call all_matches, nil, glob_base_dir
328323
else
329324
matches = []
330-
node.call matches, nil
325+
node.call matches, nil, glob_base_dir
331326
# Truffle: ensure glob'd files are always sorted in consistent order,
332327
# it avoids headaches due to platform differences (OS X is sorted, Linux not).
333328
matches.sort!
334329
all_matches.concat(matches)
335330
end
336331
end
337332

338-
def self.glob(pattern, flags, matches=[])
339-
# Rubygems uses Dir[] as a glorified File.exist? to check for multiple
333+
def self.path_join(parent, entry, separator = '/')
334+
return entry unless parent
335+
336+
if parent == '/'
337+
"/#{entry}"
338+
else
339+
"#{parent}#{separator}#{entry}"
340+
end
341+
end
342+
343+
def self.glob(base_dir, pattern, flags, matches)
344+
# Rubygems uses Dir[] as a glorified File.exist? to check for multiple
340345
# extensions. So we went ahead and sped up that specific case.
341346

342347
if flags == 0 and m = TRAILING_BRACES.match(pattern)
@@ -349,17 +354,17 @@ def self.glob(pattern, flags, matches=[])
349354

350355
braces.split(',').each do |s|
351356
path = "#{stem}#{s}"
352-
if File.exist? path
357+
if File.exist? path_join(base_dir, path)
353358
matches << path
354359
end
355360
end
356361

357362
# Split strips an empty closing part, so we need to add it back in
358363
if braces.getbyte(-1) == 44 # ?,
359-
matches << stem if File.exist? stem
364+
matches << stem if File.exist? path_join(base_dir, stem)
360365
end
361366
else
362-
matches << pattern if File.exist?(pattern)
367+
matches << pattern if File.exist?(path_join(base_dir, pattern))
363368
end
364369

365370
return matches
@@ -370,10 +375,10 @@ def self.glob(pattern, flags, matches=[])
370375
patterns = compile(pattern, left_brace_index, flags)
371376

372377
patterns.each do |node|
373-
run node, matches
378+
run node, matches, base_dir
374379
end
375380
elsif node = single_compile(pattern, flags)
376-
run node, matches
381+
run node, matches, base_dir
377382
else
378383
matches
379384
end

0 commit comments

Comments
 (0)