rubysl-pathname 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/pathname.rb +1 -0
- data/lib/rubysl/pathname.rb +2 -0
- data/lib/rubysl/pathname/pathname.rb +1062 -0
- data/lib/rubysl/pathname/version.rb +5 -0
- data/rubysl-pathname.gemspec +23 -0
- data/spec/absolute_spec.rb +22 -0
- data/spec/equal_value_spec.rb +14 -0
- data/spec/hash_spec.rb +14 -0
- data/spec/new_spec.rb +18 -0
- data/spec/parent_spec.rb +18 -0
- data/spec/relative_spec.rb +22 -0
- data/spec/root_spec.rb +26 -0
- data/spec/sub_spec.rb +15 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8f18649e8f93aac59bb0550a713693ba6fbff5cc
|
4
|
+
data.tar.gz: 3b1d2a5bd815c6c0b319fd8d23262aaaa7081043
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 158aa06fe4756074097832ce6090a2156b60d8ed5431e14a722eff594c2527bbf8e4ae63224666ee2fbe8cb39eec4d3953c96e99b97f80c2c6ff64f0565a21f2
|
7
|
+
data.tar.gz: 2823c67acff7ceefcb2e7e824d05cbc626470251026643db6008b514e688c7a8f6f1954fd80a297557359aba068111b076e377c32c2892daa0b2c9fd1cc60ae0
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2013, Brian Shirai
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
3. Neither the name of the library nor the names of its contributors may be
|
13
|
+
used to endorse or promote products derived from this software without
|
14
|
+
specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
|
20
|
+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
21
|
+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
22
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
23
|
+
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
24
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
25
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Rubysl::Pathname
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rubysl-pathname'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rubysl-pathname
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/pathname.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "rubysl/pathname"
|
@@ -0,0 +1,1062 @@
|
|
1
|
+
#
|
2
|
+
# = pathname.rb
|
3
|
+
#
|
4
|
+
# Object-Oriented Pathname Class
|
5
|
+
#
|
6
|
+
# Author:: Tanaka Akira <akr@m17n.org>
|
7
|
+
# Documentation:: Author and Gavin Sinclair
|
8
|
+
#
|
9
|
+
# For documentation, see class Pathname.
|
10
|
+
#
|
11
|
+
# <tt>pathname.rb</tt> is distributed with Ruby since 1.8.0.
|
12
|
+
#
|
13
|
+
|
14
|
+
#
|
15
|
+
# == Pathname
|
16
|
+
#
|
17
|
+
# Pathname represents a pathname which locates a file in a filesystem.
|
18
|
+
# The pathname depends on OS: Unix, Windows, etc.
|
19
|
+
# Pathname library works with pathnames of local OS.
|
20
|
+
# However non-Unix pathnames are supported experimentally.
|
21
|
+
#
|
22
|
+
# It does not represent the file itself.
|
23
|
+
# A Pathname can be relative or absolute. It's not until you try to
|
24
|
+
# reference the file that it even matters whether the file exists or not.
|
25
|
+
#
|
26
|
+
# Pathname is immutable. It has no method for destructive update.
|
27
|
+
#
|
28
|
+
# The value of this class is to manipulate file path information in a neater
|
29
|
+
# way than standard Ruby provides. The examples below demonstrate the
|
30
|
+
# difference. *All* functionality from File, FileTest, and some from Dir and
|
31
|
+
# FileUtils is included, in an unsurprising way. It is essentially a facade for
|
32
|
+
# all of these, and more.
|
33
|
+
#
|
34
|
+
# == Examples
|
35
|
+
#
|
36
|
+
# === Example 1: Using Pathname
|
37
|
+
#
|
38
|
+
# require 'pathname'
|
39
|
+
# p = Pathname.new("/usr/bin/ruby")
|
40
|
+
# size = p.size # 27662
|
41
|
+
# isdir = p.directory? # false
|
42
|
+
# dir = p.dirname # Pathname:/usr/bin
|
43
|
+
# base = p.basename # Pathname:ruby
|
44
|
+
# dir, base = p.split # [Pathname:/usr/bin, Pathname:ruby]
|
45
|
+
# data = p.read
|
46
|
+
# p.open { |f| _ }
|
47
|
+
# p.each_line { |line| _ }
|
48
|
+
#
|
49
|
+
# === Example 2: Using standard Ruby
|
50
|
+
#
|
51
|
+
# p = "/usr/bin/ruby"
|
52
|
+
# size = File.size(p) # 27662
|
53
|
+
# isdir = File.directory?(p) # false
|
54
|
+
# dir = File.dirname(p) # "/usr/bin"
|
55
|
+
# base = File.basename(p) # "ruby"
|
56
|
+
# dir, base = File.split(p) # ["/usr/bin", "ruby"]
|
57
|
+
# data = File.read(p)
|
58
|
+
# File.open(p) { |f| _ }
|
59
|
+
# File.foreach(p) { |line| _ }
|
60
|
+
#
|
61
|
+
# === Example 3: Special features
|
62
|
+
#
|
63
|
+
# p1 = Pathname.new("/usr/lib") # Pathname:/usr/lib
|
64
|
+
# p2 = p1 + "ruby/1.8" # Pathname:/usr/lib/ruby/1.8
|
65
|
+
# p3 = p1.parent # Pathname:/usr
|
66
|
+
# p4 = p2.relative_path_from(p3) # Pathname:lib/ruby/1.8
|
67
|
+
# pwd = Pathname.pwd # Pathname:/home/gavin
|
68
|
+
# pwd.absolute? # true
|
69
|
+
# p5 = Pathname.new "." # Pathname:.
|
70
|
+
# p5 = p5 + "music/../articles" # Pathname:music/../articles
|
71
|
+
# p5.cleanpath # Pathname:articles
|
72
|
+
# p5.realpath # Pathname:/home/gavin/articles
|
73
|
+
# p5.children # [Pathname:/home/gavin/articles/linux, ...]
|
74
|
+
#
|
75
|
+
# == Breakdown of functionality
|
76
|
+
#
|
77
|
+
# === Core methods
|
78
|
+
#
|
79
|
+
# These methods are effectively manipulating a String, because that's all a path
|
80
|
+
# is. Except for #mountpoint?, #children, and #realpath, they don't access the
|
81
|
+
# filesystem.
|
82
|
+
#
|
83
|
+
# - +
|
84
|
+
# - #join
|
85
|
+
# - #parent
|
86
|
+
# - #root?
|
87
|
+
# - #absolute?
|
88
|
+
# - #relative?
|
89
|
+
# - #relative_path_from
|
90
|
+
# - #each_filename
|
91
|
+
# - #cleanpath
|
92
|
+
# - #realpath
|
93
|
+
# - #children
|
94
|
+
# - #mountpoint?
|
95
|
+
#
|
96
|
+
# === File status predicate methods
|
97
|
+
#
|
98
|
+
# These methods are a facade for FileTest:
|
99
|
+
# - #blockdev?
|
100
|
+
# - #chardev?
|
101
|
+
# - #directory?
|
102
|
+
# - #executable?
|
103
|
+
# - #executable_real?
|
104
|
+
# - #exist?
|
105
|
+
# - #file?
|
106
|
+
# - #grpowned?
|
107
|
+
# - #owned?
|
108
|
+
# - #pipe?
|
109
|
+
# - #readable?
|
110
|
+
# - #world_readable?
|
111
|
+
# - #readable_real?
|
112
|
+
# - #setgid?
|
113
|
+
# - #setuid?
|
114
|
+
# - #size
|
115
|
+
# - #size?
|
116
|
+
# - #socket?
|
117
|
+
# - #sticky?
|
118
|
+
# - #symlink?
|
119
|
+
# - #writable?
|
120
|
+
# - #world_writable?
|
121
|
+
# - #writable_real?
|
122
|
+
# - #zero?
|
123
|
+
#
|
124
|
+
# === File property and manipulation methods
|
125
|
+
#
|
126
|
+
# These methods are a facade for File:
|
127
|
+
# - #atime
|
128
|
+
# - #ctime
|
129
|
+
# - #mtime
|
130
|
+
# - #chmod(mode)
|
131
|
+
# - #lchmod(mode)
|
132
|
+
# - #chown(owner, group)
|
133
|
+
# - #lchown(owner, group)
|
134
|
+
# - #fnmatch(pattern, *args)
|
135
|
+
# - #fnmatch?(pattern, *args)
|
136
|
+
# - #ftype
|
137
|
+
# - #make_link(old)
|
138
|
+
# - #open(*args, &block)
|
139
|
+
# - #readlink
|
140
|
+
# - #rename(to)
|
141
|
+
# - #stat
|
142
|
+
# - #lstat
|
143
|
+
# - #make_symlink(old)
|
144
|
+
# - #truncate(length)
|
145
|
+
# - #utime(atime, mtime)
|
146
|
+
# - #basename(*args)
|
147
|
+
# - #dirname
|
148
|
+
# - #extname
|
149
|
+
# - #expand_path(*args)
|
150
|
+
# - #split
|
151
|
+
#
|
152
|
+
# === Directory methods
|
153
|
+
#
|
154
|
+
# These methods are a facade for Dir:
|
155
|
+
# - Pathname.glob(*args)
|
156
|
+
# - Pathname.getwd / Pathname.pwd
|
157
|
+
# - #rmdir
|
158
|
+
# - #entries
|
159
|
+
# - #each_entry(&block)
|
160
|
+
# - #mkdir(*args)
|
161
|
+
# - #opendir(*args)
|
162
|
+
#
|
163
|
+
# === IO
|
164
|
+
#
|
165
|
+
# These methods are a facade for IO:
|
166
|
+
# - #each_line(*args, &block)
|
167
|
+
# - #read(*args)
|
168
|
+
# - #readlines(*args)
|
169
|
+
# - #sysopen(*args)
|
170
|
+
#
|
171
|
+
# === Utilities
|
172
|
+
#
|
173
|
+
# These methods are a mixture of Find, FileUtils, and others:
|
174
|
+
# - #find(&block)
|
175
|
+
# - #mkpath
|
176
|
+
# - #rmtree
|
177
|
+
# - #unlink / #delete
|
178
|
+
#
|
179
|
+
#
|
180
|
+
# == Method documentation
|
181
|
+
#
|
182
|
+
# As the above section shows, most of the methods in Pathname are facades. The
|
183
|
+
# documentation for these methods generally just says, for instance, "See
|
184
|
+
# FileTest.writable?", as you should be familiar with the original method
|
185
|
+
# anyway, and its documentation (e.g. through +ri+) will contain more
|
186
|
+
# information. In some cases, a brief description will follow.
|
187
|
+
#
|
188
|
+
class Pathname
|
189
|
+
|
190
|
+
# :stopdoc:
|
191
|
+
if RUBY_VERSION < "1.9"
|
192
|
+
TO_PATH = :to_str
|
193
|
+
else
|
194
|
+
# to_path is implemented so Pathname objects are usable with File.open, etc.
|
195
|
+
TO_PATH = :to_path
|
196
|
+
end
|
197
|
+
# :startdoc:
|
198
|
+
|
199
|
+
#
|
200
|
+
# Create a Pathname object from the given String (or String-like object).
|
201
|
+
# If +path+ contains a NUL character (<tt>\0</tt>), an ArgumentError is raised.
|
202
|
+
#
|
203
|
+
def initialize(path)
|
204
|
+
path = path.__send__(TO_PATH) if path.respond_to? TO_PATH
|
205
|
+
@path = path.dup
|
206
|
+
|
207
|
+
if /\0/ =~ @path
|
208
|
+
raise ArgumentError, "pathname contains \\0: #{@path.inspect}"
|
209
|
+
end
|
210
|
+
|
211
|
+
self.taint if @path.tainted?
|
212
|
+
end
|
213
|
+
|
214
|
+
def freeze() super; @path.freeze; self end
|
215
|
+
def taint() super; @path.taint; self end
|
216
|
+
def untaint() super; @path.untaint; self end
|
217
|
+
|
218
|
+
#
|
219
|
+
# Compare this pathname with +other+. The comparison is string-based.
|
220
|
+
# Be aware that two different paths (<tt>foo.txt</tt> and <tt>./foo.txt</tt>)
|
221
|
+
# can refer to the same file.
|
222
|
+
#
|
223
|
+
def ==(other)
|
224
|
+
return false unless Pathname === other
|
225
|
+
other.to_s == @path
|
226
|
+
end
|
227
|
+
alias === ==
|
228
|
+
alias eql? ==
|
229
|
+
|
230
|
+
# Provides for comparing pathnames, case-sensitively.
|
231
|
+
def <=>(other)
|
232
|
+
return nil unless Pathname === other
|
233
|
+
@path.tr('/', "\0") <=> other.to_s.tr('/', "\0")
|
234
|
+
end
|
235
|
+
|
236
|
+
def hash # :nodoc:
|
237
|
+
@path.hash
|
238
|
+
end
|
239
|
+
|
240
|
+
# Return the path as a String.
|
241
|
+
def to_s
|
242
|
+
@path.dup
|
243
|
+
end
|
244
|
+
|
245
|
+
# to_path is implemented so Pathname objects are usable with File.open, etc.
|
246
|
+
alias_method TO_PATH, :to_s
|
247
|
+
|
248
|
+
def inspect # :nodoc:
|
249
|
+
"#<#{self.class}:#{@path}>"
|
250
|
+
end
|
251
|
+
|
252
|
+
# Return a pathname which is substituted by String#sub.
|
253
|
+
def sub(pattern, *rest, &block)
|
254
|
+
self.class.new(@path.sub(pattern, *rest, &block))
|
255
|
+
end
|
256
|
+
|
257
|
+
if File::ALT_SEPARATOR
|
258
|
+
SEPARATOR_PAT = /[#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}]/
|
259
|
+
else
|
260
|
+
SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/
|
261
|
+
end
|
262
|
+
|
263
|
+
# chop_basename(path) -> [pre-basename, basename] or nil
|
264
|
+
def chop_basename(path)
|
265
|
+
base = File.basename(path)
|
266
|
+
if /\A#{SEPARATOR_PAT}?\z/o =~ base
|
267
|
+
return nil
|
268
|
+
else
|
269
|
+
return path[0, path.rindex(base)], base
|
270
|
+
end
|
271
|
+
end
|
272
|
+
private :chop_basename
|
273
|
+
|
274
|
+
# split_names(path) -> prefix, [name, ...]
|
275
|
+
def split_names(path)
|
276
|
+
names = []
|
277
|
+
while r = chop_basename(path)
|
278
|
+
path, basename = r
|
279
|
+
names.unshift basename
|
280
|
+
end
|
281
|
+
return path, names
|
282
|
+
end
|
283
|
+
private :split_names
|
284
|
+
|
285
|
+
def prepend_prefix(prefix, relpath)
|
286
|
+
if relpath.empty?
|
287
|
+
File.dirname(prefix)
|
288
|
+
elsif /#{SEPARATOR_PAT}/o =~ prefix
|
289
|
+
prefix = File.dirname(prefix)
|
290
|
+
prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a'
|
291
|
+
prefix + relpath
|
292
|
+
else
|
293
|
+
prefix + relpath
|
294
|
+
end
|
295
|
+
end
|
296
|
+
private :prepend_prefix
|
297
|
+
|
298
|
+
# Returns clean pathname of +self+ with consecutive slashes and useless dots
|
299
|
+
# removed. The filesystem is not accessed.
|
300
|
+
#
|
301
|
+
# If +consider_symlink+ is +true+, then a more conservative algorithm is used
|
302
|
+
# to avoid breaking symbolic linkages. This may retain more <tt>..</tt>
|
303
|
+
# entries than absolutely necessary, but without accessing the filesystem,
|
304
|
+
# this can't be avoided. See #realpath.
|
305
|
+
#
|
306
|
+
def cleanpath(consider_symlink=false)
|
307
|
+
if consider_symlink
|
308
|
+
cleanpath_conservative
|
309
|
+
else
|
310
|
+
cleanpath_aggressive
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
#
|
315
|
+
# Clean the path simply by resolving and removing excess "." and ".." entries.
|
316
|
+
# Nothing more, nothing less.
|
317
|
+
#
|
318
|
+
def cleanpath_aggressive
|
319
|
+
path = @path
|
320
|
+
names = []
|
321
|
+
pre = path
|
322
|
+
while r = chop_basename(pre)
|
323
|
+
pre, base = r
|
324
|
+
case base
|
325
|
+
when '.'
|
326
|
+
when '..'
|
327
|
+
names.unshift base
|
328
|
+
else
|
329
|
+
if names[0] == '..'
|
330
|
+
names.shift
|
331
|
+
else
|
332
|
+
names.unshift base
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
|
337
|
+
names.shift while names[0] == '..'
|
338
|
+
end
|
339
|
+
self.class.new(prepend_prefix(pre, File.join(*names)))
|
340
|
+
end
|
341
|
+
private :cleanpath_aggressive
|
342
|
+
|
343
|
+
# has_trailing_separator?(path) -> bool
|
344
|
+
def has_trailing_separator?(path)
|
345
|
+
if r = chop_basename(path)
|
346
|
+
pre, basename = r
|
347
|
+
pre.length + basename.length < path.length
|
348
|
+
else
|
349
|
+
false
|
350
|
+
end
|
351
|
+
end
|
352
|
+
private :has_trailing_separator?
|
353
|
+
|
354
|
+
# add_trailing_separator(path) -> path
|
355
|
+
def add_trailing_separator(path)
|
356
|
+
if File.basename(path + 'a') == 'a'
|
357
|
+
path
|
358
|
+
else
|
359
|
+
File.join(path, "") # xxx: Is File.join is appropriate to add separator?
|
360
|
+
end
|
361
|
+
end
|
362
|
+
private :add_trailing_separator
|
363
|
+
|
364
|
+
def del_trailing_separator(path)
|
365
|
+
if r = chop_basename(path)
|
366
|
+
pre, basename = r
|
367
|
+
pre + basename
|
368
|
+
elsif /#{SEPARATOR_PAT}+\z/o =~ path
|
369
|
+
$` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o]
|
370
|
+
else
|
371
|
+
path
|
372
|
+
end
|
373
|
+
end
|
374
|
+
private :del_trailing_separator
|
375
|
+
|
376
|
+
def cleanpath_conservative
|
377
|
+
path = @path
|
378
|
+
names = []
|
379
|
+
pre = path
|
380
|
+
while r = chop_basename(pre)
|
381
|
+
pre, base = r
|
382
|
+
names.unshift base if base != '.'
|
383
|
+
end
|
384
|
+
if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
|
385
|
+
names.shift while names[0] == '..'
|
386
|
+
end
|
387
|
+
if names.empty?
|
388
|
+
self.class.new(File.dirname(pre))
|
389
|
+
else
|
390
|
+
if names.last != '..' && File.basename(path) == '.'
|
391
|
+
names << '.'
|
392
|
+
end
|
393
|
+
result = prepend_prefix(pre, File.join(*names))
|
394
|
+
if /\A(?:\.|\.\.)\z/ !~ names.last && has_trailing_separator?(path)
|
395
|
+
self.class.new(add_trailing_separator(result))
|
396
|
+
else
|
397
|
+
self.class.new(result)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
private :cleanpath_conservative
|
402
|
+
|
403
|
+
def realpath_rec(prefix, unresolved, h)
|
404
|
+
resolved = []
|
405
|
+
until unresolved.empty?
|
406
|
+
n = unresolved.shift
|
407
|
+
if n == '.'
|
408
|
+
next
|
409
|
+
elsif n == '..'
|
410
|
+
resolved.pop
|
411
|
+
else
|
412
|
+
path = prepend_prefix(prefix, File.join(*(resolved + [n])))
|
413
|
+
if h.include? path
|
414
|
+
if h[path] == :resolving
|
415
|
+
raise Errno::ELOOP.new(path)
|
416
|
+
else
|
417
|
+
prefix, *resolved = h[path]
|
418
|
+
end
|
419
|
+
else
|
420
|
+
s = File.lstat(path)
|
421
|
+
if s.symlink?
|
422
|
+
h[path] = :resolving
|
423
|
+
link_prefix, link_names = split_names(File.readlink(path))
|
424
|
+
if link_prefix == ''
|
425
|
+
prefix, *resolved = h[path] = realpath_rec(prefix, resolved + link_names, h)
|
426
|
+
else
|
427
|
+
prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h)
|
428
|
+
end
|
429
|
+
else
|
430
|
+
resolved << n
|
431
|
+
h[path] = [prefix, *resolved]
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
return prefix, *resolved
|
437
|
+
end
|
438
|
+
private :realpath_rec
|
439
|
+
|
440
|
+
#
|
441
|
+
# Returns a real (absolute) pathname of +self+ in the actual filesystem.
|
442
|
+
# The real pathname doesn't contain symlinks or useless dots.
|
443
|
+
#
|
444
|
+
# No arguments should be given; the old behaviour is *obsoleted*.
|
445
|
+
#
|
446
|
+
def realpath
|
447
|
+
path = @path
|
448
|
+
prefix, names = split_names(path)
|
449
|
+
if prefix == ''
|
450
|
+
prefix, names2 = split_names(Dir.pwd)
|
451
|
+
names = names2 + names
|
452
|
+
end
|
453
|
+
prefix, *names = realpath_rec(prefix, names, {})
|
454
|
+
self.class.new(prepend_prefix(prefix, File.join(*names)))
|
455
|
+
end
|
456
|
+
|
457
|
+
# #parent returns the parent directory.
|
458
|
+
#
|
459
|
+
# This is same as <tt>self + '..'</tt>.
|
460
|
+
def parent
|
461
|
+
self + '..'
|
462
|
+
end
|
463
|
+
|
464
|
+
# #mountpoint? returns +true+ if <tt>self</tt> points to a mountpoint.
|
465
|
+
def mountpoint?
|
466
|
+
begin
|
467
|
+
stat1 = self.lstat
|
468
|
+
stat2 = self.parent.lstat
|
469
|
+
stat1.dev == stat2.dev && stat1.ino == stat2.ino ||
|
470
|
+
stat1.dev != stat2.dev
|
471
|
+
rescue Errno::ENOENT
|
472
|
+
false
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
#
|
477
|
+
# #root? is a predicate for root directories. I.e. it returns +true+ if the
|
478
|
+
# pathname consists of consecutive slashes.
|
479
|
+
#
|
480
|
+
# It doesn't access actual filesystem. So it may return +false+ for some
|
481
|
+
# pathnames which points to roots such as <tt>/usr/..</tt>.
|
482
|
+
#
|
483
|
+
def root?
|
484
|
+
!!(chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o =~ @path)
|
485
|
+
end
|
486
|
+
|
487
|
+
# Predicate method for testing whether a path is absolute.
|
488
|
+
# It returns +true+ if the pathname begins with a slash.
|
489
|
+
def absolute?
|
490
|
+
!relative?
|
491
|
+
end
|
492
|
+
|
493
|
+
# The opposite of #absolute?
|
494
|
+
def relative?
|
495
|
+
path = @path
|
496
|
+
while r = chop_basename(path)
|
497
|
+
path, basename = r
|
498
|
+
end
|
499
|
+
path == ''
|
500
|
+
end
|
501
|
+
|
502
|
+
#
|
503
|
+
# Iterates over each component of the path.
|
504
|
+
#
|
505
|
+
# Pathname.new("/usr/bin/ruby").each_filename {|filename| ... }
|
506
|
+
# # yields "usr", "bin", and "ruby".
|
507
|
+
#
|
508
|
+
def each_filename # :yield: filename
|
509
|
+
prefix, names = split_names(@path)
|
510
|
+
names.each {|filename| yield filename }
|
511
|
+
nil
|
512
|
+
end
|
513
|
+
|
514
|
+
# Iterates over and yields a new Pathname object
|
515
|
+
# for each element in the given path in descending order.
|
516
|
+
#
|
517
|
+
# Pathname.new('/path/to/some/file.rb').descend {|v| p v}
|
518
|
+
# #<Pathname:/>
|
519
|
+
# #<Pathname:/path>
|
520
|
+
# #<Pathname:/path/to>
|
521
|
+
# #<Pathname:/path/to/some>
|
522
|
+
# #<Pathname:/path/to/some/file.rb>
|
523
|
+
#
|
524
|
+
# Pathname.new('path/to/some/file.rb').descend {|v| p v}
|
525
|
+
# #<Pathname:path>
|
526
|
+
# #<Pathname:path/to>
|
527
|
+
# #<Pathname:path/to/some>
|
528
|
+
# #<Pathname:path/to/some/file.rb>
|
529
|
+
#
|
530
|
+
# It doesn't access actual filesystem.
|
531
|
+
#
|
532
|
+
# This method is available since 1.8.5.
|
533
|
+
#
|
534
|
+
def descend
|
535
|
+
vs = []
|
536
|
+
ascend {|v| vs << v }
|
537
|
+
vs.reverse_each {|v| yield v }
|
538
|
+
nil
|
539
|
+
end
|
540
|
+
|
541
|
+
# Iterates over and yields a new Pathname object
|
542
|
+
# for each element in the given path in ascending order.
|
543
|
+
#
|
544
|
+
# Pathname.new('/path/to/some/file.rb').ascend {|v| p v}
|
545
|
+
# #<Pathname:/path/to/some/file.rb>
|
546
|
+
# #<Pathname:/path/to/some>
|
547
|
+
# #<Pathname:/path/to>
|
548
|
+
# #<Pathname:/path>
|
549
|
+
# #<Pathname:/>
|
550
|
+
#
|
551
|
+
# Pathname.new('path/to/some/file.rb').ascend {|v| p v}
|
552
|
+
# #<Pathname:path/to/some/file.rb>
|
553
|
+
# #<Pathname:path/to/some>
|
554
|
+
# #<Pathname:path/to>
|
555
|
+
# #<Pathname:path>
|
556
|
+
#
|
557
|
+
# It doesn't access actual filesystem.
|
558
|
+
#
|
559
|
+
# This method is available since 1.8.5.
|
560
|
+
#
|
561
|
+
def ascend
|
562
|
+
path = @path
|
563
|
+
yield self
|
564
|
+
while r = chop_basename(path)
|
565
|
+
path, name = r
|
566
|
+
break if path.empty?
|
567
|
+
yield self.class.new(del_trailing_separator(path))
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
#
|
572
|
+
# Pathname#+ appends a pathname fragment to this one to produce a new Pathname
|
573
|
+
# object.
|
574
|
+
#
|
575
|
+
# p1 = Pathname.new("/usr") # Pathname:/usr
|
576
|
+
# p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby
|
577
|
+
# p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd
|
578
|
+
#
|
579
|
+
# This method doesn't access the file system; it is pure string manipulation.
|
580
|
+
#
|
581
|
+
def +(other)
|
582
|
+
other = Pathname.new(other) unless Pathname === other
|
583
|
+
Pathname.new(plus(@path, other.to_s))
|
584
|
+
end
|
585
|
+
|
586
|
+
def plus(path1, path2) # -> path
|
587
|
+
prefix2 = path2
|
588
|
+
index_list2 = []
|
589
|
+
basename_list2 = []
|
590
|
+
while r2 = chop_basename(prefix2)
|
591
|
+
prefix2, basename2 = r2
|
592
|
+
index_list2.unshift prefix2.length
|
593
|
+
basename_list2.unshift basename2
|
594
|
+
end
|
595
|
+
return path2 if prefix2 != ''
|
596
|
+
prefix1 = path1
|
597
|
+
while true
|
598
|
+
while !basename_list2.empty? && basename_list2.first == '.'
|
599
|
+
index_list2.shift
|
600
|
+
basename_list2.shift
|
601
|
+
end
|
602
|
+
break unless r1 = chop_basename(prefix1)
|
603
|
+
prefix1, basename1 = r1
|
604
|
+
next if basename1 == '.'
|
605
|
+
if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..'
|
606
|
+
prefix1 = prefix1 + basename1
|
607
|
+
break
|
608
|
+
end
|
609
|
+
index_list2.shift
|
610
|
+
basename_list2.shift
|
611
|
+
end
|
612
|
+
r1 = chop_basename(prefix1)
|
613
|
+
if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1)
|
614
|
+
while !basename_list2.empty? && basename_list2.first == '..'
|
615
|
+
index_list2.shift
|
616
|
+
basename_list2.shift
|
617
|
+
end
|
618
|
+
end
|
619
|
+
if !basename_list2.empty?
|
620
|
+
suffix2 = path2[index_list2.first..-1]
|
621
|
+
r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2
|
622
|
+
else
|
623
|
+
r1 ? prefix1 : File.dirname(prefix1)
|
624
|
+
end
|
625
|
+
end
|
626
|
+
private :plus
|
627
|
+
|
628
|
+
#
|
629
|
+
# Pathname#join joins pathnames.
|
630
|
+
#
|
631
|
+
# <tt>path0.join(path1, ..., pathN)</tt> is the same as
|
632
|
+
# <tt>path0 + path1 + ... + pathN</tt>.
|
633
|
+
#
|
634
|
+
def join(*args)
|
635
|
+
args.unshift self
|
636
|
+
result = args.pop
|
637
|
+
result = Pathname.new(result) unless Pathname === result
|
638
|
+
return result if result.absolute?
|
639
|
+
args.reverse_each {|arg|
|
640
|
+
arg = Pathname.new(arg) unless Pathname === arg
|
641
|
+
result = arg + result
|
642
|
+
return result if result.absolute?
|
643
|
+
}
|
644
|
+
result
|
645
|
+
end
|
646
|
+
|
647
|
+
#
|
648
|
+
# Returns the children of the directory (files and subdirectories, not
|
649
|
+
# recursive) as an array of Pathname objects. By default, the returned
|
650
|
+
# pathnames will have enough information to access the files. If you set
|
651
|
+
# +with_directory+ to +false+, then the returned pathnames will contain the
|
652
|
+
# filename only.
|
653
|
+
#
|
654
|
+
# For example:
|
655
|
+
# p = Pathname("/usr/lib/ruby/1.8")
|
656
|
+
# p.children
|
657
|
+
# # -> [ Pathname:/usr/lib/ruby/1.8/English.rb,
|
658
|
+
# Pathname:/usr/lib/ruby/1.8/Env.rb,
|
659
|
+
# Pathname:/usr/lib/ruby/1.8/abbrev.rb, ... ]
|
660
|
+
# p.children(false)
|
661
|
+
# # -> [ Pathname:English.rb, Pathname:Env.rb, Pathname:abbrev.rb, ... ]
|
662
|
+
#
|
663
|
+
# Note that the result never contain the entries <tt>.</tt> and <tt>..</tt> in
|
664
|
+
# the directory because they are not children.
|
665
|
+
#
|
666
|
+
# This method has existed since 1.8.1.
|
667
|
+
#
|
668
|
+
def children(with_directory=true)
|
669
|
+
with_directory = false if @path == '.'
|
670
|
+
result = []
|
671
|
+
Dir.foreach(@path) {|e|
|
672
|
+
next if e == '.' || e == '..'
|
673
|
+
if with_directory
|
674
|
+
result << self.class.new(File.join(@path, e))
|
675
|
+
else
|
676
|
+
result << self.class.new(e)
|
677
|
+
end
|
678
|
+
}
|
679
|
+
result
|
680
|
+
end
|
681
|
+
|
682
|
+
#
|
683
|
+
# #relative_path_from returns a relative path from the argument to the
|
684
|
+
# receiver. If +self+ is absolute, the argument must be absolute too. If
|
685
|
+
# +self+ is relative, the argument must be relative too.
|
686
|
+
#
|
687
|
+
# #relative_path_from doesn't access the filesystem. It assumes no symlinks.
|
688
|
+
#
|
689
|
+
# ArgumentError is raised when it cannot find a relative path.
|
690
|
+
#
|
691
|
+
# This method has existed since 1.8.1.
|
692
|
+
#
|
693
|
+
def relative_path_from(base_directory)
|
694
|
+
dest_directory = self.cleanpath.to_s
|
695
|
+
base_directory = base_directory.cleanpath.to_s
|
696
|
+
dest_prefix = dest_directory
|
697
|
+
dest_names = []
|
698
|
+
while r = chop_basename(dest_prefix)
|
699
|
+
dest_prefix, basename = r
|
700
|
+
dest_names.unshift basename if basename != '.'
|
701
|
+
end
|
702
|
+
base_prefix = base_directory
|
703
|
+
base_names = []
|
704
|
+
while r = chop_basename(base_prefix)
|
705
|
+
base_prefix, basename = r
|
706
|
+
base_names.unshift basename if basename != '.'
|
707
|
+
end
|
708
|
+
if dest_prefix != base_prefix
|
709
|
+
raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
|
710
|
+
end
|
711
|
+
while !dest_names.empty? &&
|
712
|
+
!base_names.empty? &&
|
713
|
+
dest_names.first == base_names.first
|
714
|
+
dest_names.shift
|
715
|
+
base_names.shift
|
716
|
+
end
|
717
|
+
if base_names.include? '..'
|
718
|
+
raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
|
719
|
+
end
|
720
|
+
base_names.fill('..')
|
721
|
+
relpath_names = base_names + dest_names
|
722
|
+
if relpath_names.empty?
|
723
|
+
Pathname.new('.')
|
724
|
+
else
|
725
|
+
Pathname.new(File.join(*relpath_names))
|
726
|
+
end
|
727
|
+
end
|
728
|
+
end
|
729
|
+
|
730
|
+
class Pathname # * IO *
|
731
|
+
#
|
732
|
+
# #each_line iterates over the line in the file. It yields a String object
|
733
|
+
# for each line.
|
734
|
+
#
|
735
|
+
# This method has existed since 1.8.1.
|
736
|
+
#
|
737
|
+
def each_line(*args, &block) # :yield: line
|
738
|
+
IO.foreach(@path, *args, &block)
|
739
|
+
end
|
740
|
+
|
741
|
+
# Pathname#foreachline is *obsoleted* at 1.8.1. Use #each_line.
|
742
|
+
def foreachline(*args, &block)
|
743
|
+
warn "Pathname#foreachline is obsoleted. Use Pathname#each_line."
|
744
|
+
each_line(*args, &block)
|
745
|
+
end
|
746
|
+
|
747
|
+
# See <tt>IO.read</tt>. Returns all the bytes from the file, or the first +N+
|
748
|
+
# if specified.
|
749
|
+
def read(*args) IO.read(@path, *args) end
|
750
|
+
|
751
|
+
# See <tt>IO.readlines</tt>. Returns all the lines from the file.
|
752
|
+
def readlines(*args) IO.readlines(@path, *args) end
|
753
|
+
|
754
|
+
# See <tt>IO.sysopen</tt>.
|
755
|
+
def sysopen(*args) IO.sysopen(@path, *args) end
|
756
|
+
end
|
757
|
+
|
758
|
+
|
759
|
+
class Pathname # * File *
|
760
|
+
|
761
|
+
# See <tt>File.atime</tt>. Returns last access time.
|
762
|
+
def atime() File.atime(@path) end
|
763
|
+
|
764
|
+
# See <tt>File.ctime</tt>. Returns last (directory entry, not file) change time.
|
765
|
+
def ctime() File.ctime(@path) end
|
766
|
+
|
767
|
+
# See <tt>File.mtime</tt>. Returns last modification time.
|
768
|
+
def mtime() File.mtime(@path) end
|
769
|
+
|
770
|
+
# See <tt>File.chmod</tt>. Changes permissions.
|
771
|
+
def chmod(mode) File.chmod(mode, @path) end
|
772
|
+
|
773
|
+
# See <tt>File.lchmod</tt>.
|
774
|
+
def lchmod(mode) File.lchmod(mode, @path) end
|
775
|
+
|
776
|
+
# See <tt>File.chown</tt>. Change owner and group of file.
|
777
|
+
def chown(owner, group) File.chown(owner, group, @path) end
|
778
|
+
|
779
|
+
# See <tt>File.lchown</tt>.
|
780
|
+
def lchown(owner, group) File.lchown(owner, group, @path) end
|
781
|
+
|
782
|
+
# See <tt>File.fnmatch</tt>. Return +true+ if the receiver matches the given
|
783
|
+
# pattern.
|
784
|
+
def fnmatch(pattern, *args) File.fnmatch(pattern, @path, *args) end
|
785
|
+
|
786
|
+
# See <tt>File.fnmatch?</tt> (same as #fnmatch).
|
787
|
+
def fnmatch?(pattern, *args) File.fnmatch?(pattern, @path, *args) end
|
788
|
+
|
789
|
+
# See <tt>File.ftype</tt>. Returns "type" of file ("file", "directory",
|
790
|
+
# etc).
|
791
|
+
def ftype() File.ftype(@path) end
|
792
|
+
|
793
|
+
# See <tt>File.link</tt>. Creates a hard link.
|
794
|
+
def make_link(old) File.link(old, @path) end
|
795
|
+
|
796
|
+
# See <tt>File.open</tt>. Opens the file for reading or writing.
|
797
|
+
def open(*args, &block) # :yield: file
|
798
|
+
File.open(@path, *args, &block)
|
799
|
+
end
|
800
|
+
|
801
|
+
# See <tt>File.readlink</tt>. Read symbolic link.
|
802
|
+
def readlink() self.class.new(File.readlink(@path)) end
|
803
|
+
|
804
|
+
# See <tt>File.rename</tt>. Rename the file.
|
805
|
+
def rename(to) File.rename(@path, to) end
|
806
|
+
|
807
|
+
# See <tt>File.stat</tt>. Returns a <tt>File::Stat</tt> object.
|
808
|
+
def stat() File.stat(@path) end
|
809
|
+
|
810
|
+
# See <tt>File.lstat</tt>.
|
811
|
+
def lstat() File.lstat(@path) end
|
812
|
+
|
813
|
+
# See <tt>File.symlink</tt>. Creates a symbolic link.
|
814
|
+
def make_symlink(old) File.symlink(old, @path) end
|
815
|
+
|
816
|
+
# See <tt>File.truncate</tt>. Truncate the file to +length+ bytes.
|
817
|
+
def truncate(length) File.truncate(@path, length) end
|
818
|
+
|
819
|
+
# See <tt>File.utime</tt>. Update the access and modification times.
|
820
|
+
def utime(atime, mtime) File.utime(atime, mtime, @path) end
|
821
|
+
|
822
|
+
# See <tt>File.basename</tt>. Returns the last component of the path.
|
823
|
+
def basename(*args) self.class.new(File.basename(@path, *args)) end
|
824
|
+
|
825
|
+
# See <tt>File.dirname</tt>. Returns all but the last component of the path.
|
826
|
+
def dirname() self.class.new(File.dirname(@path)) end
|
827
|
+
|
828
|
+
# See <tt>File.extname</tt>. Returns the file's extension.
|
829
|
+
def extname() File.extname(@path) end
|
830
|
+
|
831
|
+
# See <tt>File.expand_path</tt>.
|
832
|
+
def expand_path(*args) self.class.new(File.expand_path(@path, *args)) end
|
833
|
+
|
834
|
+
# See <tt>File.split</tt>. Returns the #dirname and the #basename in an
|
835
|
+
# Array.
|
836
|
+
def split() File.split(@path).map {|f| self.class.new(f) } end
|
837
|
+
|
838
|
+
# Pathname#link is confusing and *obsoleted* because the receiver/argument
|
839
|
+
# order is inverted to corresponding system call.
|
840
|
+
def link(old)
|
841
|
+
warn 'Pathname#link is obsoleted. Use Pathname#make_link.'
|
842
|
+
File.link(old, @path)
|
843
|
+
end
|
844
|
+
|
845
|
+
# Pathname#symlink is confusing and *obsoleted* because the receiver/argument
|
846
|
+
# order is inverted to corresponding system call.
|
847
|
+
def symlink(old)
|
848
|
+
warn 'Pathname#symlink is obsoleted. Use Pathname#make_symlink.'
|
849
|
+
File.symlink(old, @path)
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
|
854
|
+
class Pathname # * FileTest *
|
855
|
+
|
856
|
+
# See <tt>FileTest.blockdev?</tt>.
|
857
|
+
def blockdev?() FileTest.blockdev?(@path) end
|
858
|
+
|
859
|
+
# See <tt>FileTest.chardev?</tt>.
|
860
|
+
def chardev?() FileTest.chardev?(@path) end
|
861
|
+
|
862
|
+
# See <tt>FileTest.executable?</tt>.
|
863
|
+
def executable?() FileTest.executable?(@path) end
|
864
|
+
|
865
|
+
# See <tt>FileTest.executable_real?</tt>.
|
866
|
+
def executable_real?() FileTest.executable_real?(@path) end
|
867
|
+
|
868
|
+
# See <tt>FileTest.exist?</tt>.
|
869
|
+
def exist?() FileTest.exist?(@path) end
|
870
|
+
|
871
|
+
# See <tt>FileTest.grpowned?</tt>.
|
872
|
+
def grpowned?() FileTest.grpowned?(@path) end
|
873
|
+
|
874
|
+
# See <tt>FileTest.directory?</tt>.
|
875
|
+
def directory?() FileTest.directory?(@path) end
|
876
|
+
|
877
|
+
# See <tt>FileTest.file?</tt>.
|
878
|
+
def file?() FileTest.file?(@path) end
|
879
|
+
|
880
|
+
# See <tt>FileTest.pipe?</tt>.
|
881
|
+
def pipe?() FileTest.pipe?(@path) end
|
882
|
+
|
883
|
+
# See <tt>FileTest.socket?</tt>.
|
884
|
+
def socket?() FileTest.socket?(@path) end
|
885
|
+
|
886
|
+
# See <tt>FileTest.owned?</tt>.
|
887
|
+
def owned?() FileTest.owned?(@path) end
|
888
|
+
|
889
|
+
# See <tt>FileTest.readable?</tt>.
|
890
|
+
def readable?() FileTest.readable?(@path) end
|
891
|
+
|
892
|
+
# See <tt>FileTest.world_readable?</tt>.
|
893
|
+
def world_readable?() FileTest.world_readable?(@path) end
|
894
|
+
|
895
|
+
# See <tt>FileTest.readable_real?</tt>.
|
896
|
+
def readable_real?() FileTest.readable_real?(@path) end
|
897
|
+
|
898
|
+
# See <tt>FileTest.setuid?</tt>.
|
899
|
+
def setuid?() FileTest.setuid?(@path) end
|
900
|
+
|
901
|
+
# See <tt>FileTest.setgid?</tt>.
|
902
|
+
def setgid?() FileTest.setgid?(@path) end
|
903
|
+
|
904
|
+
# See <tt>FileTest.size</tt>.
|
905
|
+
def size() FileTest.size(@path) end
|
906
|
+
|
907
|
+
# See <tt>FileTest.size?</tt>.
|
908
|
+
def size?() FileTest.size?(@path) end
|
909
|
+
|
910
|
+
# See <tt>FileTest.sticky?</tt>.
|
911
|
+
def sticky?() FileTest.sticky?(@path) end
|
912
|
+
|
913
|
+
# See <tt>FileTest.symlink?</tt>.
|
914
|
+
def symlink?() FileTest.symlink?(@path) end
|
915
|
+
|
916
|
+
# See <tt>FileTest.writable?</tt>.
|
917
|
+
def writable?() FileTest.writable?(@path) end
|
918
|
+
|
919
|
+
# See <tt>FileTest.world_writable?</tt>.
|
920
|
+
def world_writable?() FileTest.world_writable?(@path) end
|
921
|
+
|
922
|
+
# See <tt>FileTest.writable_real?</tt>.
|
923
|
+
def writable_real?() FileTest.writable_real?(@path) end
|
924
|
+
|
925
|
+
# See <tt>FileTest.zero?</tt>.
|
926
|
+
def zero?() FileTest.zero?(@path) end
|
927
|
+
end
|
928
|
+
|
929
|
+
|
930
|
+
class Pathname # * Dir *
|
931
|
+
# See <tt>Dir.glob</tt>. Returns or yields Pathname objects.
|
932
|
+
def Pathname.glob(*args) # :yield: p
|
933
|
+
if block_given?
|
934
|
+
Dir.glob(*args) {|f| yield self.new(f) }
|
935
|
+
else
|
936
|
+
Dir.glob(*args).map {|f| self.new(f) }
|
937
|
+
end
|
938
|
+
end
|
939
|
+
|
940
|
+
# See <tt>Dir.getwd</tt>. Returns the current working directory as a Pathname.
|
941
|
+
def Pathname.getwd() self.new(Dir.getwd) end
|
942
|
+
class << self; alias pwd getwd end
|
943
|
+
|
944
|
+
# Pathname#chdir is *obsoleted* at 1.8.1.
|
945
|
+
def chdir(&block)
|
946
|
+
warn "Pathname#chdir is obsoleted. Use Dir.chdir."
|
947
|
+
Dir.chdir(@path, &block)
|
948
|
+
end
|
949
|
+
|
950
|
+
# Pathname#chroot is *obsoleted* at 1.8.1.
|
951
|
+
def chroot
|
952
|
+
warn "Pathname#chroot is obsoleted. Use Dir.chroot."
|
953
|
+
Dir.chroot(@path)
|
954
|
+
end
|
955
|
+
|
956
|
+
# Return the entries (files and subdirectories) in the directory, each as a
|
957
|
+
# Pathname object.
|
958
|
+
def entries() Dir.entries(@path).map {|f| self.class.new(f) } end
|
959
|
+
|
960
|
+
# Iterates over the entries (files and subdirectories) in the directory. It
|
961
|
+
# yields a Pathname object for each entry.
|
962
|
+
#
|
963
|
+
# This method has existed since 1.8.1.
|
964
|
+
def each_entry(&block) # :yield: p
|
965
|
+
Dir.foreach(@path) {|f| yield self.class.new(f) }
|
966
|
+
end
|
967
|
+
|
968
|
+
# Pathname#dir_foreach is *obsoleted* at 1.8.1.
|
969
|
+
def dir_foreach(*args, &block)
|
970
|
+
warn "Pathname#dir_foreach is obsoleted. Use Pathname#each_entry."
|
971
|
+
each_entry(*args, &block)
|
972
|
+
end
|
973
|
+
|
974
|
+
# See <tt>Dir.mkdir</tt>. Create the referenced directory.
|
975
|
+
def mkdir(*args) Dir.mkdir(@path, *args) end
|
976
|
+
|
977
|
+
# See <tt>Dir.rmdir</tt>. Remove the referenced directory.
|
978
|
+
def rmdir() Dir.rmdir(@path) end
|
979
|
+
|
980
|
+
# See <tt>Dir.open</tt>.
|
981
|
+
def opendir(&block) # :yield: dir
|
982
|
+
Dir.open(@path, &block)
|
983
|
+
end
|
984
|
+
end
|
985
|
+
|
986
|
+
|
987
|
+
class Pathname # * Find *
|
988
|
+
#
|
989
|
+
# Pathname#find is an iterator to traverse a directory tree in a depth first
|
990
|
+
# manner. It yields a Pathname for each file under "this" directory.
|
991
|
+
#
|
992
|
+
# Since it is implemented by <tt>find.rb</tt>, <tt>Find.prune</tt> can be used
|
993
|
+
# to control the traverse.
|
994
|
+
#
|
995
|
+
# If +self+ is <tt>.</tt>, yielded pathnames begin with a filename in the
|
996
|
+
# current directory, not <tt>./</tt>.
|
997
|
+
#
|
998
|
+
def find(&block) # :yield: p
|
999
|
+
require 'find'
|
1000
|
+
if @path == '.'
|
1001
|
+
Find.find(@path) {|f| yield self.class.new(f.sub(%r{\A\./}, '')) }
|
1002
|
+
else
|
1003
|
+
Find.find(@path) {|f| yield self.class.new(f) }
|
1004
|
+
end
|
1005
|
+
end
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
|
1009
|
+
class Pathname # * FileUtils *
|
1010
|
+
# See <tt>FileUtils.mkpath</tt>. Creates a full path, including any
|
1011
|
+
# intermediate directories that don't yet exist.
|
1012
|
+
def mkpath
|
1013
|
+
require 'fileutils'
|
1014
|
+
FileUtils.mkpath(@path)
|
1015
|
+
nil
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
# See <tt>FileUtils.rm_r</tt>. Deletes a directory and all beneath it.
|
1019
|
+
def rmtree
|
1020
|
+
# The name "rmtree" is borrowed from File::Path of Perl.
|
1021
|
+
# File::Path provides "mkpath" and "rmtree".
|
1022
|
+
require 'fileutils'
|
1023
|
+
FileUtils.rm_r(@path)
|
1024
|
+
nil
|
1025
|
+
end
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
|
1029
|
+
class Pathname # * mixed *
|
1030
|
+
# Removes a file or directory, using <tt>File.unlink</tt> or
|
1031
|
+
# <tt>Dir.unlink</tt> as necessary.
|
1032
|
+
def unlink()
|
1033
|
+
begin
|
1034
|
+
Dir.unlink @path
|
1035
|
+
rescue Errno::ENOTDIR
|
1036
|
+
File.unlink @path
|
1037
|
+
end
|
1038
|
+
end
|
1039
|
+
alias delete unlink
|
1040
|
+
|
1041
|
+
# This method is *obsoleted* at 1.8.1. Use #each_line or #each_entry.
|
1042
|
+
def foreach(*args, &block)
|
1043
|
+
warn "Pathname#foreach is obsoleted. Use each_line or each_entry."
|
1044
|
+
if FileTest.directory? @path
|
1045
|
+
# For polymorphism between Dir.foreach and IO.foreach,
|
1046
|
+
# Pathname#foreach doesn't yield Pathname object.
|
1047
|
+
Dir.foreach(@path, *args, &block)
|
1048
|
+
else
|
1049
|
+
IO.foreach(@path, *args, &block)
|
1050
|
+
end
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
module Kernel
|
1055
|
+
# create a pathname object.
|
1056
|
+
#
|
1057
|
+
# This method is available since 1.8.5.
|
1058
|
+
def Pathname(path) # :doc:
|
1059
|
+
Pathname.new(path)
|
1060
|
+
end
|
1061
|
+
private :Pathname
|
1062
|
+
end
|