filepath 0.2 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -2
- data/Rakefile +1 -1
- data/lib/filepath.rb +52 -18
- data/lib/filepathlist.rb +88 -16
- data/spec/filepath_spec.rb +186 -34
- data/spec/filepathlist_spec.rb +149 -1
- data/spec/spec_helper.rb +1 -0
- metadata +6 -6
data/README.md
CHANGED
@@ -16,7 +16,7 @@ Features and examples
|
|
16
16
|
|
17
17
|
The main purpose of FilePath is to able to write
|
18
18
|
|
19
|
-
require
|
19
|
+
require __FILE__.as_path / 'spec' / 'tasks'
|
20
20
|
|
21
21
|
instad of cumbersome code like
|
22
22
|
|
@@ -48,7 +48,7 @@ The main features of FilePath are…
|
|
48
48
|
|
49
49
|
converted_img = image.replace_extension("jpeg")
|
50
50
|
converted_img.to_s #=> "/home/gioele/Documents/images/cat.jpeg"
|
51
|
-
convert(image
|
51
|
+
convert(image, converted_img)
|
52
52
|
|
53
53
|
### Path traversal
|
54
54
|
|
data/Rakefile
CHANGED
data/lib/filepath.rb
CHANGED
@@ -12,7 +12,7 @@ class FilePath
|
|
12
12
|
elsif path.is_a? Array
|
13
13
|
@fragments = path
|
14
14
|
else
|
15
|
-
@fragments = split_path_string(path.
|
15
|
+
@fragments = split_path_string(path.to_str)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -100,7 +100,7 @@ class FilePath
|
|
100
100
|
msg = "cannot compare: "
|
101
101
|
msg += "`#{self}` is #{self_abs} while "
|
102
102
|
msg += "`#{base}` is #{base_abs}"
|
103
|
-
raise msg
|
103
|
+
raise ArgumentError, msg
|
104
104
|
end
|
105
105
|
|
106
106
|
self_frags = self.normalized_fragments
|
@@ -231,7 +231,7 @@ class FilePath
|
|
231
231
|
end
|
232
232
|
end
|
233
233
|
|
234
|
-
alias ext? extension?
|
234
|
+
alias :ext? :extension?
|
235
235
|
|
236
236
|
|
237
237
|
# @overload replace_extension(new_ext)
|
@@ -338,6 +338,7 @@ class FilePath
|
|
338
338
|
def normalized
|
339
339
|
return FilePath.join(self.normalized_fragments)
|
340
340
|
end
|
341
|
+
|
341
342
|
alias :normalised :normalized
|
342
343
|
|
343
344
|
|
@@ -379,7 +380,7 @@ class FilePath
|
|
379
380
|
# @return [String] this path converted to a String
|
380
381
|
|
381
382
|
def to_raw_string
|
382
|
-
|
383
|
+
@to_raw_string ||= join_fragments(@fragments)
|
383
384
|
end
|
384
385
|
|
385
386
|
alias :to_raw_str :to_raw_string
|
@@ -390,7 +391,12 @@ class FilePath
|
|
390
391
|
# @note this method operates on the normalized path
|
391
392
|
|
392
393
|
def to_s
|
393
|
-
|
394
|
+
to_str
|
395
|
+
end
|
396
|
+
|
397
|
+
|
398
|
+
def to_str
|
399
|
+
@to_str ||= join_fragments(self.normalized_fragments)
|
394
400
|
end
|
395
401
|
|
396
402
|
|
@@ -405,7 +411,21 @@ class FilePath
|
|
405
411
|
end
|
406
412
|
|
407
413
|
def ==(other)
|
408
|
-
return self.
|
414
|
+
return self.normalized_fragments == other.as_path.normalized_fragments
|
415
|
+
end
|
416
|
+
|
417
|
+
def eql?(other)
|
418
|
+
if self.equal?(other)
|
419
|
+
return true
|
420
|
+
elsif self.class != other.class
|
421
|
+
return false
|
422
|
+
end
|
423
|
+
|
424
|
+
return self.fragments == other.fragments
|
425
|
+
end
|
426
|
+
|
427
|
+
def hash
|
428
|
+
return self.fragments.hash
|
409
429
|
end
|
410
430
|
|
411
431
|
# @private
|
@@ -425,7 +445,7 @@ class FilePath
|
|
425
445
|
|
426
446
|
# @private
|
427
447
|
def normalized_fragments
|
428
|
-
normalized_relative_frags(self.fragments)
|
448
|
+
@normalized_fragments ||= normalized_relative_frags(self.fragments)
|
429
449
|
end
|
430
450
|
|
431
451
|
# @private
|
@@ -453,19 +473,32 @@ class FilePath
|
|
453
473
|
return frags
|
454
474
|
end
|
455
475
|
|
476
|
+
# @private
|
477
|
+
def join_fragments(frags)
|
478
|
+
# FIXME: windows, mac
|
479
|
+
# FIXME: avoid string substitutions and regexen
|
480
|
+
return frags.join(SEPARATOR).sub(%r{^//}, SEPARATOR).sub(/\A\Z/, '.')
|
481
|
+
end
|
482
|
+
|
456
483
|
module PathResolution
|
457
484
|
def absolute_path(base_dir = Dir.pwd) # FIXME: rename to `#absolute`?
|
458
|
-
|
459
|
-
self
|
460
|
-
else
|
461
|
-
base_dir.as_path / self
|
485
|
+
if self.absolute?
|
486
|
+
return self
|
462
487
|
end
|
463
488
|
|
489
|
+
return base_dir.as_path / self
|
490
|
+
end
|
491
|
+
|
492
|
+
def real_path(base_dir = Dir.pwd)
|
493
|
+
path = absolute_path(base_dir)
|
494
|
+
|
464
495
|
return path.resolve_link
|
465
496
|
end
|
466
497
|
|
498
|
+
alias :realpath :real_path
|
499
|
+
|
467
500
|
def resolve_link
|
468
|
-
return File.readlink(self
|
501
|
+
return File.readlink(self).as_path
|
469
502
|
end
|
470
503
|
end
|
471
504
|
|
@@ -474,7 +507,7 @@ class FilePath
|
|
474
507
|
def self.define_filetest_method(filepath_method, filetest_method = nil)
|
475
508
|
filetest_method ||= filepath_method
|
476
509
|
define_method(filepath_method) do
|
477
|
-
return FileTest.send(filetest_method, self
|
510
|
+
return FileTest.send(filetest_method, self)
|
478
511
|
end
|
479
512
|
end
|
480
513
|
|
@@ -502,27 +535,28 @@ class FilePath
|
|
502
535
|
alias :zero? :empty?
|
503
536
|
|
504
537
|
def hidden?
|
505
|
-
@fragments.last.start_with('.') # FIXME: windows, mac
|
538
|
+
@fragments.last.start_with?('.') # FIXME: windows, mac
|
506
539
|
end
|
507
540
|
end
|
508
541
|
|
509
542
|
module FileManipulationMethods
|
510
543
|
def open(*args, &block)
|
511
|
-
File.open(self
|
544
|
+
File.open(self, *args, &block)
|
512
545
|
end
|
513
546
|
|
514
547
|
def touch
|
515
|
-
self.open do ; end
|
548
|
+
self.open('a') do ; end
|
549
|
+
File.utime(File.atime(self), Time.now, self)
|
516
550
|
end
|
517
551
|
end
|
518
552
|
|
519
553
|
module DirectoryMethods
|
520
554
|
def entries(pattern = '*')
|
521
555
|
if !self.directory?
|
522
|
-
raise Errno::ENOTDIR.new(self
|
556
|
+
raise Errno::ENOTDIR.new(self)
|
523
557
|
end
|
524
558
|
|
525
|
-
raw_entries = Dir.glob((self / pattern)
|
559
|
+
raw_entries = Dir.glob((self / pattern))
|
526
560
|
entries = FilePathList.new(raw_entries)
|
527
561
|
|
528
562
|
return entries
|
data/lib/filepathlist.rb
CHANGED
@@ -4,9 +4,11 @@
|
|
4
4
|
class FilePathList
|
5
5
|
include Enumerable
|
6
6
|
|
7
|
+
SEPARATOR = ':'.freeze
|
8
|
+
|
7
9
|
def initialize(raw_entries = nil)
|
8
10
|
raw_entries ||= []
|
9
|
-
@entries = raw_entries.map { |e|
|
11
|
+
@entries = raw_entries.map { |e| e.as_path }
|
10
12
|
end
|
11
13
|
|
12
14
|
def select_entries(type)
|
@@ -26,11 +28,6 @@ class FilePathList
|
|
26
28
|
return select_entries(:directory)
|
27
29
|
end
|
28
30
|
|
29
|
-
def exclude(pattern) # FIXME: block
|
30
|
-
raw_entries = @entries.delete_if { |e| e =~ pattern }
|
31
|
-
return FilePathList.new(raw_entries)
|
32
|
-
end
|
33
|
-
|
34
31
|
def /(extra_path)
|
35
32
|
return self.map { |path| path / extra_path }
|
36
33
|
end
|
@@ -39,7 +36,19 @@ class FilePathList
|
|
39
36
|
return FilePathList.new(@entries + extra_entries.to_a)
|
40
37
|
end
|
41
38
|
|
42
|
-
def
|
39
|
+
def -(others) # FIXME: block
|
40
|
+
if others.is_a? Regexp # FIXME: support any object that responds to =~
|
41
|
+
remaining_entries = @entries.delete_if { |e| e =~ others }
|
42
|
+
else
|
43
|
+
remaining_entries = @entries - others.as_path_list.to_a
|
44
|
+
end
|
45
|
+
|
46
|
+
return FilePathList.new(remaining_entries)
|
47
|
+
end
|
48
|
+
alias :exclude :-
|
49
|
+
|
50
|
+
def <<(extra_path)
|
51
|
+
return FilePathList.new(@entries + [extra_path.as_path])
|
43
52
|
end
|
44
53
|
|
45
54
|
def *(other_list)
|
@@ -51,24 +60,87 @@ class FilePathList
|
|
51
60
|
return FilePathList.new(paths)
|
52
61
|
end
|
53
62
|
|
54
|
-
|
55
|
-
|
56
|
-
|
63
|
+
def remove_common_fragments
|
64
|
+
all_frags = @entries.map(&:fragments)
|
65
|
+
max_length = all_frags.map(&:length).min
|
66
|
+
|
67
|
+
idx_different = nil
|
68
|
+
|
69
|
+
(0..max_length).each do |i|
|
70
|
+
fragment = all_frags.first[i]
|
71
|
+
|
72
|
+
different = all_frags.any? { |frags| frags[i] != fragment }
|
73
|
+
if different
|
74
|
+
idx_different = i
|
75
|
+
break
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
idx_different ||= max_length
|
80
|
+
|
81
|
+
remaining_frags = all_frags.map { |frags| frags[idx_different..-1] }
|
82
|
+
|
83
|
+
return FilePathList.new(remaining_frags)
|
57
84
|
end
|
58
85
|
|
59
|
-
|
60
|
-
|
86
|
+
# @return [FilePathList] the path list itself
|
87
|
+
|
88
|
+
def as_path_list
|
89
|
+
self
|
61
90
|
end
|
62
91
|
|
63
|
-
def
|
64
|
-
@entries
|
92
|
+
def to_a
|
93
|
+
@entries
|
65
94
|
end
|
66
95
|
|
67
|
-
def
|
68
|
-
@entries.map(
|
96
|
+
def to_s
|
97
|
+
@to_s ||= @entries.map(&:to_str).join(SEPARATOR)
|
69
98
|
end
|
70
99
|
|
71
100
|
def inspect
|
72
101
|
@entries.inspect
|
73
102
|
end
|
103
|
+
|
104
|
+
def ==(other)
|
105
|
+
@entries == other.as_path_list.to_a
|
106
|
+
end
|
107
|
+
|
108
|
+
module ArrayMethods
|
109
|
+
def self.define_array_method(name)
|
110
|
+
define_method(name) do |*args, &block|
|
111
|
+
return @entries.send(name, *args, &block)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
define_array_method :[]
|
116
|
+
|
117
|
+
define_array_method :empty?
|
118
|
+
|
119
|
+
define_array_method :include?
|
120
|
+
|
121
|
+
define_array_method :each
|
122
|
+
|
123
|
+
define_array_method :map
|
124
|
+
end
|
125
|
+
|
126
|
+
include ArrayMethods
|
127
|
+
end
|
128
|
+
|
129
|
+
class Array
|
130
|
+
# Generates a path list from an array of paths.
|
131
|
+
#
|
132
|
+
# The elements of the array must respond to `#as_path`.
|
133
|
+
#
|
134
|
+
# `ary.as_path` is equivalent to `FilePathList.new(ary)`.
|
135
|
+
#
|
136
|
+
# @return [FilePathList] a new path list containing the elements of
|
137
|
+
# the array as FilePaths
|
138
|
+
#
|
139
|
+
# @see String#as_path
|
140
|
+
# @see Array#as_path
|
141
|
+
# @see FilePath#as_path
|
142
|
+
|
143
|
+
def as_path_list
|
144
|
+
FilePathList.new(self)
|
145
|
+
end
|
74
146
|
end
|
data/spec/filepath_spec.rb
CHANGED
@@ -23,6 +23,7 @@ describe FilePath do
|
|
23
23
|
['foo', '.', 'foo'],
|
24
24
|
['foo', '..', '.'],
|
25
25
|
['foo/bar', 'baz', 'foo/bar/baz'],
|
26
|
+
['', 'foo/bar', './foo/bar'],
|
26
27
|
]
|
27
28
|
test_data.each do |base, extra, result|
|
28
29
|
it "concatenates `#{base}` and `#{extra}` (as String) into `#{result}`" do
|
@@ -49,6 +50,20 @@ describe FilePath do
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
53
|
+
describe "#join" do
|
54
|
+
test_data = [
|
55
|
+
['', ['bar'], './bar'],
|
56
|
+
['foo/quux', ['bar', 'baz'], 'foo/quux/bar/baz'],
|
57
|
+
['/', ['a', 'b', 'c'], '/a/b/c'],
|
58
|
+
]
|
59
|
+
test_data.each do |base, extra, result|
|
60
|
+
args = extra.map { |x| x.inspect }.join(',')
|
61
|
+
it "appends #{args} to '#{base}' to get <#{result}>" do
|
62
|
+
base.as_path.join(*extra).should == result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
52
67
|
describe "filename" do
|
53
68
|
test_data = [
|
54
69
|
['/foo/bar', 'bar'],
|
@@ -107,7 +122,7 @@ describe FilePath do
|
|
107
122
|
test_data2.each do |path, base|
|
108
123
|
it "raise an exception because `#{path}` and `#{base}` have different prefixes" do
|
109
124
|
p = FilePath.new(path)
|
110
|
-
expect { p.relative_to(base) }.to raise_error
|
125
|
+
expect { p.relative_to(base) }.to raise_error(ArgumentError)
|
111
126
|
end
|
112
127
|
end
|
113
128
|
end
|
@@ -160,34 +175,38 @@ describe FilePath do
|
|
160
175
|
end
|
161
176
|
|
162
177
|
describe "#extension?" do
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
FilePath.new('foo').extension?.should be_false
|
173
|
-
end
|
174
|
-
|
175
|
-
it "says that `foo.bar/baz` has no extension" do
|
176
|
-
FilePath.new('foo.bar/baz').extension?.should be_false
|
177
|
-
end
|
178
|
-
|
179
|
-
it "says that `.foo` has no extension" do
|
180
|
-
FilePath.new('.foo').extension?.should be_false
|
178
|
+
with_extension = [
|
179
|
+
'foo.bar',
|
180
|
+
'foo.',
|
181
|
+
'.foo.conf',
|
182
|
+
]
|
183
|
+
with_extension.each do |path|
|
184
|
+
it "says that <#{path}> has an extension" do
|
185
|
+
FilePath.new(path).extension?.should be_true
|
186
|
+
end
|
181
187
|
end
|
182
188
|
|
183
|
-
|
184
|
-
|
189
|
+
no_extension = [
|
190
|
+
'foo',
|
191
|
+
'foo.bar/baz',
|
192
|
+
'.foo',
|
193
|
+
]
|
194
|
+
no_extension.each do |path|
|
195
|
+
it "says that <#{path}> has no extension" do
|
196
|
+
FilePath.new(path).extension?.should be_false
|
197
|
+
end
|
185
198
|
end
|
186
|
-
end
|
187
199
|
|
188
|
-
|
189
|
-
|
190
|
-
|
200
|
+
extension_data = [
|
201
|
+
['foo.bar', 'bar'],
|
202
|
+
['/foo/bar.', ''],
|
203
|
+
['foo/bar.baz.conf', 'conf'],
|
204
|
+
['foo.bar.boom', /oo/],
|
205
|
+
]
|
206
|
+
extension_data.each do |path, ext|
|
207
|
+
it "says that <#{path}> extesions is #{ext.inspect}" do
|
208
|
+
FilePath.new(path).extension?(ext).should be_true
|
209
|
+
end
|
191
210
|
end
|
192
211
|
|
193
212
|
it "says that `foo.bar` extension is not `baz`" do
|
@@ -341,6 +360,10 @@ describe FilePath do
|
|
341
360
|
it "returns normalized paths" do
|
342
361
|
FilePath.new("/foo/bar/..").to_s.should eql('/foo')
|
343
362
|
end
|
363
|
+
|
364
|
+
it "returns '.' for empty paths" do
|
365
|
+
FilePath.new('').to_s.should eql('.')
|
366
|
+
end
|
344
367
|
end
|
345
368
|
|
346
369
|
describe "#as_path" do
|
@@ -369,10 +392,78 @@ describe FilePath do
|
|
369
392
|
end
|
370
393
|
end
|
371
394
|
|
395
|
+
describe "#eql?" do
|
396
|
+
it "is always true when an object is compared to itself" do
|
397
|
+
ph = 'foo/bar/baz'.as_path
|
398
|
+
|
399
|
+
ph.should eql(ph)
|
400
|
+
end
|
401
|
+
|
402
|
+
it "matches two different object representing the same path" do
|
403
|
+
p1 = '/foo/bar'.as_path
|
404
|
+
p2 = '/foo/bar'.as_path
|
405
|
+
|
406
|
+
p1.should eql(p2)
|
407
|
+
end
|
408
|
+
|
409
|
+
it "does not match different objects representing different paths" do
|
410
|
+
p1 = '/foo/bar'.as_path
|
411
|
+
p2 = '/foo/bar/baz'.as_path
|
412
|
+
|
413
|
+
p1.should_not eql(p2)
|
414
|
+
end
|
415
|
+
|
416
|
+
it "does not match objects that are not FilePaths" do
|
417
|
+
p1 = '/foo/bar/baz'.as_path
|
418
|
+
p2 = '/foo/bar/baz'
|
419
|
+
|
420
|
+
p1.should eq(p2)
|
421
|
+
p1.should_not eql(p2)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
describe "#hash" do
|
426
|
+
it "has the same value for similar paths" do
|
427
|
+
p1 = '/foo/bar'.as_path
|
428
|
+
p2 = '/foo/bar'.as_path
|
429
|
+
|
430
|
+
p1.hash.should == p2.hash
|
431
|
+
end
|
432
|
+
|
433
|
+
it "has different values for different paths" do
|
434
|
+
p1 = '/foo/bar'.as_path
|
435
|
+
p2 = 'foo/quuz'.as_path
|
436
|
+
|
437
|
+
p1.hash.should_not == p2.hash
|
438
|
+
end
|
439
|
+
|
440
|
+
it "has different values for different paths with same normalized path" do
|
441
|
+
p1 = '/foo/bar/..'.as_path
|
442
|
+
p2 = '/foo'.as_path
|
443
|
+
|
444
|
+
p1.should eq(p2)
|
445
|
+
p1.hash.should_not eq(p2.hash)
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
372
449
|
describe FilePath::PathResolution do
|
373
450
|
describe "#absolute_path" do
|
374
|
-
|
375
|
-
|
451
|
+
test_data = [
|
452
|
+
['d1/l11', File.expand_path('d1/l11', FIXTURES_DIR), FIXTURES_DIR],
|
453
|
+
['/foo/bar', '/foo/bar', '.'],
|
454
|
+
]
|
455
|
+
test_data.each do |path, abs_path, cwd|
|
456
|
+
it "resolves <#{path}> to <#{abs_path}> (in #{cwd})" do
|
457
|
+
Dir.chdir(cwd) do # FIXME
|
458
|
+
FilePath.new(path).absolute_path.should == abs_path
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
describe "#real_path" do
|
465
|
+
it "resolves <d1/l11> to </dev/null>" do
|
466
|
+
(@root / 'd1' / 'l11').real_path.should == '/dev/null'
|
376
467
|
end
|
377
468
|
end
|
378
469
|
end
|
@@ -419,10 +510,71 @@ describe FilePath do
|
|
419
510
|
@root.should be_directory
|
420
511
|
end
|
421
512
|
end
|
513
|
+
|
514
|
+
describe "#hidden?" do
|
515
|
+
hidden_paths = [
|
516
|
+
'.foorc',
|
517
|
+
'foo/.bar',
|
518
|
+
'.foo.bar',
|
519
|
+
]
|
520
|
+
hidden_paths.each do |path|
|
521
|
+
it "says that <#{path}> is an hidden file" do
|
522
|
+
path.as_path.should be_hidden
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
non_hidden_paths = [
|
527
|
+
'foo.bar',
|
528
|
+
'foo/.bar/baz',
|
529
|
+
]
|
530
|
+
non_hidden_paths.each do |path|
|
531
|
+
it "says that <#{path}> not an hidden file" do
|
532
|
+
path.as_path.should_not be_hidden
|
533
|
+
end
|
534
|
+
end
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
describe FilePath::FileManipulationMethods do
|
539
|
+
describe "#touch" do
|
540
|
+
let(:ph) { @root / 'd1' / 'test-touch' }
|
541
|
+
|
542
|
+
before(:each) do
|
543
|
+
ph.should_not exist
|
544
|
+
end
|
545
|
+
|
546
|
+
after(:each) do
|
547
|
+
File.delete(ph) if File.exists?(ph)
|
548
|
+
end
|
549
|
+
|
550
|
+
it "creates an empty file" do
|
551
|
+
ph.touch
|
552
|
+
ph.should exist
|
553
|
+
end
|
554
|
+
|
555
|
+
it "updates the modification date of an existing file", :broken => true do
|
556
|
+
File.open(ph, "w+") { |file| file << "abc" }
|
557
|
+
File.utime(0, Time.now - 3200, ph)
|
558
|
+
|
559
|
+
before_stat = File.stat(ph)
|
560
|
+
before_time = Time.now
|
561
|
+
|
562
|
+
#sleep(5) # let Ruby flush its stat buffer to the disk
|
563
|
+
ph.touch
|
564
|
+
|
565
|
+
after_time = Time.now
|
566
|
+
after_stat = File.stat(ph)
|
567
|
+
|
568
|
+
before_stat.should_not eq(after_stat)
|
569
|
+
|
570
|
+
after_stat.size.should eq(before_stat.size)
|
571
|
+
after_stat.mtime.should be_between(before_time, after_time)
|
572
|
+
end
|
573
|
+
end
|
422
574
|
end
|
423
575
|
|
424
|
-
describe
|
425
|
-
describe "#
|
576
|
+
describe FilePath::DirectoryMethods do
|
577
|
+
describe "#entries" do
|
426
578
|
it "raises when path is not a directory" do
|
427
579
|
expect { (@root / 'f1').entries(:files) }.to raise_error(Errno::ENOTDIR)
|
428
580
|
end
|
@@ -433,11 +585,11 @@ describe FilePath do
|
|
433
585
|
@root.files.should have(1).item
|
434
586
|
end
|
435
587
|
|
436
|
-
it "finds 2 files in directory
|
588
|
+
it "finds 2 files in directory <d1>" do
|
437
589
|
(@root / 'd1').files.should have(2).items
|
438
590
|
end
|
439
591
|
|
440
|
-
it "finds no files in directory
|
592
|
+
it "finds no files in directory <d1/d12>" do
|
441
593
|
(@root / 'd1' / 'd12').files.should have(0).items
|
442
594
|
end
|
443
595
|
end
|
@@ -447,11 +599,11 @@ describe FilePath do
|
|
447
599
|
@root.directories.should have(4).items
|
448
600
|
end
|
449
601
|
|
450
|
-
it "finds 2 directories in directory
|
602
|
+
it "finds 2 directories in directory <d2>" do
|
451
603
|
(@root / 'd2').directories.should have(2).items
|
452
604
|
end
|
453
605
|
|
454
|
-
it "finds no directories in directory
|
606
|
+
it "finds no directories in directory <d1/d13>" do
|
455
607
|
(@root / 'd1' / 'd13').directories.should have(0).items
|
456
608
|
end
|
457
609
|
end
|
@@ -461,7 +613,7 @@ describe FilePath do
|
|
461
613
|
@root.links.should have(0).items
|
462
614
|
end
|
463
615
|
|
464
|
-
it "finds 1 link in directory
|
616
|
+
it "finds 1 link in directory <d1>" do
|
465
617
|
(@root / 'd1').links.should have(1).item
|
466
618
|
end
|
467
619
|
end
|
data/spec/filepathlist_spec.rb
CHANGED
@@ -4,6 +4,44 @@
|
|
4
4
|
require File.join(File.dirname(__FILE__), 'spec_helper')
|
5
5
|
|
6
6
|
describe FilePathList do
|
7
|
+
describe "#initialize" do
|
8
|
+
it "creates an empty FilePathList" do
|
9
|
+
list = FilePathList.new()
|
10
|
+
|
11
|
+
list.should be_empty
|
12
|
+
end
|
13
|
+
|
14
|
+
it "creates a FilePathList from an Array of Strings" do
|
15
|
+
paths = %w{a/b c/d e/f}
|
16
|
+
list = FilePathList.new(paths)
|
17
|
+
|
18
|
+
list.should have(3).items
|
19
|
+
list.each { |path| path.should be_a(FilePath) }
|
20
|
+
end
|
21
|
+
|
22
|
+
it "creates a FilePathList from an Array of FilePaths" do
|
23
|
+
paths = %w{a/b c/d e/f}.map(&:as_path)
|
24
|
+
list = FilePathList.new(paths)
|
25
|
+
|
26
|
+
list.should have(3).items
|
27
|
+
list.each { |path| path.should be_a(FilePath) }
|
28
|
+
end
|
29
|
+
|
30
|
+
it "creates a FilePathList from an Array of Arrays" do
|
31
|
+
paths = [%w{a b}, %w{c d}, %w{e f}]
|
32
|
+
list = FilePathList.new(paths)
|
33
|
+
|
34
|
+
list.should have(3).items
|
35
|
+
list.each { |path| path.should be_a(FilePath) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#exclude" do
|
40
|
+
list = FilePathList.new(%w{a.foo b.bar c.foo d.foo b.bar})
|
41
|
+
refined = list.exclude(/bar$/)
|
42
|
+
refined.each { |path| path.extension.should == 'foo' }
|
43
|
+
end
|
44
|
+
|
7
45
|
describe "#/" do
|
8
46
|
it "adds the same string to all the paths" do
|
9
47
|
list = FilePathList.new(%w{foo faa}) / 'bar'
|
@@ -12,6 +50,45 @@ describe FilePathList do
|
|
12
50
|
end
|
13
51
|
end
|
14
52
|
|
53
|
+
describe "#+" do
|
54
|
+
it "concatenates two FilePathLists" do
|
55
|
+
list1 = FilePathList.new(%w{a b c})
|
56
|
+
list2 = FilePathList.new(%w{d e})
|
57
|
+
|
58
|
+
list = list1 + list2
|
59
|
+
list.should have(5).items
|
60
|
+
list[0].should eq('a')
|
61
|
+
list[1].should eq('b')
|
62
|
+
list[2].should eq('c')
|
63
|
+
list[3].should eq('d')
|
64
|
+
list[4].should eq('e')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#-" do
|
69
|
+
it "removes a list (as array of strings) from another list" do
|
70
|
+
list1 = FilePathList.new(%w{a/b /a/c e/d})
|
71
|
+
list2 = list1 - %w{a/b e/d}
|
72
|
+
|
73
|
+
list2.should have(1).item
|
74
|
+
list2[0].should eq('/a/c')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#<<" do
|
79
|
+
it "adds a new to path to a existing FilePathList" do
|
80
|
+
list1 = FilePathList.new(%w{a/b /c/d})
|
81
|
+
list2 = list1 << "e/f"
|
82
|
+
|
83
|
+
list1.should have(2).items
|
84
|
+
list2.should have(3).items
|
85
|
+
|
86
|
+
list2[0].should eq('a/b')
|
87
|
+
list2[1].should eq('/c/d')
|
88
|
+
list2[2].should eq('e/f')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
15
92
|
describe "#*" do
|
16
93
|
describe "calculates the cartesian product between" do
|
17
94
|
it "two FilePathLists" do
|
@@ -56,10 +133,81 @@ describe FilePathList do
|
|
56
133
|
end
|
57
134
|
end
|
58
135
|
|
136
|
+
describe "#remove_common_fragments" do
|
137
|
+
it "works on lists of files from the same dir" do
|
138
|
+
paths = %w{a/b/x1 a/b/x2 a/b/x3}
|
139
|
+
list = FilePathList.new(paths).remove_common_fragments
|
140
|
+
|
141
|
+
list.should have(3).items
|
142
|
+
list.should include(*%w{x1 x2 x3})
|
143
|
+
end
|
144
|
+
|
145
|
+
it "works on lists of files from different dirs" do
|
146
|
+
list1 = FilePathList.new(%w{a/b/x1 a/b/c/x2 a/b/d/e/x3})
|
147
|
+
list2 = list1.remove_common_fragments
|
148
|
+
|
149
|
+
list2.should have(3).items
|
150
|
+
list2.should include(*%w{x1 c/x2 d/e/x3})
|
151
|
+
end
|
152
|
+
|
153
|
+
it "works on lists of files with no common fragments" do
|
154
|
+
paths = %w{a/b a/d g/f}
|
155
|
+
list1 = FilePathList.new(paths)
|
156
|
+
list2 = list1.remove_common_fragments
|
157
|
+
|
158
|
+
list1.should == list2
|
159
|
+
end
|
160
|
+
|
161
|
+
it "works on lists that contain duplicates only" do
|
162
|
+
paths = %w{a/b a/b a/b}
|
163
|
+
list1 = FilePathList.new(paths)
|
164
|
+
list2 = list1.remove_common_fragments
|
165
|
+
|
166
|
+
list2.should == FilePathList.new(['.', '.', '.'])
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
59
170
|
describe "#include?" do
|
60
|
-
it "says that
|
171
|
+
it "says that 'a/c' is included in [<a/b>, <a/c>, </a/d>]" do
|
61
172
|
list = FilePathList.new(%w{a/b a/c /a/d})
|
62
173
|
list.should include("a/c")
|
63
174
|
end
|
64
175
|
end
|
176
|
+
|
177
|
+
describe "#to_s" do
|
178
|
+
it "returns files separated by a comma`" do
|
179
|
+
list = FilePathList.new(%w{a/b a/c /a/d})
|
180
|
+
list.to_s.should == "a/b:a/c:/a/d"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "#==" do
|
185
|
+
let(:list) { ['a/b', 'c/d', 'e/f'].as_path_list }
|
186
|
+
|
187
|
+
it "compares a FilePathList to another FilePathList" do
|
188
|
+
list2 = FilePathList.new << 'a/b' << 'c/d' << 'e/f'
|
189
|
+
list3 = list2 << 'g/h'
|
190
|
+
|
191
|
+
list.should eq(list2)
|
192
|
+
list.should_not eq(list3)
|
193
|
+
end
|
194
|
+
|
195
|
+
it "compares a FilePathList to an Array of Strings" do
|
196
|
+
list.should eq(%w{a/b c/d e/f})
|
197
|
+
list.should_not eq(%w{a/a b/b c/c})
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
describe Array do
|
204
|
+
describe "#as_path_list" do
|
205
|
+
it "generates a FilePathList from an Array" do
|
206
|
+
paths = %w{/a/b c/d /f/g}
|
207
|
+
list = paths.as_path_list
|
208
|
+
|
209
|
+
list.should be_a(FilePathList)
|
210
|
+
list.should include(*paths)
|
211
|
+
end
|
212
|
+
end
|
65
213
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filepath
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-01-
|
12
|
+
date: 2012-01-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bones-rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &21597840 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 2.0.1
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *21597840
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: bones
|
27
|
-
requirement: &
|
27
|
+
requirement: &21595900 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: 3.7.3
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *21595900
|
36
36
|
description: ! 'filepath is a small library that helps dealing with files, directories
|
37
37
|
and
|
38
38
|
|