pa 1.0.3 → 1.1.3
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/Gemfile +4 -6
- data/README.md +29 -14
- data/lib/pa.rb +34 -36
- data/lib/pa/cmd.rb +506 -414
- data/lib/pa/dir.rb +215 -175
- data/lib/pa/path.rb +282 -306
- data/lib/pa/state.rb +91 -52
- data/lib/pa/util.rb +32 -0
- data/lib/pa/version.rb +3 -0
- data/pa.gemspec +3 -5
- data/spec/pa/cmd_spec.rb +1 -1
- data/spec/pa/dir_spec.rb +19 -21
- data/spec/pa/path_spec.rb +22 -47
- metadata +22 -41
- data/LICENSE +0 -20
- data/version.rb +0 -9
data/lib/pa/dir.rb
CHANGED
@@ -10,9 +10,9 @@
|
|
10
10
|
filea
|
11
11
|
dira/fileb
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ls2("tmp") => ["filea", "dira"]
|
14
|
+
ls2_r("tmp") => ["filea", "dira", "dira/fileb"]
|
15
|
+
ls2_r("tmp"){|path, rel| rel.count('/')==1} => ["dira/fileb"]
|
16
16
|
|
17
17
|
each("tmp") => Enumerate<Pa>
|
18
18
|
each("tmp") {|pa| Pa.rm pa if pa.file?}
|
@@ -20,176 +20,216 @@
|
|
20
20
|
|
21
21
|
=end
|
22
22
|
class Pa
|
23
|
-
module
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
23
|
+
module Dir
|
24
|
+
extend Util::Concern
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
# path globbing, exclude '.' '..' for :dotmatch
|
28
|
+
# @note glob is * ** ? [set] {a,b}
|
29
|
+
#
|
30
|
+
# @overload glob2(*paths, o={})
|
31
|
+
# @param [String] path
|
32
|
+
# @param [Hash] o option
|
33
|
+
# @option o [Boolean] :dotmatch glob not match dot file by default.
|
34
|
+
# @option o [Boolean] :pathname wildcard doesn't match /
|
35
|
+
# @option o [Boolean] :noescape makes '\\' ordinary
|
36
|
+
# @return [Array<String>]
|
37
|
+
# @overload glob2(*paths, o={})
|
38
|
+
# @yieldparam [String] path
|
39
|
+
# @return [nil]
|
40
|
+
def glob2(*args, &blk)
|
41
|
+
paths, o = Util.extract_options(args)
|
42
|
+
paths.map!{|v|get(v)}
|
43
|
+
|
44
|
+
flag = 0
|
45
|
+
o.each do |option, value|
|
46
|
+
next if option==:use_pa
|
47
|
+
flag |= File.const_get("FNM_#{option.upcase}") if value
|
48
|
+
end
|
49
|
+
|
50
|
+
ret = ::Dir.glob(paths, flag)
|
51
|
+
|
52
|
+
# delete . .. for '.*'
|
53
|
+
%w(. ..).each {|v| ret.delete(v)}
|
54
|
+
ret = ret.map{|v| Pa(v)} if o[:use_pa]
|
55
|
+
|
56
|
+
if blk
|
57
|
+
ret.each {|pa|
|
58
|
+
blk.call pa
|
59
|
+
}
|
60
|
+
else
|
61
|
+
ret
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def glob(*args, &blk)
|
66
|
+
args, o = Util.extract_options(args)
|
67
|
+
o[:use_pa] = true
|
68
|
+
glob2(*args, o, &blk)
|
69
|
+
end
|
70
|
+
|
71
|
+
# is directory empty?
|
72
|
+
#
|
73
|
+
# @param [String] path
|
74
|
+
# @return [Boolean]
|
75
|
+
def empty?(path)
|
76
|
+
::Dir.entries(get(path)).empty?
|
77
|
+
end
|
78
|
+
|
79
|
+
# traverse directory
|
80
|
+
# @note raise Errno::ENOTDIR, Errno::ENOENT
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# each '.' do |pa|
|
84
|
+
# p pa.path #=> "foo" not "./foo"
|
85
|
+
# end
|
86
|
+
# # => '/home' ..
|
87
|
+
#
|
88
|
+
# each('.', error: true).with_object([]) do |(pa,err),m|
|
89
|
+
# ...
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# @overload each(path=".", o={})
|
93
|
+
# @param [String,Pa] path
|
94
|
+
# @prarm [Hash] o
|
95
|
+
# @option o [Boolean] :nodot (false) include dot file
|
96
|
+
# @option o [Boolean] :nobackup (false) include backup file
|
97
|
+
# @option o [Boolean] :error (false) yield(pa, err) instead of raise Errno::EPERM when Dir.open(dir)
|
98
|
+
# @return [Enumerator<String>]
|
99
|
+
# @overload each(path=".", o={})
|
100
|
+
# @yieldparam [String] path
|
101
|
+
# @return [nil]
|
102
|
+
def each2(*args, &blk)
|
103
|
+
return Pa.to_enum(:each2, *args) unless blk
|
104
|
+
|
105
|
+
(path,), o = Util.extract_options(args)
|
106
|
+
path = path ? get(path) : "."
|
107
|
+
raise Errno::ENOENT, "`#{path}' doesn't exists." unless File.exists?(path)
|
108
|
+
raise Errno::ENOTDIR, "`#{path}' not a directoy." unless File.directory?(path)
|
109
|
+
|
110
|
+
begin
|
111
|
+
dir = ::Dir.open(path)
|
112
|
+
rescue Errno::EPERM => err
|
113
|
+
end
|
114
|
+
raise err if err and !o[:error]
|
115
|
+
|
116
|
+
while (entry=dir.read)
|
117
|
+
next if %w(. ..).include? entry
|
118
|
+
next if o[:nodot] and entry=~/^\./
|
119
|
+
next if o[:nobackup] and entry=~/~$/
|
120
|
+
|
121
|
+
# => "foo" not "./foo"
|
122
|
+
pa = path=="." ? entry : File.join(path, entry)
|
123
|
+
pa = Pa(pa) if o[:use_pa]
|
124
|
+
if o[:error]
|
125
|
+
blk.call pa, err
|
126
|
+
else
|
127
|
+
blk.call pa
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def each(*args, &blk)
|
133
|
+
args, o = Util.extract_options(args)
|
134
|
+
o[:use_pa] = true
|
135
|
+
each2(*args, o, &blk)
|
136
|
+
end
|
137
|
+
|
138
|
+
# each with recursive
|
139
|
+
# @see each2
|
140
|
+
#
|
141
|
+
# * each2_r() skip Exception
|
142
|
+
# * each2_r(){path, relative, err}
|
143
|
+
#
|
144
|
+
# @overload each2_r(path=".", o={})
|
145
|
+
# @return [Enumerator<String>]
|
146
|
+
# @overload each2_r(path=".", o={})
|
147
|
+
# @yieldparam [String] path
|
148
|
+
# @yieldparam [String] relative relative path
|
149
|
+
# @yieldparam [Errno::ENOENT,Errno::EPERM] err
|
150
|
+
# @return [nil]
|
151
|
+
def each2_r(*args, &blk)
|
152
|
+
return Pa.to_enum(:each2_r, *args) if not blk
|
153
|
+
|
154
|
+
(path,), o = Util.extract_options(args)
|
155
|
+
path ||= "."
|
156
|
+
|
157
|
+
_each2_r(path, "", o, &blk)
|
158
|
+
end
|
159
|
+
|
160
|
+
def each_r(*args, &blk)
|
161
|
+
args, o = Util.extract_options(args)
|
162
|
+
o[:use_pa] = true
|
163
|
+
each2_r(*args, o, &blk)
|
164
|
+
end
|
165
|
+
|
166
|
+
# list directory contents
|
167
|
+
# @see each2
|
168
|
+
#
|
169
|
+
# block form is a filter.
|
170
|
+
#
|
171
|
+
# @Example
|
172
|
+
# Pa.ls2(".") {|path, fname| Pa.directory?(path)} # list only directories
|
173
|
+
#
|
174
|
+
# @overload ls2(path=".", o={})
|
175
|
+
# @return [Array<String>]
|
176
|
+
# @overload ls2(path=".", o={})
|
177
|
+
# @yieldparam [String] path
|
178
|
+
# @yieldparam [String] fname
|
179
|
+
# @return [Array<String>]
|
180
|
+
def ls2(*args, &blk)
|
181
|
+
blk ||= proc {true}
|
182
|
+
each2(*args).with_object([]) { |path,m|
|
183
|
+
base = File.basename(path)
|
184
|
+
ret = blk.call(path, base)
|
185
|
+
m << base if ret
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
def ls(*args, &blk)
|
190
|
+
args, o = Util.extract_options(args)
|
191
|
+
o[:use_pa] = true
|
192
|
+
ls2(*args, o, &blk)
|
193
|
+
end
|
194
|
+
|
195
|
+
# ls2 with recursive
|
196
|
+
# @see ls2
|
197
|
+
#
|
198
|
+
# @overload ls2_r(path=".", o={})
|
199
|
+
# @return [Array<String>]
|
200
|
+
# @overload ls2_r(path=".", o={})
|
201
|
+
# @yieldparam [Pa] pa
|
202
|
+
# @yieldparam [String] rel
|
203
|
+
# @return [Array<String>]
|
204
|
+
def ls2_r(*args, &blk)
|
205
|
+
blk ||= proc {true}
|
206
|
+
each2_r(*args).with_object([]) { |(path,rel),m|
|
207
|
+
m<<rel if blk.call(path, rel)
|
208
|
+
}
|
209
|
+
end
|
210
|
+
|
211
|
+
def ls_r(*args, &blk)
|
212
|
+
args, o = Util.extract_options(args)
|
213
|
+
ls2_r(*args, o, &blk)
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
# @param [String] path
|
218
|
+
def _each2_r(path, relative, o, &blk)
|
219
|
+
o.merge!(error: true)
|
220
|
+
Pa.each2(path, o) do |path1, err|
|
221
|
+
# fix for File.join with empty string
|
222
|
+
joins=[ relative=="" ? nil : relative, File.basename(path1)].compact
|
223
|
+
relative1 = File.join(*joins)
|
224
|
+
|
225
|
+
path1 = Pa(path1) if o[:use_pa]
|
226
|
+
blk.call path1, relative1, err
|
227
|
+
|
228
|
+
if File.directory?(path1)
|
229
|
+
_each2_r(path1, relative1, o, &blk)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
195
235
|
end
|
data/lib/pa/path.rb
CHANGED
@@ -1,225 +1,9 @@
|
|
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
1
|
=begin
|
218
2
|
|
219
3
|
attribute absolute and dir return String, method absolute_path(), dirname() return Pa
|
220
|
-
|
221
|
-
|
222
|
-
|
4
|
+
|
5
|
+
Pa("/home/a").dir #=> "/home"
|
6
|
+
Pa("/home/a").dirname #=> Pa("/home")
|
223
7
|
|
224
8
|
== methods from String
|
225
9
|
* +
|
@@ -227,92 +11,284 @@ attribute absolute and dir return String, method absolute_path(), dirname() retu
|
|
227
11
|
* start_with? end_with?
|
228
12
|
|
229
13
|
=end
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
14
|
+
class Pa
|
15
|
+
NAME_EXT_PAT = /^(.+?)(?:\.([^.]+))?$/
|
16
|
+
module Path
|
17
|
+
extend Util::Concern
|
18
|
+
module ClassMethods
|
19
|
+
# get path of an object.
|
20
|
+
#
|
21
|
+
# return obj#path if object has a 'path' instance method
|
22
|
+
#
|
23
|
+
# @param [String,#path] obj
|
24
|
+
# @return [String,nil] path
|
25
|
+
def get(obj)
|
26
|
+
if obj.respond_to?(:path)
|
27
|
+
obj.path
|
28
|
+
elsif String === obj
|
29
|
+
obj
|
30
|
+
else
|
31
|
+
raise Error, "not support type -- #{obj.inspect}(#{obj.class})"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# return current work directory
|
36
|
+
# @return [String] path
|
37
|
+
def pwd2
|
38
|
+
Dir.getwd
|
39
|
+
end
|
40
|
+
|
41
|
+
def dir2(path)
|
42
|
+
File.dirname(path)
|
43
|
+
end
|
44
|
+
|
45
|
+
# get a basename of a path
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# Pa.basename("foo.bar.c", ext: true) #=> \["foo.bar", "c"]
|
49
|
+
#
|
50
|
+
# @param [String,Pa] name
|
51
|
+
# @param [Hash] o options
|
52
|
+
# @option o [Boolean, String] :ext (false) return \[name, ext] if true
|
53
|
+
#
|
54
|
+
# @return [String] basename of a path unless o[:ext]
|
55
|
+
# @return [Array<String>] \[name, ext] if o[:ext].
|
56
|
+
def base2(name, o={})
|
57
|
+
name = File.basename(get(name))
|
58
|
+
if o[:ext]
|
59
|
+
name, ext = name.match(NAME_EXT_PAT).captures
|
60
|
+
[ name, (ext || "")]
|
61
|
+
else
|
62
|
+
name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# ext of a path
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# "a.ogg" => "ogg"
|
70
|
+
# "a" => nil
|
71
|
+
#
|
72
|
+
# @param [String,Pa] path
|
73
|
+
# @return [String]
|
74
|
+
def ext2 path
|
75
|
+
_, ext = get(path).match(/\.([^.]+)$/).to_a
|
76
|
+
ext
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# alias from File.absolute_path
|
81
|
+
# @param [String,Pa] path
|
82
|
+
# @return [String]
|
83
|
+
def absolute2(path); File.absolute_path(get(path)) end
|
84
|
+
|
85
|
+
# alias from File.expand_path
|
86
|
+
# @param [String,Pa] path
|
87
|
+
# @return [String]
|
88
|
+
def expand2(path); File.expand_path(get(path)) end
|
89
|
+
|
90
|
+
# shorten2 a path,
|
91
|
+
# convert /home/user/file to ~/file
|
92
|
+
#
|
93
|
+
# @param [String,Pa] path
|
94
|
+
# @return [String]
|
95
|
+
def shorten2(path);
|
96
|
+
get(path).sub(%r!^#{Regexp.escape(ENV["HOME"])}!, "~")
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
# is path an absolute path ?
|
102
|
+
#
|
103
|
+
# @param [String,Pa] path
|
104
|
+
# @return [Boolean]
|
105
|
+
def absolute?(path) path=get(path); File.absolute_path(path) == path end
|
106
|
+
|
107
|
+
# split path
|
108
|
+
#
|
109
|
+
# @example
|
110
|
+
# path="/home/a/file"
|
111
|
+
# split(path) #=> "/home/a", "file"
|
112
|
+
# split(path, :all) #=> "/", "home", "a", "file"
|
113
|
+
#
|
114
|
+
# @param [String,Pa] name
|
115
|
+
# @param [Hash] o option
|
116
|
+
# @option o [Boolean] :all split all parts
|
117
|
+
# @return [Array<String>]
|
118
|
+
def split2(name, o={})
|
119
|
+
dir, fname = File.split(get(name))
|
120
|
+
ret = Util.wrap_array(basename(fname, o))
|
121
|
+
|
122
|
+
if o[:all]
|
123
|
+
loop do
|
124
|
+
dir1, fname = File.split(dir)
|
125
|
+
break if dir1 == dir
|
126
|
+
ret.unshift fname
|
127
|
+
dir = dir1
|
128
|
+
end
|
129
|
+
end
|
130
|
+
ret.unshift dir
|
131
|
+
ret
|
132
|
+
end
|
133
|
+
|
134
|
+
def split(*args)
|
135
|
+
dir, *names = split2(*args)
|
136
|
+
[ Pa(dir), *names]
|
137
|
+
end
|
138
|
+
|
139
|
+
# join paths, skip nil and empty string.
|
140
|
+
#
|
141
|
+
# @param [*Array<String>] *paths
|
142
|
+
# @return [String]
|
143
|
+
def join2(*paths)
|
144
|
+
paths.map!{|v|get(v)}
|
145
|
+
|
146
|
+
# skip nil
|
147
|
+
paths.compact!
|
148
|
+
|
149
|
+
# skip empty string
|
150
|
+
paths.delete("")
|
151
|
+
|
152
|
+
File.join(*paths)
|
153
|
+
end
|
154
|
+
|
155
|
+
# get parent path
|
156
|
+
#
|
157
|
+
# @param [String,Pa] path
|
158
|
+
# @param [Fixnum] n up level
|
159
|
+
# @return [String]
|
160
|
+
def parent2(path, n=1)
|
161
|
+
path = get(path)
|
162
|
+
n.times do
|
163
|
+
path = File.dirname(path)
|
164
|
+
end
|
165
|
+
path
|
166
|
+
end
|
167
|
+
|
168
|
+
# is path a dangling symlink?
|
169
|
+
#
|
170
|
+
# a dangling symlink is a dead symlink.
|
171
|
+
#
|
172
|
+
# @param [String,Pa] path
|
173
|
+
# @return [Boolean]
|
174
|
+
def dangling? path
|
175
|
+
path=get(path)
|
176
|
+
if File.symlink?(path)
|
177
|
+
src = File.readlink(path)
|
178
|
+
not File.exists?(src)
|
179
|
+
else
|
180
|
+
nil
|
181
|
+
end
|
182
|
+
end # def dsymlink?
|
183
|
+
|
184
|
+
# real path
|
185
|
+
def real2(path) File.realpath(get(path)) end
|
186
|
+
end
|
187
|
+
|
188
|
+
module InstanceMethods
|
189
|
+
# @return [String]
|
190
|
+
attr_reader :absolute2, :dir2, :base2, :name2, :short2
|
191
|
+
|
192
|
+
# @return [String] ext "", "ogg"
|
193
|
+
attr_reader :ext2
|
194
|
+
|
195
|
+
# @return [String] ext "", ".ogg"
|
196
|
+
attr_reader :fext2
|
197
|
+
|
198
|
+
def initialize_variables
|
199
|
+
super
|
200
|
+
@absolute2 = File.absolute_path(@path)
|
201
|
+
@dir2 = File.dirname(@path)
|
202
|
+
@base2 = File.basename(@path)
|
203
|
+
@name2, @ext2 = @base2.match(NAME_EXT_PAT).captures
|
204
|
+
@ext2 ||= ""
|
205
|
+
@fext2 = @ext2.empty? ? "" : "."+@ext2
|
206
|
+
end
|
207
|
+
|
208
|
+
alias a2 absolute2
|
209
|
+
alias d2 dir2
|
210
|
+
alias b2 base2
|
211
|
+
alias n2 name2
|
212
|
+
alias fname2 base2
|
213
|
+
alias fn2 fname2
|
214
|
+
alias e2 ext2
|
215
|
+
alias fe2 fext2
|
216
|
+
|
217
|
+
# fix name,2 => String
|
218
|
+
alias name name2
|
219
|
+
alias fname fname2
|
220
|
+
alias ext ext2
|
221
|
+
alias fext fext2
|
222
|
+
|
223
|
+
alias fn fname
|
224
|
+
alias n name
|
225
|
+
alias e ext
|
226
|
+
alias fe fext
|
227
|
+
|
228
|
+
def short2
|
229
|
+
@short2 ||= Pa.shorten2(@path)
|
230
|
+
end
|
231
|
+
|
232
|
+
# add string to path
|
233
|
+
#
|
234
|
+
# @example
|
235
|
+
# pa = Pa('/home/foo/a.txt')
|
236
|
+
# pa+'~' #=> new Pa('/home/foo/a.txt~')
|
237
|
+
#
|
238
|
+
# @param [String] str
|
239
|
+
# @return [Pa]
|
240
|
+
def +(str)
|
241
|
+
Pa(path+str)
|
242
|
+
end
|
243
|
+
|
244
|
+
# @return [String]
|
245
|
+
def sub2(*args,&blk)
|
246
|
+
path.sub(*args,&blk)
|
247
|
+
end
|
248
|
+
|
249
|
+
# @return [String]
|
250
|
+
def gsub2(*args,&blk)
|
251
|
+
path.gsub(*args,&blk)
|
252
|
+
end
|
253
|
+
|
254
|
+
# @return [Pa]
|
255
|
+
def sub!(*args,&blk)
|
256
|
+
self.replace path.sub(*args,&blk)
|
257
|
+
end
|
258
|
+
|
259
|
+
# @return [Pa]
|
260
|
+
def gsub!(*args,&blk)
|
261
|
+
self.replace path.gsub(*args,&blk)
|
262
|
+
end
|
263
|
+
|
264
|
+
# @return [MatchData]
|
265
|
+
def match(*args,&blk)
|
266
|
+
path.match(*args,&blk)
|
267
|
+
end
|
268
|
+
|
269
|
+
# @return [Boolean]
|
270
|
+
def start_with?(*args)
|
271
|
+
path.start_with?(*args)
|
272
|
+
end
|
273
|
+
|
274
|
+
# @return [Boolean]
|
275
|
+
def end_with?(*args)
|
276
|
+
path.end_with?(*args)
|
277
|
+
end
|
278
|
+
|
279
|
+
def =~(regexp)
|
280
|
+
path =~ regexp
|
281
|
+
end
|
282
|
+
|
283
|
+
def ==(other)
|
284
|
+
case other
|
285
|
+
when Pa
|
286
|
+
self.path == other.path
|
287
|
+
else
|
288
|
+
false
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
317
293
|
end
|
318
294
|
|