pa 1.0.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/.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