pa 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+