pa 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +20 -0
- data/LICENSE +20 -0
- data/README.md +57 -0
- data/Ragfile +5 -0
- data/lib/pa/cmd.rb +410 -0
- data/lib/pa/core.rb +106 -0
- data/lib/pa/dir.rb +195 -0
- data/lib/pa/path.rb +301 -0
- data/lib/pa/state.rb +67 -0
- data/lib/pa.rb +175 -0
- data/pa.gemspec +22 -0
- data/pa.watchr +22 -0
- data/spec/pa/cmd_spec.rb +278 -0
- data/spec/pa/dir_spec.rb +137 -0
- data/spec/pa/path_spec.rb +142 -0
- data/spec/pa/state_spec.rb +7 -0
- data/spec/spec_helper.rb +2 -0
- data/version.rb +9 -0
- metadata +111 -0
data/lib/pa/dir.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
=begin
|
2
|
+
== ls family
|
3
|
+
* Dir[*path] _support globbing_
|
4
|
+
* Pa.glob(*path,o),(){} _support globbing with option and block_
|
5
|
+
* each(path),(){} each_r(),(){} _support Enumerator. not support globbing_
|
6
|
+
* ls(path) ls_r(path) _sample ls. not support globbing._
|
7
|
+
|
8
|
+
=== Example
|
9
|
+
tmp/
|
10
|
+
filea
|
11
|
+
dira/fileb
|
12
|
+
|
13
|
+
ls("tmp") => ["filea", "dira"]
|
14
|
+
ls_r("tmp") => ["filea", "dira", "dira/fileb"]
|
15
|
+
ls_r("tmp"){|pa, rel| rel.count('/')==1} => ["dira/fileb"]
|
16
|
+
|
17
|
+
each("tmp") => Enumerate<Pa>
|
18
|
+
each("tmp") {|pa| Pa.rm pa if pa.file?}
|
19
|
+
each("tmp").with_object([]){|pa,m| m<<pa.dir} #=> ["tmp", "tmp/dira"]
|
20
|
+
|
21
|
+
=end
|
22
|
+
class Pa
|
23
|
+
module ClassMethods::Dir
|
24
|
+
|
25
|
+
# path globbing, exclude '.' '..' for :dotmatch
|
26
|
+
# @note glob is * ** ? [set] {a,b}
|
27
|
+
#
|
28
|
+
# @overload glob(*paths, o={})
|
29
|
+
# @param [String] path
|
30
|
+
# @param [Hash] o option
|
31
|
+
# @option o [Boolean] :dotmatch glob not match dot file by default.
|
32
|
+
# @option o [Boolean] :pathname wildcard doesn't match /
|
33
|
+
# @option o [Boolean] :noescape makes '\\' ordinary
|
34
|
+
# @return [Array<Pa>]
|
35
|
+
# @overload glob(*paths, o={})
|
36
|
+
# @yieldparam [Pa] path
|
37
|
+
# @return [nil]
|
38
|
+
def glob(*args, &blk)
|
39
|
+
paths, o = args.extract_options
|
40
|
+
paths.map!{|v|get(v)}
|
41
|
+
|
42
|
+
flag = 0
|
43
|
+
o.each do |option, value|
|
44
|
+
flag |= File.const_get("FNM_#{option.upcase}") if value
|
45
|
+
end
|
46
|
+
|
47
|
+
ret = Dir.glob(paths, flag)
|
48
|
+
|
49
|
+
# delete . .. for '.*'
|
50
|
+
ret.tap{|paths| %w(. ..).each{|v| paths.delete(v)}}
|
51
|
+
ret.map!{|path|Pa(path)}
|
52
|
+
|
53
|
+
if blk
|
54
|
+
ret.each {|pa|
|
55
|
+
blk.call pa
|
56
|
+
}
|
57
|
+
else
|
58
|
+
ret
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# is directory empty?
|
63
|
+
#
|
64
|
+
# @param [String] path
|
65
|
+
# @return [Boolean]
|
66
|
+
def empty?(path) Dir.entries(get(path)).empty? end
|
67
|
+
|
68
|
+
# traverse directory
|
69
|
+
# @note raise Errno::ENOTDIR, Errno::ENOENT
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# each '.' do |pa|
|
73
|
+
# p pa.path #=> "foo" not "./foo"
|
74
|
+
# end
|
75
|
+
# # => '/home' ..
|
76
|
+
#
|
77
|
+
# each('.', error: true).with_object([]) do |(pa,err),m|
|
78
|
+
# ...
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# @overload each(path=".", o={})
|
82
|
+
# @param [String,Pa] path
|
83
|
+
# @prarm [Hash] o
|
84
|
+
# @option o [Boolean] :nodot (false) include dot file
|
85
|
+
# @option o [Boolean] :nobackup (false) include backup file
|
86
|
+
# @option o [Boolean] :error (false) yield(pa, err) instead of raise Errno::EPERM when Dir.open(dir)
|
87
|
+
# @return [Enumerator<Pa>]
|
88
|
+
# @overload each(path=".", o={})
|
89
|
+
# @yieldparam [Pa] path
|
90
|
+
# @return [nil]
|
91
|
+
def each(*args, &blk)
|
92
|
+
return Pa.to_enum(:each, *args) unless blk
|
93
|
+
|
94
|
+
(path,), o = args.extract_options
|
95
|
+
path = path ? get(path) : "."
|
96
|
+
raise Errno::ENOENT, "`#{path}' doesn't exists." unless File.exists?(path)
|
97
|
+
raise Errno::ENOTDIR, "`#{path}' not a directoyr." unless File.directory?(path)
|
98
|
+
|
99
|
+
begin
|
100
|
+
dir = Dir.open(path)
|
101
|
+
rescue Errno::EPERM => err
|
102
|
+
end
|
103
|
+
raise err if err and !o[:error]
|
104
|
+
|
105
|
+
while (entry=dir.read)
|
106
|
+
next if %w(. ..).include? entry
|
107
|
+
next if o[:nodot] and entry=~/^\./
|
108
|
+
next if o[:nobackup] and entry=~/~$/
|
109
|
+
|
110
|
+
# => "foo" not "./foo"
|
111
|
+
pa = path=="." ? Pa(entry) : Pa(File.join(path, entry))
|
112
|
+
if o[:error]
|
113
|
+
blk.call pa, err
|
114
|
+
else
|
115
|
+
blk.call pa
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# each with recursive
|
121
|
+
# @see each
|
122
|
+
#
|
123
|
+
# * each_r() skip Exception
|
124
|
+
# * each_r(){pa, err}
|
125
|
+
#
|
126
|
+
# @overload each_r(path=".", o={})
|
127
|
+
# @return [Enumerator<Pa>]
|
128
|
+
# @overload each_r(path=".", o={})
|
129
|
+
# @yieldparam [Pa] pa
|
130
|
+
# @yieldparam [String] relative relative path
|
131
|
+
# @yieldparam [Errno::ENOENT,Errno::EPERM] err
|
132
|
+
# @return [nil]
|
133
|
+
def each_r(*args, &blk)
|
134
|
+
return Pa.to_enum(:each_r, *args) if not blk
|
135
|
+
|
136
|
+
(path,), o = args.extract_options
|
137
|
+
path ||= "."
|
138
|
+
|
139
|
+
_each_r(path, "", o, &blk)
|
140
|
+
end
|
141
|
+
|
142
|
+
# @param [String] path
|
143
|
+
def _each_r path, relative, o, &blk
|
144
|
+
o.merge!(error: true)
|
145
|
+
Pa.each(path, o) do |pa1, err|
|
146
|
+
relative1 = Pa.join(relative, pa1.b)
|
147
|
+
|
148
|
+
blk.call pa1, relative1, err
|
149
|
+
|
150
|
+
if pa1.directory?
|
151
|
+
_each_r(pa1.p, relative1, o, &blk)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
private :_each_r
|
156
|
+
|
157
|
+
# list directory contents
|
158
|
+
# @see each
|
159
|
+
#
|
160
|
+
# block form is a filter.
|
161
|
+
#
|
162
|
+
# @Example
|
163
|
+
# Pa.ls(".") {|pa, fname| pa.directory?} # list only directories
|
164
|
+
#
|
165
|
+
# @overload ls(path=".", o={})
|
166
|
+
# @return [Array<String>]
|
167
|
+
# @overload ls(path=".", o={})
|
168
|
+
# @yieldparam [Pa] pa
|
169
|
+
# @yieldparam [String] fname
|
170
|
+
# @return [Array<String>]
|
171
|
+
def ls *args, &blk
|
172
|
+
blk ||= proc {true}
|
173
|
+
each(*args).with_object([]) { |pa,m|
|
174
|
+
m<<pa.fname if blk.call(pa, pa.fname)
|
175
|
+
}
|
176
|
+
end
|
177
|
+
|
178
|
+
# ls with recursive
|
179
|
+
# @see ls
|
180
|
+
#
|
181
|
+
# @overload ls_r(path=".", o={})
|
182
|
+
# @return [Array<String>]
|
183
|
+
# @overload ls_r(path=".", o={})
|
184
|
+
# @yieldparam [Pa] pa
|
185
|
+
# @yieldparam [String] rel
|
186
|
+
# @return [Array<String>]
|
187
|
+
def ls_r *args, &blk
|
188
|
+
blk ||= proc {true}
|
189
|
+
each_r(*args).with_object([]) { |(pa,rel),m|
|
190
|
+
m<<rel if blk.call(pa, rel)
|
191
|
+
}
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|
data/lib/pa/path.rb
ADDED
@@ -0,0 +1,301 @@
|
|
1
|
+
class Pa
|
2
|
+
NAME_EXT_PAT = /^(.+?)(?:\.([^.]+))?$/
|
3
|
+
module ClassMethods::Path
|
4
|
+
|
5
|
+
# alias from File.absolute_path
|
6
|
+
# @param [String,Pa] path
|
7
|
+
# @return [String]
|
8
|
+
def absolute(path); File.absolute_path(get(path)) end
|
9
|
+
|
10
|
+
# alias from File.expand_path
|
11
|
+
# @param [String,Pa] path
|
12
|
+
# @return [String]
|
13
|
+
def expand(path); File.expand_path(get(path)) end
|
14
|
+
|
15
|
+
# shorten a path,
|
16
|
+
# convert /home/user/file to ~/file
|
17
|
+
#
|
18
|
+
# @param [String,Pa] path
|
19
|
+
# @return [String]
|
20
|
+
def shorten(path);
|
21
|
+
get(path).sub(%r!^#{Regexp.escape(ENV["HOME"])}!, "~")
|
22
|
+
end
|
23
|
+
|
24
|
+
# return current work directory
|
25
|
+
# @return [String] path
|
26
|
+
def pwd() Dir.getwd end
|
27
|
+
|
28
|
+
# @return [Pa] path
|
29
|
+
def pwd2() Pa(Dir.getwd) end
|
30
|
+
|
31
|
+
# change directory
|
32
|
+
#
|
33
|
+
# @param [String,Pa] path
|
34
|
+
def cd(path=ENV["HOME"], &blk) Dir.chdir(get(path), &blk) end
|
35
|
+
|
36
|
+
# get path of an object.
|
37
|
+
#
|
38
|
+
# return obj#path if object has a 'path' instance method
|
39
|
+
#
|
40
|
+
# @param [String,#path] obj
|
41
|
+
# @return [String,nil] path
|
42
|
+
def get obj
|
43
|
+
if obj.respond_to?(:path)
|
44
|
+
obj.path
|
45
|
+
elsif String === obj
|
46
|
+
obj
|
47
|
+
else
|
48
|
+
raise Error, "not support type -- #{obj.inspect}(#{obj.class})"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# extname of a path
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# "a.ogg" => "ogg"
|
56
|
+
# "a" => nil
|
57
|
+
#
|
58
|
+
# @param [String,Pa] path
|
59
|
+
# @return [String]
|
60
|
+
def extname path
|
61
|
+
_, ext = get(path).match(/\.([^.]+)$/).to_a
|
62
|
+
ext
|
63
|
+
end
|
64
|
+
|
65
|
+
# is path an absolute path ?
|
66
|
+
#
|
67
|
+
# @param [String,Pa] path
|
68
|
+
# @return [Boolean]
|
69
|
+
def absolute?(path) path=get(path); File.absolute_path(path) == path end
|
70
|
+
|
71
|
+
# get a basename of a path
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# Pa.basename("foo.bar.c", ext: true) #=> \["foo.bar", "c"]
|
75
|
+
#
|
76
|
+
# @param [String,Pa] name
|
77
|
+
# @param [Hash] o options
|
78
|
+
# @option o [Boolean, String] :ext (false) return \[name, ext] if true
|
79
|
+
#
|
80
|
+
# @return [String] basename of a path unless o[:ext]
|
81
|
+
# @return [Array<String>] \[name, ext] if o[:ext].
|
82
|
+
def basename(name, o={})
|
83
|
+
name = File.basename(get(name))
|
84
|
+
if o[:ext]
|
85
|
+
name, ext = name.match(NAME_EXT_PAT).captures
|
86
|
+
[ name, (ext || "")]
|
87
|
+
else
|
88
|
+
name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# split path
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# path="/home/a/file"
|
96
|
+
# split(path) #=> "/home/a", "file"
|
97
|
+
# split(path, :all) #=> "/", "home", "a", "file"
|
98
|
+
#
|
99
|
+
# @param [String,Pa] name
|
100
|
+
# @param [Hash] o option
|
101
|
+
# @option o [Boolean] :all split all parts
|
102
|
+
# @return [Array<String>]
|
103
|
+
def split(name, o={})
|
104
|
+
dir, fname = File.split(get(name))
|
105
|
+
ret = Array.wrap(basename(fname, o))
|
106
|
+
|
107
|
+
if o[:all]
|
108
|
+
loop do
|
109
|
+
dir1, fname = File.split(dir)
|
110
|
+
break if dir1 == dir
|
111
|
+
ret.unshift fname
|
112
|
+
dir = dir1
|
113
|
+
end
|
114
|
+
end
|
115
|
+
ret.unshift dir
|
116
|
+
ret
|
117
|
+
end
|
118
|
+
|
119
|
+
# join paths, skip nil and empty string.
|
120
|
+
#
|
121
|
+
# @param [*Array<String>] *paths
|
122
|
+
# @return [String]
|
123
|
+
def join *paths
|
124
|
+
paths.map!{|v|get(v)}
|
125
|
+
|
126
|
+
# skip nil
|
127
|
+
paths.compact!
|
128
|
+
|
129
|
+
# skip empty string
|
130
|
+
paths.delete("")
|
131
|
+
|
132
|
+
File.join(*paths)
|
133
|
+
end
|
134
|
+
|
135
|
+
# get parent path
|
136
|
+
#
|
137
|
+
# @param [String,Pa] path
|
138
|
+
# @param [Fixnum] n up level
|
139
|
+
# @return [String]
|
140
|
+
def parent path, n=1
|
141
|
+
path = get(path)
|
142
|
+
n.times do
|
143
|
+
path = File.dirname(path)
|
144
|
+
end
|
145
|
+
path
|
146
|
+
end
|
147
|
+
|
148
|
+
# link
|
149
|
+
#
|
150
|
+
# @overload ln(src, dest)
|
151
|
+
# @overload ln([src,..], directory)
|
152
|
+
#
|
153
|
+
# @param [Array<String>, String] src_s support globbing
|
154
|
+
# @param [String,Pa] dest
|
155
|
+
# @param [Hash] o option
|
156
|
+
# @option o [Boolean] :force overwrite if exists.
|
157
|
+
# @return [nil]
|
158
|
+
def ln(src_s, dest, o={}) _ln(File.method(:link), src_s, dest, o) end
|
159
|
+
|
160
|
+
# ln force
|
161
|
+
#
|
162
|
+
# @see ln
|
163
|
+
# @return [nil]
|
164
|
+
def ln_f(src_s, dest, o) o[:force]=true; _ln(File.method(:link), src_s, dest, o) end
|
165
|
+
|
166
|
+
# symbol link
|
167
|
+
#
|
168
|
+
# @see ln
|
169
|
+
# @return [nil]
|
170
|
+
def symln(src_s, dest, o) _ln(File.method(:symlink), src_s, dest, o) end
|
171
|
+
alias symlink ln
|
172
|
+
|
173
|
+
# symln force
|
174
|
+
#
|
175
|
+
# @see ln
|
176
|
+
# @return [nil]
|
177
|
+
def symln_f(src_s, dest, o) o[:force]=true; _ln(File.method(:symlink), src_s, dest, o) end
|
178
|
+
|
179
|
+
# @param [Array,String,#path] src_s
|
180
|
+
# @param [String,#path] dest
|
181
|
+
def _ln(method, src_s, dest, o={})
|
182
|
+
dest = get(dest)
|
183
|
+
glob(*Array.wrap(src_s)) {|src|
|
184
|
+
src = get(src)
|
185
|
+
dest = File.join(dest, File.basename(src)) if File.directory?(dest)
|
186
|
+
Pa.rm_r(dest) if o[:force] and File.exists?(dest)
|
187
|
+
method.call(src, dest)
|
188
|
+
}
|
189
|
+
end
|
190
|
+
private :_ln
|
191
|
+
|
192
|
+
# @see File.readlink
|
193
|
+
def readlink(path) File.readlink(get(path)) end
|
194
|
+
|
195
|
+
# is path a dangling symlink?
|
196
|
+
#
|
197
|
+
# a dangling symlink is a dead symlink.
|
198
|
+
#
|
199
|
+
# @param [String,Pa] path
|
200
|
+
# @return [Boolean]
|
201
|
+
def dangling? path
|
202
|
+
path=get(path)
|
203
|
+
if File.symlink?(path)
|
204
|
+
src = File.readlink(path)
|
205
|
+
not File.exists?(src)
|
206
|
+
else
|
207
|
+
nil
|
208
|
+
end
|
209
|
+
end # def dsymlink?
|
210
|
+
|
211
|
+
def realpath(path) File.realpath(get(path)) end
|
212
|
+
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
class Pa
|
217
|
+
=begin
|
218
|
+
|
219
|
+
attribute absolute and dir return String, method absolute_path(), dirname() return Pa
|
220
|
+
|
221
|
+
Pa("/home/a").dir #=> "/home"
|
222
|
+
Pa("/home/a").dirname #=> Pa("/home")
|
223
|
+
|
224
|
+
== methods from String
|
225
|
+
* +
|
226
|
+
* [g]sub[!] match =~
|
227
|
+
* start_with? end_with?
|
228
|
+
|
229
|
+
=end
|
230
|
+
module Path
|
231
|
+
# @return [String]
|
232
|
+
attr_reader :absolute, :dir, :base, :name, :ext, :fext, :short
|
233
|
+
|
234
|
+
def initialize_variables
|
235
|
+
super
|
236
|
+
@absolute = File.absolute_path(@path)
|
237
|
+
@dir = File.dirname(@path)
|
238
|
+
@base = File.basename(@path)
|
239
|
+
@name, @ext = @base.match(NAME_EXT_PAT).captures
|
240
|
+
@ext ||= ""
|
241
|
+
@fext = @ext.empty? ? "" : "."+@ext
|
242
|
+
end
|
243
|
+
|
244
|
+
alias a absolute
|
245
|
+
alias d dir
|
246
|
+
alias b base
|
247
|
+
alias n name
|
248
|
+
alias fname base
|
249
|
+
alias fn fname
|
250
|
+
alias e ext
|
251
|
+
alias fe fext
|
252
|
+
|
253
|
+
def short
|
254
|
+
@short ||= Pa.shorten(@path)
|
255
|
+
end
|
256
|
+
|
257
|
+
# @return [Pa] absolute path
|
258
|
+
def absolute2() @absolute2 ||= Pa(absolute) end
|
259
|
+
|
260
|
+
# @return [Pa] dirname
|
261
|
+
# @example
|
262
|
+
# Pa(__FILE__).dirname.join('.opts')
|
263
|
+
def dir2() @dir2 ||= Pa(dir) end
|
264
|
+
|
265
|
+
# add string to path
|
266
|
+
#
|
267
|
+
# @example
|
268
|
+
# pa = Pa('/home/foo/a.txt')
|
269
|
+
# pa+'~' #=> new Pa('/home/foo/a.txt~')
|
270
|
+
#
|
271
|
+
# @param [String] str
|
272
|
+
# @return [Pa]
|
273
|
+
def +(str) Pa(path+str) end
|
274
|
+
|
275
|
+
# @return [Pa]
|
276
|
+
def sub(*args,&blk) Pa(path.sub(*args,&blk)) end
|
277
|
+
|
278
|
+
# @return [Pa]
|
279
|
+
def gsub(*args,&blk) Pa(path.gsub(*args,&blk)) end
|
280
|
+
|
281
|
+
# @return [Pa]
|
282
|
+
def sub!(*args,&blk) self.replace path.sub(*args,&blk) end
|
283
|
+
|
284
|
+
# @return [Pa]
|
285
|
+
def gsub!(*args,&blk) self.replace path.gsub(*args,&blk) end
|
286
|
+
|
287
|
+
# @return [MatchData]
|
288
|
+
def match(*args,&blk) path.match(*args,&blk) end
|
289
|
+
|
290
|
+
# @return [Boolean]
|
291
|
+
def start_with?(*args) path.start_with?(*args) end
|
292
|
+
|
293
|
+
# @return [Boolean]
|
294
|
+
def end_with?(*args) path.end_with?(*args) end
|
295
|
+
|
296
|
+
def =~(regexp) path =~ regexp end
|
297
|
+
|
298
|
+
def ==(other) self.path == other.path end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
data/lib/pa/state.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
class Pa
|
2
|
+
module ClassMethods::State
|
3
|
+
|
4
|
+
# @see File.chmod
|
5
|
+
def chmod(mode, *paths) paths.map!{|v|get(v)}; File.chmod(mode, *paths) end
|
6
|
+
|
7
|
+
# @see File.lchmod
|
8
|
+
def lchmod(mode, *paths) paths.map!{|v|get(v)}; File.lchmod(mode, *paths) end
|
9
|
+
|
10
|
+
# @see File.chown
|
11
|
+
def chown(user, group=nil, *paths) paths.map!{|v|get(v)}; File.chown(user, group, *paths) end
|
12
|
+
|
13
|
+
# @see File.lchown
|
14
|
+
def lchown(user, group=nil, *paths) paths.map!{|v|get(v)}; File.lchown(user, group, *paths) end
|
15
|
+
|
16
|
+
# @see File.utime
|
17
|
+
def utime(atime, mtime, *paths) paths.map!{|v|get(v)}; File.utime(atime, mtime, *paths) end
|
18
|
+
|
19
|
+
|
20
|
+
# get file type
|
21
|
+
#
|
22
|
+
# file types:
|
23
|
+
# "chardev" "blockdev" "symlink" ..
|
24
|
+
#
|
25
|
+
# @param [String] path
|
26
|
+
# @return [String]
|
27
|
+
def type(path)
|
28
|
+
case (t=File.ftype(get(path)))
|
29
|
+
when "characterSpecial"
|
30
|
+
"chardev"
|
31
|
+
when "blockSpecial"
|
32
|
+
"blockdev"
|
33
|
+
when "link"
|
34
|
+
"symlink"
|
35
|
+
else
|
36
|
+
t
|
37
|
+
end
|
38
|
+
end # def type
|
39
|
+
|
40
|
+
|
41
|
+
# is path a mountpoint?
|
42
|
+
#
|
43
|
+
# @param[String] path
|
44
|
+
# @return [Boolean]
|
45
|
+
def mountpoint? path
|
46
|
+
path=get(path)
|
47
|
+
begin
|
48
|
+
stat1 = File.lstat(path)
|
49
|
+
stat2 = File.lstat(File.join(path, '..'))
|
50
|
+
stat1.dev == stat2.dev && stat1.ino == stat2.ino || stat1.dev != stat2.dev
|
51
|
+
rescue Errno::ENOENT
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module State
|
58
|
+
def chmod(mode); File.chmod(mode, path) end
|
59
|
+
def lchmod(mode); File.lchmod(mode, path) end
|
60
|
+
def chown(uid, gid=nil); File.chown(uid, gid, path) end
|
61
|
+
def lchown(uid, gid=nil); File.lchown(uid, gid, path) end
|
62
|
+
def utime(atime, mtime); File.utime(atime, mtime, path) end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
|