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