epath 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +60 -21
- data/epath.gemspec +3 -1
- data/lib/epath.rb +37 -126
- data/lib/epath/dir.rb +123 -0
- data/lib/epath/file.rb +109 -0
- data/lib/epath/file_dir.rb +12 -0
- data/lib/epath/file_predicates.rb +149 -0
- data/lib/epath/fileutils.rb +45 -0
- data/lib/epath/find.rb +21 -0
- data/lib/epath/identity.rb +58 -0
- data/lib/epath/implementation.rb +148 -666
- data/lib/epath/io.rb +59 -0
- data/lib/epath/load.rb +26 -0
- data/lib/epath/parts.rb +115 -0
- data/lib/epath/predicates.rb +35 -0
- data/lib/epath/require_tree.rb +14 -0
- metadata +65 -19
data/lib/epath/file.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
class Path
|
2
|
+
# Returns last access time. See +File.atime+.
|
3
|
+
def atime
|
4
|
+
File.atime(@path)
|
5
|
+
end
|
6
|
+
|
7
|
+
# Returns last (directory entry, not file) change time. See +File.ctime+.
|
8
|
+
def ctime
|
9
|
+
File.ctime(@path)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns last modification time. See +File.mtime+.
|
13
|
+
def mtime
|
14
|
+
File.mtime(@path)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Changes permissions. See +File.chmod+.
|
18
|
+
def chmod(mode) File.chmod(mode, @path) end
|
19
|
+
|
20
|
+
# See +File.lchmod+.
|
21
|
+
def lchmod(mode) File.lchmod(mode, @path) end
|
22
|
+
|
23
|
+
# Change owner and group of file. See +File.chown+.
|
24
|
+
def chown(owner, group)
|
25
|
+
File.chown(owner, group, @path)
|
26
|
+
end
|
27
|
+
|
28
|
+
# See +File.lchown+.
|
29
|
+
def lchown(owner, group)
|
30
|
+
File.lchown(owner, group, @path)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns "type" of file ("file", "directory", etc). See +File.ftype+.
|
34
|
+
def ftype
|
35
|
+
File.ftype(@path)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Creates a hard link to +target+ and returns self.
|
39
|
+
#
|
40
|
+
# Raises Errno::EEXIST if self already exist.
|
41
|
+
# See +File.link+ (arguments are swapped).
|
42
|
+
def make_link(target)
|
43
|
+
File.link(target, @path)
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Read symbolic link. See +File.readlink+.
|
48
|
+
def readlink
|
49
|
+
Path.new(File.readlink(@path))
|
50
|
+
end
|
51
|
+
|
52
|
+
# Rename the file and returns the new Path. See +File.rename+.
|
53
|
+
def rename(to)
|
54
|
+
File.rename(@path, to)
|
55
|
+
Path(to)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns a +File::Stat+ object. See +File.stat+.
|
59
|
+
def stat
|
60
|
+
File.stat(@path)
|
61
|
+
end
|
62
|
+
|
63
|
+
# See +File.lstat+.
|
64
|
+
def lstat
|
65
|
+
File.lstat(@path)
|
66
|
+
end
|
67
|
+
|
68
|
+
# See +File.size+.
|
69
|
+
def size
|
70
|
+
File.size(@path)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Creates a symbolic link to +target+ and returns self.
|
74
|
+
#
|
75
|
+
# Raises Errno::EEXIST if self already exist.
|
76
|
+
# See +File.symlink+ (arguments are swapped).
|
77
|
+
def make_symlink(target)
|
78
|
+
File.symlink(target, @path)
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
# Truncate the file to +length+ bytes. See +File.truncate+.
|
83
|
+
def truncate(length) File.truncate(@path, length) end
|
84
|
+
|
85
|
+
# Update the access and modification times. See +File.utime+.
|
86
|
+
def utime(atime, mtime) File.utime(atime, mtime, @path) end
|
87
|
+
|
88
|
+
# See +File.expand_path+.
|
89
|
+
def expand_path(*args)
|
90
|
+
Path.new(File.expand_path(@path, *args))
|
91
|
+
end
|
92
|
+
alias :expand :expand_path
|
93
|
+
|
94
|
+
# Returns the real (absolute) path of +self+ in the actual
|
95
|
+
# filesystem not containing symlinks or useless dots.
|
96
|
+
#
|
97
|
+
# All components of the path must exist when this method is called.
|
98
|
+
def realpath(basedir=nil)
|
99
|
+
Path.new(real_path_internal(true, basedir))
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the real (absolute) path of +self+ in the actual filesystem.
|
103
|
+
# The real path doesn't contain symlinks or useless dots.
|
104
|
+
#
|
105
|
+
# The last component of the real path can be nonexistent.
|
106
|
+
def realdirpath(basedir=nil)
|
107
|
+
Path.new(real_path_internal(false, basedir))
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# All methods from FileTest and all predicates from File are included
|
2
|
+
|
3
|
+
class Path
|
4
|
+
# See +File.blockdev?+.
|
5
|
+
def blockdev?
|
6
|
+
File.blockdev?(@path)
|
7
|
+
end
|
8
|
+
|
9
|
+
# See +File.chardev?+.
|
10
|
+
def chardev?
|
11
|
+
File.chardev?(@path)
|
12
|
+
end
|
13
|
+
|
14
|
+
# See +File.executable?+.
|
15
|
+
def executable?
|
16
|
+
File.executable?(@path)
|
17
|
+
end
|
18
|
+
|
19
|
+
# See +File.executable_real?+.
|
20
|
+
def executable_real?
|
21
|
+
File.executable_real?(@path)
|
22
|
+
end
|
23
|
+
|
24
|
+
# See +File.exist?+.
|
25
|
+
def exist?
|
26
|
+
File.exist?(@path)
|
27
|
+
end
|
28
|
+
alias :exists? :exist?
|
29
|
+
|
30
|
+
# See +File.grpowned?+.
|
31
|
+
def grpowned?
|
32
|
+
File.grpowned?(@path)
|
33
|
+
end
|
34
|
+
|
35
|
+
# See +File.directory?+.
|
36
|
+
def directory?
|
37
|
+
File.directory?(@path)
|
38
|
+
end
|
39
|
+
alias :dir? :directory?
|
40
|
+
|
41
|
+
# See +File.file?+.
|
42
|
+
def file?
|
43
|
+
File.file?(@path)
|
44
|
+
end
|
45
|
+
|
46
|
+
# See +File.pipe?+.
|
47
|
+
def pipe?
|
48
|
+
File.pipe?(@path)
|
49
|
+
end
|
50
|
+
|
51
|
+
# See +File.socket?+.
|
52
|
+
def socket?
|
53
|
+
File.socket?(@path)
|
54
|
+
end
|
55
|
+
|
56
|
+
# See +File.owned?+.
|
57
|
+
def owned?
|
58
|
+
File.owned?(@path)
|
59
|
+
end
|
60
|
+
|
61
|
+
# See +File.readable?+.
|
62
|
+
def readable?
|
63
|
+
File.readable?(@path)
|
64
|
+
end
|
65
|
+
|
66
|
+
if File.respond_to? :world_readable?
|
67
|
+
# See +File.world_readable?+.
|
68
|
+
def world_readable?
|
69
|
+
File.world_readable?(@path)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
def world_readable?
|
73
|
+
mode = File.stat(@path).mode & 0777
|
74
|
+
mode if (mode & 04).nonzero?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# See +File.readable_real?+.
|
79
|
+
def readable_real?
|
80
|
+
File.readable_real?(@path)
|
81
|
+
end
|
82
|
+
|
83
|
+
# See +File.setuid?+.
|
84
|
+
def setuid?
|
85
|
+
File.setuid?(@path)
|
86
|
+
end
|
87
|
+
|
88
|
+
# See +File.setgid?+.
|
89
|
+
def setgid?
|
90
|
+
File.setgid?(@path)
|
91
|
+
end
|
92
|
+
|
93
|
+
# See +File.size?+.
|
94
|
+
def size?
|
95
|
+
File.size?(@path)
|
96
|
+
end
|
97
|
+
|
98
|
+
# See +File.sticky?+.
|
99
|
+
def sticky?
|
100
|
+
File.sticky?(@path)
|
101
|
+
end
|
102
|
+
|
103
|
+
# See +File.symlink?+.
|
104
|
+
def symlink?
|
105
|
+
File.symlink?(@path)
|
106
|
+
end
|
107
|
+
|
108
|
+
# See +File.writable?+.
|
109
|
+
def writable?
|
110
|
+
File.writable?(@path)
|
111
|
+
end
|
112
|
+
|
113
|
+
if File.respond_to? :world_writable?
|
114
|
+
# See +File.world_writable?+.
|
115
|
+
def world_writable?
|
116
|
+
File.world_writable?(@path)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
def world_writable?
|
120
|
+
mode = File.stat(@path).mode & 0777
|
121
|
+
mode if (mode & 02).nonzero?
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# See +File.writable_real?+.
|
126
|
+
def writable_real?
|
127
|
+
File.writable_real?(@path)
|
128
|
+
end
|
129
|
+
|
130
|
+
# See +File.zero?+.
|
131
|
+
# empty? is not defined in File/FileTest, but is is clearer
|
132
|
+
def zero?
|
133
|
+
File.zero?(@path)
|
134
|
+
end
|
135
|
+
alias :empty? :zero?
|
136
|
+
|
137
|
+
# See +File.identical?+.
|
138
|
+
def identical?(path)
|
139
|
+
File.identical?(@path, path)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Only in File, not FileTest
|
143
|
+
|
144
|
+
# Return +true+ if the receiver matches the given pattern.
|
145
|
+
# See +File.fnmatch?+.
|
146
|
+
def fnmatch?(pattern, *args)
|
147
|
+
File.fnmatch?(pattern, @path, *args)
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
class Path
|
4
|
+
# Creates a full path, including any intermediate directories that don't yet exist.
|
5
|
+
# See +FileUtils.mkpath+.
|
6
|
+
def mkpath
|
7
|
+
FileUtils.mkpath(@path)
|
8
|
+
self
|
9
|
+
end
|
10
|
+
alias :mkdir_p :mkpath
|
11
|
+
|
12
|
+
# Deletes a directory and all beneath it. See +FileUtils.rm_r+.
|
13
|
+
def rmtree
|
14
|
+
# The name "rmtree" is borrowed from File::Path of Perl.
|
15
|
+
# File::Path provides "mkpath" and "rmtree".
|
16
|
+
FileUtils.rm_r(@path)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
alias :rm_r :rmtree
|
20
|
+
|
21
|
+
def rm
|
22
|
+
FileUtils.rm(@path)
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def rm_f
|
27
|
+
FileUtils.rm_f(@path)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def rm_rf
|
32
|
+
FileUtils.rm_rf(@path)
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def touch
|
37
|
+
FileUtils.touch(@path)
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def touch!
|
42
|
+
dirname.mkpath
|
43
|
+
touch
|
44
|
+
end
|
45
|
+
end
|
data/lib/epath/find.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
class Path
|
2
|
+
# Path#find is an iterator to traverse a directory tree in a depth first
|
3
|
+
# manner. It yields a Path for each file under "this" directory.
|
4
|
+
#
|
5
|
+
# Returns an enumerator if no block is given.
|
6
|
+
#
|
7
|
+
# Since it is implemented by +find.rb+, +Find.prune+ can be used
|
8
|
+
# to control the traversal.
|
9
|
+
#
|
10
|
+
# If +self+ is +.+, yielded paths begin with a filename in the
|
11
|
+
# current directory, not +./+.
|
12
|
+
def find # :yield: path
|
13
|
+
return to_enum(__method__) unless block_given?
|
14
|
+
require 'find'
|
15
|
+
if @path == '.'
|
16
|
+
Find.find(@path) { |f| yield Path.new(f.sub(%r{\A\./}, '')) }
|
17
|
+
else
|
18
|
+
Find.find(@path) { |f| yield Path.new(f) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Path
|
2
|
+
class << self
|
3
|
+
def new(*args)
|
4
|
+
if args.size == 1 and Path === args[0]
|
5
|
+
args[0]
|
6
|
+
else
|
7
|
+
super(*args)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
alias :[] :new
|
11
|
+
|
12
|
+
def to_proc
|
13
|
+
lambda { |path| new(path) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Compare this path with +other+. The comparison is string-based.
|
18
|
+
# Be aware that two different paths (+foo.txt+ and +./foo.txt+)
|
19
|
+
# can refer to the same file.
|
20
|
+
def == other
|
21
|
+
Path === other and @path == other.to_path
|
22
|
+
end
|
23
|
+
alias :eql? :==
|
24
|
+
|
25
|
+
# Provides for comparing paths, case-sensitively.
|
26
|
+
def <=>(other)
|
27
|
+
return nil unless Path === other
|
28
|
+
@path.tr('/', "\0") <=> other.to_s.tr('/', "\0")
|
29
|
+
end
|
30
|
+
|
31
|
+
def hash
|
32
|
+
@path.hash
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return the path as a String.
|
36
|
+
def to_s
|
37
|
+
@path
|
38
|
+
end
|
39
|
+
|
40
|
+
# to_path is implemented so Path objects are usable with File.open, etc.
|
41
|
+
alias :to_path :to_s
|
42
|
+
|
43
|
+
alias :to_str :to_s if RUBY_VERSION < '1.9'
|
44
|
+
|
45
|
+
def to_sym
|
46
|
+
@path.to_sym
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
"#<Path #{@path}>"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
unless defined? NO_EPATH_GLOBAL_FUNCTION
|
55
|
+
def Path(*args)
|
56
|
+
Path.new(*args)
|
57
|
+
end
|
58
|
+
end
|
data/lib/epath/implementation.rb
CHANGED
@@ -1,82 +1,156 @@
|
|
1
1
|
# Path's low-level implementation based on Pathname
|
2
2
|
|
3
|
-
require 'fileutils'
|
4
|
-
|
5
3
|
class Path
|
6
|
-
#
|
4
|
+
# @private
|
7
5
|
SAME_PATHS = if File::FNM_SYSCASE.nonzero?
|
8
|
-
|
6
|
+
lambda { |a,b| a.casecmp(b).zero? }
|
9
7
|
else
|
10
|
-
|
8
|
+
lambda { |a,b| a == b }
|
11
9
|
end
|
12
10
|
|
13
|
-
|
11
|
+
def initialize(*parts)
|
12
|
+
path = parts.size > 1 ? File.join(parts) : parts.first
|
13
|
+
@path = case path
|
14
|
+
when Tempfile
|
15
|
+
@_tmpfile = path # We would not want it to be GC'd
|
16
|
+
path.path.dup
|
17
|
+
when String
|
18
|
+
path.dup
|
19
|
+
else
|
20
|
+
path.to_s.dup
|
21
|
+
end
|
22
|
+
|
23
|
+
validate(@path)
|
14
24
|
|
15
|
-
|
16
|
-
|
17
|
-
|
25
|
+
taint if @path.tainted?
|
26
|
+
@path.freeze
|
27
|
+
freeze
|
28
|
+
end
|
18
29
|
|
30
|
+
# Returns clean path of +self+ with consecutive slashes and useless dots removed.
|
31
|
+
# The filesystem is not accessed.
|
19
32
|
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
33
|
+
# If +consider_symlink+ is +true+, then a more conservative algorithm is used
|
34
|
+
# to avoid breaking symbolic linkages. This may retain more +..+
|
35
|
+
# entries than absolutely necessary, but without accessing the filesystem,
|
36
|
+
# this can't be avoided. See #realpath.
|
37
|
+
def cleanpath(consider_symlink=false)
|
38
|
+
if consider_symlink
|
39
|
+
cleanpath_conservative
|
40
|
+
else
|
41
|
+
cleanpath_aggressive
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# #parent returns the parent directory.
|
23
46
|
#
|
24
|
-
|
25
|
-
|
47
|
+
# This is same as <tt>self + '..'</tt>.
|
48
|
+
def parent
|
49
|
+
self + '..'
|
26
50
|
end
|
27
|
-
alias eql? ==
|
28
51
|
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
52
|
+
# Path#+ appends a path fragment to this one to produce a new Path.
|
53
|
+
#
|
54
|
+
# p1 = Path.new("/usr") # => #<Path /usr>
|
55
|
+
# p2 = p1 + "bin/ruby" # => #<Path /usr/bin/ruby>
|
56
|
+
# p3 = p1 + "/etc/passwd" # => #<Path /etc/passwd>
|
57
|
+
#
|
58
|
+
# This method doesn't access the file system, it is pure string manipulation.
|
59
|
+
def +(other)
|
60
|
+
Path.new(plus(@path, other.to_s))
|
33
61
|
end
|
34
62
|
|
35
|
-
|
36
|
-
|
63
|
+
# Path#join joins paths.
|
64
|
+
#
|
65
|
+
# <tt>path0.join(path1, ..., pathN)</tt> is the same as
|
66
|
+
# <tt>path0 + path1 + ... + pathN</tt>.
|
67
|
+
def join(*args)
|
68
|
+
args.unshift self
|
69
|
+
result = Path.new(args.pop)
|
70
|
+
return result if result.absolute?
|
71
|
+
args.reverse_each { |arg|
|
72
|
+
arg = Path.new(arg)
|
73
|
+
result = arg + result
|
74
|
+
return result if result.absolute?
|
75
|
+
}
|
76
|
+
result
|
37
77
|
end
|
38
78
|
|
39
|
-
#
|
40
|
-
|
41
|
-
|
79
|
+
# #relative_path_from returns a relative path from the argument to the
|
80
|
+
# receiver. If +self+ is absolute, the argument must be absolute too.
|
81
|
+
# If +self+ is relative, the argument must be relative too.
|
82
|
+
#
|
83
|
+
# #relative_path_from doesn't access the filesystem. It assumes no symlinks.
|
84
|
+
#
|
85
|
+
# ArgumentError is raised when it cannot find a relative path.
|
86
|
+
def relative_path_from(base_directory)
|
87
|
+
dest_directory = cleanpath.to_s
|
88
|
+
base_directory = Path.new(base_directory).cleanpath.to_s
|
89
|
+
dest_prefix = dest_directory
|
90
|
+
dest_names = []
|
91
|
+
while r = chop_basename(dest_prefix)
|
92
|
+
dest_prefix, basename = r
|
93
|
+
dest_names.unshift basename if basename != '.'
|
94
|
+
end
|
95
|
+
base_prefix = base_directory
|
96
|
+
base_names = []
|
97
|
+
while r = chop_basename(base_prefix)
|
98
|
+
base_prefix, basename = r
|
99
|
+
base_names.unshift basename if basename != '.'
|
100
|
+
end
|
101
|
+
unless SAME_PATHS[dest_prefix, base_prefix]
|
102
|
+
raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
|
103
|
+
end
|
104
|
+
until dest_names.empty? or base_names.empty? or !SAME_PATHS[dest_names.first, base_names.first]
|
105
|
+
dest_names.shift
|
106
|
+
base_names.shift
|
107
|
+
end
|
108
|
+
if base_names.include? '..'
|
109
|
+
raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
|
110
|
+
end
|
111
|
+
base_names.fill('..')
|
112
|
+
relpath_names = base_names + dest_names
|
113
|
+
if relpath_names.empty?
|
114
|
+
Path.new('.')
|
115
|
+
else
|
116
|
+
Path.new(*relpath_names)
|
117
|
+
end
|
42
118
|
end
|
43
119
|
|
44
|
-
#
|
45
|
-
|
120
|
+
# @private
|
121
|
+
module Helpers
|
122
|
+
private
|
46
123
|
|
47
|
-
|
124
|
+
# remove the leading . of +ext+ if present.
|
125
|
+
def pure_ext(ext)
|
126
|
+
ext.start_with?('.') ? ext[1..-1] : ext
|
127
|
+
end
|
48
128
|
|
49
|
-
|
50
|
-
|
129
|
+
# add a leading . to +ext+ if missing. Returns '' if +ext+ is empty.
|
130
|
+
def dotted_ext(ext)
|
131
|
+
(ext.empty? or ext.start_with?('.')) ? ext : ".#{ext}"
|
132
|
+
end
|
51
133
|
end
|
52
134
|
|
53
|
-
|
54
|
-
|
55
|
-
SEPARATOR_PAT = /[#{SEPARATOR_LIST}]/
|
56
|
-
else
|
57
|
-
SEPARATOR_LIST = "#{Regexp.quote File::SEPARATOR}"
|
58
|
-
SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/
|
59
|
-
end
|
135
|
+
include Helpers
|
136
|
+
extend Helpers
|
60
137
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
ext = File.extname(@path)
|
67
|
-
Path.new(@path.chomp(ext) + repl)
|
138
|
+
private
|
139
|
+
|
140
|
+
def validate(path)
|
141
|
+
raise ArgumentError, "path contains a null byte: #{path.inspect}" if path.include? "\0"
|
142
|
+
path.gsub!(File::ALT_SEPARATOR, '/') if File::ALT_SEPARATOR
|
68
143
|
end
|
69
144
|
|
70
145
|
# chop_basename(path) -> [pre-basename, basename] or nil
|
71
146
|
def chop_basename(path)
|
72
147
|
base = File.basename(path)
|
73
|
-
if
|
148
|
+
if base.empty? or base == '/'
|
74
149
|
return nil
|
75
150
|
else
|
76
151
|
return path[0, path.rindex(base)], base
|
77
152
|
end
|
78
153
|
end
|
79
|
-
private :chop_basename
|
80
154
|
|
81
155
|
# split_names(path) -> prefix, [name, ...]
|
82
156
|
def split_names(path)
|
@@ -87,41 +161,47 @@ class Path
|
|
87
161
|
end
|
88
162
|
return path, names
|
89
163
|
end
|
90
|
-
private :split_names
|
91
164
|
|
92
165
|
def prepend_prefix(prefix, relpath)
|
93
166
|
if relpath.empty?
|
94
167
|
File.dirname(prefix)
|
95
|
-
elsif
|
96
|
-
|
97
|
-
prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a'
|
98
|
-
prefix + relpath
|
168
|
+
elsif prefix.include? '/'
|
169
|
+
add_trailing_separator(File.dirname(prefix)) + relpath
|
99
170
|
else
|
100
171
|
prefix + relpath
|
101
172
|
end
|
102
173
|
end
|
103
|
-
private :prepend_prefix
|
104
174
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
# to avoid breaking symbolic linkages. This may retain more <tt>..</tt>
|
110
|
-
# entries than absolutely necessary, but without accessing the filesystem,
|
111
|
-
# this can't be avoided. See #realpath.
|
112
|
-
#
|
113
|
-
def cleanpath(consider_symlink=false)
|
114
|
-
if consider_symlink
|
115
|
-
cleanpath_conservative
|
175
|
+
def has_trailing_separator?(path)
|
176
|
+
if r = chop_basename(path)
|
177
|
+
pre, basename = r
|
178
|
+
pre.length + basename.length < path.length
|
116
179
|
else
|
117
|
-
|
180
|
+
false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def add_trailing_separator(path)
|
185
|
+
if File.basename(path + 'a') == 'a'
|
186
|
+
path
|
187
|
+
else
|
188
|
+
path + '/'
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def del_trailing_separator(path)
|
193
|
+
if r = chop_basename(path)
|
194
|
+
pre, basename = r
|
195
|
+
pre + basename
|
196
|
+
elsif /\/+\z/o =~ path
|
197
|
+
$` + File.dirname(path)[/\/*\z/o]
|
198
|
+
else
|
199
|
+
path
|
118
200
|
end
|
119
201
|
end
|
120
202
|
|
121
|
-
#
|
122
203
|
# Clean the path simply by resolving and removing excess "." and ".." entries.
|
123
204
|
# Nothing more, nothing less.
|
124
|
-
#
|
125
205
|
def cleanpath_aggressive
|
126
206
|
path = @path
|
127
207
|
names = []
|
@@ -140,45 +220,11 @@ class Path
|
|
140
220
|
end
|
141
221
|
end
|
142
222
|
end
|
143
|
-
if
|
223
|
+
if File.basename(pre).include? '/'
|
144
224
|
names.shift while names[0] == '..'
|
145
225
|
end
|
146
226
|
Path.new(prepend_prefix(pre, File.join(*names)))
|
147
227
|
end
|
148
|
-
private :cleanpath_aggressive
|
149
|
-
|
150
|
-
# has_trailing_separator?(path) -> bool
|
151
|
-
def has_trailing_separator?(path)
|
152
|
-
if r = chop_basename(path)
|
153
|
-
pre, basename = r
|
154
|
-
pre.length + basename.length < path.length
|
155
|
-
else
|
156
|
-
false
|
157
|
-
end
|
158
|
-
end
|
159
|
-
private :has_trailing_separator?
|
160
|
-
|
161
|
-
# add_trailing_separator(path) -> path
|
162
|
-
def add_trailing_separator(path)
|
163
|
-
if File.basename(path + 'a') == 'a'
|
164
|
-
path
|
165
|
-
else
|
166
|
-
File.join(path, "") # xxx: Is File.join is appropriate to add separator?
|
167
|
-
end
|
168
|
-
end
|
169
|
-
private :add_trailing_separator
|
170
|
-
|
171
|
-
def del_trailing_separator(path)
|
172
|
-
if r = chop_basename(path)
|
173
|
-
pre, basename = r
|
174
|
-
pre + basename
|
175
|
-
elsif /#{SEPARATOR_PAT}+\z/o =~ path
|
176
|
-
$` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o]
|
177
|
-
else
|
178
|
-
path
|
179
|
-
end
|
180
|
-
end
|
181
|
-
private :del_trailing_separator
|
182
228
|
|
183
229
|
def cleanpath_conservative
|
184
230
|
path = @path
|
@@ -188,7 +234,7 @@ class Path
|
|
188
234
|
pre, base = r
|
189
235
|
names.unshift base if base != '.'
|
190
236
|
end
|
191
|
-
if
|
237
|
+
if File.basename(pre).include? '/'
|
192
238
|
names.shift while names[0] == '..'
|
193
239
|
end
|
194
240
|
if names.empty?
|
@@ -205,13 +251,11 @@ class Path
|
|
205
251
|
end
|
206
252
|
end
|
207
253
|
end
|
208
|
-
private :cleanpath_conservative
|
209
254
|
|
210
255
|
if File.respond_to?(:realpath) and File.respond_to?(:realdirpath)
|
211
256
|
def real_path_internal(strict = false, basedir = nil)
|
212
257
|
strict ? File.realpath(@path, basedir) : File.realdirpath(@path, basedir)
|
213
258
|
end
|
214
|
-
private :real_path_internal
|
215
259
|
else
|
216
260
|
def realpath_rec(prefix, unresolved, h, strict, last = true)
|
217
261
|
resolved = []
|
@@ -254,7 +298,6 @@ class Path
|
|
254
298
|
end
|
255
299
|
return prefix, *resolved
|
256
300
|
end
|
257
|
-
private :realpath_rec
|
258
301
|
|
259
302
|
def real_path_internal(strict = false, basedir = nil)
|
260
303
|
path = @path
|
@@ -267,151 +310,6 @@ class Path
|
|
267
310
|
prefix, *names = realpath_rec(prefix, names, {}, strict)
|
268
311
|
prepend_prefix(prefix, File.join(*names))
|
269
312
|
end
|
270
|
-
private :real_path_internal
|
271
|
-
end
|
272
|
-
|
273
|
-
#
|
274
|
-
# Returns the real (absolute) pathname of +self+ in the actual
|
275
|
-
# filesystem not containing symlinks or useless dots.
|
276
|
-
#
|
277
|
-
# All components of the pathname must exist when this method is
|
278
|
-
# called.
|
279
|
-
#
|
280
|
-
def realpath(basedir=nil)
|
281
|
-
Path.new(real_path_internal(true, basedir))
|
282
|
-
end
|
283
|
-
|
284
|
-
#
|
285
|
-
# Returns the real (absolute) pathname of +self+ in the actual filesystem.
|
286
|
-
# The real pathname doesn't contain symlinks or useless dots.
|
287
|
-
#
|
288
|
-
# The last component of the real pathname can be nonexistent.
|
289
|
-
#
|
290
|
-
def realdirpath(basedir=nil)
|
291
|
-
Path.new(real_path_internal(false, basedir))
|
292
|
-
end
|
293
|
-
|
294
|
-
# #parent returns the parent directory.
|
295
|
-
#
|
296
|
-
# This is same as <tt>self + '..'</tt>.
|
297
|
-
def parent
|
298
|
-
self + '..'
|
299
|
-
end
|
300
|
-
|
301
|
-
# #mountpoint? returns +true+ if <tt>self</tt> points to a mountpoint.
|
302
|
-
def mountpoint?
|
303
|
-
begin
|
304
|
-
stat1 = lstat
|
305
|
-
stat2 = parent.lstat
|
306
|
-
stat1.dev != stat2.dev or stat1.ino == stat2.ino
|
307
|
-
rescue Errno::ENOENT
|
308
|
-
false
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
312
|
-
#
|
313
|
-
# #root? is a predicate for root directories. I.e. it returns +true+ if the
|
314
|
-
# pathname consists of consecutive slashes.
|
315
|
-
#
|
316
|
-
# It doesn't access actual filesystem. So it may return +false+ for some
|
317
|
-
# pathnames which points to roots such as <tt>/usr/..</tt>.
|
318
|
-
#
|
319
|
-
def root?
|
320
|
-
!!(chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o =~ @path)
|
321
|
-
end
|
322
|
-
|
323
|
-
# Predicate method for testing whether a path is absolute.
|
324
|
-
# It returns +true+ if the pathname begins with a slash.
|
325
|
-
def absolute?
|
326
|
-
!relative?
|
327
|
-
end
|
328
|
-
|
329
|
-
# The opposite of #absolute?
|
330
|
-
def relative?
|
331
|
-
path = @path
|
332
|
-
while r = chop_basename(path)
|
333
|
-
path, = r
|
334
|
-
end
|
335
|
-
path == ''
|
336
|
-
end
|
337
|
-
|
338
|
-
#
|
339
|
-
# Iterates over each component of the path.
|
340
|
-
#
|
341
|
-
# Path.new("/usr/bin/ruby").each_filename {|filename| ... }
|
342
|
-
# # yields "usr", "bin", and "ruby".
|
343
|
-
#
|
344
|
-
def each_filename # :yield: filename
|
345
|
-
return to_enum(__method__) unless block_given?
|
346
|
-
_, names = split_names(@path)
|
347
|
-
names.each {|filename| yield filename }
|
348
|
-
nil
|
349
|
-
end
|
350
|
-
|
351
|
-
# Iterates over and yields a new Path object
|
352
|
-
# for each element in the given path in descending order.
|
353
|
-
#
|
354
|
-
# Path.new('/path/to/some/file.rb').descend {|v| p v}
|
355
|
-
# #<Path:/>
|
356
|
-
# #<Path:/path>
|
357
|
-
# #<Path:/path/to>
|
358
|
-
# #<Path:/path/to/some>
|
359
|
-
# #<Path:/path/to/some/file.rb>
|
360
|
-
#
|
361
|
-
# Path.new('path/to/some/file.rb').descend {|v| p v}
|
362
|
-
# #<Path:path>
|
363
|
-
# #<Path:path/to>
|
364
|
-
# #<Path:path/to/some>
|
365
|
-
# #<Path:path/to/some/file.rb>
|
366
|
-
#
|
367
|
-
# It doesn't access actual filesystem.
|
368
|
-
def descend
|
369
|
-
vs = []
|
370
|
-
ascend {|v| vs << v }
|
371
|
-
vs.reverse_each {|v| yield v }
|
372
|
-
nil
|
373
|
-
end
|
374
|
-
|
375
|
-
# Iterates over and yields a new Path object
|
376
|
-
# for each element in the given path in ascending order.
|
377
|
-
#
|
378
|
-
# Path.new('/path/to/some/file.rb').ascend {|v| p v}
|
379
|
-
# #<Path:/path/to/some/file.rb>
|
380
|
-
# #<Path:/path/to/some>
|
381
|
-
# #<Path:/path/to>
|
382
|
-
# #<Path:/path>
|
383
|
-
# #<Path:/>
|
384
|
-
#
|
385
|
-
# Path.new('path/to/some/file.rb').ascend {|v| p v}
|
386
|
-
# #<Path:path/to/some/file.rb>
|
387
|
-
# #<Path:path/to/some>
|
388
|
-
# #<Path:path/to>
|
389
|
-
# #<Path:path>
|
390
|
-
#
|
391
|
-
# It doesn't access actual filesystem.
|
392
|
-
def ascend
|
393
|
-
path = @path
|
394
|
-
yield self
|
395
|
-
while r = chop_basename(path)
|
396
|
-
path, = r
|
397
|
-
break if path.empty?
|
398
|
-
yield Path.new(del_trailing_separator(path))
|
399
|
-
end
|
400
|
-
end
|
401
|
-
|
402
|
-
#
|
403
|
-
# Path#+ appends a pathname fragment to this one to produce a new Path
|
404
|
-
# object.
|
405
|
-
#
|
406
|
-
# p1 = Path.new("/usr") # Path:/usr
|
407
|
-
# p2 = p1 + "bin/ruby" # Path:/usr/bin/ruby
|
408
|
-
# p3 = p1 + "/etc/passwd" # Path:/etc/passwd
|
409
|
-
#
|
410
|
-
# This method doesn't access the file system; it is pure string manipulation.
|
411
|
-
#
|
412
|
-
def +(other)
|
413
|
-
other = Path.new(other) unless Path === other
|
414
|
-
Path.new(plus(@path, other.to_s))
|
415
313
|
end
|
416
314
|
|
417
315
|
def plus(path1, path2) # -> path
|
@@ -441,7 +339,7 @@ class Path
|
|
441
339
|
basename_list2.shift
|
442
340
|
end
|
443
341
|
r1 = chop_basename(prefix1)
|
444
|
-
if !r1 &&
|
342
|
+
if !r1 && File.basename(prefix1).include?('/')
|
445
343
|
while !basename_list2.empty? && basename_list2.first == '..'
|
446
344
|
index_list2.shift
|
447
345
|
basename_list2.shift
|
@@ -454,420 +352,4 @@ class Path
|
|
454
352
|
r1 ? prefix1 : File.dirname(prefix1)
|
455
353
|
end
|
456
354
|
end
|
457
|
-
private :plus
|
458
|
-
|
459
|
-
#
|
460
|
-
# Path#join joins pathnames.
|
461
|
-
#
|
462
|
-
# <tt>path0.join(path1, ..., pathN)</tt> is the same as
|
463
|
-
# <tt>path0 + path1 + ... + pathN</tt>.
|
464
|
-
#
|
465
|
-
def join(*args)
|
466
|
-
args.unshift self
|
467
|
-
result = args.pop
|
468
|
-
result = Path.new(result) unless Path === result
|
469
|
-
return result if result.absolute?
|
470
|
-
args.reverse_each {|arg|
|
471
|
-
arg = Path.new(arg) unless Path === arg
|
472
|
-
result = arg + result
|
473
|
-
return result if result.absolute?
|
474
|
-
}
|
475
|
-
result
|
476
|
-
end
|
477
|
-
|
478
|
-
#
|
479
|
-
# Returns the children of the directory (files and subdirectories, not
|
480
|
-
# recursive) as an array of Path objects. By default, the returned
|
481
|
-
# pathnames will have enough information to access the files. If you set
|
482
|
-
# +with_directory+ to +false+, then the returned pathnames will contain the
|
483
|
-
# filename only.
|
484
|
-
#
|
485
|
-
# For example:
|
486
|
-
# pn = Path("/usr/lib/ruby/1.8")
|
487
|
-
# pn.children
|
488
|
-
# # -> [ Path:/usr/lib/ruby/1.8/English.rb,
|
489
|
-
# Path:/usr/lib/ruby/1.8/Env.rb,
|
490
|
-
# Path:/usr/lib/ruby/1.8/abbrev.rb, ... ]
|
491
|
-
# pn.children(false)
|
492
|
-
# # -> [ Path:English.rb, Path:Env.rb, Path:abbrev.rb, ... ]
|
493
|
-
#
|
494
|
-
# Note that the results never contain the entries <tt>.</tt> and <tt>..</tt> in
|
495
|
-
# the directory because they are not children.
|
496
|
-
#
|
497
|
-
def children(with_directory=true)
|
498
|
-
with_directory = false if @path == '.'
|
499
|
-
result = []
|
500
|
-
Dir.foreach(@path) {|e|
|
501
|
-
next if e == '.' || e == '..'
|
502
|
-
if with_directory
|
503
|
-
result << Path.new(File.join(@path, e))
|
504
|
-
else
|
505
|
-
result << Path.new(e)
|
506
|
-
end
|
507
|
-
}
|
508
|
-
result
|
509
|
-
end
|
510
|
-
|
511
|
-
# Iterates over the children of the directory
|
512
|
-
# (files and subdirectories, not recursive).
|
513
|
-
# It yields Path object for each child.
|
514
|
-
# By default, the yielded pathnames will have enough information to access the files.
|
515
|
-
# If you set +with_directory+ to +false+, then the returned pathnames will contain the filename only.
|
516
|
-
#
|
517
|
-
# Path("/usr/local").each_child {|f| p f }
|
518
|
-
# #=> #<Path:/usr/local/share>
|
519
|
-
# # #<Path:/usr/local/bin>
|
520
|
-
# # #<Path:/usr/local/games>
|
521
|
-
# # #<Path:/usr/local/lib>
|
522
|
-
# # #<Path:/usr/local/include>
|
523
|
-
# # #<Path:/usr/local/sbin>
|
524
|
-
# # #<Path:/usr/local/src>
|
525
|
-
# # #<Path:/usr/local/man>
|
526
|
-
#
|
527
|
-
# Path("/usr/local").each_child(false) {|f| p f }
|
528
|
-
# #=> #<Path:share>
|
529
|
-
# # #<Path:bin>
|
530
|
-
# # #<Path:games>
|
531
|
-
# # #<Path:lib>
|
532
|
-
# # #<Path:include>
|
533
|
-
# # #<Path:sbin>
|
534
|
-
# # #<Path:src>
|
535
|
-
# # #<Path:man>
|
536
|
-
#
|
537
|
-
def each_child(with_directory=true, &b)
|
538
|
-
children(with_directory).each(&b)
|
539
|
-
end
|
540
|
-
|
541
|
-
#
|
542
|
-
# #relative_path_from returns a relative path from the argument to the
|
543
|
-
# receiver. If +self+ is absolute, the argument must be absolute too. If
|
544
|
-
# +self+ is relative, the argument must be relative too.
|
545
|
-
#
|
546
|
-
# #relative_path_from doesn't access the filesystem. It assumes no symlinks.
|
547
|
-
#
|
548
|
-
# ArgumentError is raised when it cannot find a relative path.
|
549
|
-
#
|
550
|
-
def relative_path_from(base_directory)
|
551
|
-
dest_directory = cleanpath.to_s
|
552
|
-
base_directory = base_directory.cleanpath.to_s
|
553
|
-
dest_prefix = dest_directory
|
554
|
-
dest_names = []
|
555
|
-
while r = chop_basename(dest_prefix)
|
556
|
-
dest_prefix, basename = r
|
557
|
-
dest_names.unshift basename if basename != '.'
|
558
|
-
end
|
559
|
-
base_prefix = base_directory
|
560
|
-
base_names = []
|
561
|
-
while r = chop_basename(base_prefix)
|
562
|
-
base_prefix, basename = r
|
563
|
-
base_names.unshift basename if basename != '.'
|
564
|
-
end
|
565
|
-
unless SAME_PATHS[dest_prefix, base_prefix]
|
566
|
-
raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
|
567
|
-
end
|
568
|
-
while !dest_names.empty? &&
|
569
|
-
!base_names.empty? &&
|
570
|
-
SAME_PATHS[dest_names.first, base_names.first]
|
571
|
-
dest_names.shift
|
572
|
-
base_names.shift
|
573
|
-
end
|
574
|
-
if base_names.include? '..'
|
575
|
-
raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
|
576
|
-
end
|
577
|
-
base_names.fill('..')
|
578
|
-
relpath_names = base_names + dest_names
|
579
|
-
if relpath_names.empty?
|
580
|
-
Path.new('.')
|
581
|
-
else
|
582
|
-
Path.new(File.join(*relpath_names))
|
583
|
-
end
|
584
|
-
end
|
585
|
-
end
|
586
|
-
|
587
|
-
class Path # * IO *
|
588
|
-
#
|
589
|
-
# #each_line iterates over the line in the file. It yields a String object
|
590
|
-
# for each line.
|
591
|
-
#
|
592
|
-
def each_line(*args, &block) # :yield: line
|
593
|
-
IO.foreach(@path, *args, &block)
|
594
|
-
end
|
595
|
-
|
596
|
-
# See <tt>IO.read</tt>. Returns all data from the file, or the first +N+ bytes
|
597
|
-
# if specified.
|
598
|
-
def read(*args) IO.read(@path, *args) end
|
599
|
-
|
600
|
-
# See <tt>IO.binread</tt>. Returns all the bytes from the file, or the first +N+
|
601
|
-
# if specified.
|
602
|
-
def binread(*args) IO.binread(@path, *args) end
|
603
|
-
|
604
|
-
# See <tt>IO.readlines</tt>. Returns all the lines from the file.
|
605
|
-
def readlines(*args) IO.readlines(@path, *args) end
|
606
|
-
|
607
|
-
# See <tt>IO.sysopen</tt>.
|
608
|
-
def sysopen(*args) IO.sysopen(@path, *args) end
|
609
|
-
end
|
610
|
-
|
611
|
-
|
612
|
-
class Path # * File *
|
613
|
-
|
614
|
-
# See <tt>File.atime</tt>. Returns last access time.
|
615
|
-
def atime() File.atime(@path) end
|
616
|
-
|
617
|
-
# See <tt>File.ctime</tt>. Returns last (directory entry, not file) change time.
|
618
|
-
def ctime() File.ctime(@path) end
|
619
|
-
|
620
|
-
# See <tt>File.mtime</tt>. Returns last modification time.
|
621
|
-
def mtime() File.mtime(@path) end
|
622
|
-
|
623
|
-
# See <tt>File.chmod</tt>. Changes permissions.
|
624
|
-
def chmod(mode) File.chmod(mode, @path) end
|
625
|
-
|
626
|
-
# See <tt>File.lchmod</tt>.
|
627
|
-
def lchmod(mode) File.lchmod(mode, @path) end
|
628
|
-
|
629
|
-
# See <tt>File.chown</tt>. Change owner and group of file.
|
630
|
-
def chown(owner, group) File.chown(owner, group, @path) end
|
631
|
-
|
632
|
-
# See <tt>File.lchown</tt>.
|
633
|
-
def lchown(owner, group) File.lchown(owner, group, @path) end
|
634
|
-
|
635
|
-
# See <tt>File.fnmatch</tt>. Return +true+ if the receiver matches the given
|
636
|
-
# pattern.
|
637
|
-
def fnmatch(pattern, *args) File.fnmatch(pattern, @path, *args) end
|
638
|
-
|
639
|
-
# See <tt>File.fnmatch?</tt> (same as #fnmatch).
|
640
|
-
def fnmatch?(pattern, *args) File.fnmatch?(pattern, @path, *args) end
|
641
|
-
|
642
|
-
# See <tt>File.ftype</tt>. Returns "type" of file ("file", "directory",
|
643
|
-
# etc).
|
644
|
-
def ftype() File.ftype(@path) end
|
645
|
-
|
646
|
-
# See <tt>File.link</tt>. Creates a hard link.
|
647
|
-
def make_link(old) File.link(old, @path) end
|
648
|
-
|
649
|
-
# See <tt>File.open</tt>. Opens the file for reading or writing.
|
650
|
-
def open(*args, &block) # :yield: file
|
651
|
-
File.open(@path, *args, &block)
|
652
|
-
end
|
653
|
-
|
654
|
-
# See <tt>File.readlink</tt>. Read symbolic link.
|
655
|
-
def readlink() Path.new(File.readlink(@path)) end
|
656
|
-
|
657
|
-
# See <tt>File.rename</tt>. Rename the file.
|
658
|
-
def rename(to) File.rename(@path, to) end
|
659
|
-
|
660
|
-
# See <tt>File.stat</tt>. Returns a <tt>File::Stat</tt> object.
|
661
|
-
def stat() File.stat(@path) end
|
662
|
-
|
663
|
-
# See <tt>File.lstat</tt>.
|
664
|
-
def lstat() File.lstat(@path) end
|
665
|
-
|
666
|
-
# See <tt>File.symlink</tt>. Creates a symbolic link.
|
667
|
-
def make_symlink(old) File.symlink(old, @path) end
|
668
|
-
|
669
|
-
# See <tt>File.truncate</tt>. Truncate the file to +length+ bytes.
|
670
|
-
def truncate(length) File.truncate(@path, length) end
|
671
|
-
|
672
|
-
# See <tt>File.utime</tt>. Update the access and modification times.
|
673
|
-
def utime(atime, mtime) File.utime(atime, mtime, @path) end
|
674
|
-
|
675
|
-
# See <tt>File.basename</tt>. Returns the last component of the path.
|
676
|
-
def basename(*args) Path.new(File.basename(@path, *args)) end
|
677
|
-
|
678
|
-
# See <tt>File.dirname</tt>. Returns all but the last component of the path.
|
679
|
-
def dirname() Path.new(File.dirname(@path)) end
|
680
|
-
|
681
|
-
# See <tt>File.extname</tt>. Returns the file's extension.
|
682
|
-
def extname() File.extname(@path) end
|
683
|
-
|
684
|
-
# See <tt>File.expand_path</tt>.
|
685
|
-
def expand_path(*args) Path.new(File.expand_path(@path, *args)) end
|
686
|
-
|
687
|
-
# See <tt>File.split</tt>. Returns the #dirname and the #basename in an
|
688
|
-
# Array.
|
689
|
-
def split() File.split(@path).map {|f| Path.new(f) } end
|
690
|
-
end
|
691
|
-
|
692
|
-
|
693
|
-
class Path # * FileTest *
|
694
|
-
|
695
|
-
# See <tt>FileTest.blockdev?</tt>.
|
696
|
-
def blockdev?() FileTest.blockdev?(@path) end
|
697
|
-
|
698
|
-
# See <tt>FileTest.chardev?</tt>.
|
699
|
-
def chardev?() FileTest.chardev?(@path) end
|
700
|
-
|
701
|
-
# See <tt>FileTest.executable?</tt>.
|
702
|
-
def executable?() FileTest.executable?(@path) end
|
703
|
-
|
704
|
-
# See <tt>FileTest.executable_real?</tt>.
|
705
|
-
def executable_real?() FileTest.executable_real?(@path) end
|
706
|
-
|
707
|
-
# See <tt>FileTest.exist?</tt>.
|
708
|
-
def exist?() FileTest.exist?(@path) end
|
709
|
-
|
710
|
-
# See <tt>FileTest.grpowned?</tt>.
|
711
|
-
def grpowned?() FileTest.grpowned?(@path) end
|
712
|
-
|
713
|
-
# See <tt>FileTest.directory?</tt>.
|
714
|
-
def directory?() FileTest.directory?(@path) end
|
715
|
-
|
716
|
-
# See <tt>FileTest.file?</tt>.
|
717
|
-
def file?() FileTest.file?(@path) end
|
718
|
-
|
719
|
-
# See <tt>FileTest.pipe?</tt>.
|
720
|
-
def pipe?() FileTest.pipe?(@path) end
|
721
|
-
|
722
|
-
# See <tt>FileTest.socket?</tt>.
|
723
|
-
def socket?() FileTest.socket?(@path) end
|
724
|
-
|
725
|
-
# See <tt>FileTest.owned?</tt>.
|
726
|
-
def owned?() FileTest.owned?(@path) end
|
727
|
-
|
728
|
-
# See <tt>FileTest.readable?</tt>.
|
729
|
-
def readable?() FileTest.readable?(@path) end
|
730
|
-
|
731
|
-
if FileTest.respond_to? :world_readable?
|
732
|
-
# See <tt>FileTest.world_readable?</tt>.
|
733
|
-
def world_readable?() FileTest.world_readable?(@path) end
|
734
|
-
else
|
735
|
-
def world_readable?
|
736
|
-
mode = File.stat(@path).mode & 0777
|
737
|
-
mode if (mode & 04).nonzero?
|
738
|
-
end
|
739
|
-
end
|
740
|
-
|
741
|
-
# See <tt>FileTest.readable_real?</tt>.
|
742
|
-
def readable_real?() FileTest.readable_real?(@path) end
|
743
|
-
|
744
|
-
# See <tt>FileTest.setuid?</tt>.
|
745
|
-
def setuid?() FileTest.setuid?(@path) end
|
746
|
-
|
747
|
-
# See <tt>FileTest.setgid?</tt>.
|
748
|
-
def setgid?() FileTest.setgid?(@path) end
|
749
|
-
|
750
|
-
# See <tt>FileTest.size</tt>.
|
751
|
-
def size() FileTest.size(@path) end
|
752
|
-
|
753
|
-
# See <tt>FileTest.size?</tt>.
|
754
|
-
def size?() FileTest.size?(@path) end
|
755
|
-
|
756
|
-
# See <tt>FileTest.sticky?</tt>.
|
757
|
-
def sticky?() FileTest.sticky?(@path) end
|
758
|
-
|
759
|
-
# See <tt>FileTest.symlink?</tt>.
|
760
|
-
def symlink?() FileTest.symlink?(@path) end
|
761
|
-
|
762
|
-
# See <tt>FileTest.writable?</tt>.
|
763
|
-
def writable?() FileTest.writable?(@path) end
|
764
|
-
|
765
|
-
if FileTest.respond_to? :world_writable?
|
766
|
-
# See <tt>FileTest.world_writable?</tt>.
|
767
|
-
def world_writable?() FileTest.world_writable?(@path) end
|
768
|
-
else
|
769
|
-
def world_writable?
|
770
|
-
mode = File.stat(@path).mode & 0777
|
771
|
-
mode if (mode & 02).nonzero?
|
772
|
-
end
|
773
|
-
end
|
774
|
-
|
775
|
-
# See <tt>FileTest.writable_real?</tt>.
|
776
|
-
def writable_real?() FileTest.writable_real?(@path) end
|
777
|
-
|
778
|
-
# See <tt>FileTest.zero?</tt>.
|
779
|
-
def zero?() FileTest.zero?(@path) end
|
780
|
-
end
|
781
|
-
|
782
|
-
|
783
|
-
class Path # * Dir *
|
784
|
-
class << self
|
785
|
-
# See <tt>Dir.glob</tt>. Returns or yields Path objects.
|
786
|
-
def glob(*args) # :yield: pathname
|
787
|
-
if block_given?
|
788
|
-
Dir.glob(*args) {|f| yield new(f) }
|
789
|
-
else
|
790
|
-
Dir.glob(*args).map {|f| new(f) }
|
791
|
-
end
|
792
|
-
end
|
793
|
-
|
794
|
-
# See <tt>Dir.getwd</tt>. Returns the current working directory as a Path.
|
795
|
-
def Path.getwd
|
796
|
-
new Dir.getwd
|
797
|
-
end
|
798
|
-
|
799
|
-
alias pwd getwd
|
800
|
-
end
|
801
|
-
|
802
|
-
# Iterates over the entries (files and subdirectories) in the directory. It
|
803
|
-
# yields a Path object for each entry.
|
804
|
-
def each_entry(&block) # :yield: pathname
|
805
|
-
Dir.foreach(@path) {|f| yield Path.new(f) }
|
806
|
-
end
|
807
|
-
|
808
|
-
# See <tt>Dir.mkdir</tt>. Create the referenced directory.
|
809
|
-
def mkdir(*args) Dir.mkdir(@path, *args) end
|
810
|
-
|
811
|
-
# See <tt>Dir.rmdir</tt>. Remove the referenced directory.
|
812
|
-
def rmdir() Dir.rmdir(@path) end
|
813
|
-
|
814
|
-
# See <tt>Dir.open</tt>.
|
815
|
-
def opendir(&block) # :yield: dir
|
816
|
-
Dir.open(@path, &block)
|
817
|
-
end
|
818
|
-
end
|
819
|
-
|
820
|
-
|
821
|
-
class Path # * Find *
|
822
|
-
#
|
823
|
-
# Path#find is an iterator to traverse a directory tree in a depth first
|
824
|
-
# manner. It yields a Path for each file under "this" directory.
|
825
|
-
#
|
826
|
-
# Since it is implemented by <tt>find.rb</tt>, <tt>Find.prune</tt> can be used
|
827
|
-
# to control the traversal.
|
828
|
-
#
|
829
|
-
# If +self+ is <tt>.</tt>, yielded pathnames begin with a filename in the
|
830
|
-
# current directory, not <tt>./</tt>.
|
831
|
-
#
|
832
|
-
def find # :yield: pathname
|
833
|
-
return to_enum(__method__) unless block_given?
|
834
|
-
require 'find'
|
835
|
-
if @path == '.'
|
836
|
-
Find.find(@path) {|f| yield Path.new(f.sub(%r{\A\./}, '')) }
|
837
|
-
else
|
838
|
-
Find.find(@path) {|f| yield Path.new(f) }
|
839
|
-
end
|
840
|
-
end
|
841
|
-
end
|
842
|
-
|
843
|
-
|
844
|
-
class Path # * FileUtils *
|
845
|
-
# See <tt>FileUtils.mkpath</tt>. Creates a full path, including any
|
846
|
-
# intermediate directories that don't yet exist.
|
847
|
-
def mkpath
|
848
|
-
FileUtils.mkpath(@path)
|
849
|
-
nil
|
850
|
-
end
|
851
|
-
|
852
|
-
# See <tt>FileUtils.rm_r</tt>. Deletes a directory and all beneath it.
|
853
|
-
def rmtree
|
854
|
-
# The name "rmtree" is borrowed from File::Path of Perl.
|
855
|
-
# File::Path provides "mkpath" and "rmtree".
|
856
|
-
FileUtils.rm_r(@path)
|
857
|
-
nil
|
858
|
-
end
|
859
|
-
end
|
860
|
-
|
861
|
-
|
862
|
-
class Path # * mixed *
|
863
|
-
# Removes a file or directory, using <tt>File.unlink</tt> or
|
864
|
-
# <tt>Dir.unlink</tt> as necessary.
|
865
|
-
def unlink()
|
866
|
-
begin
|
867
|
-
Dir.unlink @path
|
868
|
-
rescue Errno::ENOTDIR
|
869
|
-
File.unlink @path
|
870
|
-
end
|
871
|
-
end
|
872
|
-
alias delete unlink
|
873
355
|
end
|