pa 1.1.4 → 1.2.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/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
-