filepath 0.2 → 0.3.1
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/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
|
|