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 +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/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*~
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
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 © 2011 by Guten. this library released under MIT-LICENSE, See {file:LICENSE} for futher details.
|
data/Ragfile
ADDED
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
|