pathname2 1.6.5-universal-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ ########################################################################
2
+ # example_pathname.rb
3
+ #
4
+ # Some examples to demonstrate the behavior of the pathname2 library.
5
+ ########################################################################
6
+ require 'pathname2'
7
+
8
+ puts "VERSION: " + Pathname::VERSION
9
+
10
+ path1 = Pathname.new("foo/bar")
11
+ path2 = Pathname.new("baz/blah")
12
+
13
+ path3 = Pathname.new("foo/../bar")
14
+ path4 = Pathname.new("../baz")
15
+
16
+ p path1 + path2 # foo/bar/baz/blah
17
+ p path3 + path4 # baz
18
+
19
+ # Shortcut syntax
20
+ path = pn{ "C:\\Documents and Settings\\snoopy\\My Documents" }
21
+
22
+ p path[0] # C:
23
+ p path[1] # Documents and Settings
24
+ p path[0,2] # C:\\Documents and Settings
25
+ p path[0..2] # C:\\Documents and Settings\\snoopy
data/lib/pathname2.rb ADDED
@@ -0,0 +1,1131 @@
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 Windows, including UNC path names. Note
10
+ # that forward slashes are converted to backslashes on Windows systems.
11
+ #
12
+ # == Usage
13
+ #
14
+ # require "pathname2"
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
+ # # Windows
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-2011 Daniel J. Berger.
38
+ # Licensed under the same terms as Ruby itself.
39
+ #
40
+ require 'facade'
41
+ require 'fileutils'
42
+
43
+ if File::ALT_SEPARATOR
44
+ require 'windows/path'
45
+ require 'windows/file'
46
+ require 'windows/error'
47
+ require 'windows/limits'
48
+ end
49
+
50
+ # You're mine now.
51
+ Object.send(:remove_const, :Pathname) if defined?(Pathname)
52
+
53
+ class Pathname < String
54
+ class Error < StandardError; end
55
+ extend Facade
56
+
57
+ facade File, File.methods(false).map{ |m| m.to_sym } - [
58
+ :chmod, :lchmod, :chown, :lchown, :dirname, :fnmatch, :fnmatch?,
59
+ :link, :open, :realpath, :rename, :symlink, :truncate, :utime,
60
+ :basename, :expand_path, :join
61
+ ]
62
+
63
+ facade Dir, Dir.methods(false).map{ |m| m.to_sym } - [
64
+ :chdir, :entries, :glob, :foreach, :mkdir, :open
65
+ ]
66
+
67
+ private
68
+
69
+ alias :_plus_ :+ # Used to prevent infinite loops in some cases
70
+
71
+ if File::ALT_SEPARATOR
72
+ include Windows::Path
73
+ include Windows::File
74
+ include Windows::Error
75
+ include Windows::Limits
76
+ end
77
+
78
+ public
79
+
80
+ # The version of the pathname2 library
81
+ VERSION = '1.6.5'
82
+
83
+ # The maximum length of a path
84
+ MAXPATH = 1024 unless defined? MAXPATH # Yes, I willfully violate POSIX
85
+
86
+ # Returns the expanded path of the current working directory.
87
+ #
88
+ # Synonym for Pathname.new(Dir.pwd).
89
+ #
90
+ def self.pwd
91
+ new(Dir.pwd)
92
+ end
93
+
94
+ class << self
95
+ alias getwd pwd
96
+ end
97
+
98
+ # Creates and returns a new Pathname object.
99
+ #
100
+ # On platforms that define File::ALT_SEPARATOR, all forward slashes are
101
+ # replaced with the value of File::ALT_SEPARATOR. On MS Windows, for
102
+ # example, all forward slashes are replaced with backslashes.
103
+ #
104
+ # File URL's will be converted to Pathname objects, e.g. the file URL
105
+ # "file:///C:/Documents%20and%20Settings" will become
106
+ # 'C:\Documents and Settings'.
107
+ #
108
+ # Examples:
109
+ #
110
+ # Pathname.new("/foo/bar/baz")
111
+ # Pathname.new("foo")
112
+ # Pathname.new("file:///foo/bar/baz")
113
+ # Pathname.new("C:\\Documents and Settings\\snoopy")
114
+ #
115
+ def initialize(path)
116
+ if path.length > MAXPATH
117
+ msg = "string too long. maximum string length is " + MAXPATH.to_s
118
+ raise Error, msg
119
+ end
120
+
121
+ @sep = File::ALT_SEPARATOR || File::SEPARATOR
122
+ @win = File::ALT_SEPARATOR
123
+
124
+ # Handle File URL's. The separate approach for Windows is necessary
125
+ # because Ruby's URI class does not (currently) parse absolute file URL's
126
+ # properly when they include a drive letter.
127
+ if @win
128
+ if PathIsURL(path)
129
+ buf = 0.chr * MAXPATH
130
+ len = [buf.length].pack("l")
131
+ if PathCreateFromUrl(path, buf, len, 0) == S_OK
132
+ path = buf.strip
133
+ else
134
+ raise Error, "invalid file url: #{path}"
135
+ end
136
+ end
137
+ else
138
+ if path.index('file:///', 0)
139
+ require 'uri'
140
+ if RUBY_VERSION.to_f >= 1.9
141
+ path = URI::Parser.new.unescape(path)[7..-1] # Blech
142
+ else
143
+ path = URI.decode(URI.parse(path).path)
144
+ end
145
+ end
146
+ end
147
+
148
+ # Convert forward slashes to backslashes on Windows
149
+ path = path.tr("/", @sep) if @win
150
+ super(path)
151
+ end
152
+
153
+ # Returns a real (absolute) pathname of +self+ in the actual filesystem.
154
+ #
155
+ # Unlike most Pathname methods, this one assumes that the path actually
156
+ # exists on your filesystem. If it doesn't, an error is raised. If a
157
+ # circular symlink is encountered a system error will be raised.
158
+ #
159
+ # Example:
160
+ #
161
+ # Dir.pwd # => /usr/local
162
+ # File.exists?('foo') # => true
163
+ # Pathname.new('foo').realpath # => /usr/local/foo
164
+ #
165
+ def realpath
166
+ File.stat(self) # Check to ensure that the path exists
167
+
168
+ if File.symlink?(self)
169
+ file = self.dup
170
+
171
+ while true
172
+ file = File.join(File.dirname(file), File.readlink(file))
173
+ break unless File.symlink?(file)
174
+ end
175
+
176
+ self.class.new(file).clean
177
+ else
178
+ self.class.new(Dir.pwd) + self
179
+ end
180
+ end
181
+
182
+ # Returns the children of the directory, files and subdirectories, as an
183
+ # array of Pathname objects. If you set +with_directory+ to +false+, then
184
+ # the returned pathnames will contain the filename only.
185
+ #
186
+ # Note that the result never contain the entries '.' and '..' in the
187
+ # the directory because they are not children. Also note that this method
188
+ # is *not* recursive.
189
+ #
190
+ # Example:
191
+ #
192
+ # path = Pathname.new('/usr/bin')
193
+ # path.children # => ['/usr/bin/ruby', '/usr/bin/perl', ...]
194
+ # path.children(false) # => ['ruby', 'perl', ...]
195
+ #
196
+ def children(with_directory = true)
197
+ with_directory = false if self == '.'
198
+ result = []
199
+ Dir.foreach(self) { |file|
200
+ next if file == '.' || file == '..'
201
+ if with_directory
202
+ result << self.class.new(File.join(self, file))
203
+ else
204
+ result << self.class.new(file)
205
+ end
206
+ }
207
+ result
208
+ end
209
+
210
+ # Windows only
211
+ #
212
+ # Removes the decoration from a path string. Non-destructive.
213
+ #
214
+ # Example:
215
+ #
216
+ # path = Pathname.new('C:\Path\File[5].txt')
217
+ # path.undecorate # => C:\Path\File.txt.
218
+ #
219
+ def undecorate
220
+ unless @win
221
+ raise NotImplementedError, "not supported on this platform"
222
+ end
223
+ buf = 0.chr * MAXPATH
224
+ buf[0..self.length-1] = self
225
+ PathUndecorate(buf)
226
+ self.class.new(buf.split(0.chr).first)
227
+ end
228
+
229
+ # Windows only
230
+ #
231
+ # Performs the substitution of Pathname#undecorate in place.
232
+ #
233
+ def undecorate!
234
+ unless @win
235
+ raise NotImplementedError, "not supported on this platform"
236
+ end
237
+ buf = 0.chr * MAXPATH
238
+ buf[0..self.length-1] = self
239
+ PathUndecorate(buf)
240
+ replace(buf.split(0.chr).first)
241
+ self
242
+ end
243
+
244
+ # Windows only
245
+ #
246
+ # Returns the short path for a long path name.
247
+ #
248
+ # Example:
249
+ #
250
+ # path = Pathname.new('C:\Program Files\Java')
251
+ # path.short_path # => C:\Progra~1\Java.
252
+ #
253
+ def short_path
254
+ unless @win
255
+ raise NotImplementedError, "not supported on this platform"
256
+ end
257
+ buf = 0.chr * MAXPATH
258
+ buf[0..self.length-1] = self
259
+ GetShortPathName(self, buf, buf.length)
260
+ self.class.new(buf.split(0.chr).first)
261
+ end
262
+
263
+ # Windows only
264
+ #
265
+ # Returns the long path for a long path name.
266
+ #
267
+ # Example:
268
+ #
269
+ # path = Pathname.new('C:\Progra~1\Java')
270
+ # path.long_path # => C:\Program Files\Java.
271
+ #
272
+ def long_path
273
+ unless @win
274
+ raise NotImplementedError, "not supported on this platform"
275
+ end
276
+ buf = 0.chr * MAXPATH
277
+ buf[0..self.length-1] = self
278
+ GetLongPathName(self, buf, buf.length)
279
+ self.class.new(buf.split(0.chr).first)
280
+ end
281
+
282
+ # Removes trailing slash, if present. Non-destructive.
283
+ #
284
+ # Example:
285
+ #
286
+ # path = Pathname.new('/usr/local/')
287
+ # path.pstrip # => '/usr/local'
288
+ #
289
+ def pstrip
290
+ str = self.dup
291
+ if @win
292
+ PathRemoveBackslash(str)
293
+ str.strip!
294
+ else
295
+ if str.to_s[-1].chr == @sep
296
+ str.strip!
297
+ str.chop!
298
+ end
299
+ end
300
+ self.class.new(str)
301
+ end
302
+
303
+ # Performs the substitution of Pathname#pstrip in place.
304
+ #
305
+ def pstrip!
306
+ if @win
307
+ PathRemoveBackslash(self)
308
+ strip!
309
+ else
310
+ if self.to_s[-1].chr == @sep
311
+ strip!
312
+ chop!
313
+ end
314
+ end
315
+ self
316
+ end
317
+
318
+ # Splits a pathname into strings based on the path separator.
319
+ #
320
+ # Examples:
321
+ #
322
+ # Pathname.new('/usr/local/bin').to_a # => ['usr', 'local', 'bin']
323
+ # Pathname.new('C:\WINNT\Fonts').to_a # => ['C:', 'WINNT', 'Fonts']
324
+ #
325
+ def to_a
326
+ array = split(@sep) # Split string by path separator
327
+ array.delete("") # Remove empty elements
328
+ array
329
+ end
330
+
331
+ # Yields each component of the path name to a block.
332
+ #
333
+ # Example:
334
+ #
335
+ # Pathname.new('/usr/local/bin').each{ |element|
336
+ # puts "Element: #{element}"
337
+ # }
338
+ #
339
+ # Yields 'usr', 'local', and 'bin', in turn
340
+ #
341
+ def each
342
+ to_a.each{ |element| yield element }
343
+ end
344
+
345
+ # Returns the path component at +index+, up to +length+ components, joined
346
+ # by the path separator. If the +index+ is a Range, then that is used
347
+ # instead and the +length+ is ignored.
348
+ #
349
+ # Keep in mind that on MS Windows the drive letter is the first element.
350
+ #
351
+ # Examples:
352
+ #
353
+ # path = Pathname.new('/home/john/source/ruby')
354
+ # path[0] # => 'home'
355
+ # path[1] # => 'john'
356
+ # path[0, 3] # => '/home/john/source'
357
+ # path[0..1] # => '/home/john'
358
+ #
359
+ # path = Pathname.new('C:/Documents and Settings/John/Source/Ruby')
360
+ # path[0] # => 'C:\'
361
+ # path[1] # => 'Documents and Settings'
362
+ # path[0, 3] # => 'C:\Documents and Settings\John'
363
+ # path[0..1] # => 'C:\Documents and Settings'
364
+ #
365
+ def [](index, length=nil)
366
+ if index.is_a?(Fixnum)
367
+ if length
368
+ path = File.join(to_a[index, length])
369
+ else
370
+ path = to_a[index]
371
+ end
372
+ elsif index.is_a?(Range)
373
+ if length
374
+ warn 'Length argument ignored'
375
+ end
376
+ path = File.join(to_a[index])
377
+ else
378
+ raise TypeError, "Only Fixnums and Ranges allowed as first argument"
379
+ end
380
+
381
+ if path && @win
382
+ path = path.tr("/", "\\")
383
+ end
384
+
385
+ path
386
+ end
387
+
388
+ # Yields each component of the path, concatenating the next component on
389
+ # each iteration as a new Pathname object, starting with the root path.
390
+ #
391
+ # Example:
392
+ #
393
+ # path = Pathname.new('/usr/local/bin')
394
+ #
395
+ # path.descend{ |name|
396
+ # puts name
397
+ # }
398
+ #
399
+ # First iteration => '/'
400
+ # Second iteration => '/usr'
401
+ # Third iteration => '/usr/local'
402
+ # Fourth iteration => '/usr/local/bin'
403
+ #
404
+ def descend
405
+ if root?
406
+ yield root
407
+ return
408
+ end
409
+
410
+ if @win
411
+ path = unc? ? "#{root}\\" : ""
412
+ else
413
+ path = absolute? ? root : ""
414
+ end
415
+
416
+ # Yield the root directory if an absolute path (and not Windows)
417
+ unless @win && !unc?
418
+ yield root if absolute?
419
+ end
420
+
421
+ each{ |element|
422
+ if @win && unc?
423
+ next if root.to_a.include?(element)
424
+ end
425
+ path << element << @sep
426
+ yield self.class.new(path.chop)
427
+ }
428
+ end
429
+
430
+ # Yields the path, minus one component on each iteration, as a new
431
+ # Pathname object, ending with the root path.
432
+ #
433
+ # Example:
434
+ #
435
+ # path = Pathname.new('/usr/local/bin')
436
+ #
437
+ # path.ascend{ |name|
438
+ # puts name
439
+ # }
440
+ #
441
+ # First iteration => '/usr/local/bin'
442
+ # Second iteration => '/usr/local'
443
+ # Third iteration => '/usr'
444
+ # Fourth iteration => '/'
445
+ #
446
+ def ascend
447
+ if root?
448
+ yield root
449
+ return
450
+ end
451
+
452
+ n = to_a.length
453
+
454
+ while n > 0
455
+ path = to_a[0..n-1].join(@sep)
456
+ if absolute?
457
+ if @win && unc?
458
+ path = "\\\\" << path
459
+ end
460
+ unless @win
461
+ path = root << path
462
+ end
463
+ end
464
+
465
+ path = self.class.new(path)
466
+ yield path
467
+
468
+ if @win && unc?
469
+ break if path.root?
470
+ end
471
+
472
+ n -= 1
473
+ end
474
+
475
+ # Yield the root directory if an absolute path (and not Windows)
476
+ unless @win
477
+ yield root if absolute?
478
+ end
479
+ end
480
+
481
+ # Returns the root directory of the path, or '.' if there is no root
482
+ # directory.
483
+ #
484
+ # On Unix, this means the '/' character. On Windows, this can refer
485
+ # to the drive letter, or the server and share path if the path is a
486
+ # UNC path.
487
+ #
488
+ # Examples:
489
+ #
490
+ # Pathname.new('/usr/local').root # => '/'
491
+ # Pathname.new('lib').root # => '.'
492
+ #
493
+ # On MS Windows:
494
+ #
495
+ # Pathname.new('C:\WINNT').root # => 'C:'
496
+ # Pathname.new('\\some\share\foo').root # => '\\some\share'
497
+ #
498
+ def root
499
+ dir = "."
500
+
501
+ if @win
502
+ buf = 0.chr * MAXPATH
503
+ buf[0..self.length-1] = self
504
+
505
+ if PathStripToRoot(buf)
506
+ dir = buf.split(0.chr).first
507
+ end
508
+ else
509
+ dir = "/" if self =~ /^\//
510
+ end
511
+
512
+ self.class.new(dir)
513
+ end
514
+
515
+ # Returns whether or not the path consists only of a root directory.
516
+ #
517
+ # Examples:
518
+ #
519
+ # Pathname.new('/').root? # => true
520
+ # Pathname.new('/foo').root? # => false
521
+ #
522
+ def root?
523
+ if @win
524
+ PathIsRoot(self)
525
+ else
526
+ self == root
527
+ end
528
+ end
529
+
530
+ # MS Windows only
531
+ #
532
+ # Determines if the string is a valid Universal Naming Convention (UNC)
533
+ # for a server and share path.
534
+ #
535
+ # Examples:
536
+ #
537
+ # Pathname.new("\\\\foo\\bar").unc? # => true
538
+ # Pathname.new('C:\Program Files').unc? # => false
539
+ #
540
+ def unc?
541
+ unless @win
542
+ raise NotImplementedError, "not supported on this platform"
543
+ end
544
+
545
+ PathIsUNC(self)
546
+ end
547
+
548
+ # MS Windows only
549
+ #
550
+ # Returns the drive number that corresponds to the root, or nil if not
551
+ # applicable.
552
+ #
553
+ # Example:
554
+ #
555
+ # Pathname.new("C:\\foo").drive_number # => 2
556
+ #
557
+ def drive_number
558
+ unless @win
559
+ raise NotImplementedError, "not supported on this platform"
560
+ end
561
+
562
+ num = PathGetDriveNumber(self)
563
+ num >= 0 ? num : nil
564
+ end
565
+
566
+ # Compares two Pathname objects. Note that Pathnames may only be compared
567
+ # against other Pathnames, not strings. Otherwise nil is returned.
568
+ #
569
+ # Example:
570
+ #
571
+ # path1 = Pathname.new('/usr/local')
572
+ # path2 = Pathname.new('/usr/local')
573
+ # path3 = Pathname.new('/usr/local/bin')
574
+ #
575
+ # path1 <=> path2 # => 0
576
+ # path1 <=> path3 # => -1
577
+ #
578
+ def <=>(string)
579
+ return nil unless string.kind_of?(Pathname)
580
+ super
581
+ end
582
+
583
+ # Returns the parent directory of the given path.
584
+ #
585
+ # Example:
586
+ #
587
+ # Pathname.new('/usr/local/bin').parent # => '/usr/local'
588
+ #
589
+ def parent
590
+ self + ".." # Use our custom '+' method
591
+ end
592
+
593
+ # Returns a relative path from the argument to the receiver. If +self+
594
+ # is absolute, the argument must be absolute too. If +self+ is relative,
595
+ # the argument must be relative too. For relative paths, this method uses
596
+ # an imaginary, common parent path.
597
+ #
598
+ # This method does not access the filesystem. It assumes no symlinks.
599
+ # You should only compare directories against directories, or files against
600
+ # files, or you may get unexpected results.
601
+ #
602
+ # Raises an ArgumentError if it cannot find a relative path.
603
+ #
604
+ # Examples:
605
+ #
606
+ # path = Pathname.new('/usr/local/bin')
607
+ # path.relative_path_from('/usr/bin') # => "../local/bin"
608
+ #
609
+ # path = Pathname.new("C:\\WINNT\\Fonts")
610
+ # path.relative_path_from("C:\\Program Files") # => "..\\WINNT\\Fonts"
611
+ #
612
+ def relative_path_from(base)
613
+ base = self.class.new(base) unless base.kind_of?(Pathname)
614
+
615
+ if self.absolute? != base.absolute?
616
+ raise ArgumentError, "relative path between absolute and relative path"
617
+ end
618
+
619
+ return self.class.new(".") if self == base
620
+ return self if base == "."
621
+
622
+ # Because of the way the Windows version handles Pathname#clean, we need
623
+ # a little extra help here.
624
+ if @win
625
+ if root != base.root
626
+ msg = 'cannot determine relative paths from different root paths'
627
+ raise ArgumentError, msg
628
+ end
629
+ if base == '..' && (self != '..' || self != '.')
630
+ raise ArgumentError, "base directory may not contain '..'"
631
+ end
632
+ end
633
+
634
+ dest_arr = self.clean.to_a
635
+ base_arr = base.clean.to_a
636
+ dest_arr.delete('.')
637
+ base_arr.delete('.')
638
+
639
+ diff_arr = dest_arr - base_arr
640
+
641
+ while !base_arr.empty? && !dest_arr.empty? && base_arr[0] == dest_arr[0]
642
+ base_arr.shift
643
+ dest_arr.shift
644
+ end
645
+
646
+ if base_arr.include?("..")
647
+ raise ArgumentError, "base directory may not contain '..'"
648
+ end
649
+
650
+ base_arr.fill("..")
651
+ rel_path = base_arr + dest_arr
652
+
653
+ if rel_path.empty?
654
+ self.class.new(".")
655
+ else
656
+ self.class.new(rel_path.join(@sep))
657
+ end
658
+ end
659
+
660
+ # Adds two Pathname objects together, or a Pathname and a String. It
661
+ # also automatically cleans the Pathname.
662
+ #
663
+ # Adding a root path to an existing path merely replaces the current
664
+ # path. Adding '.' to an existing path does nothing.
665
+ #
666
+ # Example:
667
+ #
668
+ # path1 = '/foo/bar'
669
+ # path2 = '../baz'
670
+ # path1 + path2 # '/foo/baz'
671
+ #
672
+ def +(string)
673
+ unless string.kind_of?(Pathname)
674
+ string = self.class.new(string)
675
+ end
676
+
677
+ # Any path plus "." is the same directory
678
+ return self if string == "."
679
+ return string if self == "."
680
+
681
+ # Use the builtin PathAppend() function if on Windows - much easier
682
+ if @win
683
+ buf = 0.chr * MAXPATH
684
+ buf[0..self.length-1] = self
685
+ PathAppend(buf, string)
686
+ buf = buf.split("\0").first
687
+ return self.class.new(buf) # PathAppend cleans automatically
688
+ end
689
+
690
+ # If the string is an absolute directory, return it
691
+ return string if string.absolute?
692
+
693
+ array = to_a + string.to_a
694
+ new_string = array.join(@sep)
695
+
696
+ unless relative? || @win
697
+ temp = @sep + new_string # Add root path back if needed
698
+ new_string.replace(temp)
699
+ end
700
+
701
+ self.class.new(new_string).clean
702
+ end
703
+
704
+ alias :/ :+
705
+
706
+ # Returns whether or not the path is an absolute path.
707
+ #
708
+ # Example:
709
+ #
710
+ # Pathname.new('/usr/bin').absolute? # => true
711
+ # Pathname.new('usr').absolute? # => false
712
+ #
713
+ def absolute?
714
+ !relative?
715
+ end
716
+
717
+ # Returns whether or not the path is a relative path.
718
+ #
719
+ # Example:
720
+ #
721
+ # Pathname.new('/usr/bin').relative? # => true
722
+ # Pathname.new('usr').relative? # => false
723
+ #
724
+ def relative?
725
+ if @win
726
+ PathIsRelative(self)
727
+ else
728
+ root == "."
729
+ end
730
+ end
731
+
732
+ # Removes unnecessary '.' paths and ellides '..' paths appropriately.
733
+ # This method is non-destructive.
734
+ #
735
+ # Example:
736
+ #
737
+ # path = Pathname.new('/usr/./local/../bin')
738
+ # path.clean # => '/usr/bin'
739
+ #
740
+ def clean
741
+ return self if self.empty?
742
+
743
+ if @win
744
+ path = 0.chr * MAXPATH
745
+ if PathCanonicalize(path, self)
746
+ return self.class.new(path.split(0.chr).first)
747
+ else
748
+ return self
749
+ end
750
+ end
751
+
752
+ final = []
753
+
754
+ to_a.each{ |element|
755
+ next if element == "."
756
+ final.push(element)
757
+ if element == ".." && self != ".."
758
+ 2.times{ final.pop }
759
+ end
760
+ }
761
+
762
+ final = final.join(@sep)
763
+ final = root._plus_(final) if root != "."
764
+ final = "." if final.empty?
765
+
766
+ self.class.new(final)
767
+ end
768
+
769
+ alias :cleanpath :clean
770
+
771
+ # Identical to Pathname#clean, except that it modifies the receiver
772
+ # in place.
773
+ #
774
+ def clean!
775
+ return self if self.empty?
776
+
777
+ if @win
778
+ path = 0.chr * MAXPATH
779
+ if PathCanonicalize(path, self)
780
+ replace(path.split(0.chr).first)
781
+ end
782
+ return self
783
+ end
784
+
785
+ final = []
786
+
787
+ to_a.each{ |element|
788
+ next if element == "."
789
+ final.push(element)
790
+ if element == ".." && self != ".."
791
+ 2.times{ final.pop }
792
+ end
793
+ }
794
+
795
+ final = final.join(@sep)
796
+ final = root + final if root != "."
797
+ final = "." if final.empty?
798
+ replace(self.class.new(final))
799
+
800
+ self
801
+ end
802
+
803
+ alias cleanpath! clean!
804
+
805
+ # Similar to File.dirname, but this method allows you to specify the number
806
+ # of levels up you wish to refer to.
807
+ #
808
+ # The default level is 1, i.e. it works the same as File.dirname. A level of
809
+ # 0 will return the original path. A level equal to or greater than the
810
+ # number of path elements will return the root path.
811
+ #
812
+ # A number less than 0 will raise an ArgumentError.
813
+ #
814
+ # Example:
815
+ #
816
+ # path = Pathname.new('/usr/local/bin/ruby')
817
+ #
818
+ # puts path.dirname # => /usr/local/bin
819
+ # puts path.dirname(2) # => /usr/local
820
+ # puts path.dirname(3) # => /usr
821
+ # puts path.dirname(9) # => /
822
+ #
823
+ def dirname(level = 1)
824
+ raise ArgumentError if level < 0
825
+ local_path = self.dup
826
+
827
+ level.times{ |n| local_path = File.dirname(local_path) }
828
+ local_path
829
+ end
830
+
831
+ #-- Find facade
832
+
833
+ # Pathname#find is an iterator to traverse a directory tree in a depth first
834
+ # manner. It yields a Pathname for each file under the directory passed to
835
+ # Pathname.new.
836
+ #
837
+ # Since it is implemented by the Find module, Find.prune can be used to
838
+ # control the traverse.
839
+ #
840
+ # If +self+ is ".", yielded pathnames begin with a filename in the current
841
+ # current directory, not ".".
842
+ #
843
+ def find(&block)
844
+ require "find"
845
+ if self == "."
846
+ Find.find(self){ |f| yield self.class.new(f.sub(%r{\A\./}, '')) }
847
+ else
848
+ Find.find(self){ |f| yield self.class.new(f) }
849
+ end
850
+ end
851
+
852
+ #-- IO methods not handled by facade
853
+
854
+ # IO.foreach
855
+ def foreach(*args, &block)
856
+ IO.foreach(self, *args, &block)
857
+ end
858
+
859
+ # IO.read
860
+ def read(*args)
861
+ IO.read(self, *args)
862
+ end
863
+
864
+ # IO.readlines
865
+ def readlines(*args)
866
+ IO.readlines(self, *args)
867
+ end
868
+
869
+ # IO.sysopen
870
+ def sysopen(*args)
871
+ IO.sysopen(self, *args)
872
+ end
873
+
874
+ #-- Dir methods not handled by facade
875
+
876
+ # Dir.glob
877
+ #
878
+ # :no-doc:
879
+ # This differs from Tanaka's implementation in that it does a temporary
880
+ # chdir to the path in question, then performs the glob.
881
+ #
882
+ def glob(*args)
883
+ Dir.chdir(self){
884
+ if block_given?
885
+ Dir.glob(*args){ |file| yield self.class.new(file) }
886
+ else
887
+ Dir.glob(*args).map{ |file| self.class.new(file) }
888
+ end
889
+ }
890
+ end
891
+
892
+ # Dir.chdir
893
+ def chdir(&block)
894
+ Dir.chdir(self, &block)
895
+ end
896
+
897
+ # Dir.entries
898
+ def entries
899
+ Dir.entries(self).map{ |file| self.class.new(file) }
900
+ end
901
+
902
+ # Dir.mkdir
903
+ def mkdir(*args)
904
+ Dir.mkdir(self, *args)
905
+ end
906
+
907
+ # Dir.opendir
908
+ def opendir(&block)
909
+ Dir.open(self, &block)
910
+ end
911
+
912
+ #-- File methods not handled by facade
913
+
914
+ # File.chmod
915
+ def chmod(mode)
916
+ File.chmod(mode, self)
917
+ end
918
+
919
+ # File.lchmod
920
+ def lchmod(mode)
921
+ File.lchmod(mode, self)
922
+ end
923
+
924
+ # File.chown
925
+ def chown(owner, group)
926
+ File.chown(owner, group, self)
927
+ end
928
+
929
+ # File.lchown
930
+ def lchown(owner, group)
931
+ File.lchown(owner, group, self)
932
+ end
933
+
934
+ # File.fnmatch
935
+ def fnmatch(pattern, *args)
936
+ File.fnmatch(pattern, self, *args)
937
+ end
938
+
939
+ # File.fnmatch?
940
+ def fnmatch?(pattern, *args)
941
+ File.fnmatch?(pattern, self, *args)
942
+ end
943
+
944
+ # File.link
945
+ def link(old)
946
+ File.link(old, self)
947
+ end
948
+
949
+ # File.open
950
+ def open(*args, &block)
951
+ File.open(self, *args, &block)
952
+ end
953
+
954
+ # File.rename
955
+ def rename(name)
956
+ File.rename(self, name)
957
+ end
958
+
959
+ # File.symlink
960
+ def symlink(old)
961
+ File.symlink(old, self)
962
+ end
963
+
964
+ # File.truncate
965
+ def truncate(length)
966
+ File.truncate(self, length)
967
+ end
968
+
969
+ # File.utime
970
+ def utime(atime, mtime)
971
+ File.utime(atime, mtime, self)
972
+ end
973
+
974
+ # File.basename
975
+ def basename(*args)
976
+ File.basename(self, *args)
977
+ end
978
+
979
+ # File.expand_path
980
+ def expand_path(*args)
981
+ File.expand_path(self, *args)
982
+ end
983
+
984
+ # File.join
985
+ def join(*args)
986
+ File.join(self, *args)
987
+ end
988
+
989
+ #--
990
+ # FileUtils facade. Note that methods already covered by File and Dir
991
+ # are not defined here (pwd, mkdir, etc).
992
+ #++
993
+
994
+ # FileUtils.cd
995
+ def cd(*args, &block)
996
+ FileUtils.cd(self, *args, &block)
997
+ end
998
+
999
+ # FileUtils.mkdir_p
1000
+ def mkdir_p(*args)
1001
+ FileUtils.mkdir_p(self, *args)
1002
+ end
1003
+
1004
+ alias mkpath mkdir_p
1005
+
1006
+ # FileUtils.ln
1007
+ def ln(*args)
1008
+ FileUtils.ln(self, *args)
1009
+ end
1010
+
1011
+ # FileUtils.ln_s
1012
+ def ln_s(*args)
1013
+ FileUtils.ln_s(self, *args)
1014
+ end
1015
+
1016
+ # FileUtils.ln_sf
1017
+ def ln_sf(*args)
1018
+ FileUtils.ln_sf(self, *args)
1019
+ end
1020
+
1021
+ # FileUtils.cp
1022
+ def cp(*args)
1023
+ FileUtils.cp(self, *args)
1024
+ end
1025
+
1026
+ # FileUtils.cp_r
1027
+ def cp_r(*args)
1028
+ FileUtils.cp_r(self, *args)
1029
+ end
1030
+
1031
+ # FileUtils.mv
1032
+ def mv(*args)
1033
+ FileUtils.mv(self, *args)
1034
+ end
1035
+
1036
+ # FileUtils.rm
1037
+ def rm(*args)
1038
+ FileUtils.rm(self, *args)
1039
+ end
1040
+
1041
+ alias remove rm
1042
+
1043
+ # FileUtils.rm_f
1044
+ def rm_f(*args)
1045
+ FileUtils.rm_f(self, *args)
1046
+ end
1047
+
1048
+ # FileUtils.rm_r
1049
+ def rm_r(*args)
1050
+ FileUtils.rm_r(self, *args)
1051
+ end
1052
+
1053
+ # FileUtils.rm_rf
1054
+ def rm_rf(*args)
1055
+ FileUtils.rm_rf(self, *args)
1056
+ end
1057
+
1058
+ # FileUtils.rmtree
1059
+ def rmtree(*args)
1060
+ FileUtils.rmtree(self, *args)
1061
+ end
1062
+
1063
+ # FileUtils.install
1064
+ def install(*args)
1065
+ FileUtils.install(self, *args)
1066
+ end
1067
+
1068
+ # FileUtils.touch
1069
+ def touch(*args)
1070
+ FileUtils.touch(*args)
1071
+ end
1072
+
1073
+ # FileUtils.compare_file
1074
+ def compare_file(file)
1075
+ FileUtils.compare_file(self, file)
1076
+ end
1077
+
1078
+ # FileUtils.uptodate?
1079
+ def uptodate?(*args)
1080
+ FileUtils.uptodate(self, *args)
1081
+ end
1082
+
1083
+ # FileUtils.copy_file
1084
+ def copy_file(*args)
1085
+ FileUtils.copy_file(self, *args)
1086
+ end
1087
+
1088
+ # FileUtils.remove_dir
1089
+ def remove_dir(*args)
1090
+ FileUtils.remove_dir(self, *args)
1091
+ end
1092
+
1093
+ # FileUtils.remove_file
1094
+ def remove_file(*args)
1095
+ FileUtils.remove_dir(self, *args)
1096
+ end
1097
+
1098
+ # FileUtils.copy_entry
1099
+ def copy_entry(*args)
1100
+ FileUtils.copy_entry(self, *args)
1101
+ end
1102
+ end
1103
+
1104
+ module Kernel
1105
+ # Usage: pn{ path }
1106
+ #
1107
+ # A shortcut for Pathname.new
1108
+ #
1109
+ def pn
1110
+ instance_eval{ Pathname.new(yield) }
1111
+ end
1112
+
1113
+ begin
1114
+ remove_method(:Pathname)
1115
+ rescue NoMethodError, NameError
1116
+ # Do nothing, not defined.
1117
+ end
1118
+
1119
+ # Synonym for Pathname.new
1120
+ #
1121
+ def Pathname(path)
1122
+ Pathname.new(path)
1123
+ end
1124
+ end
1125
+
1126
+ class String
1127
+ # Convert a string directly into a Pathname object.
1128
+ def to_path
1129
+ Pathname.new(self)
1130
+ end
1131
+ end