pa 1.1.4 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/pa.rb CHANGED
@@ -1,9 +1,13 @@
1
+ require "tmpdir"
2
+
1
3
  =begin rdoc
2
4
  Pa(Path) is similary to Pathname, but more powerful.
3
5
  it combines fileutils, tmpdir, find, tempfile, File, Dir, Pathname
4
6
 
5
7
  all class methods support Pa as parameter.
6
8
 
9
+ support "~/foo" path. Pa("~/foo") is "/home/x/foo"
10
+
7
11
  Examples:
8
12
  ---------
9
13
  pa = Pa('/home/a.vim')
@@ -71,52 +75,229 @@ class Pa
71
75
  EUnkonwType = Class.new Error
72
76
 
73
77
  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, &blk)
79
- return case ret
80
- when Array
81
- ret.map{|v| Pa(v)}
82
- when String
83
- Pa(ret)
78
+ DELEGATE_METHODS = [:join, :build]
79
+
80
+ # get path of an object.
81
+ #
82
+ # return obj#path if object has a 'path' instance method
83
+ #
84
+ # nil -> nil
85
+ #
86
+ #
87
+ # @param [String,#path] obj
88
+ # @return [String,nil] path
89
+ def get(obj)
90
+ if String === obj
91
+ obj
92
+ elsif obj.respond_to?(:path)
93
+ obj.path
94
+ elsif obj.nil?
95
+ nil
96
+ else
97
+ raise ArgumentError, "Pa.get() not support type -- #{obj.inspect}(#{obj.class})"
98
+ end
99
+ end
100
+
101
+ # split path
102
+ #
103
+ # @example
104
+ # path="/home/a/file"
105
+ # split2(path) #=> "/home/a", "file"
106
+ # split2(path, :all => true) #=> "/", "home", "a", "file"
107
+ #
108
+ # @param [String,Pa] name
109
+ # @param [Hash] o option
110
+ # @option o [Boolean] :all split all parts
111
+ # @return [Array<String>]
112
+ def split2(name, o={})
113
+ dir, fname = File.split(get(name))
114
+ ret = Util.wrap_array(File.basename(fname))
115
+
116
+ if o[:all]
117
+ loop do
118
+ dir1, fname = File.split(dir)
119
+ break if dir1 == dir
120
+ ret.unshift fname
121
+ dir = dir1
84
122
  end
85
123
  end
124
+ ret.unshift dir
125
+ ret
126
+ end
127
+
128
+ # special case
129
+ def split(*args)
130
+ dir, *names = split2(*args)
131
+ [ Pa(dir), *names]
132
+ end
133
+
134
+ # join paths, skip nil and empty string.
135
+ #
136
+ # @param [*Array<String>] *paths
137
+ # @return [String]
138
+ def join2(*paths)
139
+ paths.map!{|v|get(v)}
140
+
141
+ # skip nil
142
+ paths.compact!
143
+ # skip empty string
144
+ paths.delete("")
86
145
 
87
- raise NoMethodError, "no method -- #{name}"
146
+ File.join(*paths)
147
+ end
148
+
149
+ # build a path
150
+ # options :path, :dir, :fname, :base, :name, :fext, :ext
151
+ # use Pa.join2
152
+ # @example
153
+ #
154
+ # Pa.build2(dir: "/home", name: "guten", ext: "avi") => "/home/guten.avi
155
+ # Pa.build2("/home/guten.avi") { |pa| "#{pa.dir}/foo.#{pa.ext}" } => "/home/foo.avi
156
+ #
157
+ # @overload build2(path){|pa|=>String}
158
+ # @overload build2(data={})
159
+ # @overload build2(data={}){}
160
+ def build2(*args, &blk)
161
+ data = Hash===args.last ? args.pop : {}
162
+ path = args[0] || build_path2(data)
163
+ blk ||= proc {|pa| pa.p }
164
+
165
+ blk.call(Pa(path))
166
+ end
167
+
168
+ DELEGATE_METHODS.each { |mth|
169
+ class_eval <<-EOF
170
+ def #{mth}(*args, &blk)
171
+ Pa(Pa.#{mth}2(*args, &blk))
172
+ end
173
+ EOF
174
+ }
175
+
176
+ private
177
+
178
+ # wrap result to Pa
179
+ def _wrap(obj)
180
+ case obj
181
+ when Array
182
+ obj.map{|v| Pa(v)}
183
+ when String
184
+ Pa(obj)
185
+ end
186
+ end
187
+
188
+ def build_path2(data={})
189
+ if data[:path]
190
+ path = data[:path]
191
+ elsif data[:fname] || data[:base]
192
+ path = join2(data[:dir], data[:fname] || data[:base])
193
+ else
194
+ path = join2(data[:dir], data[:name])
195
+ if data[:fext]
196
+ path << data[:fext]
197
+ elsif data[:ext]
198
+ path << ".#{data[:ext]}"
199
+ end
200
+ end
201
+
202
+ path
88
203
  end
89
204
  end
90
205
 
91
- attr_reader :path
92
- alias p path
93
- alias path2 path
94
- alias p2 path
206
+ DELEGATE_METHODS2 = [ :join2 ]
207
+ DELEGATE_METHODS = [ :dir, :build, :join ]
95
208
 
96
- # @param [String,#path] path
209
+ attr_reader :path2
210
+ attr_reader :absolute2, :dir2, :dir_strict2, :base2, :fname2, :name2, :short2, :ext2, :fext2
211
+
212
+ # @param [String, #path] path
97
213
  def initialize(path)
98
- @path = path.respond_to?(:path) ? path.path : path
214
+ # convert ~ to ENV["HOME"]
215
+ @path2 = Pa.get(path)
216
+ @path2.sub!(/^~/, ENV["HOME"]) if @path2 # nil
217
+
99
218
  initialize_variables
100
- end
219
+ end
101
220
 
102
221
  chainable = Module.new do
103
222
  def initialize_variables; end
104
223
  end
105
224
  include chainable
106
225
 
107
- # @param [String,#path]
108
- # @return [Pa] the same Pa object
109
- def replace(path)
110
- @path = path.respond_to?(:path) ? path.path : path
111
- initialize_variables
112
- end
226
+ def absolute2
227
+ @absolute2 ||= File.absolute_path(path)
228
+ end
229
+
230
+ # => ".", "..", "/", "c:"
231
+ def dir2
232
+ @dir2 ||= File.dirname(path)
233
+ end
234
+
235
+ # Pa("foo") => ""
236
+ # Pa("./foo") => "."
237
+ def dir_strict2
238
+ return @dir_strict2 if @dir_strict2
239
+
240
+ dir = File.dirname(path)
241
+
242
+ @dir_strict2 = if %w[. ..].include?(dir) && path !~ %r!^\.\.?/!
243
+ ""
244
+ else
245
+ dir
246
+ end
247
+ end
248
+
249
+ def base2
250
+ @base2 ||= File.basename(path)
251
+ end
252
+
253
+ def name2
254
+ @name2 ||= File.basename(path).match(/^(.+?)(?:\.([^.]+))?$/)[1]
255
+ end
256
+
257
+ # => "ogg", ""
258
+ def ext2
259
+ @ext2 ||= File.basename(path).match(/^(.+?)(?:\.([^.]+))?$/)[2] || ""
260
+ end
261
+
262
+ # => ".ogg", ""
263
+ def fext2
264
+ @fext2 ||= ext2.empty? ? "" : ".#{ext2}"
265
+ end
266
+
267
+ alias fname2 base2
268
+
269
+ # both x, x2 return String
270
+ alias path path2
271
+ alias base base2
272
+ alias fname fname2
273
+ alias name name2
274
+ alias ext ext2
275
+ alias fext fext2
276
+
277
+ # abbretive
278
+ alias p2 path2
279
+ alias p2 path
280
+ alias a2 absolute2
281
+ alias d2 dir2
282
+ alias d_s2 dir_strict2
283
+ alias b2 base2
284
+ alias n2 name2
285
+ alias fn2 fname2
286
+ alias e2 ext2
287
+ alias fe2 fext2
288
+ alias p path
289
+ alias b base
290
+ alias fn fname
291
+ alias n name
292
+ alias e ext
293
+ alias fe fext
113
294
 
114
295
  # return '#<Pa @path="foo", @absolute="/home/foo">'
115
296
  #
116
297
  # @return [String]
117
298
  def inspect
118
299
  ret="#<" + self.class.to_s + " "
119
- ret += "@path=\"#{path}\", @absolute=\"#{absolute}\""
300
+ ret += "@path=\"#{path}\", @absolute2=\"#{absolute2}\""
120
301
  ret += " >"
121
302
  ret
122
303
  end
@@ -125,25 +306,124 @@ class Pa
125
306
  #
126
307
  # @return [String] path
127
308
  def to_s
128
- @path
309
+ path
129
310
  end
130
311
 
131
- # missing method goes to Pa.class-method
132
- def method_missing(name, *args, &blk)
133
- self.class.__send__(name, path, *args, &blk)
312
+ # @param [String,#path]
313
+ # @return [Pa] the same Pa object
314
+ def replace(path)
315
+ @path2 = Pa.get(path)
316
+ initialize_variables
134
317
  end
135
318
 
136
- def <=> other
137
- other_path = if other.respond_to?(:path)
138
- other.path
139
- elsif String === other
140
- other
141
- else
142
- raise Error, "not support type -- #{other.class}"
143
- end
319
+ def ==(other)
320
+ case other
321
+ when Pa
322
+ self.path == other.path
323
+ else
324
+ false
325
+ end
326
+ end
144
327
 
145
- path <=> other_path
328
+ def <=>(other)
329
+ path <=> Pa.get(other)
146
330
  end
331
+
332
+ def =~(regexp)
333
+ path =~ regexp
334
+ end
335
+
336
+ # add string to path
337
+ #
338
+ # @example
339
+ # pa = Pa('/home/foo/a.txt')
340
+ # pa+'~' #=> new Pa('/home/foo/a.txt~')
341
+ #
342
+ # @param [String] str
343
+ # @return [Pa]
344
+ def +(str)
345
+ Pa(path+str)
346
+ end
347
+
348
+ def short2
349
+ @short2 ||= Pa.shorten2(@path)
350
+ end
351
+
352
+ # @return [String]
353
+ def sub2(*args, &blk)
354
+ path.sub(*args, &blk)
355
+ end
356
+
357
+ # @return [String]
358
+ def gsub2(*args, &blk)
359
+ path.gsub(*args, &blk)
360
+ end
361
+
362
+ # @return [Pa]
363
+ def sub(*args, &blk)
364
+ Pa(sub2(*args, &blk))
365
+ end
366
+
367
+ # @return [Pa]
368
+ def gsub(*args, &blk)
369
+ Pa(gsub2(*args, &blk))
370
+ end
371
+
372
+ # @return [Pa]
373
+ def sub!(*args,&blk)
374
+ self.replace path.sub(*args,&blk)
375
+ end
376
+
377
+ # @return [Pa]
378
+ def gsub!(*args,&blk)
379
+ self.replace path.gsub(*args,&blk)
380
+ end
381
+
382
+ # @return [MatchData]
383
+ def match(*args,&blk)
384
+ path.match(*args,&blk)
385
+ end
386
+
387
+ # @return [Boolean]
388
+ def start_with?(*args)
389
+ path.start_with?(*args)
390
+ end
391
+
392
+ # @return [Boolean]
393
+ def end_with?(*args)
394
+ path.end_with?(*args)
395
+ end
396
+
397
+ # @return [String]
398
+ def build2(data={}, &blk)
399
+ return Pa.new(blk.call(self)) if blk
400
+
401
+ d = if data[:path]
402
+ {path: data[:path]}
403
+ elsif data[:fname] || data[:base]
404
+ {dir: dir_strict2, fname: data[:fname], base: data[:base]}
405
+ else
406
+ {dir: dir_strict2, name: name2, ext: ext2}.merge(data)
407
+ end
408
+
409
+ Pa.build2(d)
410
+ end
411
+
412
+ DELEGATE_METHODS2.each { |mth2|
413
+ class_eval <<-EOF
414
+ def #{mth2}(*args, &blk)
415
+ Pa.#{mth2}(path, *args, &blk)
416
+ end
417
+ EOF
418
+ }
419
+
420
+ DELEGATE_METHODS.each {|mth|
421
+ class_eval <<-EOF
422
+ def #{mth}(*args, &blk)
423
+ Pa(#{mth}2(*args, &blk))
424
+ end
425
+ EOF
426
+ }
147
427
  end
148
428
 
149
429
  require "pa/path"
data/spec/pa/cmd_spec.rb CHANGED
@@ -3,7 +3,7 @@ require "fileutils"
3
3
  require "tmpdir"
4
4
 
5
5
  module Pa::Cmd::ClassMethods
6
- public :_copy, :_touch, :_mkdir, :_mktmpname, :_rmdir, :_copy, :_move
6
+ public :_copy, :_touch, :_mkdir, :_mktmpname, :_rmdir, :_copy, :_move, :_ln
7
7
  end
8
8
 
9
9
  describe Pa do
@@ -12,12 +12,27 @@ describe Pa do
12
12
  @tmpdir = Dir.mktmpdir
13
13
  Dir.chdir(@tmpdir)
14
14
  end
15
-
16
- after(:all) do
15
+ after :all do
17
16
  Dir.chdir(@curdir)
18
17
  FileUtils.rm_r @tmpdir
19
18
  end
20
19
 
20
+ describe "#_ln" do
21
+ # lna
22
+ before :all do
23
+ FileUtils.touch(%w[lna])
24
+ end
25
+
26
+ it "works" do
27
+ output = capture :stdout do
28
+ Pa._ln(:link, "lna", "lnb", :verbose => true)
29
+ end
30
+
31
+ output.should == "ln lna lnb\n"
32
+ File.identical?("lna", "lnb").should be_true
33
+ end
34
+ end
35
+
21
36
  describe "#_rmdir" do
22
37
  # dir/
23
38
  # a
@@ -138,7 +153,6 @@ describe Pa do
138
153
  end
139
154
 
140
155
  context "with :symlink" do
141
-
142
156
  it "_copy" do
143
157
  Pa._copy 'symfile', 'symfilea'
144
158
  File.symlink?('symfilea').should be_true
@@ -269,18 +283,37 @@ describe Pa do
269
283
  end
270
284
  end
271
285
 
272
- describe ".rename2" do
273
- it "works" do
274
- a = Pa.rename2('/home/guten.jpg') {|pa| pa.name+'_1'+pa.fext }
275
- a.should == '/home/guten_1.jpg'
276
- end
277
- end
286
+ describe "#_mktmpname" do
287
+ it "works" do
288
+ path = Pa._mktmpname("foo", :tmpdir => "guten")
278
289
 
279
- describe "#rename" do
280
- it "works" do
281
- Pa('/home/guten.jpg').rename {|pa|
282
- pa.name+'_1'+pa.fext
283
- }.should == Pa('/home/guten_1.jpg')
284
- end
285
- end
290
+ path.should =~ %r~guten/foo\..{6}~
291
+ end
292
+ end
293
+
294
+ describe "#mktmpdir" do
295
+ it "works" do
296
+ File.should_receive(:mkdir)
297
+
298
+ path = Pa.mktmpdir("foo")
299
+
300
+ path.should =~ %r~#{Regexp.escape(ENV["TEMP"])}/foo~
301
+ end
302
+ end
303
+
304
+ describe "#mktmpfile2" do
305
+ it "works" do
306
+ path = Pa.mktmpfile2 :tmpdir => "foo"
307
+
308
+ path.should =~ %r~foo/#{$$}~
309
+ end
310
+ end
311
+
312
+ describe "#mktmpfile" do
313
+ it "works" do
314
+ path = Pa.mktmpfile
315
+
316
+ path.should be_an_instance_of(Pa)
317
+ end
318
+ end
286
319
  end
@@ -15,12 +15,24 @@ describe Pa do
15
15
  Dir.chdir(@tmpdir)
16
16
  end
17
17
 
18
- after(:all) do
18
+ after :all do
19
19
  Dir.chdir(@curdir)
20
20
  FileUtils.rm_r @tmpdir
21
21
  end
22
22
 
23
- describe "#glob2" do
23
+ describe ".tmpdir2" do
24
+ it "works" do
25
+ Pa.tmpdir2.should == Dir.tmpdir
26
+ end
27
+ end
28
+
29
+ describe ".tmpdir" do
30
+ it "works" do
31
+ Pa.tmpdir.should == Pa(Dir.tmpdir)
32
+ end
33
+ end
34
+
35
+ describe ".glob2" do
24
36
  before(:each) do
25
37
  @files = %w(fa .fa)
26
38
  FileUtils.touch(@files)
@@ -42,7 +54,7 @@ describe Pa do
42
54
  end
43
55
  end
44
56
 
45
- describe "#each2" do
57
+ describe ".each2" do
46
58
  # fa .fa fa~
47
59
  # dira/
48
60
  # dirb/
@@ -58,7 +70,7 @@ describe Pa do
58
70
  FileUtils.rm_r @dirs
59
71
  end
60
72
 
61
- it "runs on" do
73
+ it "works" do
62
74
  ret = []
63
75
  Pa.each2{|pa| ret << pa}
64
76
  ret.sort.should == %w(.fa dira fa fa~)
@@ -80,10 +92,19 @@ describe Pa do
80
92
  Pa.each2.with_object([]){|(pa),m| m<<pa}.sort.should == %w(.fa dira fa fa~)
81
93
  end
82
94
 
83
- it "each2(nodot: true) -> list all files except dot file" do
84
- Pa.each2(nodot: true).with_object([]){|(pa),m|m<<pa}.sort.should == %w(dira fa fa~)
95
+ it ".each2 with :dot => false -> list all files except dot file" do
96
+ Pa.each2(:dot => false).with_object([]){|(pa),m|m<<pa}.sort.should == %w[dira fa fa~]
85
97
  end
86
98
 
99
+ it ".each2 with :backup => false" do
100
+ Pa.each2(:backup => false).with_object([]){|(pa),m|m<<pa}.sort.should == %w[.fa dira fa]
101
+ end
102
+
103
+ it ".each2 with :absolute => true" do
104
+ b = %w[.fa dira fa fa~].map{|v| File.join(Dir.pwd, v)}
105
+ Pa.each2(:absolute => true).with_object([]){|(pa),m|m<<pa}.sort.should == b
106
+ end
107
+
87
108
  it "each returns Pa" do
88
109
  Pa.each { |pa|
89
110
  pa.should be_an_instance_of Pa
@@ -92,7 +113,35 @@ describe Pa do
92
113
  end
93
114
  end
94
115
 
95
- describe "#each2_r" do
116
+ describe ".each" do
117
+ # fa .fa fa~
118
+ # dira/
119
+ # dirb/
120
+ # b
121
+ before(:each) do
122
+ @dirs = %w(dira/dirb)
123
+ @files = %w(fa .fa fa~ dira/dirb/b)
124
+ FileUtils.mkdir_p(@dirs)
125
+ FileUtils.touch(@files)
126
+ end
127
+
128
+ after(:each) do
129
+ FileUtils.rm @files
130
+ FileUtils.rm_r @dirs
131
+ end
132
+
133
+ it "works" do
134
+ ret = []
135
+ Pa.each{|pa| ret << pa.p }
136
+ ret.sort.should == %w(.fa dira fa fa~)
137
+ end
138
+
139
+ it "return a Enumerator when call without block" do
140
+ Pa.each.should be_an_instance_of Enumerator
141
+ end
142
+ end
143
+
144
+ describe ".each2_r" do
96
145
  # fa .fa fa~
97
146
  # dira/
98
147
  # dirb/
@@ -110,9 +159,14 @@ describe Pa do
110
159
 
111
160
  it "each2_r -> Enumerator" do
112
161
  Pa.each2_r.should be_an_instance_of Enumerator
113
- Pa.each2_r.with_object([]){|(pa,r),m|m<<r}.sort.should == %w(.fa dira dira/dirb dira/dirb/b fa fa~)
162
+ Pa.each2_r.with_object([]){|(pa,r),m|m<<r}.sort.should == %w[.fa dira dira/dirb dira/dirb/b fa fa~]
114
163
  end
115
164
 
165
+ it "with :absolute => true" do
166
+ Pa.each2_r(:absolute => true).to_a[0][0].should == File.join(Dir.pwd, "fa~")
167
+ end
168
+
169
+
116
170
  it "#each_r returns Pa" do
117
171
  Pa.each_r { |pa|
118
172
  pa.should be_an_instance_of Pa
@@ -121,7 +175,36 @@ describe Pa do
121
175
  end
122
176
  end
123
177
 
124
- describe "#ls2" do
178
+ describe ".ls2" do
179
+ # filea
180
+ # dira/
181
+ # fileb
182
+ before(:each) do
183
+ @dirs = %w(dira)
184
+ @files = %w(filea dira/fileb)
185
+ FileUtils.mkdir_p(@dirs)
186
+ FileUtils.touch(@files)
187
+ end
188
+ after(:each) do
189
+ FileUtils.rm @files
190
+ FileUtils.rm_r @dirs
191
+ end
192
+
193
+ it "works" do
194
+ Pa.ls2.should == %w[filea dira]
195
+ Pa.ls2(Dir.pwd).should == %w[filea dira]
196
+ end
197
+
198
+ it "with :absolute => true" do
199
+ Pa.ls2(:absolute => true).should == %w[filea dira].map{|v| File.join(Dir.pwd, v)}
200
+ end
201
+
202
+ it "call a block" do
203
+ Pa.ls2 { |p, fn| File.directory?(p) }.should == ["dira"]
204
+ end
205
+ end
206
+
207
+ describe ".ls" do
125
208
  # filea
126
209
  # dira/
127
210
  # fileb
@@ -136,17 +219,25 @@ describe Pa do
136
219
  FileUtils.rm_r @dirs
137
220
  end
138
221
 
139
- it "runs ok -> Array" do
140
- Pa.ls2.should == ["filea", "dira"]
222
+ it "works" do
223
+ Pa.ls.should == %w[filea dira].map{|v| Pa(v)}
224
+ Pa.ls(Dir.pwd).should == %w[filea dira].map{|v| Pa(v)}
141
225
  end
142
226
 
227
+ it "with :absolute => true" do
228
+ Pa.ls(:absolute => true).should == %w[filea dira].map{|v| Pa(File.join(Dir.pwd, v))}
229
+ end
230
+
143
231
  it "call a block" do
144
- Pa.ls2 { |pa, fname| File.directory?(pa) }.should == ["dira"]
232
+ Pa.ls{|p, fn| p.directory? }.should == %w[dira].map{|v| Pa(v)}
145
233
  end
234
+ end
146
235
 
147
- it "#ls returns string" do
148
- Pa.ls[0].should be_an_instance_of String
236
+ describe "instance DELEGATE_METHODS" do
237
+ it "works" do
238
+ Pa.should_receive(:each2).with("x", 1, 2)
239
+
240
+ Pa("x").each2(1,2)
149
241
  end
150
- end
242
+ end
151
243
  end
152
-