pa 1.0.3 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,11 +1,9 @@
1
- source :rubygems
2
-
3
- gem 'tagen', '~>1.0.0', :require => 'tagen/core'
1
+ source "http://rubygems.org"
4
2
 
5
3
  group :development do
6
- gem 'thor'
7
- gem 'rspec'
8
- gem 'watchr'
4
+ gem "thor"
5
+ gem "rspec"
6
+ gem "watchr"
9
7
  end
10
8
 
11
9
  #gemspec
data/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  Pa, a path libraray for Ruby
2
2
  ========================
3
3
 
4
- **Homepage**: [https://github.com/GutenYe/pa](https://github.com/GutenYe/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/GutenYe/pa/issues](https://github.com/GutenYe/pa/issues) <br/>
4
+ | Homepage: | https://github.com/GutenYe/pa |
5
+ |----------------|--------------------------------------|
6
+ | Author: | Guten |
7
+ | License: | MIT-LICENSE |
8
+ | Documentation: | http://rubydoc.info/gems/pa/frames |
9
+ | Issue Tracker: | https://github.com/GutenYe/pa/issues |
10
+
9
11
 
10
12
  Overview
11
13
  --------
@@ -31,20 +33,33 @@ used with rspec
31
33
 
32
34
  more see API doc
33
35
 
36
+ Install
37
+ ----------
38
+
39
+ gem install pa
40
+
34
41
  Contributing
35
42
  -------------
36
43
 
37
- * join the project.
38
- * report bugs/featues to issue tracker.
39
- * fork it and pull a request.
40
- * improve documentation.
41
- * feel free to post any ideas.
44
+ * Feel free to join the project and make contributions (by submitting a pull request)
45
+ * Submit any bugs/features/ideas to github issue tracker
46
+ * Coding Style Guide: https://gist.github.com/1105334
42
47
 
43
- Install
44
- ----------
48
+ Contributors
49
+ ------------
50
+
51
+ * [contributors](https://github.com/GutenYe/pa/contributors)
45
52
 
46
- gem install pa
47
53
 
48
54
  Copyright
49
55
  ---------
50
- Copyright &copy; 2011 by Guten. this library released under MIT-LICENSE, See {file:LICENSE} for futher details.
56
+
57
+ (the MIT License)
58
+
59
+ Copyright (c) 2011 Guten
60
+
61
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
62
+
63
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
64
+
65
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/pa.rb CHANGED
@@ -1,5 +1,3 @@
1
- require "tagen/core"
2
-
3
1
  =begin rdoc
4
2
  Pa(Path) is similary to Pathname, but more powerful.
5
3
  it combines fileutils, tmpdir, find, tempfile, File, Dir, Pathname
@@ -66,13 +64,37 @@ Example3:
66
64
 
67
65
  =end
68
66
  class Pa
67
+ autoload :Util, "pa/util"
68
+ autoload :VERSION, "pa/version"
69
+
69
70
  Error = Class.new Exception
70
71
  EUnkonwType = Class.new Error
71
72
 
73
+ class << self
74
+ def method_missing(name, *args, &blk)
75
+ # dir -> dir2
76
+ name2 = "#{name}2".to_sym
77
+ if public_methods.include?(name2)
78
+ ret = __send__(name2, *args)
79
+ return case ret
80
+ when Array
81
+ ret.map{|v| Pa(v)}
82
+ when String
83
+ Pa(ret)
84
+ end
85
+ end
86
+
87
+ raise NoMethodError, "no method -- #{name}"
88
+ end
89
+ end
90
+
72
91
  attr_reader :path
92
+ alias p path
93
+ alias path2 path
94
+ alias p2 path
73
95
 
74
96
  # @param [String,#path] path
75
- def initialize path
97
+ def initialize(path)
76
98
  @path = path.respond_to?(:path) ? path.path : path
77
99
  initialize_variables
78
100
  end
@@ -82,11 +104,9 @@ class Pa
82
104
  end
83
105
  include chainable
84
106
 
85
- alias p path
86
-
87
107
  # @param [String,#path]
88
108
  # @return [Pa] the same Pa object
89
- def replace path
109
+ def replace(path)
90
110
  @path = path.respond_to?(:path) ? path.path : path
91
111
  initialize_variables
92
112
  end
@@ -113,11 +133,9 @@ class Pa
113
133
  ret = self.class.__send__(name, path, *args, &blk)
114
134
 
115
135
  case ret
116
-
117
136
  # e.g. readlink ..
118
137
  when String
119
138
  Pa(ret)
120
-
121
139
  # e.g. directory?
122
140
  else
123
141
  ret
@@ -126,9 +144,7 @@ class Pa
126
144
  end
127
145
 
128
146
  def <=> other
129
- other_path = nil
130
- other_path =
131
- if other.respond_to?(:path)
147
+ other_path = if other.respond_to?(:path)
132
148
  other.path
133
149
  elsif String === other
134
150
  other
@@ -138,37 +154,20 @@ class Pa
138
154
 
139
155
  path <=> other_path
140
156
  end
141
-
142
157
  end
143
158
 
159
+ require "pa/path"
160
+ require "pa/cmd"
161
+ require "pa/dir"
162
+ require "pa/state"
144
163
  class Pa
145
- module ClassMethods
146
- UNDEFS = [:open, :fstat]
147
-
148
- # missing method goes to File class method
149
- def method_missing name, *args, &blk
150
- raise NoMethodError, name.inspect if UNDEFS.include?(name)
151
- return if args.size>1
152
- File.__send__ name, get(args[0]), &blk
153
- end
154
- end
155
- end
156
-
157
- require_relative "pa/path"
158
- require_relative "pa/cmd"
159
- require_relative "pa/dir"
160
- require_relative "pa/state"
161
- class Pa
162
- extend ClassMethods
163
- extend ClassMethods::Path
164
- extend ClassMethods::Dir
165
- extend ClassMethods::State
166
- extend ClassMethods::Cmd
167
-
168
164
  include Path
165
+ include Dir
169
166
  include State
167
+ include Cmd
170
168
  end
171
169
 
170
+
172
171
  module Kernel
173
172
  private
174
173
  # a very convient function.
@@ -180,4 +179,3 @@ private
180
179
  Pa.new path
181
180
  end
182
181
  end
183
-
@@ -9,418 +9,510 @@ rm family
9
9
  rmdir path # it's clear: remove a directory
10
10
  =end
11
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
- case type=File.ftype(src)
297
-
298
- when "file", "socket"
299
- puts "cp #{src} #{dest}" if o[:verbose]
300
- File.copy_stream(src, dest)
301
-
302
- when "directory"
303
- begin
304
- Pa.mkdir dest
305
- puts "mkdir #{dest}" if o[:verbose]
306
- rescue Errno::EEXIST
307
- end
308
-
309
- return if o[:special]
310
-
311
- each(src) { |pa|
312
- _copy(pa.p, File.join(dest, File.basename(pa.p)), o)
313
- }
314
-
315
- when "link" # symbol link
316
- if o[:folsymlink]
317
- _copy(Pa.readlink(src), dest)
318
- else
319
- Pa.symln(Pa.readlink(src), dest, force: true)
320
- puts "symlink #{src} #{dest}" if o[:verbose]
321
- end
322
-
323
- when "unknow"
324
- raise EUnKnownType, "Can't handle unknow type(#{:type}) -- #{src}"
325
- end
326
-
327
- # chmod chown utime
328
- src_stat = o[:folsymlink] ? File.stat(src) : File.lstat(src)
329
- begin
330
- File.chmod(src_stat.mode, dest)
331
- #File.chown(src_stat.uid, src_stat.gid, dest)
332
- File.utime(src_stat.atime, src_stat.mtime, dest)
333
- rescue Errno::ENOENT
334
- end
335
- end # _copy
336
- private :_copy
337
-
338
- # a rename util
339
- #
340
- # @example
341
- #
342
- # Pa.rename('/home/guten.jpg') {|pa| pa.name+'_1'+pa.fext} # => '/home/guten_1.jpg'
343
- # Pa('/home/guten.jpg').rename {|pa| pa.name+'_1'+pa.fext} # => <#Pa('/home/guten_1.jpg')>
344
- #
345
- # @param [String,Pa] src
346
- # @yieldparam [Pa] pa
347
- # @yieldreturn [String] fname
348
- # @return [String,Pa] # Pa.rename return String. Pa#rename return Pa.
349
- def rename src, &blk
350
- src = Pa(src)
351
- fname = blk.call(src)
352
- src.dir_pa.join(fname).path
353
- end
354
-
355
- # move, use rename for same device. and cp for cross device.
356
- # @see cp
357
- #
358
- # @param [Hash] o option
359
- # @option o [Boolean] :verbose
360
- # @option o [Boolean] :mkdir
361
- # @option o [Boolean] :fore
362
- # @return [nil]
363
- def mv(src_s, dest, o={}, &blk)
364
- srcs = glob(*Array.wrap(src_s)).map{|v| get(v)}
365
- dest = get(dest)
366
-
367
- if o[:mkdir] and (not File.exists?(dest))
368
- mkdir dest
369
- end
370
-
371
- # mv file1 file2 .. dir
372
- if srcs.size>1 and (not File.directory?(dest))
373
- raise Errno::ENOTDIR, "dest not a directory when mv more than one src -- #{dest}"
374
- end
375
-
376
- srcs.each do |src|
377
- dest1 = File.directory?(dest) ? File.join(dest, File.basename(src)) : dest
378
-
379
- if blk
380
- blk.call src, dest1, o, proc{_move(src, dest1, o)}
381
- else
382
- _move src, dest1, o
383
- end
384
-
385
- end
386
- end
387
-
388
- def mv_f src_s, dest, o={}, &blk
389
- o[:force] = true
390
- mv src_s, dest, o, &blk
391
- end
392
-
393
- # I'm recusive
394
- #
395
- # _move "file", "dir/file"
396
- #
397
- # @param [String] src
398
- # @param [String] dest
399
- def _move(src, dest, o)
400
- raise Errno::EEXIST, "dest exists -- #{dest}" if File.exists?(dest) and (not o[:force])
401
-
402
- # :force. mv "dir", "dira" and 'dira' exists and is a directory.
403
- if File.exists?(dest) and File.directory?(dest)
404
- ls(src) { |pa|
405
- dest1 = File.join(dest, File.basename(pa.p))
406
- _move pa.p, dest1, o
407
- }
408
- Pa.rm_r src
409
-
410
- else
411
- begin
412
- Pa.rm_r dest if o[:force] and File.exists?(dest)
413
- puts "rename #{src} #{dest}" if o[:verbose]
414
- File.rename(src, dest)
415
- rescue Errno::EXDEV # cross-device
416
- _copy(src, dest, o)
417
- Pa.rm_r src
418
- end
419
-
420
- end
421
- end # def _move
422
- private :_move
423
-
424
-
425
- end
12
+ module Cmd
13
+ extend Util::Concern
14
+ module ClassMethods
15
+ # link
16
+ #
17
+ # @overload ln(src, dest)
18
+ # @overload ln([src,..], directory)
19
+ #
20
+ # @param [Array<String>, String] src_s support globbing
21
+ # @param [String,Pa] dest
22
+ # @param [Hash] o option
23
+ # @option o [Boolean] :force overwrite if exists.
24
+ # @return [nil]
25
+ def ln(src_s, dest, o={})
26
+ _ln(File.method(:link), src_s, dest, o)
27
+ end
28
+
29
+ # ln force
30
+ #
31
+ # @see ln
32
+ # @return [nil]
33
+ def ln_f(src_s, dest, o)
34
+ o[:force]=true
35
+ _ln(File.method(:link), src_s, dest, o)
36
+ end
37
+
38
+ # symbol link
39
+ #
40
+ # @see ln
41
+ # @return [nil]
42
+ def symln(src_s, dest, o)
43
+ _ln(File.method(:symlink), src_s, dest, o)
44
+ end
45
+ alias symlink ln
46
+
47
+ # symln force
48
+ #
49
+ # @see ln
50
+ # @return [nil]
51
+ def symln_f(src_s, dest, o)
52
+ o[:force]=true
53
+ _ln(File.method(:symlink), src_s, dest, o)
54
+ end
55
+
56
+
57
+ # @see File.readlink
58
+ def readlink(path)
59
+ File.readlink(get(path))
60
+ end
61
+
62
+
63
+ # change directory
64
+ #
65
+ # @param [String,Pa] path
66
+ def cd(path=ENV["HOME"], &blk)
67
+ ::Dir.chdir(get(path), &blk)
68
+ end
69
+
70
+ # chroot
71
+ # @see {Dir.chroot}
72
+ #
73
+ # @param [String] path
74
+ # @return [nil]
75
+ def chroot(path)
76
+ ::Dir.chroot(get(path))
77
+ end
78
+
79
+ # touch a blank file
80
+ #
81
+ # @overload touch(*paths, o={})
82
+ # @param [String] *paths
83
+ # @param [Hash] o option
84
+ # @option o [Fixnum,String] :mode
85
+ # @option o [Boolean] :mkdir auto mkdir if path contained directory not exists.
86
+ # @option o [Boolean] :force
87
+ # @return [nil]
88
+ def touch(*args)
89
+ paths, o = Util.extract_options(args)
90
+ _touch(*paths, o)
91
+ end
92
+
93
+ # touch force
94
+ # @see touch
95
+ #
96
+ # @overload touch_f(*paths, o={})
97
+ # @return [nil]
98
+ def touch_f(*args)
99
+ paths, o = Util.extract_options(args)
100
+ o[:force]=true
101
+ _touch(*paths, o)
102
+ end
103
+
104
+ # make a directory
105
+ #
106
+ # @overload mkdir(*paths, o={})
107
+ # @param [String, Pa] *paths
108
+ # @param [Hash] o option
109
+ # @option o [Fixnum] :mode
110
+ # @option o [Boolean] :force
111
+ # @return [nil]
112
+ def mkdir(*args)
113
+ paths, o = Util.extract_options(args)
114
+ _mkdir(paths, o)
115
+ end
116
+
117
+ # mkdir force
118
+ # @see mkdir
119
+ #
120
+ # @overload mkdir_f(*paths, o={})
121
+ # @return [nil]
122
+ def mkdir_f(*args)
123
+ paths, o = Util.extract_options(args)
124
+ o[:force]=true
125
+ _mkdir(paths, o)
126
+ end
127
+
128
+ # make temp directory
129
+ #
130
+ # @param [Hash] o options
131
+ # @option o [Symbol] :prefix ("")
132
+ # @option o [Symbol] :suffix ("")
133
+ # @option o [Symbol] :tmpdir (ENV["TEMP"])
134
+ # @return [String] path
135
+ def mktmpdir(o={}, &blk)
136
+ p = _mktmpname(o)
137
+ File.mkdir(p)
138
+ begin blk.call(p) ensure ::Dir.delete(p) end if blk
139
+ p
140
+ end # def mktmpdir
141
+
142
+ def home(user=nil)
143
+ ::Dir.home
144
+ end
145
+
146
+ # make temp file
147
+ # @see mktmpdir
148
+ #
149
+ # @param [Hash] o options
150
+ # @return [String] path
151
+ def mktmpfile(o={}, &blk)
152
+ p = _mktmpname(o)
153
+ begin
154
+ blk.call(p)
155
+ ensure
156
+ File.delete(p)
157
+ end if blk
158
+ p
159
+ end # mktmpfile
160
+
161
+
162
+ # rm file only
163
+ #
164
+ # @param [String] *paths support globbing
165
+ # @return [nil]
166
+ def rm(*paths)
167
+ paths, o = Util.extract_options(paths)
168
+ glob(*paths) { |pa|
169
+ if File.directory?(pa.p)
170
+ if o[:force]
171
+ next
172
+ else
173
+ raise Errno::EISDIR, "is a directory -- #{pa.p}"
174
+ end
175
+ end
176
+ next if pa.directory?
177
+ File.delete(pa.p)
178
+ }
179
+ end
180
+
181
+ def rm_f(*paths)
182
+ paths, o = Util.extract_options(paths)
183
+ o[:force] = true
184
+ rm *paths, o
185
+ end
186
+
187
+ # rm directory only. still remove if directory is not empty.
188
+ #
189
+ # @param [String] *paths support globbing
190
+ # @return [nil]
191
+ def rmdir(*paths)
192
+ paths, o = Util.extract_options(paths)
193
+ glob(*paths) { |pa|
194
+ if not File.directory?(pa.p)
195
+ if o[:force]
196
+ next
197
+ else
198
+ raise Errno::ENOTDIR, "not a directory -- #{pa.p}"
199
+ end
200
+ end
201
+ _rmdir(pa)
202
+ }
203
+ end
204
+
205
+ def rmdir_f(*paths)
206
+ paths, o = Util.extract_options(paths)
207
+ o[:force] = true
208
+ rmdir *paths, o
209
+ end
210
+
211
+ # rm recusive, rm both file and directory
212
+ #
213
+ # @see rm
214
+ # @return [nil]
215
+ def rm_r(*paths)
216
+ glob(*paths){ |pa|
217
+ File.directory?(pa.p) ? _rmdir(pa) : File.delete(pa.p)
218
+ }
219
+ end
220
+ alias rm_rf rm_r
221
+
222
+ # rm_if(path) if condition is true
223
+ #
224
+ # @example
225
+ # Pa.rm_if '/tmp/**/*.rb' do |pa|
226
+ # pa.name == 'old'
227
+ # end
228
+ #
229
+ # @param [String] *paths support globbing
230
+ # @yield [path]
231
+ # @yieldparam [Pa] path
232
+ # @yieldreturn [Boolean] rm_r path if true
233
+ # @return [nil]
234
+ def rm_if(*paths, &blk)
235
+ glob(*paths) do |pa|
236
+ rm_r pa if blk.call(pa)
237
+ end
238
+ end
239
+
240
+ # copy
241
+ #
242
+ # cp file dir
243
+ # cp 'a', 'dir' #=> dir/a
244
+ # cp 'a', 'dir/a' #=> dir/a
245
+ #
246
+ # cp file1 file2 .. dir
247
+ # cp ['a','b'], 'dir' #=> dir/a dir/b
248
+ #
249
+ # @example
250
+ # cp '*', 'dir' do |src, dest, o|
251
+ # skip if src.name=~'.o$'
252
+ # dest.replace 'dirc' if src.name=="foo"
253
+ # yield # use yield to do the actuactal cp work
254
+ # end
255
+ #
256
+ # @overload cp(src_s, dest, o)
257
+ # @param [Array<String>, String] src_s support globbing
258
+ # @param [String,Pa] dest
259
+ # @param [Hash] o option
260
+ # @option o [Boolean] :mkdir mkdir(dest) if dest not exists.
261
+ # @option o [Boolean] :verbose puts cmd when execute
262
+ # @option o [Boolean] :folsymlink follow symlink
263
+ # @option o [Boolean] :force force dest file if dest is a file
264
+ # @option o [Boolean] :special special copy, when cp a directory, only mkdir, not cp the directory's content, usefull in Pa.each_r
265
+ # @return [nil]
266
+ # @overload cp(src_s, dest, o)
267
+ # @yield [src,dest,o]
268
+ # @return [nil]
269
+ def cp(src_s, dest, o={}, &blk)
270
+ srcs = glob(*Util.wrap_array(src_s)).map{|v| v.path}
271
+ dest = Pa.get(dest)
272
+
273
+ if o[:mkdir] and (not File.exists?(dest))
274
+ Pa.mkdir dest
275
+ end
276
+
277
+ # cp file1 file2 .. dir
278
+ if srcs.size>1 and (not File.directory?(dest))
279
+ raise Errno::ENOTDIR, "dest not a directory when cp more than one src -- #{dest}"
280
+ end
281
+
282
+ srcs.each do |src|
283
+ dest1 = File.directory?(dest) ? File.join(dest, File.basename(src)) : dest
284
+
285
+ if blk
286
+ blk.call src, dest1, o, proc{_copy(src, dest1, o)}
287
+ else
288
+ _copy src, dest1, o
289
+ end
290
+
291
+ end
292
+ end
293
+
294
+ def cp_f(src_s, dest, o={}, &blk)
295
+ o[:force] = true
296
+ cp src_s, dest, o, &blk
297
+ end
298
+
299
+
300
+ # a rename util
301
+ #
302
+ # @example
303
+ #
304
+ # Pa.rename('/home/guten.jpg') {|pa| pa.name+'_1'+pa.fext} # => '/home/guten_1.jpg'
305
+ # Pa('/home/guten.jpg').rename {|pa| pa.name+'_1'+pa.fext} # => <#Pa('/home/guten_1.jpg')>
306
+ #
307
+ # @param [String,Pa] src
308
+ # @yieldparam [Pa] pa
309
+ # @yieldreturn [String] fname
310
+ # @return [String,Pa] # Pa.rename return String. Pa#rename return Pa.
311
+ def rename(src, &blk)
312
+ src = Pa(src)
313
+ fname = blk.call(src)
314
+ src.dir.join(fname).path
315
+ end
316
+
317
+ # move, use rename for same device. and cp for cross device.
318
+ # @see cp
319
+ #
320
+ # @param [Hash] o option
321
+ # @option o [Boolean] :verbose
322
+ # @option o [Boolean] :mkdir
323
+ # @option o [Boolean] :fore
324
+ # @return [nil]
325
+ def mv(src_s, dest, o={}, &blk)
326
+ srcs = glob(*Util.wrap_array(src_s)).map{|v| get(v)}
327
+ dest = get(dest)
328
+
329
+ if o[:mkdir] and (not File.exists?(dest))
330
+ mkdir dest
331
+ end
332
+
333
+ # mv file1 file2 .. dir
334
+ if srcs.size>1 and (not File.directory?(dest))
335
+ raise Errno::ENOTDIR, "dest not a directory when mv more than one src -- #{dest}"
336
+ end
337
+
338
+ srcs.each do |src|
339
+ dest1 = File.directory?(dest) ? File.join(dest, File.basename(src)) : dest
340
+
341
+ if blk
342
+ blk.call src, dest1, o, proc{_move(src, dest1, o)}
343
+ else
344
+ _move src, dest1, o
345
+ end
346
+
347
+ end
348
+ end
349
+
350
+ def mv_f(src_s, dest, o={}, &blk)
351
+ o[:force] = true
352
+ mv src_s, dest, o, &blk
353
+ end
354
+
355
+ # I'm recusive
356
+ #
357
+ # _move "file", "dir/file"
358
+ #
359
+ # @param [String] src
360
+ # @param [String] dest
361
+ def _move(src, dest, o)
362
+ raise Errno::EEXIST, "dest exists -- #{dest}" if File.exists?(dest) and (not o[:force])
363
+
364
+ # :force. mv "dir", "dira" and 'dira' exists and is a directory.
365
+ if File.exists?(dest) and File.directory?(dest)
366
+ ls(src) { |pa|
367
+ dest1 = File.join(dest, File.basename(pa.p))
368
+ _move pa.p, dest1, o
369
+ }
370
+ Pa.rm_r src
371
+
372
+ else
373
+ begin
374
+ Pa.rm_r dest if o[:force] and File.exists?(dest)
375
+ puts "rename #{src} #{dest}" if o[:verbose]
376
+ File.rename(src, dest)
377
+ rescue Errno::EXDEV # cross-device
378
+ _copy(src, dest, o)
379
+ Pa.rm_r src
380
+ end
381
+
382
+ end
383
+ end # def _move
384
+
385
+ def _touch(paths, o)
386
+ o[:mode] ||= 0644
387
+ paths.map!{|v|get(v)}
388
+ paths.each {|path|
389
+ if File.exists?(path)
390
+ o[:force] ? next : raise(Errno::EEXIST, "File exist -- #{path}")
391
+ end
392
+
393
+ mkdir(File.dirname(p)) if o[:mkdir]
394
+
395
+ if win32?
396
+ # win32 BUG. must f.write("") then file can be deleted.
397
+ File.open(p, "w"){|f| f.chmod(o[:mode]); f.write("")}
398
+ else
399
+ File.open(p, "w"){|f| f.chmod(o[:mode])}
400
+ end
401
+ }
402
+ end
403
+
404
+ # @param [Array,String,#path] src_s
405
+ # @param [String,#path] dest
406
+ def _ln(method, src_s, dest, o={})
407
+ dest = get(dest)
408
+ glob(*Util.wrap_array(src_s)) {|src|
409
+ src = get(src)
410
+ dest = File.join(dest, File.basename(src)) if File.directory?(dest)
411
+ Pa.rm_r(dest) if o[:force] and File.exists?(dest)
412
+ method.call(src, dest)
413
+ }
414
+ end
415
+
416
+ def _mkdir(paths, o)
417
+ o[:mode] ||= 0744
418
+ paths.map!{|v|get(v)}
419
+ paths.each {|p|
420
+ if File.exists?(p)
421
+ o[:force] ? next : raise(Errno::EEXIST, "File exist -- #{p}")
422
+ end
423
+
424
+ stack = []
425
+ until p == stack.last
426
+ break if File.exists?(p)
427
+ stack << p
428
+ p = File.dirname(p)
429
+ end
430
+
431
+ stack.reverse.each do |path|
432
+ ::Dir.mkdir(path)
433
+ File.chmod(o[:mode], path)
434
+ end
435
+ }
436
+ end
437
+
438
+ def _mktmpname(o={})
439
+ # :prefix :suffix :tmpdir
440
+ # $$-(time*100_000).to_i.to_s(36)
441
+ # parse o
442
+ o[:dir] ||= ENV["TEMP"]
443
+ o[:prefix] ||= ""
444
+ o[:suffix] ||= ""
445
+
446
+ # begin
447
+ collision = 0
448
+ path = "#{o[:dir]}/#{o[:prefix]}#{$$}-#{(Time.time*100_000).to_i.to_s(36)}"
449
+ orgi_path = path.dup
450
+ while File.exists?(path)
451
+ path = orgi_path+ collision.to_s
452
+ collision +=1
453
+ end
454
+ path << o[:suffix]
455
+
456
+ path
457
+ end # def mktmpname
458
+
459
+ # I'm recusive
460
+ # param@ [Pa] path
461
+ def _rmdir(pa, o={})
462
+ return if not File.exists?(pa.p)
463
+ pa.each {|pa1|
464
+ File.directory?(pa1.p) ? _rmdir(pa1, o) : File.delete(pa1.p)
465
+ }
466
+ File.directory?(pa.p) ? ::Dir.rmdir(pa.p) : File.delete(pa.p)
467
+ end
468
+
469
+ # I'm recursive
470
+ #
471
+ # @param [String] src
472
+ # @param [String] dest
473
+ def _copy(src, dest, o={})
474
+ raise Errno::EEXIST, "dest exists -- #{dest}" if File.exists?(dest) and (not o[:force])
475
+
476
+ case type=File.ftype(src)
477
+
478
+ when "file", "socket"
479
+ puts "cp #{src} #{dest}" if o[:verbose]
480
+ File.copy_stream(src, dest)
481
+
482
+ when "directory"
483
+ begin
484
+ Pa.mkdir dest
485
+ puts "mkdir #{dest}" if o[:verbose]
486
+ rescue Errno::EEXIST
487
+ end
488
+
489
+ return if o[:special]
490
+
491
+ each(src) { |pa|
492
+ _copy(pa.p, File.join(dest, File.basename(pa.p)), o)
493
+ }
494
+
495
+ when "link" # symbol link
496
+ if o[:folsymlink]
497
+ _copy(Pa.readlink(src), dest)
498
+ else
499
+ Pa.symln(Pa.readlink(src), dest, force: true)
500
+ puts "symlink #{src} #{dest}" if o[:verbose]
501
+ end
502
+
503
+ when "unknow"
504
+ raise EUnKnownType, "Can't handle unknow type(#{:type}) -- #{src}"
505
+ end
506
+
507
+ # chmod chown utime
508
+ src_stat = o[:folsymlink] ? File.stat(src) : File.lstat(src)
509
+ begin
510
+ File.chmod(src_stat.mode, dest)
511
+ #File.chown(src_stat.uid, src_stat.gid, dest)
512
+ File.utime(src_stat.atime, src_stat.mtime, dest)
513
+ rescue Errno::ENOENT
514
+ end
515
+ end # _copy
516
+ end
517
+ end
426
518
  end