pathname2 1.8.2

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