filepath 0.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.
@@ -0,0 +1,74 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+ # See the `UNLICENSE` file or <http://unlicense.org/> for more details.
3
+
4
+ class FilePathList
5
+ include Enumerable
6
+
7
+ def initialize(raw_entries = nil)
8
+ raw_entries ||= []
9
+ @entries = raw_entries.map { |e| FilePath.new(e) }
10
+ end
11
+
12
+ def select_entries(type)
13
+ raw_entries = @entries.delete_if { |e| !e.send(type.to_s + '?') }
14
+ return FilePathList.new(raw_entries)
15
+ end
16
+
17
+ def files
18
+ return select_entries(:file)
19
+ end
20
+
21
+ def links
22
+ return select_entries(:link)
23
+ end
24
+
25
+ def directories
26
+ return select_entries(:directory)
27
+ end
28
+
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
+ def /(extra_path)
35
+ return self.map { |path| path / extra_path }
36
+ end
37
+
38
+ def +(extra_entries)
39
+ return FilePathList.new(@entries + extra_entries.to_a)
40
+ end
41
+
42
+ def <<(extra_path) # TODO: implement
43
+ end
44
+
45
+ def *(other_list)
46
+ if !other_list.is_a? FilePathList
47
+ other_list = FilePathList.new(Array(other_list))
48
+ end
49
+ other_entries = other_list.entries
50
+ paths = @entries.product(other_entries).map { |p1, p2| p1 / p2 }
51
+ return FilePathList.new(paths)
52
+ end
53
+
54
+ # FIXME: delegate :to => @entries
55
+ def [](index)
56
+ @entries[index]
57
+ end
58
+
59
+ def include?(*others)
60
+ @entries.include?(*others)
61
+ end
62
+
63
+ def each(&block)
64
+ @entries.each(&block)
65
+ end
66
+
67
+ def map(&block)
68
+ @entries.map(&block)
69
+ end
70
+
71
+ def inspect
72
+ @entries.inspect
73
+ end
74
+ end
@@ -0,0 +1,440 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+ # See the `UNLICENSE` file or <http://unlicense.org/> for more details.
3
+
4
+ require File.join(File.dirname(__FILE__), 'spec_helper')
5
+
6
+ describe FilePath do
7
+ before(:all) do
8
+ @root = FilePath.new(FIXTURES_DIR)
9
+ end
10
+
11
+ it "can be created from a string" do
12
+ FilePath.new("foo").should be_a FilePath
13
+ end
14
+
15
+ it "can be created from another FilePath" do
16
+ orig = FilePath.new("foo")
17
+ FilePath.new(orig).should be_a FilePath
18
+ end
19
+
20
+ describe "#/" do
21
+ test_data = [
22
+ ['foo', 'bar', 'foo/bar'],
23
+ ['foo', '.', 'foo'],
24
+ ['foo', '..', '.'],
25
+ ['foo/bar', 'baz', 'foo/bar/baz'],
26
+ ]
27
+ test_data.each do |base, extra, result|
28
+ it "concatenates `#{base}` and `#{extra}` (as String) into `#{result}`" do
29
+ p = FilePath.new(base) / extra
30
+ p.should == result
31
+ end
32
+ end
33
+
34
+ test_data.each do |base, extra, result|
35
+ it "concatenates `#{base}` and `#{extra}` (as FilePath) into `#{result}`" do
36
+ p = FilePath.new(base) / FilePath.new(extra)
37
+ p.should == result
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "#+" do
43
+ it "is deprecated but performs as FilePath#/" do
44
+ p1 = FilePath.new("a")
45
+ p2 = FilePath.new("b")
46
+
47
+ p1.should_receive(:warn).with(/is deprecated/)
48
+ (p1 + p2).should == (p1 / p2)
49
+ end
50
+ end
51
+
52
+ describe "filename" do
53
+ test_data = [
54
+ ['/foo/bar', 'bar'],
55
+ ['foo', 'foo'],
56
+ ['/', ''],
57
+ ['/foo/bar/.', 'bar'],
58
+ ['a/b/../c', 'c'],
59
+ ]
60
+ test_data.each do |path, result|
61
+ it "says that `#{result}` is the filename of `#{path}`" do
62
+ p = FilePath.new(path)
63
+ p.filename.should == result
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "parent_dir" do
69
+ test_data = [
70
+ ['/foo/bar', '/foo'],
71
+ ['foo', '.'],
72
+ ['/', '/'],
73
+ ['/foo/bar/.', '/foo'],
74
+ ['a/b/../c', 'a'],
75
+ ]
76
+ test_data.each do |path, result|
77
+ it "says that `#{result}` is the parent dir of `#{path}`" do
78
+ p = FilePath.new(path)
79
+ p.parent_dir.should == result
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "#relative_to(FilePath)" do
85
+ test_data = [
86
+ ['/a/b/c', '/a/b', 'c'],
87
+ ['/a/b/c', '/a/d', '../b/c'],
88
+ ['/a/b/c', '/a/b/c/d', '..'],
89
+ ['/a/b/c', '/a/b/c', '.'],
90
+ ]
91
+ test_data.each do |path, base, result|
92
+ it "says that `#{path}` relative to `#{base}` is `#{result}`" do
93
+ p = FilePath.new(path)
94
+ p.relative_to(base).should == result
95
+ end
96
+ end
97
+
98
+ test_data2 = [
99
+ # FIXME: testare /a/b/c con ../d (bisogna prima rendere assoluto quel path)
100
+ ['../e', '/a/b/c'],
101
+ ['g', '/a/b/c'],
102
+ ['/a/b/c', 'm'],
103
+ ]
104
+ test_data2.each do |path, base|
105
+ it "raise an exception because `#{path}` and `#{base}` have different prefixes" do
106
+ p = FilePath.new(path)
107
+ expect { p.relative_to(base) }.to raise_error
108
+ end
109
+ end
110
+ end
111
+
112
+ describe "#replace_filename" do
113
+ test_data = [
114
+ ['foo/bar', 'quux', 'foo/quux'],
115
+ ['foo/baz/..', 'quux', 'quux'],
116
+ ['/', 'foo', '/foo'],
117
+ ]
118
+ test_data.each do |base, new, result|
119
+ it "changes `#{base}` + `#{new}` into `#{result}`" do
120
+ p = FilePath.new(base)
121
+ p.replace_filename(new).should == result
122
+ end
123
+ end
124
+ end
125
+
126
+ describe "#extension" do
127
+ test_data = [
128
+ ['foo.bar', 'bar'],
129
+ ['foo.', ''],
130
+ ['foo', nil],
131
+ ['foo.bar/baz.buz', 'buz'],
132
+ ['foo.bar/baz', nil],
133
+ ['.foo', nil],
134
+ ['.foo.conf', 'conf'],
135
+ ]
136
+ test_data.each do |path, ext|
137
+ it "says that `#{path}` has extension `#{ext}`" do
138
+ FilePath.new(path).extension.should == ext
139
+ end
140
+ end
141
+ end
142
+
143
+ describe "#extension?" do
144
+ it "says that `foo.bar` has an extension" do
145
+ FilePath.new('foo.bar').extension?.should be_true
146
+ end
147
+
148
+ it "says that `foo.` has an extension" do
149
+ FilePath.new('foo.').extension?.should be_true
150
+ end
151
+
152
+ it "says that `foo` has no extension" do
153
+ FilePath.new('foo').extension?.should be_false
154
+ end
155
+
156
+ it "says that `foo.bar/baz` has no extension" do
157
+ FilePath.new('foo.bar/baz').extension?.should be_false
158
+ end
159
+
160
+ it "says that `.foo` has no extension" do
161
+ FilePath.new('.foo').extension?.should be_false
162
+ end
163
+
164
+ it "says that `.foo.conf` has no extension" do
165
+ FilePath.new('.foo.conf').extension?.should be_true
166
+ end
167
+ end
168
+
169
+ describe "#extension?(String)" do
170
+ it "says that `foo.bar` extesions is `bar`" do
171
+ FilePath.new('foo.bar').extension?('bar').should be_true
172
+ end
173
+
174
+ it "says that `foo.bar` extension is not `baz`" do
175
+ FilePath.new('foo.bar').extension?('baz').should be_false
176
+ end
177
+ end
178
+
179
+ describe "#replace_extension(String)" do
180
+ test_data = [
181
+ ['foo.bar', 'foo.baz'],
182
+ ['foo.', 'foo.baz'],
183
+ ['foo', 'foo.baz'],
184
+ ['foo.bar/baz.buz', 'baz.baz'],
185
+ ['foo.bar/baz', 'baz.baz'],
186
+ ]
187
+ test_data.each do |path, result|
188
+ it "replaces `#{path}` with `baz` into `#{result}`" do
189
+ new = FilePath.new(path).replace_extension('baz')
190
+ new.basename.to_s.should == result
191
+ end
192
+ end
193
+ end
194
+
195
+ describe "#remove_extension" do
196
+ test_data = [
197
+ ['foo.bar', 'foo'],
198
+ ['foo.', 'foo'],
199
+ ['foo', 'foo'],
200
+ ['foo.bar/baz.buz', 'baz'],
201
+ ['foo.bar/baz', 'baz'],
202
+ ]
203
+ test_data.each do |path, result|
204
+ it "turns `#{path}` into `#{result}`" do
205
+ new = FilePath.new(path).remove_extension
206
+ new.basename.to_s.should == result
207
+ end
208
+ end
209
+ end
210
+
211
+ describe "=~" do
212
+ it "matches `/foo/bar` with /foo/" do
213
+ FilePath.new('/foo/bar').should =~ /foo/
214
+ end
215
+
216
+ it "does not match `/foo/bar` with /baz/" do
217
+ FilePath.new('/foo/bar').should_not =~ /baz/
218
+ end
219
+
220
+ it "matches `/foo/bar` with /o\\/ba" do
221
+ FilePath.new('/foo/bar').should =~ /o\/b/
222
+ end
223
+ end
224
+
225
+ describe "#absolute?" do
226
+ it "says that `/foo/bar` is absolute" do
227
+ FilePath.new('/foo/bar').should be_absolute
228
+ end
229
+
230
+ it "sasys that `foo/bar` is not absolute" do
231
+ FilePath.new('foo/bar').should_not be_absolute
232
+ end
233
+ end
234
+
235
+ describe "#ascend" do
236
+ it "goes through all the fragments of an absolute path" do
237
+ steps = []
238
+ FilePath.new("/a/b/c").ascend do |p|
239
+ steps << p
240
+ end
241
+
242
+ steps.should have(4).items
243
+ steps[0].should eq("/a/b/c")
244
+ steps[1].should eq("/a/b")
245
+ steps[2].should eq("/a")
246
+ steps[3].should eq("/")
247
+ end
248
+
249
+ it "goes through all the fragments of a relative path" do
250
+ steps = []
251
+ FilePath.new("a/b/c").ascend do |p|
252
+ steps << p
253
+ end
254
+
255
+ steps.should have(3).items
256
+ steps[0].should eq("a/b/c")
257
+ steps[1].should eq("a/b")
258
+ steps[2].should eq("a")
259
+ end
260
+ end
261
+
262
+ describe "#descend" do
263
+ it "goes through all the fragments of an absolute path" do
264
+ steps = []
265
+ FilePath.new("/a/b/c").descend do |p|
266
+ steps << p
267
+ end
268
+
269
+ steps.should have(4).items
270
+ steps[0].should eq("/")
271
+ steps[1].should eq("/a")
272
+ steps[2].should eq("/a/b")
273
+ steps[3].should eq("/a/b/c")
274
+ end
275
+
276
+ it "goes through all the fragments of a relative path" do
277
+ steps = []
278
+ FilePath.new("a/b/c").descend do |p|
279
+ steps << p
280
+ end
281
+
282
+ steps.should have(3).items
283
+ steps[0].should eq("a")
284
+ steps[1].should eq("a/b")
285
+ steps[2].should eq("a/b/c")
286
+ end
287
+ end
288
+
289
+ describe "#to_s" do
290
+ it "works on computed absolute paths" do
291
+ (FilePath.new('/') / 'a' / 'b').to_s.should eql('/a/b')
292
+ end
293
+
294
+ it "works on computed relative paths" do
295
+ (FilePath.new('a') / 'b').to_s.should eql('a/b')
296
+ end
297
+
298
+ it "returns normalized paths" do
299
+ FilePath.new("/foo/bar/..").to_s.should eql('/foo')
300
+ end
301
+ end
302
+
303
+ describe "#==(String)" do
304
+ test_data = [
305
+ ['./', '.'],
306
+ ['a/../b', 'b'],
307
+ ['a/.././b', 'b'],
308
+ ['a/./../b', 'b'],
309
+ ['./foo', 'foo'],
310
+ ['a/./b/c', 'a/b/c'],
311
+ ['a/b/.', 'a/b'],
312
+ ['a/b/', 'a/b'],
313
+ ['../a/../b/c/d/../../e', '../b/e'],
314
+ ]
315
+ test_data.each do |ver1, ver2|
316
+ it "says that `#{ver1}` is equivalent to `#{ver2}`" do
317
+ p = FilePath.new(ver1)
318
+ p.should == ver2
319
+ end
320
+ end
321
+ end
322
+
323
+ describe FilePath::PathResolution do
324
+ describe "#absolute_path" do
325
+ it "resolves `d1/l11` to `/dev/null`" do
326
+ (@root / 'd1' / 'l11').absolute_path.should == '/dev/null'
327
+ end
328
+ end
329
+ end
330
+
331
+ describe FilePath::FileInfo do
332
+ describe "#file?" do
333
+ it "says that `f1` is a file" do
334
+ (@root / 'f1').should be_file
335
+ end
336
+
337
+ it "says that `d1/l11` is not a file" do
338
+ (@root / 'd1' / 'l11').should_not be_file
339
+ end
340
+
341
+ it "says that the root directory is not a file" do
342
+ @root.should_not be_file
343
+ end
344
+ end
345
+
346
+ describe "#link?" do
347
+ it "says that `f1` is not a link" do
348
+ (@root / 'f1').should_not be_link
349
+ end
350
+
351
+ it "says that `d1/l11` is a link" do
352
+ (@root / 'd1' / 'l11').should be_link
353
+ end
354
+
355
+ it "says that the root directory is not a link" do
356
+ @root.should_not be_link
357
+ end
358
+ end
359
+
360
+ describe "#directory?" do
361
+ it "says that `f1` is not a directory" do
362
+ (@root / 'f1').should_not be_directory
363
+ end
364
+
365
+ it "says that `d1/l11` is not a directory" do
366
+ (@root / 'd1' / 'l11').should_not be_directory
367
+ end
368
+
369
+ it "says that the root directory is file" do
370
+ @root.should be_directory
371
+ end
372
+ end
373
+ end
374
+
375
+ describe "methods that operate on directories" do
376
+ describe "#select_entries" do
377
+ it "raises when path is not a directory" do
378
+ expect { (@root / 'f1').entries(:files) }.to raise_error(Errno::ENOTDIR)
379
+ end
380
+ end
381
+
382
+ describe "#files" do
383
+ it "finds 1 file in the root directory" do
384
+ @root.files.should have(1).item
385
+ end
386
+
387
+ it "finds 2 files in directory `d1`" do
388
+ (@root / 'd1').files.should have(2).items
389
+ end
390
+
391
+ it "finds no files in directory `d1/d12`" do
392
+ (@root / 'd1' / 'd12').files.should have(0).items
393
+ end
394
+ end
395
+
396
+ describe "#directories" do
397
+ it "finds 4 directories in the root directory" do
398
+ @root.directories.should have(4).items
399
+ end
400
+
401
+ it "finds 2 directories in directory `d2`" do
402
+ (@root / 'd2').directories.should have(2).items
403
+ end
404
+
405
+ it "finds no directories in directory `d1/d13" do
406
+ (@root / 'd1' / 'd13').directories.should have(0).items
407
+ end
408
+ end
409
+
410
+ describe "#links" do
411
+ it "finds no links in the root directory" do
412
+ @root.links.should have(0).items
413
+ end
414
+
415
+ it "finds 1 link in directory `d1`" do
416
+ (@root / 'd1').links.should have(1).item
417
+ end
418
+ end
419
+ end
420
+ end
421
+
422
+ describe String do
423
+ describe "#as_path" do
424
+ it "generates a FilePath from a String" do
425
+ path = "/a/b/c".as_path
426
+ path.should be_a(FilePath)
427
+ path.should eq("/a/b/c")
428
+ end
429
+ end
430
+ end
431
+
432
+ describe Array do
433
+ describe "#as_path" do
434
+ it "generates a FilePath from a String" do
435
+ path = ['/', 'a', 'b', 'c'].as_path
436
+ path.should be_a(FilePath)
437
+ path.should eq("/a/b/c")
438
+ end
439
+ end
440
+ end