pa 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *~
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ group :development do
4
+ gem 'rspec'
5
+ gem 'watchr'
6
+ end
7
+
8
+ #gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,20 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ rspec (2.6.0)
6
+ rspec-core (~> 2.6.0)
7
+ rspec-expectations (~> 2.6.0)
8
+ rspec-mocks (~> 2.6.0)
9
+ rspec-core (2.6.4)
10
+ rspec-expectations (2.6.0)
11
+ diff-lcs (~> 1.1.2)
12
+ rspec-mocks (2.6.0)
13
+ watchr (0.7)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ rspec
20
+ watchr
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Guten
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ 'Software'), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ Pa, a path libraray for Ruby
2
+ ========================
3
+
4
+ **Homepage**: [https://github.com/GutenLinux/pa](https://github.com/GutenLinux/pa) <br/>
5
+ **Author**: Guten <br/>
6
+ **License**: MIT-LICENSE <br/>
7
+ **Documentation**: [http://rubydoc.info/gems/pa/frames](http://rubydoc.info/gems/pa/frames) <br/>
8
+ **Issue Tracker**: [https://github.com/GutenLinux/pa/issues](https://github.com/GutenLinux/pa/issues) <br/>
9
+
10
+ Overview
11
+ --------
12
+
13
+ An Introduction to Pa
14
+ ---------------------
15
+
16
+ require "tagen/core"
17
+ pa = Pa('/home/foo')
18
+ pa.exists? #=> false
19
+ pa.dir #=> '/home'
20
+ pa.base #=> 'foo'
21
+ pa.join('a.ogg') #=> '/home/a.ogg'
22
+ pa.join(a.ogg).exists? #=> true.
23
+
24
+ Pa.exists?('/home/foo') # alternate way
25
+
26
+ which rspec
27
+
28
+ Pa('/home/foo').should be_exists
29
+
30
+ more see API doc
31
+
32
+ Usage
33
+ -----
34
+
35
+
36
+ Contributing
37
+ -------------
38
+
39
+ * join the project.
40
+ * report bugs/featues to issue tracker.
41
+ * fork it and pull a request.
42
+ * improve documentation.
43
+ * feel free to post any ideas.
44
+
45
+ Install
46
+ ----------
47
+
48
+ gem install pa
49
+
50
+ Resources
51
+ ---------
52
+
53
+ some related resources to help each other.
54
+
55
+ Copyright
56
+ ---------
57
+ Copyright &copy; 2011 by Guten. this library released under MIT-LICENSE, See {file:LICENSE} for futher details.
data/Ragfile ADDED
@@ -0,0 +1,5 @@
1
+ # add your own task in Ragfile or in tasks/*.rag
2
+ class Rag < Thor
3
+ end
4
+
5
+ # vim: filetype=ruby
data/lib/pa/cmd.rb ADDED
@@ -0,0 +1,410 @@
1
+ =begin
2
+ rm family
3
+ * rm _rm file only_
4
+ * rmdir _rm directory only_
5
+ * rm_r _rm recurive, both file and directory_
6
+ * rm_if _with condition, use rm_r_
7
+ === Example
8
+ rm path # it's clear: remove a file
9
+ rmdir path # it's clear: remove a directory
10
+ =end
11
+ class Pa
12
+ module ClassMethods::Cmd
13
+
14
+ # chroot
15
+ # @see {Dir.chroot}
16
+ #
17
+ # @param [String] path
18
+ # @return [nil]
19
+ def chroot(path) Dir.chroot(get(path)) end
20
+
21
+ # touch a blank file
22
+ #
23
+ # @overload touch(*paths, o={})
24
+ # @param [String] *paths
25
+ # @param [Hash] o option
26
+ # @option o [Fixnum,String] :mode
27
+ # @option o [Boolean] :mkdir auto mkdir if path contained directory not exists.
28
+ # @option o [Boolean] :force
29
+ # @return [nil]
30
+ def touch(*args) paths, o = args.extract_options; _touch(*paths, o) end
31
+
32
+ # touch force
33
+ # @see touch
34
+ #
35
+ # @overload touch_f(*paths, o={})
36
+ # @return [nil]
37
+ def touch_f(*args) paths, o = args.extract_options; o[:force]=true; _touch(*paths, o) end
38
+
39
+ def _touch(paths, o)
40
+ o[:mode] ||= 0644
41
+ paths.map!{|v|get(v)}
42
+ paths.each {|path|
43
+ if File.exists?(path)
44
+ o[:force] ? next : raise(Errno::EEXIST, "File exist -- #{path}")
45
+ end
46
+
47
+ mkdir(File.dirname(p)) if o[:mkdir]
48
+
49
+ if win32?
50
+ # win32 BUG. must f.write("") then file can be deleted.
51
+ File.open(p, "w"){|f| f.chmod(o[:mode]); f.write("")}
52
+ else
53
+ File.open(p, "w"){|f| f.chmod(o[:mode])}
54
+ end
55
+ }
56
+ end
57
+ private :_touch
58
+
59
+ # make a directory
60
+ #
61
+ # @overload mkdir(*paths, o={})
62
+ # @param [String, Pa] *paths
63
+ # @param [Hash] o option
64
+ # @option o [Fixnum] :mode
65
+ # @option o [Boolean] :force
66
+ # @return [nil]
67
+ def mkdir(*args) paths, o = args.extract_options; _mkdir(paths, o) end
68
+
69
+ # mkdir force
70
+ # @see mkdir
71
+ #
72
+ # @overload mkdir_f(*paths, o={})
73
+ # @return [nil]
74
+ def mkdir_f(*args) paths, o = args.extract_options; o[:force]=true; _mkdir(paths, o) end
75
+
76
+ def _mkdir(paths, o)
77
+ o[:mode] ||= 0744
78
+ paths.map!{|v|get(v)}
79
+ paths.each {|p|
80
+ if File.exists?(p)
81
+ o[:force] ? next : raise(Errno::EEXIST, "File exist -- #{p}")
82
+ end
83
+
84
+ stack = []
85
+ until p == stack.last
86
+ break if File.exists?(p)
87
+ stack << p
88
+ p = File.dirname(p)
89
+ end
90
+
91
+ stack.reverse.each do |path|
92
+ Dir.mkdir(path)
93
+ File.chmod(o[:mode], path)
94
+ end
95
+ }
96
+ end
97
+ private :_mkdir
98
+
99
+ # make temp directory
100
+ #
101
+ # @param [Hash] o options
102
+ # @option o [Symbol] :prefix ("")
103
+ # @option o [Symbol] :suffix ("")
104
+ # @option o [Symbol] :tmpdir (ENV["TEMP"])
105
+ # @return [String] path
106
+ def mktmpdir(o={}, &blk)
107
+ p = _mktmpname(o)
108
+ File.mkdir(p)
109
+ begin blk.call(p) ensure Dir.delete(p) end if blk
110
+ p
111
+ end # def mktmpdir
112
+
113
+ def home(user=nil) Dir.home end
114
+
115
+ # make temp file
116
+ # @see mktmpdir
117
+ #
118
+ # @param [Hash] o options
119
+ # @return [String] path
120
+ def mktmpfile(o={}, &blk)
121
+ p = _mktmpname(o)
122
+ begin blk.call(p) ensure File.delete(p) end if blk
123
+ p
124
+ end # mktmpfile
125
+
126
+ def _mktmpname(o={})
127
+ # :prefix :suffix :tmpdir
128
+ # $$-(time*100_000).to_i.to_s(36)
129
+ # parse o
130
+ o[:dir] ||= ENV["TEMP"]
131
+ o[:prefix] ||= ""
132
+ o[:suffix] ||= ""
133
+
134
+ # begin
135
+ collision = 0
136
+ path = "#{o[:dir]}/#{o[:prefix]}#{$$}-#{(Time.time*100_000).to_i.to_s(36)}"
137
+ orgi_path = path.dup
138
+ while File.exists?(path)
139
+ path = orgi_path+ collision.to_s
140
+ collision +=1
141
+ end
142
+ path << o[:suffix]
143
+
144
+ path
145
+ end # def mktmpname
146
+ private :_mktmpname
147
+
148
+ # rm file only
149
+ #
150
+ # @param [String] *paths support globbing
151
+ # @return [nil]
152
+ def rm *paths
153
+ paths, o = paths.extract_options
154
+ glob(*paths) { |pa|
155
+ if File.directory?(pa.p)
156
+ if o[:force]; next else raise Errno::EISDIR, "is a directory -- #{pa.p}" end
157
+ end
158
+ next if pa.directory?
159
+ File.delete(pa.p)
160
+ }
161
+ end
162
+
163
+ def rm_f *paths
164
+ paths, o = paths.extract_options
165
+ o[:force] = true
166
+ rm *paths, o
167
+ end
168
+
169
+ # rm directory only. still remove if directory is not empty.
170
+ #
171
+ # @param [String] *paths support globbing
172
+ # @return [nil]
173
+ def rmdir *paths
174
+ paths, o = paths.extract_options
175
+ glob(*paths) { |pa|
176
+ if not File.directory?(pa.p)
177
+ if o[:force]; next else raise Errno::ENOTDIR, "not a directory -- #{pa.p}" end
178
+ end
179
+ _rmdir(pa)
180
+ }
181
+ end
182
+
183
+ def rmdir_f *paths
184
+ paths, o = paths.extract_options
185
+ o[:force] = true
186
+ rmdir *paths, o
187
+ end
188
+
189
+ # rm recusive, rm both file and directory
190
+ #
191
+ # @see rm
192
+ # @return [nil]
193
+ def rm_r *paths
194
+ glob(*paths){ |pa|
195
+ File.directory?(pa.p) ? _rmdir(pa) : File.delete(pa.p)
196
+ }
197
+ end
198
+ alias rm_rf rm_r
199
+
200
+
201
+ # rm_if(path) if condition is true
202
+ #
203
+ # @example
204
+ # Pa.rm_if '/tmp/**/*.rb' do |pa|
205
+ # pa.name == 'old'
206
+ # end
207
+ #
208
+ # @param [String] *paths support globbing
209
+ # @yield [path]
210
+ # @yieldparam [Pa] path
211
+ # @yieldreturn [Boolean] rm_r path if true
212
+ # @return [nil]
213
+ def rm_if(*paths, &blk)
214
+ glob(*paths) do |pa|
215
+ rm_r pa if blk.call(pa)
216
+ end
217
+ end
218
+
219
+ # I'm recusive
220
+ # param@ [Pa] path
221
+ def _rmdir(pa, o={})
222
+ return if not File.exists?(pa.p)
223
+ pa.each {|pa1|
224
+ File.directory?(pa1.p) ? _rmdir(pa1, o) : File.delete(pa1.p)
225
+ }
226
+ File.directory?(pa.p) ? Dir.rmdir(pa.p) : File.delete(pa.p)
227
+ end
228
+ private :_rmdir
229
+
230
+ # copy
231
+ #
232
+ # cp file dir
233
+ # cp 'a', 'dir' #=> dir/a
234
+ # cp 'a', 'dir/a' #=> dir/a
235
+ #
236
+ # cp file1 file2 .. dir
237
+ # cp ['a','b'], 'dir' #=> dir/a dir/b
238
+ #
239
+ # @example
240
+ # cp '*', 'dir' do |src, dest, o|
241
+ # skip if src.name=~'.o$'
242
+ # dest.replace 'dirc' if src.name=="foo"
243
+ # yield # use yield to do the actuactal cp work
244
+ # end
245
+ #
246
+ # @overload cp(src_s, dest, o)
247
+ # @param [Array<String>, String] src_s support globbing
248
+ # @param [String,Pa] dest
249
+ # @param [Hash] o option
250
+ # @option o [Boolean] :mkdir mkdir(dest) if dest not exists.
251
+ # @option o [Boolean] :verbose puts cmd when execute
252
+ # @option o [Boolean] :folsymlink follow symlink
253
+ # @option o [Boolean] :force force dest file if dest is a file
254
+ # @option o [Boolean] :special special copy, when cp a directory, only mkdir, not cp the directory's content, usefull in Pa.each_r
255
+ # @return [nil]
256
+ # @overload cp(src_s, dest, o)
257
+ # @yield [src,dest,o]
258
+ # @return [nil]
259
+ def cp(src_s, dest, o={}, &blk)
260
+ srcs = glob(*Array.wrap(src_s)).map{|v| v.path}
261
+ dest = Pa.get(dest)
262
+
263
+ if o[:mkdir] and (not File.exists?(dest))
264
+ Pa.mkdir dest
265
+ end
266
+
267
+ # cp file1 file2 .. dir
268
+ if srcs.size>1 and (not File.directory?(dest))
269
+ raise Errno::ENOTDIR, "dest not a directory when cp more than one src -- #{dest}"
270
+ end
271
+
272
+ srcs.each do |src|
273
+ dest1 = File.directory?(dest) ? File.join(dest, File.basename(src)) : dest
274
+
275
+ if blk
276
+ blk.call src, dest1, o, proc{_copy(src, dest1, o)}
277
+ else
278
+ _copy src, dest1, o
279
+ end
280
+
281
+ end
282
+ end
283
+
284
+ def cp_f src_s, dest, o={}, &blk
285
+ o[:force] = true
286
+ cp src_s, dest, o, &blk
287
+ end
288
+
289
+ # I'm recursive
290
+ #
291
+ # @param [String] src
292
+ # @param [String] dest
293
+ def _copy(src, dest, o={})
294
+ raise Errno::EEXIST, "dest exists -- #{dest}" if File.exists?(dest) and (not o[:force])
295
+
296
+
297
+ case type=File.ftype(src)
298
+
299
+ when "file", "socket"
300
+ puts "cp #{src} #{dest}" if o[:verbose]
301
+ File.copy_stream(src, dest)
302
+
303
+ when "directory"
304
+ begin
305
+ Pa.mkdir dest
306
+ puts "mkdir #{dest}" if o[:verbose]
307
+ rescue Errno::EEXIST
308
+ end
309
+
310
+ return if o[:special]
311
+
312
+ each(src) { |pa|
313
+ _copy(pa.p, File.join(dest, File.basename(pa.p)), o)
314
+ }
315
+
316
+ when "link" # symbol link
317
+ if o[:folsymlink]
318
+ _copy(Pa.readlink(src), dest)
319
+ else
320
+ Pa.symln(Pa.readlink(src), dest, force: true)
321
+ puts "symlink #{src} #{dest}" if o[:verbose]
322
+ end
323
+
324
+ when "unknow"
325
+ raise EUnKnownType, "Can't handle unknow type(#{:type}) -- #{src}"
326
+ end
327
+
328
+ # chmod chown utime
329
+ src_stat = o[:folsymlink] ? File.stat(src) : File.lstat(src)
330
+ begin
331
+ File.chmod(src_stat.mode, dest)
332
+ #File.chown(src_stat.uid, src_stat.gid, dest)
333
+ File.utime(src_stat.atime, src_stat.mtime, dest)
334
+ rescue Errno::ENOENT
335
+ end
336
+ end # _copy
337
+ private :_copy
338
+
339
+ # move, use rename for same device. and cp for cross device.
340
+ # @see cp
341
+ #
342
+ # @param [Hash] o option
343
+ # @option o [Boolean] :verbose
344
+ # @option o [Boolean] :mkdir
345
+ # @option o [Boolean] :fore
346
+ # @return [nil]
347
+ def mv(src_s, dest, o={}, &blk)
348
+ srcs = glob(*Array.wrap(src_s)).map{|v| get(v)}
349
+ dest = get(dest)
350
+
351
+ if o[:mkdir] and (not File.exists?(dest))
352
+ mkdir dest
353
+ end
354
+
355
+ # mv file1 file2 .. dir
356
+ if srcs.size>1 and (not File.directory?(dest))
357
+ raise Errno::ENOTDIR, "dest not a directory when mv more than one src -- #{dest}"
358
+ end
359
+
360
+ srcs.each do |src|
361
+ dest1 = File.directory?(dest) ? File.join(dest, File.basename(src)) : dest
362
+
363
+ if blk
364
+ blk.call src, dest1, o, proc{_move(src, dest1, o)}
365
+ else
366
+ _move src, dest1, o
367
+ end
368
+
369
+ end
370
+ end
371
+
372
+ def mv_f src_s, dest, o={}, &blk
373
+ o[:force] = true
374
+ mv src_s, dest, o, &blk
375
+ end
376
+
377
+ # I'm recusive
378
+ #
379
+ # _move "file", "dir/file"
380
+ #
381
+ # @param [String] src
382
+ # @param [String] dest
383
+ def _move(src, dest, o)
384
+ raise Errno::EEXIST, "dest exists -- #{dest}" if File.exists?(dest) and (not o[:force])
385
+
386
+ # :force. mv "dir", "dira" and 'dira' exists and is a directory.
387
+ if File.exists?(dest) and File.directory?(dest)
388
+ ls(src) { |pa|
389
+ dest1 = File.join(dest, File.basename(pa.p))
390
+ _move pa.p, dest1, o
391
+ }
392
+ Pa.rm_r src
393
+
394
+ else
395
+ begin
396
+ Pa.rm_r dest if o[:force] and File.exists?(dest)
397
+ puts "rename #{src} #{dest}" if o[:verbose]
398
+ File.rename(src, dest)
399
+ rescue Errno::EXDEV # cross-device
400
+ _copy(src, dest, o)
401
+ Pa.rm_r src
402
+ end
403
+
404
+ end
405
+ end # def _move
406
+ private :_move
407
+
408
+
409
+ end
410
+ end
data/lib/pa/core.rb ADDED
@@ -0,0 +1,106 @@
1
+ class Array
2
+
3
+ =begin
4
+ # extend options are symbols and hash, symbol as a boolean option.
5
+ #
6
+ # :a #=> { a: true }
7
+ # :_a #=> { a: false}
8
+ #
9
+ # @example
10
+ # def foo(*args)
11
+ # paths, o = args.extract_extend_options
12
+ # end
13
+ #
14
+ # foo(1, :a, :_b, :c => 2)
15
+ # #=> paths is [1]
16
+ # #=> o is {a: true, b: false, c: 2}
17
+ #
18
+ # @param [Symbol, Hash] *defaults
19
+ # @return [Array<Object>, Hash] \[args, options]
20
+ def extract_extend_options *defaults
21
+ args, o = _parse_o(defaults)
22
+ args1, o1 = _parse_o(self)
23
+ [args+args1, o.merge(o1)]
24
+ end
25
+
26
+ # modify args IN PLACE.
27
+ # @ see extract_extend_options
28
+ #
29
+ # @example
30
+ # def foo(*args)
31
+ # options = args.extract_extend_options!
32
+ # end
33
+ #
34
+ # foo(1, :a, :_b, c: 2)
35
+ # #=> args is [1]
36
+ # #=> o is {a: true, b: false, c:2}
37
+ #
38
+ # @param [Symbol, Hash] *defaults
39
+ # @return [Hash] options
40
+ def extract_extend_options! *defaults
41
+ args, o = extract_extend_options *defaults
42
+ self.replace args
43
+ o
44
+ end
45
+
46
+ # @param [Array,Hash] args
47
+ def _parse_o args
48
+ args = args.dup
49
+ # name:1
50
+ o1 = Hash === args.last ? args.pop : {}
51
+ # :force :_force
52
+ rst = args.select{|v| Symbol===v}
53
+ args.delete_if{|v| Symbol===v}
54
+ o2={}
55
+ rst.each do |k|
56
+ v = true
57
+ if k=~/^_/
58
+ k = k[1..-1].to_sym
59
+ v = false
60
+ end
61
+ o2[k] = v
62
+ end
63
+ [args, o1.merge(o2)]
64
+ end
65
+ private :_parse_o
66
+ =end
67
+
68
+ # Extracts options from a set of arguments. Removes and returns the last
69
+ # element in the array if it's a hash, otherwise returns a blank hash.
70
+ # you can also pass a default option.
71
+ #
72
+ # @example
73
+ # def options(*args)
74
+ # o = args.extract_options!(:a=>1)
75
+ # end
76
+ #
77
+ # options(1, 2) # => {:a=>1}
78
+ # options(1, 2, :a => :b) # => {:a=>:b}
79
+ #
80
+ # @param [Hash] default default options
81
+ # @return [Hash]
82
+ def extract_options! default={}
83
+ if self.last.is_a?(Hash) && self.last.instance_of?(Hash)
84
+ self.pop.merge default
85
+ else
86
+ default
87
+ end
88
+ end
89
+
90
+ # extract options
91
+ # @see extract_options!
92
+ # @example
93
+ # def mkdir(*args)
94
+ # paths, o = args.extract_options
95
+ # end
96
+ #
97
+ # @return [Array<Array,Hash>]
98
+ def extract_options default={}
99
+ if self.last.is_a?(Hash) && self.last.instance_of?(Hash)
100
+ [self[0...-1], self[-1].merge(default)]
101
+ else
102
+ [self, default]
103
+ end
104
+ end
105
+
106
+ end