pathname2 1.0.0 → 1.1.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.
Files changed (5) hide show
  1. data/CHANGES +3 -0
  2. data/lib/pathname2.rb +557 -536
  3. data/test/tc_pathname.rb +192 -182
  4. data/test/tc_pathname_win.rb +237 -227
  5. metadata +5 -5
data/CHANGES CHANGED
@@ -1,2 +1,5 @@
1
+ == 1.1.0 - 13-Jul-2005
2
+ * Added the 'find' facade.
3
+
1
4
  == 1.0.0 - 11-Jun-2005
2
5
  * Initial release
data/lib/pathname2.rb CHANGED
@@ -1,536 +1,557 @@
1
- # == Synopsis
2
- #
3
- # Pathname represents a path name on a filesystem. A Pathname can be
4
- # relative or absolute. It does not matter whether the path exists or not.
5
- #
6
- # All functionality from File, FileTest, and Dir is included, using a facade
7
- # pattern.
8
- #
9
- # This class works on both Unix and Win32, including UNC path names. Note
10
- # that forward slashes are converted to backslashes on Win32 systems.
11
- #
12
- # == Usage
13
- #
14
- # require "pathname"
15
- #
16
- # # Unix
17
- # path1 = Pathname.new("/foo/bar/baz")
18
- # path2 = Pathname.new("../zap")
19
- #
20
- # path1 + path2 # "/foo/bar/zap"
21
- # path1.dirname # "/foo/bar"
22
- #
23
- # # Win32
24
- # path1 = Pathname.new("C:\\foo\\bar\\baz")
25
- # path2 = Pathname.new("..\\zap")
26
- #
27
- # path1 + path2 # "C:\\foo\\bar\\zap"
28
- # path1.exists? # Does the path exist?
29
- #
30
- # == Author
31
- #
32
- # Daniel J. Berger
33
- # djberg96 at gmail dot com
34
- # imperator on IRC (irc.freenode.net)
35
- #
36
- # == Copyright
37
- # Copyright (c) 2005 Daniel J. Berger.
38
- # Licensed under the same terms as Ruby itself.
39
- #
40
- require "facade"
41
- require "ftools"
42
- require "fileutils"
43
-
44
- class Pathname < String
45
- extend Facade
46
- facade File
47
- facade Dir
48
-
49
- if PLATFORM.match("mswin32")
50
- require "Win32API"
51
- @@PathStripToRoot = Win32API.new("shlwapi","PathStripToRoot","P","L")
52
- @@PathIsUNC = Win32API.new("shlwapi","PathIsUNC","P","L")
53
- @@PathIsURL = Win32API.new("shlwapi","PathIsURL","P","L")
54
- @@PathCanonicalize = Win32API.new("shlwapi","PathCanonicalize","PP","L")
55
- @@PathAppend = Win32API.new("shlwapi","PathAppend","PP","L")
56
- @@PathIsRoot = Win32API.new("shlwapi","PathIsRoot","P","L")
57
- @@PathIsDirectory = Win32API.new("shlwapi","PathIsDirectory","P","L")
58
- @@PathIsRelative = Win32API.new("shlwapi","PathIsRelative","P","L")
59
- @@PathFileExists = Win32API.new("shlwapi","PathFileExists","P","L")
60
- @@PathUndecorate = Win32API.new("shlwapi","PathUndecorate","P","L")
61
-
62
- @@PathGetDriveNumber =
63
- Win32API.new("shlwapi","PathGetDriveNumber","P","L")
64
-
65
- @@PathCreateFromUrl =
66
- Win32API.new("shlwapi", "PathCreateFromUrl", "PPPL", "L")
67
-
68
- @@PathRemoveBackslash =
69
- Win32API.new("shlwapi", "PathRemoveBackslash", "P", "P")
70
- end
71
-
72
- VERSION = "1.0.0"
73
- MAX_PATH = 260
74
-
75
- # Creates and returns a new Pathname object.
76
- #
77
- # On Win32 systems, all forward slashes are replaced with backslashes.
78
- def initialize(path)
79
- if path.length > MAX_PATH
80
- msg = "string too long. maximum string length is " + MAX_PATH.to_s
81
- raise PathnameError, msg
82
- end
83
-
84
- @sep = File::ALT_SEPARATOR || File::SEPARATOR
85
- @win32 = PLATFORM.match("mswin32")
86
-
87
- # Handle File URL's for Windows
88
- if @win32
89
- if @@PathIsURL.call(path) > 0
90
- buf = 0.chr * MAX_PATH
91
- len = [buf.length].pack("l")
92
- if @@PathCreateFromUrl.call(path, buf, len, 0) >= 0
93
- path = buf.strip
94
- else
95
- raise PathnameError, "invalid file url: #{path}"
96
- end
97
- end
98
- end
99
-
100
- # Convert forward slashes to backslashes on Win32
101
- path = path.tr("/", @sep) if @win32
102
- super(path)
103
- end
104
-
105
- # Win32 only
106
- #
107
- # Removes the decoration from a path string. For example,
108
- # C:\Path\File[5].txt would become C:\Path\File.txt.
109
- def undecorate
110
- unless @win32
111
- raise NotImplementedError, "not supported on this platform"
112
- end
113
- buf = 0.chr * MAX_PATH
114
- buf[0..self.length-1] = self
115
- @@PathUndecorate.call(buf)
116
- Pathname.new(buf.split(0.chr).first)
117
- end
118
-
119
- # Removes trailing slash, if present.
120
- def pstrip
121
- if @win32
122
- @@PathRemoveBackslash.call(self)
123
- self.strip!
124
- else
125
- if self[-1] == @sep
126
- self.strip!
127
- self.chop!
128
- end
129
- end
130
- self
131
- end
132
-
133
- # Splits a pathname into pieces based on the path separator. For example,
134
- # "/foo/bar/baz" would return a three element array of ['foo','bar','baz'].
135
- def to_a
136
- array = split(@sep) # Split string by path separator
137
- array.delete("") # Remove empty elements
138
- array
139
- end
140
-
141
- # Yields each component of the path name to a block.
142
- def each
143
- self.to_a.each{ |element| yield element }
144
- end
145
-
146
- # Returns the root directory of the path, or '.' if there is no root
147
- # directory.
148
- #
149
- # On Unix, this means the '/' character. On Win32 systems, this can
150
- # refer to the drive letter, or the server and share path if the path
151
- # is a UNC path.
152
- def root
153
- dir = "."
154
- if @win32
155
- buf = 0.chr * MAX_PATH
156
- buf[0..self.length-1] = self
157
-
158
- if @@PathStripToRoot.call(buf) > 0
159
- dir = Pathname.new(buf.split(0.chr).first)
160
- end
161
- else
162
- dir = File.dirname(self)
163
- while dir != "/" && dir != "."
164
- dir = File.dirname(dir)
165
- end
166
- end
167
- dir = "." if dir.empty?
168
- dir
169
- end
170
-
171
- # Returns whether or not the path consists only of a root directory.
172
- def root?
173
- if @win32
174
- @@PathIsRoot.call(self) > 0
175
- else
176
- self == root
177
- end
178
- end
179
-
180
- # Win32 only
181
- #
182
- # Determines if the string is a valid Universal Naming Convention (UNC)
183
- # for a server and share path.
184
- def unc?
185
- unless @win32
186
- raise NotImplementedError, "not supported on this platform"
187
- end
188
-
189
- @@PathIsUNC.call(self) > 0
190
- end
191
-
192
- # Win32 only
193
- #
194
- # Returns the drive number that corresponds to the root, or nil if not
195
- # applicable.
196
- #
197
- # For example, Pathname.new("C:\\foo").drive_number would return 2.
198
- def drive_number
199
- unless @win32
200
- raise NotImplementedError, "not supported on this platform"
201
- end
202
-
203
- num = @@PathGetDriveNumber.call(self)
204
- num >= 0 ? num : nil
205
- end
206
-
207
- # Pathnames may only be compared against other Pathnames, not strings.
208
- def <=>(string)
209
- return nil unless string.kind_of?(Pathname)
210
- super
211
- end
212
-
213
- # Adds two Pathname objects together, or a Pathname and a String. It
214
- # also automatically cleans the Pathname.
215
- #
216
- # Example:
217
- # path1 = '/foo/bar'
218
- # path2 = '../baz'
219
- # path1 + path2 # '/foo/baz'
220
- #
221
- # Adding a root path to an existing path merely replaces the current
222
- # path. Adding '.' to an existing path does nothing.
223
- def +(string)
224
- unless string.kind_of?(Pathname)
225
- string = Pathname.new(string)
226
- end
227
-
228
- # Any path plus "." is the same directory
229
- return self if string == "."
230
-
231
- # Use the builtin PathAppend method if on Windows - much easier
232
- if @win32
233
- buf = 0.chr * MAX_PATH
234
- buf[0..self.length-1] = self
235
- @@PathAppend.call(buf, string << 0.chr)
236
- buf.strip!
237
- return Pathname.new(buf) # PathAppend cleans automatically
238
- end
239
-
240
- # If the string is an absolute directory, return it
241
- return string if string.absolute?
242
-
243
- array = self.to_a + string.to_a
244
- new_string = array.join(@sep)
245
-
246
- unless self.relative? || @win32
247
- new_string = @sep + new_string # Add root path back if needed
248
- end
249
-
250
- Pathname.new(new_string).clean
251
- end
252
-
253
- # Returns whether or not the path is an absolute path.
254
- def absolute?
255
- !relative?
256
- end
257
-
258
- # Returns whether or not the path is a relative path.
259
- def relative?
260
- if @win32
261
- @@PathIsRelative.call(self) > 0
262
- else
263
- root == "."
264
- end
265
- end
266
-
267
- # Removes unnecessary '.' paths and ellides '..' paths appropriately.
268
- def clean
269
- return self if self.empty?
270
-
271
- if @win32
272
- path = 0.chr * MAX_PATH
273
- if @@PathCanonicalize.call(path, self) > 0
274
- return Pathname.new(path.split(0.chr).first)
275
- else
276
- return self
277
- end
278
- end
279
-
280
- final = []
281
- self.to_a.each{ |element|
282
- next if element == "."
283
- final.push(element)
284
- if element == ".." && self != ".."
285
- 2.times{ final.pop }
286
- end
287
- }
288
- final = final.join(@sep)
289
- final = root + final if root != "."
290
- final = "." if final.empty?
291
- Pathname.new(final)
292
- end
293
- alias :cleanpath :clean
294
-
295
- #-- IO methods not handled by facade
296
-
297
- # IO.foreach
298
- def foreach(*args, &block)
299
- IO.foreach(self, *args, &block)
300
- end
301
-
302
- # IO.read
303
- def read(*args)
304
- IO.read(self, *args)
305
- end
306
-
307
- # IO.readlines
308
- def readlines(*args)
309
- IO.readlines(self, *args)
310
- end
311
-
312
- # IO.sysopen
313
- def sysopen(*args)
314
- IO.sysopen(self, *args)
315
- end
316
-
317
- #-- Dir methods not handled by facade
318
-
319
- # Dir.glob
320
- def glob(*args)
321
- if block_given?
322
- Dir.glob(*args){ |file| yield Pathname.new(file) }
323
- else
324
- Dir.glob(*args).map{ |file| Pathname.new(file) }
325
- end
326
- end
327
-
328
- # Dir.chdir
329
- def chdir(&block)
330
- Dir.chdir(self, &block)
331
- end
332
-
333
- # Dir.entries
334
- def entries
335
- Dir.entries(self).map{ |file| Pathname.new(file) }
336
- end
337
-
338
- # Dir.mkdir
339
- def mkdir(*args)
340
- Dir.mkdir(self, *args)
341
- end
342
-
343
- # Dir.opendir
344
- def opendir(&block)
345
- Dir.open(self, &block)
346
- end
347
-
348
- #-- File methods not handled by facade
349
-
350
- # File.chmod
351
- def chmod(mode)
352
- File.chmod(mode, self)
353
- end
354
-
355
- # File.lchmod
356
- def lchmod(mode)
357
- File.lchmod(mode, self)
358
- end
359
-
360
- # File.chown
361
- def chown(owner, group)
362
- File.chown(owner, group, self)
363
- end
364
-
365
- # File.lchown
366
- def lchown(owner, group)
367
- File.lchown(owner, group, self)
368
- end
369
-
370
- # File.fnmatch
371
- def fnmatch(pattern, *args)
372
- File.fnmatch(pattern, self, *args)
373
- end
374
-
375
- # File.fnmatch?
376
- def fnmatch?(pattern, *args)
377
- File.fnmatch?(pattern, self, *args)
378
- end
379
-
380
- # File.link
381
- def link(old)
382
- File.link(old, self)
383
- end
384
-
385
- # File.open
386
- def open(*args, &block)
387
- File.open(self, *args, &block)
388
- end
389
-
390
- # File.rename
391
- def rename(name)
392
- File.rename(self, name)
393
- end
394
-
395
- # File.symlink
396
- def symlink(old)
397
- File.symlink(old, self)
398
- end
399
-
400
- # File.truncate
401
- def truncate(length)
402
- File.truncate(self, length)
403
- end
404
-
405
- # File.utime
406
- def utime(atime, mtime)
407
- File.utime(atime, mtime, self)
408
- end
409
-
410
- # File.basename
411
- def basename(*args)
412
- File.basename(self, *args)
413
- end
414
-
415
- # File.expand_path
416
- def expand_path(*args)
417
- File.expand_path(self, *args)
418
- end
419
-
420
- #--
421
- # ftools facade
422
- #++
423
-
424
- # ftools File.copy
425
- def copy(*args)
426
- File.copy(self, *args)
427
- end
428
-
429
- #--
430
- # FileUtils facade. Note that methods already covered by File and Dir
431
- # are not defined here (pwd, mkdir, etc).
432
- #++
433
-
434
- # FileUtils.cd
435
- def cd(*args, &block)
436
- FileUtils.cd(self, *args, &block)
437
- end
438
-
439
- # FileUtils.mkdir_p
440
- def mkdir_p(*args)
441
- FileUtils.mkdir_p(self, *args)
442
- end
443
- alias mkpath mkdir_p
444
-
445
- # FileUtils.ln
446
- def ln(*args)
447
- FileUtils.ln(self, *args)
448
- end
449
-
450
- # FileUtils.ln_s
451
- def ln_s(*args)
452
- FileUtils.ln_s(self, *args)
453
- end
454
-
455
- # FileUtils.ln_sf
456
- def ln_sf(*args)
457
- FileUtils.ln_sf(self, *args)
458
- end
459
-
460
- # FileUtils.cp
461
- def cp(*args)
462
- FileUtils.cp(self, *args)
463
- end
464
-
465
- # FileUtils.cp_r
466
- def cp_r(*args)
467
- FileUtils.cp_r(self, *args)
468
- end
469
-
470
- # FileUtils.mv
471
- def mv(*args)
472
- FileUtils.mv(self, *args)
473
- end
474
-
475
- # FileUtils.rm
476
- def rm(*args)
477
- FileUtils.rm(self, *args)
478
- end
479
- alias remove rm
480
-
481
- # FileUtils.rm_f
482
- def rm_f(*args)
483
- FileUtils.rm_f(self, *args)
484
- end
485
-
486
- # FileUtils.rm_r
487
- def rm_r(*args)
488
- FileUtils.rm_r(self, *args)
489
- end
490
-
491
- # FileUtils.rm_rf
492
- def rm_rf(*args)
493
- FileUtils.rm_rf(self, *args)
494
- end
495
- alias rmtree rm_rf
496
-
497
- # FileUtils.install
498
- def install(*args)
499
- FileUtils.install(self, *args)
500
- end
501
-
502
- # FileUtils.touch
503
- def touch(*args)
504
- FileUtils.touch(*args)
505
- end
506
-
507
- # FileUtils.compare_file
508
- def compare_file(file)
509
- FileUtils.compare_file(self, file)
510
- end
511
-
512
- # FileUtils.uptodate?
513
- def uptodate?(*args)
514
- FileUtils.uptodate(self, *args)
515
- end
516
-
517
- # FileUtils.copy_file
518
- def copy_file(*args)
519
- FileUtils.copy_file(self, *args)
520
- end
521
-
522
- # FileUtils.remove_dir
523
- def remove_dir(*args)
524
- FileUtils.remove_dir(self, *args)
525
- end
526
-
527
- # FileUtils.remove_file
528
- def remove_file(*args)
529
- FileUtils.remove_dir(self, *args)
530
- end
531
-
532
- # FileUtils.copy_entry
533
- def copy_entry(*args)
534
- FileUtils.copy_entry(self, *args)
535
- end
536
- end
1
+ # == Synopsis
2
+ #
3
+ # Pathname represents a path name on a filesystem. A Pathname can be
4
+ # relative or absolute. It does not matter whether the path exists or not.
5
+ #
6
+ # All functionality from File, FileTest, and Dir is included, using a facade
7
+ # pattern.
8
+ #
9
+ # This class works on both Unix and Win32, including UNC path names. Note
10
+ # that forward slashes are converted to backslashes on Win32 systems.
11
+ #
12
+ # == Usage
13
+ #
14
+ # require "pathname"
15
+ #
16
+ # # Unix
17
+ # path1 = Pathname.new("/foo/bar/baz")
18
+ # path2 = Pathname.new("../zap")
19
+ #
20
+ # path1 + path2 # "/foo/bar/zap"
21
+ # path1.dirname # "/foo/bar"
22
+ #
23
+ # # Win32
24
+ # path1 = Pathname.new("C:\\foo\\bar\\baz")
25
+ # path2 = Pathname.new("..\\zap")
26
+ #
27
+ # path1 + path2 # "C:\\foo\\bar\\zap"
28
+ # path1.exists? # Does the path exist?
29
+ #
30
+ # == Author
31
+ #
32
+ # Daniel J. Berger
33
+ # djberg96 at gmail dot com
34
+ # imperator on IRC (irc.freenode.net)
35
+ #
36
+ # == Copyright
37
+ # Copyright (c) 2005 Daniel J. Berger.
38
+ # Licensed under the same terms as Ruby itself.
39
+ #
40
+ require "facade"
41
+ require "ftools"
42
+ require "fileutils"
43
+
44
+ class Pathname < String
45
+ extend Facade
46
+ facade File
47
+ facade Dir
48
+
49
+ if PLATFORM.match("mswin32")
50
+ require "Win32API"
51
+ @@PathStripToRoot = Win32API.new("shlwapi","PathStripToRoot","P","L")
52
+ @@PathIsUNC = Win32API.new("shlwapi","PathIsUNC","P","L")
53
+ @@PathIsURL = Win32API.new("shlwapi","PathIsURL","P","L")
54
+ @@PathCanonicalize = Win32API.new("shlwapi","PathCanonicalize","PP","L")
55
+ @@PathAppend = Win32API.new("shlwapi","PathAppend","PP","L")
56
+ @@PathIsRoot = Win32API.new("shlwapi","PathIsRoot","P","L")
57
+ @@PathIsDirectory = Win32API.new("shlwapi","PathIsDirectory","P","L")
58
+ @@PathIsRelative = Win32API.new("shlwapi","PathIsRelative","P","L")
59
+ @@PathFileExists = Win32API.new("shlwapi","PathFileExists","P","L")
60
+ @@PathUndecorate = Win32API.new("shlwapi","PathUndecorate","P","L")
61
+
62
+ @@PathGetDriveNumber =
63
+ Win32API.new("shlwapi","PathGetDriveNumber","P","L")
64
+
65
+ @@PathCreateFromUrl =
66
+ Win32API.new("shlwapi", "PathCreateFromUrl", "PPPL", "L")
67
+
68
+ @@PathRemoveBackslash =
69
+ Win32API.new("shlwapi", "PathRemoveBackslash", "P", "P")
70
+ end
71
+
72
+ VERSION = "1.1.0"
73
+ MAX_PATH = 260
74
+
75
+ # Creates and returns a new Pathname object.
76
+ #
77
+ # On Win32 systems, all forward slashes are replaced with backslashes.
78
+ def initialize(path)
79
+ if path.length > MAX_PATH
80
+ msg = "string too long. maximum string length is " + MAX_PATH.to_s
81
+ raise PathnameError, msg
82
+ end
83
+
84
+ @sep = File::ALT_SEPARATOR || File::SEPARATOR
85
+ @win32 = PLATFORM.match("mswin32")
86
+
87
+ # Handle File URL's for Windows
88
+ if @win32
89
+ if @@PathIsURL.call(path) > 0
90
+ buf = 0.chr * MAX_PATH
91
+ len = [buf.length].pack("l")
92
+ if @@PathCreateFromUrl.call(path, buf, len, 0) >= 0
93
+ path = buf.strip
94
+ else
95
+ raise PathnameError, "invalid file url: #{path}"
96
+ end
97
+ end
98
+ end
99
+
100
+ # Convert forward slashes to backslashes on Win32
101
+ path = path.tr("/", @sep) if @win32
102
+ super(path)
103
+ end
104
+
105
+ # Win32 only
106
+ #
107
+ # Removes the decoration from a path string. For example,
108
+ # C:\Path\File[5].txt would become C:\Path\File.txt.
109
+ def undecorate
110
+ unless @win32
111
+ raise NotImplementedError, "not supported on this platform"
112
+ end
113
+ buf = 0.chr * MAX_PATH
114
+ buf[0..self.length-1] = self
115
+ @@PathUndecorate.call(buf)
116
+ Pathname.new(buf.split(0.chr).first)
117
+ end
118
+
119
+ # Removes trailing slash, if present.
120
+ def pstrip
121
+ if @win32
122
+ @@PathRemoveBackslash.call(self)
123
+ self.strip!
124
+ else
125
+ if self[-1] == @sep
126
+ self.strip!
127
+ self.chop!
128
+ end
129
+ end
130
+ self
131
+ end
132
+
133
+ # Splits a pathname into pieces based on the path separator. For example,
134
+ # "/foo/bar/baz" would return a three element array of ['foo','bar','baz'].
135
+ def to_a
136
+ array = split(@sep) # Split string by path separator
137
+ array.delete("") # Remove empty elements
138
+ array
139
+ end
140
+
141
+ # Yields each component of the path name to a block.
142
+ def each
143
+ self.to_a.each{ |element| yield element }
144
+ end
145
+
146
+ # Returns the root directory of the path, or '.' if there is no root
147
+ # directory.
148
+ #
149
+ # On Unix, this means the '/' character. On Win32 systems, this can
150
+ # refer to the drive letter, or the server and share path if the path
151
+ # is a UNC path.
152
+ def root
153
+ dir = "."
154
+ if @win32
155
+ buf = 0.chr * MAX_PATH
156
+ buf[0..self.length-1] = self
157
+
158
+ if @@PathStripToRoot.call(buf) > 0
159
+ dir = Pathname.new(buf.split(0.chr).first)
160
+ end
161
+ else
162
+ dir = File.dirname(self)
163
+ while dir != "/" && dir != "."
164
+ dir = File.dirname(dir)
165
+ end
166
+ end
167
+ dir = "." if dir.empty?
168
+ dir
169
+ end
170
+
171
+ # Returns whether or not the path consists only of a root directory.
172
+ def root?
173
+ if @win32
174
+ @@PathIsRoot.call(self) > 0
175
+ else
176
+ self == root
177
+ end
178
+ end
179
+
180
+ # Win32 only
181
+ #
182
+ # Determines if the string is a valid Universal Naming Convention (UNC)
183
+ # for a server and share path.
184
+ def unc?
185
+ unless @win32
186
+ raise NotImplementedError, "not supported on this platform"
187
+ end
188
+
189
+ @@PathIsUNC.call(self) > 0
190
+ end
191
+
192
+ # Win32 only
193
+ #
194
+ # Returns the drive number that corresponds to the root, or nil if not
195
+ # applicable.
196
+ #
197
+ # For example, Pathname.new("C:\\foo").drive_number would return 2.
198
+ def drive_number
199
+ unless @win32
200
+ raise NotImplementedError, "not supported on this platform"
201
+ end
202
+
203
+ num = @@PathGetDriveNumber.call(self)
204
+ num >= 0 ? num : nil
205
+ end
206
+
207
+ # Pathnames may only be compared against other Pathnames, not strings.
208
+ def <=>(string)
209
+ return nil unless string.kind_of?(Pathname)
210
+ super
211
+ end
212
+
213
+ # Adds two Pathname objects together, or a Pathname and a String. It
214
+ # also automatically cleans the Pathname.
215
+ #
216
+ # Example:
217
+ # path1 = '/foo/bar'
218
+ # path2 = '../baz'
219
+ # path1 + path2 # '/foo/baz'
220
+ #
221
+ # Adding a root path to an existing path merely replaces the current
222
+ # path. Adding '.' to an existing path does nothing.
223
+ def +(string)
224
+ unless string.kind_of?(Pathname)
225
+ string = Pathname.new(string)
226
+ end
227
+
228
+ # Any path plus "." is the same directory
229
+ return self if string == "."
230
+
231
+ # Use the builtin PathAppend method if on Windows - much easier
232
+ if @win32
233
+ buf = 0.chr * MAX_PATH
234
+ buf[0..self.length-1] = self
235
+ @@PathAppend.call(buf, string << 0.chr)
236
+ buf.strip!
237
+ return Pathname.new(buf) # PathAppend cleans automatically
238
+ end
239
+
240
+ # If the string is an absolute directory, return it
241
+ return string if string.absolute?
242
+
243
+ array = self.to_a + string.to_a
244
+ new_string = array.join(@sep)
245
+
246
+ unless self.relative? || @win32
247
+ new_string = @sep + new_string # Add root path back if needed
248
+ end
249
+
250
+ Pathname.new(new_string).clean
251
+ end
252
+
253
+ # Returns whether or not the path is an absolute path.
254
+ def absolute?
255
+ !relative?
256
+ end
257
+
258
+ # Returns whether or not the path is a relative path.
259
+ def relative?
260
+ if @win32
261
+ @@PathIsRelative.call(self) > 0
262
+ else
263
+ root == "."
264
+ end
265
+ end
266
+
267
+ # Removes unnecessary '.' paths and ellides '..' paths appropriately.
268
+ def clean
269
+ return self if self.empty?
270
+
271
+ if @win32
272
+ path = 0.chr * MAX_PATH
273
+ if @@PathCanonicalize.call(path, self) > 0
274
+ return Pathname.new(path.split(0.chr).first)
275
+ else
276
+ return self
277
+ end
278
+ end
279
+
280
+ final = []
281
+ self.to_a.each{ |element|
282
+ next if element == "."
283
+ final.push(element)
284
+ if element == ".." && self != ".."
285
+ 2.times{ final.pop }
286
+ end
287
+ }
288
+ final = final.join(@sep)
289
+ final = root + final if root != "."
290
+ final = "." if final.empty?
291
+ Pathname.new(final)
292
+ end
293
+ alias :cleanpath :clean
294
+
295
+ #-- Find facade
296
+
297
+ # Pathname#find is an iterator to traverse a directory tree in a depth first
298
+ # manner. It yields a Pathname for each file under the directory passed to
299
+ # Pathname.new.
300
+ #
301
+ # Since it is implemented by the Find module, Find.prune can be used to
302
+ # control the traverse.
303
+ #
304
+ # If +self+ is ".", yielded pathnames begin with a filename in the current
305
+ # current directory, not ".".
306
+ #
307
+ def find(&block)
308
+ require "find"
309
+ if self == "."
310
+ Find.find(self){ |f| yield Pathname.new(f.sub(%r{\A\./}, '')) }
311
+ else
312
+ Find.find(self) {|f| yield Pathname.new(f) }
313
+ end
314
+ end
315
+
316
+ #-- IO methods not handled by facade
317
+
318
+ # IO.foreach
319
+ def foreach(*args, &block)
320
+ IO.foreach(self, *args, &block)
321
+ end
322
+
323
+ # IO.read
324
+ def read(*args)
325
+ IO.read(self, *args)
326
+ end
327
+
328
+ # IO.readlines
329
+ def readlines(*args)
330
+ IO.readlines(self, *args)
331
+ end
332
+
333
+ # IO.sysopen
334
+ def sysopen(*args)
335
+ IO.sysopen(self, *args)
336
+ end
337
+
338
+ #-- Dir methods not handled by facade
339
+
340
+ # Dir.glob
341
+ def glob(*args)
342
+ if block_given?
343
+ Dir.glob(*args){ |file| yield Pathname.new(file) }
344
+ else
345
+ Dir.glob(*args).map{ |file| Pathname.new(file) }
346
+ end
347
+ end
348
+
349
+ # Dir.chdir
350
+ def chdir(&block)
351
+ Dir.chdir(self, &block)
352
+ end
353
+
354
+ # Dir.entries
355
+ def entries
356
+ Dir.entries(self).map{ |file| Pathname.new(file) }
357
+ end
358
+
359
+ # Dir.mkdir
360
+ def mkdir(*args)
361
+ Dir.mkdir(self, *args)
362
+ end
363
+
364
+ # Dir.opendir
365
+ def opendir(&block)
366
+ Dir.open(self, &block)
367
+ end
368
+
369
+ #-- File methods not handled by facade
370
+
371
+ # File.chmod
372
+ def chmod(mode)
373
+ File.chmod(mode, self)
374
+ end
375
+
376
+ # File.lchmod
377
+ def lchmod(mode)
378
+ File.lchmod(mode, self)
379
+ end
380
+
381
+ # File.chown
382
+ def chown(owner, group)
383
+ File.chown(owner, group, self)
384
+ end
385
+
386
+ # File.lchown
387
+ def lchown(owner, group)
388
+ File.lchown(owner, group, self)
389
+ end
390
+
391
+ # File.fnmatch
392
+ def fnmatch(pattern, *args)
393
+ File.fnmatch(pattern, self, *args)
394
+ end
395
+
396
+ # File.fnmatch?
397
+ def fnmatch?(pattern, *args)
398
+ File.fnmatch?(pattern, self, *args)
399
+ end
400
+
401
+ # File.link
402
+ def link(old)
403
+ File.link(old, self)
404
+ end
405
+
406
+ # File.open
407
+ def open(*args, &block)
408
+ File.open(self, *args, &block)
409
+ end
410
+
411
+ # File.rename
412
+ def rename(name)
413
+ File.rename(self, name)
414
+ end
415
+
416
+ # File.symlink
417
+ def symlink(old)
418
+ File.symlink(old, self)
419
+ end
420
+
421
+ # File.truncate
422
+ def truncate(length)
423
+ File.truncate(self, length)
424
+ end
425
+
426
+ # File.utime
427
+ def utime(atime, mtime)
428
+ File.utime(atime, mtime, self)
429
+ end
430
+
431
+ # File.basename
432
+ def basename(*args)
433
+ File.basename(self, *args)
434
+ end
435
+
436
+ # File.expand_path
437
+ def expand_path(*args)
438
+ File.expand_path(self, *args)
439
+ end
440
+
441
+ #--
442
+ # ftools facade
443
+ #++
444
+
445
+ # ftools File.copy
446
+ def copy(*args)
447
+ File.copy(self, *args)
448
+ end
449
+
450
+ #--
451
+ # FileUtils facade. Note that methods already covered by File and Dir
452
+ # are not defined here (pwd, mkdir, etc).
453
+ #++
454
+
455
+ # FileUtils.cd
456
+ def cd(*args, &block)
457
+ FileUtils.cd(self, *args, &block)
458
+ end
459
+
460
+ # FileUtils.mkdir_p
461
+ def mkdir_p(*args)
462
+ FileUtils.mkdir_p(self, *args)
463
+ end
464
+ alias mkpath mkdir_p
465
+
466
+ # FileUtils.ln
467
+ def ln(*args)
468
+ FileUtils.ln(self, *args)
469
+ end
470
+
471
+ # FileUtils.ln_s
472
+ def ln_s(*args)
473
+ FileUtils.ln_s(self, *args)
474
+ end
475
+
476
+ # FileUtils.ln_sf
477
+ def ln_sf(*args)
478
+ FileUtils.ln_sf(self, *args)
479
+ end
480
+
481
+ # FileUtils.cp
482
+ def cp(*args)
483
+ FileUtils.cp(self, *args)
484
+ end
485
+
486
+ # FileUtils.cp_r
487
+ def cp_r(*args)
488
+ FileUtils.cp_r(self, *args)
489
+ end
490
+
491
+ # FileUtils.mv
492
+ def mv(*args)
493
+ FileUtils.mv(self, *args)
494
+ end
495
+
496
+ # FileUtils.rm
497
+ def rm(*args)
498
+ FileUtils.rm(self, *args)
499
+ end
500
+ alias remove rm
501
+
502
+ # FileUtils.rm_f
503
+ def rm_f(*args)
504
+ FileUtils.rm_f(self, *args)
505
+ end
506
+
507
+ # FileUtils.rm_r
508
+ def rm_r(*args)
509
+ FileUtils.rm_r(self, *args)
510
+ end
511
+
512
+ # FileUtils.rm_rf
513
+ def rm_rf(*args)
514
+ FileUtils.rm_rf(self, *args)
515
+ end
516
+ alias rmtree rm_rf
517
+
518
+ # FileUtils.install
519
+ def install(*args)
520
+ FileUtils.install(self, *args)
521
+ end
522
+
523
+ # FileUtils.touch
524
+ def touch(*args)
525
+ FileUtils.touch(*args)
526
+ end
527
+
528
+ # FileUtils.compare_file
529
+ def compare_file(file)
530
+ FileUtils.compare_file(self, file)
531
+ end
532
+
533
+ # FileUtils.uptodate?
534
+ def uptodate?(*args)
535
+ FileUtils.uptodate(self, *args)
536
+ end
537
+
538
+ # FileUtils.copy_file
539
+ def copy_file(*args)
540
+ FileUtils.copy_file(self, *args)
541
+ end
542
+
543
+ # FileUtils.remove_dir
544
+ def remove_dir(*args)
545
+ FileUtils.remove_dir(self, *args)
546
+ end
547
+
548
+ # FileUtils.remove_file
549
+ def remove_file(*args)
550
+ FileUtils.remove_dir(self, *args)
551
+ end
552
+
553
+ # FileUtils.copy_entry
554
+ def copy_entry(*args)
555
+ FileUtils.copy_entry(self, *args)
556
+ end
557
+ end