pathname2 1.8.4 → 2.0.0
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
- checksums.yaml.gz.sig +0 -0
- data/CHANGES.md +7 -0
- data/Gemfile +2 -3
- data/MANIFEST.md +2 -2
- data/README.md +6 -0
- data/Rakefile +227 -222
- data/benchmarks/bench_pathname2.rb +127 -0
- data/examples/{example_pathname.rb → example_pathname2.rb} +7 -7
- data/lib/pathname2.rb +160 -169
- data/pathname2.gemspec +13 -9
- data/test/{test_pathname.rb → test_pathname2.rb} +67 -63
- data/test/test_version.rb +3 -3
- data/test/windows/test_append.rb +7 -7
- data/test/windows/test_aref.rb +3 -3
- data/test/windows/test_ascend.rb +5 -5
- data/test/windows/test_children.rb +7 -7
- data/test/windows/test_clean.rb +17 -17
- data/test/windows/test_clean_bang.rb +17 -17
- data/test/windows/test_constructor.rb +12 -12
- data/test/windows/test_descend.rb +5 -5
- data/test/windows/test_drive_number.rb +13 -13
- data/test/windows/test_each.rb +3 -3
- data/test/windows/test_facade.rb +6 -6
- data/test/windows/test_is_absolute.rb +8 -8
- data/test/windows/test_is_relative.rb +7 -7
- data/test/windows/test_is_root.rb +8 -8
- data/test/windows/test_is_unc.rb +18 -18
- data/test/windows/test_join.rb +8 -8
- data/test/windows/test_long_path.rb +6 -6
- data/test/windows/test_misc.rb +7 -7
- data/test/windows/test_parent.rb +9 -9
- data/test/windows/test_pstrip.rb +9 -9
- data/test/windows/test_pstrip_bang.rb +10 -10
- data/test/windows/test_realpath.rb +5 -5
- data/test/windows/test_relative_path_from.rb +4 -4
- data/test/windows/test_root.rb +14 -14
- data/test/windows/test_short_path.rb +6 -6
- data/test/windows/test_to_a.rb +12 -12
- data/test/windows/test_undecorate.rb +12 -12
- data/test/windows/test_undecorate_bang.rb +13 -13
- data.tar.gz.sig +0 -0
- metadata +61 -54
- metadata.gz.sig +0 -0
- data/benchmarks/bench_pathname.rb +0 -127
data/lib/pathname2.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# == Synopsis
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
# relative or absolute.
|
|
3
|
+
# Pathname2 represents a path name on a filesystem. A path name can be
|
|
4
|
+
# relative or absolute. It does not matter whether the path exists or not.
|
|
5
5
|
#
|
|
6
6
|
# All functionality from File, FileTest, and Dir is included, using a facade
|
|
7
7
|
# pattern.
|
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
# require "pathname2"
|
|
15
15
|
#
|
|
16
16
|
# # Unix
|
|
17
|
-
# path1 =
|
|
18
|
-
# path2 =
|
|
17
|
+
# path1 = Pathname2.new("/foo/bar/baz")
|
|
18
|
+
# path2 = Pathname2.new("../zap")
|
|
19
19
|
#
|
|
20
20
|
# path1 + path2 # "/foo/bar/zap"
|
|
21
21
|
# path1.dirname # "/foo/bar"
|
|
22
22
|
#
|
|
23
23
|
# # Windows
|
|
24
|
-
# path1 =
|
|
25
|
-
# path2 =
|
|
24
|
+
# path1 = Pathname2.new("C:\\foo\\bar\\baz")
|
|
25
|
+
# path2 = Pathname2.new("..\\zap")
|
|
26
26
|
#
|
|
27
27
|
# path1 + path2 # "C:\\foo\\bar\\zap"
|
|
28
28
|
# path1.exists? # Does the path exist?
|
|
@@ -37,34 +37,31 @@ if File::ALT_SEPARATOR
|
|
|
37
37
|
# Convenience method for converting strings to UTF-16LE for wide character
|
|
38
38
|
# functions that require it.
|
|
39
39
|
def wincode
|
|
40
|
-
if
|
|
41
|
-
temp =
|
|
40
|
+
if encoding.name != 'UTF-16LE'
|
|
41
|
+
temp = dup
|
|
42
42
|
(temp.tr(File::SEPARATOR, File::ALT_SEPARATOR) << 0.chr).encode('UTF-16LE')
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
Object.send(:remove_const, :Pathname) if defined?(Pathname)
|
|
50
|
-
|
|
51
|
-
class Pathname < String
|
|
48
|
+
class Pathname2 < String
|
|
52
49
|
class Error < StandardError; end
|
|
53
50
|
extend Facade
|
|
54
51
|
|
|
55
52
|
undef_method :pretty_print
|
|
56
53
|
|
|
57
|
-
facade File, File.methods(false).map
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
facade File, File.methods(false).map(&:to_sym) - %i[
|
|
55
|
+
chmod lchmod chown lchown dirname fnmatch fnmatch?
|
|
56
|
+
link open realpath rename symlink truncate utime
|
|
57
|
+
basename expand_path join
|
|
61
58
|
]
|
|
62
59
|
|
|
63
|
-
facade Dir, Dir.methods(false).map
|
|
64
|
-
|
|
60
|
+
facade Dir, Dir.methods(false).map(&:to_sym) - %i[
|
|
61
|
+
chdir entries glob foreach mkdir open children
|
|
65
62
|
]
|
|
66
63
|
|
|
67
|
-
alias
|
|
64
|
+
alias _plus_ + # Used to prevent infinite loops in some cases
|
|
68
65
|
|
|
69
66
|
protected :_plus_
|
|
70
67
|
|
|
@@ -72,9 +69,9 @@ class Pathname < String
|
|
|
72
69
|
extend FFI::Library
|
|
73
70
|
ffi_lib :shlwapi
|
|
74
71
|
|
|
75
|
-
attach_function :PathAppendW, [
|
|
76
|
-
attach_function :PathCanonicalizeW, [
|
|
77
|
-
attach_function :PathCreateFromUrlW, [
|
|
72
|
+
attach_function :PathAppendW, %i[pointer pointer], :bool
|
|
73
|
+
attach_function :PathCanonicalizeW, %i[pointer buffer_in], :bool
|
|
74
|
+
attach_function :PathCreateFromUrlW, %i[buffer_in pointer pointer ulong], :long
|
|
78
75
|
attach_function :PathGetDriveNumberW, [:buffer_in], :int
|
|
79
76
|
attach_function :PathIsRelativeW, [:buffer_in], :bool
|
|
80
77
|
attach_function :PathIsRootW, [:buffer_in], :bool
|
|
@@ -86,8 +83,8 @@ class Pathname < String
|
|
|
86
83
|
|
|
87
84
|
ffi_lib :kernel32
|
|
88
85
|
|
|
89
|
-
attach_function :GetLongPathNameW, [
|
|
90
|
-
attach_function :GetShortPathNameW, [
|
|
86
|
+
attach_function :GetLongPathNameW, %i[buffer_in buffer_out ulong], :ulong
|
|
87
|
+
attach_function :GetShortPathNameW, %i[buffer_in pointer ulong], :ulong
|
|
91
88
|
|
|
92
89
|
private_class_method :PathAppendW, :PathCanonicalizeW, :PathCreateFromUrlW
|
|
93
90
|
private_class_method :PathGetDriveNumberW, :PathIsRelativeW, :PathIsRelativeW
|
|
@@ -98,14 +95,14 @@ class Pathname < String
|
|
|
98
95
|
public
|
|
99
96
|
|
|
100
97
|
# The version of the pathname2 library
|
|
101
|
-
VERSION = '
|
|
98
|
+
VERSION = '2.0.0'.freeze
|
|
102
99
|
|
|
103
100
|
# The maximum length of a path
|
|
104
101
|
MAXPATH = 1024 unless defined? MAXPATH # Yes, I willfully violate POSIX
|
|
105
102
|
|
|
106
103
|
# Returns the expanded path of the current working directory.
|
|
107
104
|
#
|
|
108
|
-
# Synonym for
|
|
105
|
+
# Synonym for Pathname2.new(Dir.pwd).
|
|
109
106
|
#
|
|
110
107
|
def self.pwd
|
|
111
108
|
new(Dir.pwd)
|
|
@@ -115,25 +112,25 @@ class Pathname < String
|
|
|
115
112
|
alias getwd pwd
|
|
116
113
|
end
|
|
117
114
|
|
|
118
|
-
# Creates and returns a new
|
|
115
|
+
# Creates and returns a new Pathname2 object.
|
|
119
116
|
#
|
|
120
117
|
# On platforms that define File::ALT_SEPARATOR, all forward slashes are
|
|
121
118
|
# replaced with the value of File::ALT_SEPARATOR. On MS Windows, for
|
|
122
119
|
# example, all forward slashes are replaced with backslashes.
|
|
123
120
|
#
|
|
124
|
-
# File URL's will be converted to
|
|
121
|
+
# File URL's will be converted to Pathname2 objects, e.g. the file URL
|
|
125
122
|
# "file:///C:/Documents%20and%20Settings" will become 'C:\Documents and Settings'.
|
|
126
123
|
#
|
|
127
124
|
# Examples:
|
|
128
125
|
#
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
#
|
|
132
|
-
#
|
|
126
|
+
# Pathname2.new("/foo/bar/baz")
|
|
127
|
+
# Pathname2.new("foo")
|
|
128
|
+
# Pathname2.new("file:///foo/bar/baz")
|
|
129
|
+
# Pathname2.new("C:\\Documents and Settings\\snoopy")
|
|
133
130
|
#
|
|
134
131
|
def initialize(path)
|
|
135
132
|
if path.length > MAXPATH
|
|
136
|
-
msg =
|
|
133
|
+
msg = 'string too long. maximum string length is ' + MAXPATH.to_s
|
|
137
134
|
raise ArgumentError, msg
|
|
138
135
|
end
|
|
139
136
|
|
|
@@ -159,8 +156,8 @@ class Pathname < String
|
|
|
159
156
|
end
|
|
160
157
|
else
|
|
161
158
|
if path.index('file:///', 0)
|
|
162
|
-
require '
|
|
163
|
-
path = URI
|
|
159
|
+
require 'addressable'
|
|
160
|
+
path = Addressable::URI.unescape(path)[7..-1]
|
|
164
161
|
end
|
|
165
162
|
end
|
|
166
163
|
|
|
@@ -172,7 +169,7 @@ class Pathname < String
|
|
|
172
169
|
|
|
173
170
|
# Returns a real (absolute) pathname of +self+ in the actual filesystem.
|
|
174
171
|
#
|
|
175
|
-
# Unlike most
|
|
172
|
+
# Unlike most Pathname2 methods, this one assumes that the path actually
|
|
176
173
|
# exists on your filesystem. If it doesn't, an error is raised. If a
|
|
177
174
|
# circular symlink is encountered a system error will be raised.
|
|
178
175
|
#
|
|
@@ -180,15 +177,15 @@ class Pathname < String
|
|
|
180
177
|
#
|
|
181
178
|
# Dir.pwd # => /usr/local
|
|
182
179
|
# File.exists?('foo') # => true
|
|
183
|
-
#
|
|
180
|
+
# Pathname2.new('foo').realpath # => /usr/local/foo
|
|
184
181
|
#
|
|
185
182
|
def realpath
|
|
186
183
|
File.stat(self) # Check to ensure that the path exists
|
|
187
184
|
|
|
188
185
|
if File.symlink?(self)
|
|
189
|
-
file =
|
|
186
|
+
file = dup
|
|
190
187
|
|
|
191
|
-
|
|
188
|
+
loop do
|
|
192
189
|
file = File.join(File.dirname(file), File.readlink(file))
|
|
193
190
|
break unless File.symlink?(file)
|
|
194
191
|
end
|
|
@@ -200,7 +197,7 @@ class Pathname < String
|
|
|
200
197
|
end
|
|
201
198
|
|
|
202
199
|
# Returns the children of the directory, files and subdirectories, as an
|
|
203
|
-
# array of
|
|
200
|
+
# array of Pathname2 objects. If you set +with_directory+ to +false+, then
|
|
204
201
|
# the returned pathnames will contain the filename only.
|
|
205
202
|
#
|
|
206
203
|
# Note that the result never contain the entries '.' and '..' in the
|
|
@@ -209,21 +206,21 @@ class Pathname < String
|
|
|
209
206
|
#
|
|
210
207
|
# Example:
|
|
211
208
|
#
|
|
212
|
-
# path =
|
|
209
|
+
# path = Pathname2.new('/usr/bin')
|
|
213
210
|
# path.children # => ['/usr/bin/ruby', '/usr/bin/perl', ...]
|
|
214
211
|
# path.children(false) # => ['ruby', 'perl', ...]
|
|
215
212
|
#
|
|
216
213
|
def children(with_directory = true)
|
|
217
214
|
with_directory = false if self == '.'
|
|
218
215
|
result = []
|
|
219
|
-
Dir.foreach(self)
|
|
216
|
+
Dir.foreach(self) do |file|
|
|
220
217
|
next if file == '.' || file == '..'
|
|
221
218
|
if with_directory
|
|
222
219
|
result << self.class.new(File.join(self, file))
|
|
223
220
|
else
|
|
224
221
|
result << self.class.new(file)
|
|
225
222
|
end
|
|
226
|
-
|
|
223
|
+
end
|
|
227
224
|
result
|
|
228
225
|
end
|
|
229
226
|
|
|
@@ -233,15 +230,15 @@ class Pathname < String
|
|
|
233
230
|
#
|
|
234
231
|
# Example:
|
|
235
232
|
#
|
|
236
|
-
# path =
|
|
233
|
+
# path = Pathname2.new('C:\Path\File[5].txt')
|
|
237
234
|
# path.undecorate # => C:\Path\File.txt.
|
|
238
235
|
#
|
|
239
236
|
def undecorate
|
|
240
237
|
unless @win
|
|
241
|
-
raise NotImplementedError,
|
|
238
|
+
raise NotImplementedError, 'not supported on this platform'
|
|
242
239
|
end
|
|
243
240
|
|
|
244
|
-
wpath = FFI::MemoryPointer.from_string(
|
|
241
|
+
wpath = FFI::MemoryPointer.from_string(wincode)
|
|
245
242
|
|
|
246
243
|
PathUndecorateW(wpath)
|
|
247
244
|
|
|
@@ -250,10 +247,10 @@ class Pathname < String
|
|
|
250
247
|
|
|
251
248
|
# Windows only
|
|
252
249
|
#
|
|
253
|
-
# Performs the substitution of
|
|
250
|
+
# Performs the substitution of Pathname2#undecorate in place.
|
|
254
251
|
#
|
|
255
252
|
def undecorate!
|
|
256
|
-
|
|
253
|
+
replace(undecorate)
|
|
257
254
|
end
|
|
258
255
|
|
|
259
256
|
# Windows only
|
|
@@ -262,14 +259,14 @@ class Pathname < String
|
|
|
262
259
|
#
|
|
263
260
|
# Example:
|
|
264
261
|
#
|
|
265
|
-
# path =
|
|
262
|
+
# path = Pathname2.new('C:\Program Files\Java')
|
|
266
263
|
# path.short_path # => C:\Progra~1\Java.
|
|
267
264
|
#
|
|
268
265
|
def short_path
|
|
269
|
-
raise NotImplementedError,
|
|
266
|
+
raise NotImplementedError, 'not supported on this platform' unless @win
|
|
270
267
|
|
|
271
268
|
buf = FFI::MemoryPointer.new(:char, MAXPATH)
|
|
272
|
-
wpath =
|
|
269
|
+
wpath = wincode
|
|
273
270
|
|
|
274
271
|
size = GetShortPathNameW(wpath, buf, buf.size)
|
|
275
272
|
|
|
@@ -284,14 +281,14 @@ class Pathname < String
|
|
|
284
281
|
#
|
|
285
282
|
# Example:
|
|
286
283
|
#
|
|
287
|
-
# path =
|
|
284
|
+
# path = Pathname2.new('C:\Progra~1\Java')
|
|
288
285
|
# path.long_path # => C:\Program Files\Java.
|
|
289
286
|
#
|
|
290
287
|
def long_path
|
|
291
|
-
raise NotImplementedError,
|
|
288
|
+
raise NotImplementedError, 'not supported on this platform' unless @win
|
|
292
289
|
|
|
293
290
|
buf = FFI::MemoryPointer.new(:char, MAXPATH)
|
|
294
|
-
wpath =
|
|
291
|
+
wpath = wincode
|
|
295
292
|
|
|
296
293
|
size = GetLongPathNameW(wpath, buf, buf.size)
|
|
297
294
|
|
|
@@ -304,14 +301,14 @@ class Pathname < String
|
|
|
304
301
|
#
|
|
305
302
|
# Example:
|
|
306
303
|
#
|
|
307
|
-
# path =
|
|
304
|
+
# path = Pathname2.new('/usr/local/')
|
|
308
305
|
# path.pstrip # => '/usr/local'
|
|
309
306
|
#
|
|
310
307
|
def pstrip
|
|
311
|
-
str =
|
|
308
|
+
str = dup
|
|
312
309
|
return str if str.empty?
|
|
313
310
|
|
|
314
|
-
while [
|
|
311
|
+
while [File::SEPARATOR, File::ALT_SEPARATOR].include?(str.to_s[-1].chr)
|
|
315
312
|
str.strip!
|
|
316
313
|
str.chop!
|
|
317
314
|
end
|
|
@@ -319,18 +316,18 @@ class Pathname < String
|
|
|
319
316
|
self.class.new(str)
|
|
320
317
|
end
|
|
321
318
|
|
|
322
|
-
# Performs the substitution of
|
|
319
|
+
# Performs the substitution of Pathname2#pstrip in place.
|
|
323
320
|
#
|
|
324
321
|
def pstrip!
|
|
325
|
-
|
|
322
|
+
replace(pstrip)
|
|
326
323
|
end
|
|
327
324
|
|
|
328
325
|
# Splits a pathname into strings based on the path separator.
|
|
329
326
|
#
|
|
330
327
|
# Examples:
|
|
331
328
|
#
|
|
332
|
-
#
|
|
333
|
-
#
|
|
329
|
+
# Pathname2.new('/usr/local/bin').to_a # => ['usr', 'local', 'bin']
|
|
330
|
+
# Pathname2.new('C:\WINNT\Fonts').to_a # => ['C:', 'WINNT', 'Fonts']
|
|
334
331
|
#
|
|
335
332
|
def to_a
|
|
336
333
|
# Split string by path separator
|
|
@@ -339,7 +336,7 @@ class Pathname < String
|
|
|
339
336
|
else
|
|
340
337
|
array = split(@sep)
|
|
341
338
|
end
|
|
342
|
-
array.delete(
|
|
339
|
+
array.delete('') # Remove empty elements
|
|
343
340
|
array
|
|
344
341
|
end
|
|
345
342
|
|
|
@@ -347,7 +344,7 @@ class Pathname < String
|
|
|
347
344
|
#
|
|
348
345
|
# Example:
|
|
349
346
|
#
|
|
350
|
-
#
|
|
347
|
+
# Pathname2.new('/usr/local/bin').each{ |element|
|
|
351
348
|
# puts "Element: #{element}"
|
|
352
349
|
# }
|
|
353
350
|
#
|
|
@@ -365,13 +362,13 @@ class Pathname < String
|
|
|
365
362
|
#
|
|
366
363
|
# Examples:
|
|
367
364
|
#
|
|
368
|
-
# path =
|
|
365
|
+
# path = Pathname2.new('/home/john/source/ruby')
|
|
369
366
|
# path[0] # => 'home'
|
|
370
367
|
# path[1] # => 'john'
|
|
371
368
|
# path[0, 3] # => '/home/john/source'
|
|
372
369
|
# path[0..1] # => '/home/john'
|
|
373
370
|
#
|
|
374
|
-
# path =
|
|
371
|
+
# path = Pathname2.new('C:/Documents and Settings/John/Source/Ruby')
|
|
375
372
|
# path[0] # => 'C:\'
|
|
376
373
|
# path[1] # => 'Documents and Settings'
|
|
377
374
|
# path[0, 3] # => 'C:\Documents and Settings\John'
|
|
@@ -390,22 +387,22 @@ class Pathname < String
|
|
|
390
387
|
end
|
|
391
388
|
path = File.join(to_a[index])
|
|
392
389
|
else
|
|
393
|
-
raise TypeError,
|
|
390
|
+
raise TypeError, 'Only Numerics and Ranges allowed as first argument'
|
|
394
391
|
end
|
|
395
392
|
|
|
396
393
|
if path && @win
|
|
397
|
-
path = path.tr(
|
|
394
|
+
path = path.tr(File::SEPARATOR, File::ALT_SEPARATOR)
|
|
398
395
|
end
|
|
399
396
|
|
|
400
397
|
path
|
|
401
398
|
end
|
|
402
399
|
|
|
403
400
|
# Yields each component of the path, concatenating the next component on
|
|
404
|
-
# each iteration as a new
|
|
401
|
+
# each iteration as a new Pathname2 object, starting with the root path.
|
|
405
402
|
#
|
|
406
403
|
# Example:
|
|
407
404
|
#
|
|
408
|
-
# path =
|
|
405
|
+
# path = Pathname2.new('/usr/local/bin')
|
|
409
406
|
#
|
|
410
407
|
# path.descend{ |name|
|
|
411
408
|
# puts name
|
|
@@ -423,9 +420,9 @@ class Pathname < String
|
|
|
423
420
|
end
|
|
424
421
|
|
|
425
422
|
if @win
|
|
426
|
-
path = unc? ? "#{root}\\" :
|
|
423
|
+
path = unc? ? "#{root}\\" : ''
|
|
427
424
|
else
|
|
428
|
-
path = absolute? ? root :
|
|
425
|
+
path = absolute? ? root : ''
|
|
429
426
|
end
|
|
430
427
|
|
|
431
428
|
# Yield the root directory if an absolute path (and not Windows)
|
|
@@ -433,21 +430,21 @@ class Pathname < String
|
|
|
433
430
|
yield root if absolute?
|
|
434
431
|
end
|
|
435
432
|
|
|
436
|
-
each
|
|
433
|
+
each do |element|
|
|
437
434
|
if @win && unc?
|
|
438
435
|
next if root.to_a.include?(element)
|
|
439
436
|
end
|
|
440
437
|
path << element << @sep
|
|
441
438
|
yield self.class.new(path.chop)
|
|
442
|
-
|
|
439
|
+
end
|
|
443
440
|
end
|
|
444
441
|
|
|
445
442
|
# Yields the path, minus one component on each iteration, as a new
|
|
446
|
-
#
|
|
443
|
+
# Pathname2 object, ending with the root path.
|
|
447
444
|
#
|
|
448
445
|
# Example:
|
|
449
446
|
#
|
|
450
|
-
# path =
|
|
447
|
+
# path = Pathname2.new('/usr/local/bin')
|
|
451
448
|
#
|
|
452
449
|
# path.ascend{ |name|
|
|
453
450
|
# puts name
|
|
@@ -502,24 +499,24 @@ class Pathname < String
|
|
|
502
499
|
#
|
|
503
500
|
# Examples:
|
|
504
501
|
#
|
|
505
|
-
#
|
|
506
|
-
#
|
|
502
|
+
# Pathname2.new('/usr/local').root # => '/'
|
|
503
|
+
# Pathname2.new('lib').root # => '.'
|
|
507
504
|
#
|
|
508
505
|
# On MS Windows:
|
|
509
506
|
#
|
|
510
|
-
#
|
|
511
|
-
#
|
|
507
|
+
# Pathname2.new('C:\WINNT').root # => 'C:'
|
|
508
|
+
# Pathname2.new('\\some\share\foo').root # => '\\some\share'
|
|
512
509
|
#
|
|
513
510
|
def root
|
|
514
|
-
dir =
|
|
511
|
+
dir = '.'
|
|
515
512
|
|
|
516
513
|
if @win
|
|
517
|
-
wpath = FFI::MemoryPointer.from_string(
|
|
514
|
+
wpath = FFI::MemoryPointer.from_string(wincode)
|
|
518
515
|
if PathStripToRootW(wpath)
|
|
519
516
|
dir = wpath.read_string(wpath.size).split("\000\000").first.tr(0.chr, '')
|
|
520
517
|
end
|
|
521
518
|
else
|
|
522
|
-
dir =
|
|
519
|
+
dir = '/' if self =~ /^\//
|
|
523
520
|
end
|
|
524
521
|
|
|
525
522
|
self.class.new(dir)
|
|
@@ -529,12 +526,12 @@ class Pathname < String
|
|
|
529
526
|
#
|
|
530
527
|
# Examples:
|
|
531
528
|
#
|
|
532
|
-
#
|
|
533
|
-
#
|
|
529
|
+
# Pathname2.new('/').root? # => true
|
|
530
|
+
# Pathname2.new('/foo').root? # => false
|
|
534
531
|
#
|
|
535
532
|
def root?
|
|
536
533
|
if @win
|
|
537
|
-
PathIsRootW(
|
|
534
|
+
PathIsRootW(wincode)
|
|
538
535
|
else
|
|
539
536
|
self == root
|
|
540
537
|
end
|
|
@@ -547,12 +544,12 @@ class Pathname < String
|
|
|
547
544
|
#
|
|
548
545
|
# Examples:
|
|
549
546
|
#
|
|
550
|
-
#
|
|
551
|
-
#
|
|
547
|
+
# Pathname2.new("\\\\foo\\bar").unc? # => true
|
|
548
|
+
# Pathname2.new('C:\Program Files').unc? # => false
|
|
552
549
|
#
|
|
553
550
|
def unc?
|
|
554
|
-
raise NotImplementedError,
|
|
555
|
-
PathIsUNCW(
|
|
551
|
+
raise NotImplementedError, 'not supported on this platform' unless @win
|
|
552
|
+
PathIsUNCW(wincode)
|
|
556
553
|
end
|
|
557
554
|
|
|
558
555
|
# MS Windows only
|
|
@@ -562,31 +559,32 @@ class Pathname < String
|
|
|
562
559
|
#
|
|
563
560
|
# Example:
|
|
564
561
|
#
|
|
565
|
-
#
|
|
562
|
+
# Pathname2.new("C:\\foo").drive_number # => 2
|
|
566
563
|
#
|
|
567
564
|
def drive_number
|
|
568
565
|
unless @win
|
|
569
|
-
raise NotImplementedError,
|
|
566
|
+
raise NotImplementedError, 'not supported on this platform'
|
|
570
567
|
end
|
|
571
568
|
|
|
572
|
-
num = PathGetDriveNumberW(
|
|
569
|
+
num = PathGetDriveNumberW(wincode)
|
|
573
570
|
num >= 0 ? num : nil
|
|
574
571
|
end
|
|
575
572
|
|
|
576
|
-
# Compares two
|
|
577
|
-
# against other
|
|
573
|
+
# Compares two Pathname2 objects. Note that Pathname2 objects may only be
|
|
574
|
+
# compared against other Pathname2 objects, not strings, otherwise nil is
|
|
575
|
+
# returned.
|
|
578
576
|
#
|
|
579
577
|
# Example:
|
|
580
578
|
#
|
|
581
|
-
# path1 =
|
|
582
|
-
# path2 =
|
|
583
|
-
# path3 =
|
|
579
|
+
# path1 = Pathname2.new('/usr/local')
|
|
580
|
+
# path2 = Pathname2.new('/usr/local')
|
|
581
|
+
# path3 = Pathname2.new('/usr/local/bin')
|
|
584
582
|
#
|
|
585
583
|
# path1 <=> path2 # => 0
|
|
586
584
|
# path1 <=> path3 # => -1
|
|
587
585
|
#
|
|
588
586
|
def <=>(string)
|
|
589
|
-
return nil unless string.
|
|
587
|
+
return nil unless string.is_a?(Pathname2)
|
|
590
588
|
super
|
|
591
589
|
end
|
|
592
590
|
|
|
@@ -594,11 +592,11 @@ class Pathname < String
|
|
|
594
592
|
#
|
|
595
593
|
# Example:
|
|
596
594
|
#
|
|
597
|
-
#
|
|
595
|
+
# Pathname2.new('/usr/local/bin').parent # => '/usr/local'
|
|
598
596
|
#
|
|
599
597
|
def parent
|
|
600
598
|
return self if root?
|
|
601
|
-
self +
|
|
599
|
+
self + '..' # Use our custom '+' method
|
|
602
600
|
end
|
|
603
601
|
|
|
604
602
|
# Returns a relative path from the argument to the receiver. If +self+
|
|
@@ -614,23 +612,23 @@ class Pathname < String
|
|
|
614
612
|
#
|
|
615
613
|
# Examples:
|
|
616
614
|
#
|
|
617
|
-
# path =
|
|
615
|
+
# path = Pathname2.new('/usr/local/bin')
|
|
618
616
|
# path.relative_path_from('/usr/bin') # => "../local/bin"
|
|
619
617
|
#
|
|
620
|
-
# path =
|
|
618
|
+
# path = Pathname2.new("C:\\WINNT\\Fonts")
|
|
621
619
|
# path.relative_path_from("C:\\Program Files") # => "..\\WINNT\\Fonts"
|
|
622
620
|
#
|
|
623
621
|
def relative_path_from(base)
|
|
624
|
-
base = self.class.new(base) unless base.
|
|
622
|
+
base = self.class.new(base) unless base.is_a?(Pathname2)
|
|
625
623
|
|
|
626
|
-
if
|
|
627
|
-
raise ArgumentError,
|
|
624
|
+
if absolute? != base.absolute?
|
|
625
|
+
raise ArgumentError, 'relative path between absolute and relative path'
|
|
628
626
|
end
|
|
629
627
|
|
|
630
|
-
return self.class.new(
|
|
631
|
-
return self if base ==
|
|
628
|
+
return self.class.new('.') if self == base
|
|
629
|
+
return self if base == '.'
|
|
632
630
|
|
|
633
|
-
# Because of the way the Windows version handles
|
|
631
|
+
# Because of the way the Windows version handles Pathname2#clean, we need
|
|
634
632
|
# a little extra help here.
|
|
635
633
|
if @win
|
|
636
634
|
if root != base.root
|
|
@@ -642,7 +640,7 @@ class Pathname < String
|
|
|
642
640
|
end
|
|
643
641
|
end
|
|
644
642
|
|
|
645
|
-
dest_arr =
|
|
643
|
+
dest_arr = clean.to_a
|
|
646
644
|
base_arr = base.clean.to_a
|
|
647
645
|
dest_arr.delete('.')
|
|
648
646
|
base_arr.delete('.')
|
|
@@ -654,22 +652,22 @@ class Pathname < String
|
|
|
654
652
|
dest_arr.shift
|
|
655
653
|
end
|
|
656
654
|
|
|
657
|
-
if base_arr.include?(
|
|
655
|
+
if base_arr.include?('..')
|
|
658
656
|
raise ArgumentError, "base directory may not contain '..'"
|
|
659
657
|
end
|
|
660
658
|
|
|
661
|
-
base_arr.fill(
|
|
659
|
+
base_arr.fill('..')
|
|
662
660
|
rel_path = base_arr + dest_arr
|
|
663
661
|
|
|
664
662
|
if rel_path.empty?
|
|
665
|
-
self.class.new(
|
|
663
|
+
self.class.new('.')
|
|
666
664
|
else
|
|
667
665
|
self.class.new(rel_path.join(@sep))
|
|
668
666
|
end
|
|
669
667
|
end
|
|
670
668
|
|
|
671
|
-
# Adds two
|
|
672
|
-
# also automatically cleans the
|
|
669
|
+
# Adds two Pathname2 objects together, or a Pathname2 and a String. It
|
|
670
|
+
# also automatically cleans the Pathname2.
|
|
673
671
|
#
|
|
674
672
|
# Adding a root path to an existing path merely replaces the current
|
|
675
673
|
# path. Adding '.' to an existing path does nothing.
|
|
@@ -681,18 +679,18 @@ class Pathname < String
|
|
|
681
679
|
# path1 + path2 # '/foo/baz'
|
|
682
680
|
#
|
|
683
681
|
def +(string)
|
|
684
|
-
unless string.
|
|
682
|
+
unless string.is_a?(Pathname2)
|
|
685
683
|
string = self.class.new(string)
|
|
686
684
|
end
|
|
687
685
|
|
|
688
686
|
# Any path plus "." is the same directory
|
|
689
|
-
return self if string ==
|
|
690
|
-
return string if self ==
|
|
687
|
+
return self if string == '.'
|
|
688
|
+
return string if self == '.'
|
|
691
689
|
|
|
692
690
|
# Use the builtin PathAppend() function if on Windows - much easier
|
|
693
691
|
if @win
|
|
694
692
|
path = FFI::MemoryPointer.new(:char, MAXPATH)
|
|
695
|
-
path.write_string(
|
|
693
|
+
path.write_string(dup.wincode)
|
|
696
694
|
more = FFI::MemoryPointer.from_string(string.wincode)
|
|
697
695
|
|
|
698
696
|
PathAppendW(path, more)
|
|
@@ -716,14 +714,14 @@ class Pathname < String
|
|
|
716
714
|
self.class.new(new_string).clean
|
|
717
715
|
end
|
|
718
716
|
|
|
719
|
-
alias
|
|
717
|
+
alias / +
|
|
720
718
|
|
|
721
719
|
# Returns whether or not the path is an absolute path.
|
|
722
720
|
#
|
|
723
721
|
# Example:
|
|
724
722
|
#
|
|
725
|
-
#
|
|
726
|
-
#
|
|
723
|
+
# Pathname2.new('/usr/bin').absolute? # => true
|
|
724
|
+
# Pathname2.new('usr').absolute? # => false
|
|
727
725
|
#
|
|
728
726
|
def absolute?
|
|
729
727
|
!relative?
|
|
@@ -733,14 +731,14 @@ class Pathname < String
|
|
|
733
731
|
#
|
|
734
732
|
# Example:
|
|
735
733
|
#
|
|
736
|
-
#
|
|
737
|
-
#
|
|
734
|
+
# Pathname2.new('/usr/bin').relative? # => true
|
|
735
|
+
# Pathname2.new('usr').relative? # => false
|
|
738
736
|
#
|
|
739
737
|
def relative?
|
|
740
738
|
if @win
|
|
741
|
-
PathIsRelativeW(
|
|
739
|
+
PathIsRelativeW(wincode)
|
|
742
740
|
else
|
|
743
|
-
root ==
|
|
741
|
+
root == '.'
|
|
744
742
|
end
|
|
745
743
|
end
|
|
746
744
|
|
|
@@ -749,15 +747,15 @@ class Pathname < String
|
|
|
749
747
|
#
|
|
750
748
|
# Example:
|
|
751
749
|
#
|
|
752
|
-
# path =
|
|
750
|
+
# path = Pathname2.new('/usr/./local/../bin')
|
|
753
751
|
# path.clean # => '/usr/bin'
|
|
754
752
|
#
|
|
755
753
|
def clean
|
|
756
|
-
return self if
|
|
754
|
+
return self if empty?
|
|
757
755
|
|
|
758
756
|
if @win
|
|
759
757
|
ptr = FFI::MemoryPointer.new(:char, MAXPATH)
|
|
760
|
-
if PathCanonicalizeW(ptr,
|
|
758
|
+
if PathCanonicalizeW(ptr, wincode)
|
|
761
759
|
return self.class.new(ptr.read_string(ptr.size).delete(0.chr))
|
|
762
760
|
else
|
|
763
761
|
return self
|
|
@@ -766,28 +764,28 @@ class Pathname < String
|
|
|
766
764
|
|
|
767
765
|
final = []
|
|
768
766
|
|
|
769
|
-
to_a.each
|
|
770
|
-
next if element ==
|
|
767
|
+
to_a.each do |element|
|
|
768
|
+
next if element == '.'
|
|
771
769
|
final.push(element)
|
|
772
|
-
if element ==
|
|
770
|
+
if element == '..' && self != '..'
|
|
773
771
|
2.times{ final.pop }
|
|
774
772
|
end
|
|
775
|
-
|
|
773
|
+
end
|
|
776
774
|
|
|
777
775
|
final = final.join(@sep)
|
|
778
|
-
final = root._plus_(final) if root !=
|
|
779
|
-
final =
|
|
776
|
+
final = root._plus_(final) if root != '.'
|
|
777
|
+
final = '.' if final.empty?
|
|
780
778
|
|
|
781
779
|
self.class.new(final)
|
|
782
780
|
end
|
|
783
781
|
|
|
784
|
-
alias
|
|
782
|
+
alias cleanpath clean
|
|
785
783
|
|
|
786
|
-
# Identical to
|
|
784
|
+
# Identical to Pathname2#clean, except that it modifies the receiver
|
|
787
785
|
# in place.
|
|
788
786
|
#
|
|
789
787
|
def clean!
|
|
790
|
-
|
|
788
|
+
replace(clean)
|
|
791
789
|
end
|
|
792
790
|
|
|
793
791
|
alias cleanpath! clean!
|
|
@@ -803,7 +801,7 @@ class Pathname < String
|
|
|
803
801
|
#
|
|
804
802
|
# Example:
|
|
805
803
|
#
|
|
806
|
-
# path =
|
|
804
|
+
# path = Pathname2.new('/usr/local/bin/ruby')
|
|
807
805
|
#
|
|
808
806
|
# puts path.dirname # => /usr/local/bin
|
|
809
807
|
# puts path.dirname(2) # => /usr/local
|
|
@@ -812,15 +810,15 @@ class Pathname < String
|
|
|
812
810
|
#
|
|
813
811
|
def dirname(level = 1)
|
|
814
812
|
raise ArgumentError if level < 0
|
|
815
|
-
local_path =
|
|
813
|
+
local_path = dup
|
|
816
814
|
|
|
817
815
|
level.times{ local_path = File.dirname(local_path) }
|
|
818
816
|
self.class.new(local_path)
|
|
819
817
|
end
|
|
820
818
|
|
|
821
|
-
# Joins the given pathnames onto +self+ to create a new
|
|
819
|
+
# Joins the given pathnames onto +self+ to create a new Pathname2 object.
|
|
822
820
|
#
|
|
823
|
-
# path =
|
|
821
|
+
# path = Pathname2.new("C:/Users")
|
|
824
822
|
# path = path.join("foo", "Downloads") # => C:/Users/foo/Downloads
|
|
825
823
|
#
|
|
826
824
|
def join(*args)
|
|
@@ -829,11 +827,11 @@ class Pathname < String
|
|
|
829
827
|
result = self.class.new(result) unless result === self.class
|
|
830
828
|
return result if result.absolute?
|
|
831
829
|
|
|
832
|
-
args.reverse_each
|
|
830
|
+
args.reverse_each do |path|
|
|
833
831
|
path = self.class.new(path) unless path === self.class
|
|
834
832
|
result = path + result
|
|
835
833
|
break if result.absolute?
|
|
836
|
-
|
|
834
|
+
end
|
|
837
835
|
|
|
838
836
|
result
|
|
839
837
|
end
|
|
@@ -841,17 +839,17 @@ class Pathname < String
|
|
|
841
839
|
# A custom pretty printer
|
|
842
840
|
def pretty_print(q)
|
|
843
841
|
if File::ALT_SEPARATOR
|
|
844
|
-
q.text(
|
|
842
|
+
q.text(to_s.tr(File::SEPARATOR, File::ALT_SEPARATOR))
|
|
845
843
|
else
|
|
846
|
-
q.text(
|
|
844
|
+
q.text(to_s)
|
|
847
845
|
end
|
|
848
846
|
end
|
|
849
847
|
|
|
850
848
|
#-- Find facade
|
|
851
849
|
|
|
852
|
-
#
|
|
853
|
-
# manner. It yields a
|
|
854
|
-
#
|
|
850
|
+
# Pathname2#find is an iterator to traverse a directory tree in a depth first
|
|
851
|
+
# manner. It yields a Pathname2 for each file under the directory passed to
|
|
852
|
+
# Pathname2.new.
|
|
855
853
|
#
|
|
856
854
|
# Since it is implemented by the Find module, Find.prune can be used to
|
|
857
855
|
# control the traverse.
|
|
@@ -861,7 +859,7 @@ class Pathname < String
|
|
|
861
859
|
#
|
|
862
860
|
def find
|
|
863
861
|
require 'find'
|
|
864
|
-
if self ==
|
|
862
|
+
if self == '.'
|
|
865
863
|
Find.find(self){ |f| yield self.class.new(f.sub(%r{\A\./}, '')) }
|
|
866
864
|
else
|
|
867
865
|
Find.find(self){ |f| yield self.class.new(f) }
|
|
@@ -872,17 +870,17 @@ class Pathname < String
|
|
|
872
870
|
|
|
873
871
|
# IO.foreach
|
|
874
872
|
def foreach(*args, &block)
|
|
875
|
-
|
|
873
|
+
File.foreach(self, *args, &block)
|
|
876
874
|
end
|
|
877
875
|
|
|
878
876
|
# IO.read
|
|
879
877
|
def read(*args)
|
|
880
|
-
|
|
878
|
+
File.read(self, *args)
|
|
881
879
|
end
|
|
882
880
|
|
|
883
881
|
# IO.readlines
|
|
884
882
|
def readlines(*args)
|
|
885
|
-
|
|
883
|
+
File.readlines(self, *args)
|
|
886
884
|
end
|
|
887
885
|
|
|
888
886
|
# IO.sysopen
|
|
@@ -899,13 +897,13 @@ class Pathname < String
|
|
|
899
897
|
# chdir to the path in question, then performs the glob.
|
|
900
898
|
#
|
|
901
899
|
def glob(*args)
|
|
902
|
-
Dir.chdir(self)
|
|
900
|
+
Dir.chdir(self) do
|
|
903
901
|
if block_given?
|
|
904
902
|
Dir.glob(*args){ |file| yield self.class.new(file) }
|
|
905
903
|
else
|
|
906
904
|
Dir.glob(*args).map{ |file| self.class.new(file) }
|
|
907
905
|
end
|
|
908
|
-
|
|
906
|
+
end
|
|
909
907
|
end
|
|
910
908
|
|
|
911
909
|
# Dir.chdir
|
|
@@ -1047,7 +1045,7 @@ class Pathname < String
|
|
|
1047
1045
|
FileUtils.mv(self, *args)
|
|
1048
1046
|
end
|
|
1049
1047
|
|
|
1050
|
-
|
|
1048
|
+
# FileUtils.rm
|
|
1051
1049
|
def rm(*args)
|
|
1052
1050
|
FileUtils.rm(self, *args)
|
|
1053
1051
|
end
|
|
@@ -1115,33 +1113,26 @@ class Pathname < String
|
|
|
1115
1113
|
end
|
|
1116
1114
|
end
|
|
1117
1115
|
|
|
1116
|
+
# Re-open the Kernel module to add some global methods.
|
|
1118
1117
|
module Kernel
|
|
1119
1118
|
# Usage: pn{ path }
|
|
1120
1119
|
#
|
|
1121
|
-
# A shortcut for
|
|
1120
|
+
# A shortcut for Pathname2.new
|
|
1122
1121
|
#
|
|
1123
1122
|
def pn
|
|
1124
|
-
instance_eval{
|
|
1125
|
-
end
|
|
1126
|
-
|
|
1127
|
-
# rubocop:disable Lint/ShadowedException
|
|
1128
|
-
begin
|
|
1129
|
-
remove_method(:Pathname)
|
|
1130
|
-
rescue NoMethodError, NameError
|
|
1131
|
-
# Do nothing, not defined.
|
|
1123
|
+
instance_eval{ Pathname2.new(yield) }
|
|
1132
1124
|
end
|
|
1133
|
-
# rubocop:enable Lint/ShadowedException
|
|
1134
1125
|
|
|
1135
|
-
# Synonym for
|
|
1126
|
+
# Synonym for Pathname2.new
|
|
1136
1127
|
#
|
|
1137
|
-
def
|
|
1138
|
-
|
|
1128
|
+
def Pathname2(path)
|
|
1129
|
+
Pathname2.new(path)
|
|
1139
1130
|
end
|
|
1140
1131
|
end
|
|
1141
1132
|
|
|
1142
1133
|
class String
|
|
1143
1134
|
# Convert a string directly into a Pathname object.
|
|
1144
1135
|
def to_path
|
|
1145
|
-
|
|
1136
|
+
Pathname2.new(self)
|
|
1146
1137
|
end
|
|
1147
1138
|
end
|