filepath 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +1 -1
- data/README.md +8 -6
- data/Rakefile +3 -1
- data/lib/filepath.rb +95 -68
- data/spec/filepath_spec.rb +50 -1
- metadata +20 -9
data/.yardopts
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
FilePath
|
2
2
|
========
|
3
3
|
|
4
|
-
|
5
|
-
general; a modern replacement for the standard Pathname.
|
4
|
+
filepath is a small library that helps dealing with files, directories and
|
5
|
+
paths in general; a modern replacement for the standard Pathname.
|
6
|
+
|
7
|
+
filepath is built around two main classes: `FilePath`, that represents paths,
|
8
|
+
and `FilePathList`, lists of paths. The instances of these classes are
|
9
|
+
immutable objects with dozens of convience methods for common operations such
|
10
|
+
as calculating relative paths, concatenating paths, finding all the files in
|
11
|
+
a directory or modifing all the extensions of a list of file names at once.
|
6
12
|
|
7
|
-
`FilePath` instances are immutable objects with dozens of convience methods
|
8
|
-
for common operations such as calculating relative paths, concatenating paths
|
9
|
-
or finding all the files in a directory. There is also a companion class
|
10
|
-
`FilePathList` to perform operations on multiple files at once.
|
11
13
|
|
12
14
|
Features and examples
|
13
15
|
---------------------
|
data/Rakefile
CHANGED
@@ -13,9 +13,11 @@ Bones {
|
|
13
13
|
email 'gioele@svario.it'
|
14
14
|
url 'http://github.com/gioele/filepath'
|
15
15
|
|
16
|
-
version '0.
|
16
|
+
version '0.2'
|
17
17
|
|
18
18
|
ignore_file '.gitignore'
|
19
|
+
|
20
|
+
depend_on 'bones-rspec', :development => true
|
19
21
|
}
|
20
22
|
|
21
23
|
require File.join(File.dirname(__FILE__), 'spec/tasks')
|
data/lib/filepath.rb
CHANGED
@@ -27,7 +27,7 @@ class FilePath
|
|
27
27
|
raw_paths = raw_paths.first
|
28
28
|
end
|
29
29
|
|
30
|
-
paths = raw_paths.map { |p|
|
30
|
+
paths = raw_paths.map { |p| p.as_path }
|
31
31
|
|
32
32
|
frags = []
|
33
33
|
paths.each { |path| frags += path.fragments }
|
@@ -40,12 +40,12 @@ class FilePath
|
|
40
40
|
#
|
41
41
|
# @example Append a string
|
42
42
|
#
|
43
|
-
#
|
43
|
+
# "a/b".as_path / "c" #=> <a/b/c>
|
44
44
|
#
|
45
45
|
# @example Append another FilePath
|
46
46
|
#
|
47
|
-
# home =
|
48
|
-
# conf_dir =
|
47
|
+
# home = (ENV["HOME"] || "/root").as_path
|
48
|
+
# conf_dir = '.config'.as_path
|
49
49
|
#
|
50
50
|
# home / conf_dir #=> </home/user/.config>
|
51
51
|
#
|
@@ -82,15 +82,17 @@ class FilePath
|
|
82
82
|
end
|
83
83
|
|
84
84
|
|
85
|
-
# Calculates the relative path from
|
85
|
+
# Calculates the relative path from a given directory.
|
86
86
|
#
|
87
|
-
# @param [FilePath, String] base the
|
87
|
+
# @param [FilePath, String] base the directory to use as base for the
|
88
88
|
# relative path
|
89
89
|
#
|
90
90
|
# @return [FilePath] the relative path
|
91
|
+
#
|
92
|
+
# @note this method operates on the normalized paths
|
91
93
|
|
92
94
|
def relative_to(base)
|
93
|
-
base =
|
95
|
+
base = base.as_path
|
94
96
|
|
95
97
|
if self.absolute? != base.absolute?
|
96
98
|
self_abs = self.absolute? ? "absolute" : "relative"
|
@@ -101,21 +103,37 @@ class FilePath
|
|
101
103
|
raise msg # FIXME: argerror error class
|
102
104
|
end
|
103
105
|
|
104
|
-
self_frags = self.
|
105
|
-
base_frags = base.
|
106
|
+
self_frags = self.normalized_fragments
|
107
|
+
base_frags = base.normalized_fragments
|
108
|
+
|
109
|
+
base_frags_tmp = base_frags.dup
|
106
110
|
num_same = self_frags.find_index do |frag|
|
107
|
-
|
111
|
+
base_frags_tmp.delete_at(0) != frag
|
108
112
|
end
|
109
113
|
|
110
114
|
# find_index returns nil if `self` is a subset of `base`
|
111
|
-
num_same ||=
|
115
|
+
num_same ||= self_frags.length
|
112
116
|
|
113
|
-
num_parent_dirs =
|
114
|
-
left_in_self =
|
117
|
+
num_parent_dirs = base_frags.length - num_same
|
118
|
+
left_in_self = self_frags[num_same..-1]
|
115
119
|
|
116
120
|
frags = [".."] * num_parent_dirs + left_in_self
|
121
|
+
normalized_frags = normalized_relative_frags(frags)
|
117
122
|
|
118
|
-
return FilePath.join(
|
123
|
+
return FilePath.join(normalized_frags)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Calculates the relative path from a given file.
|
127
|
+
#
|
128
|
+
# @param [FilePath, String] base the file to use as base for the
|
129
|
+
# relative path
|
130
|
+
#
|
131
|
+
# @return [FilePath] the relative path
|
132
|
+
#
|
133
|
+
# @see #relative_to
|
134
|
+
|
135
|
+
def relative_to_file(base_file)
|
136
|
+
return relative_to(base_file.as_path.parent_dir)
|
119
137
|
end
|
120
138
|
|
121
139
|
|
@@ -128,11 +146,11 @@ class FilePath
|
|
128
146
|
|
129
147
|
def filename
|
130
148
|
if self.root?
|
131
|
-
return
|
149
|
+
return ''.as_path
|
132
150
|
end
|
133
151
|
|
134
152
|
filename = self.normalized_fragments.last
|
135
|
-
return
|
153
|
+
return filename.as_path
|
136
154
|
end
|
137
155
|
|
138
156
|
alias :basename :filename
|
@@ -157,7 +175,7 @@ class FilePath
|
|
157
175
|
|
158
176
|
def replace_filename(new_path)
|
159
177
|
dir = self.parent_dir
|
160
|
-
return dir /
|
178
|
+
return dir / new_path
|
161
179
|
end
|
162
180
|
|
163
181
|
alias :replace_basename :replace_filename
|
@@ -232,21 +250,24 @@ class FilePath
|
|
232
250
|
def replace_extension(new_ext) # FIXME: accept block
|
233
251
|
if !self.extension?
|
234
252
|
if new_ext.nil?
|
235
|
-
|
253
|
+
new_filename = filename
|
236
254
|
else
|
237
|
-
|
255
|
+
new_filename = filename.to_s + '.' + new_ext
|
238
256
|
end
|
239
257
|
else
|
240
258
|
if new_ext.nil?
|
241
259
|
pattern = /\.[^.]*?\Z/
|
242
|
-
|
260
|
+
new_filename = filename.to_s.sub(pattern, '')
|
243
261
|
else
|
244
|
-
pattern = '.' + extension
|
245
|
-
|
262
|
+
pattern = Regexp.new('.' + extension + '\\Z')
|
263
|
+
new_filename = filename.to_s.sub(pattern, '.' + new_ext)
|
246
264
|
end
|
247
265
|
end
|
248
266
|
|
249
|
-
|
267
|
+
frags = @fragments[0..-2]
|
268
|
+
frags << new_filename
|
269
|
+
|
270
|
+
return FilePath.join(frags)
|
250
271
|
end
|
251
272
|
|
252
273
|
alias :replace_ext :replace_extension
|
@@ -264,6 +285,16 @@ class FilePath
|
|
264
285
|
alias :remove_ext :remove_extension
|
265
286
|
|
266
287
|
|
288
|
+
# Matches a pattern against this path.
|
289
|
+
#
|
290
|
+
# @param [Regexp, Object] pattern the pattern to match against
|
291
|
+
# this path
|
292
|
+
#
|
293
|
+
# @return [Fixnum, nil] the position of the pattern in the path, or
|
294
|
+
# nil if there is no match
|
295
|
+
#
|
296
|
+
# @note this method operates on the normalized path
|
297
|
+
|
267
298
|
def =~(pattern)
|
268
299
|
return self.to_s =~ pattern
|
269
300
|
end
|
@@ -319,11 +350,7 @@ class FilePath
|
|
319
350
|
# @yield [path] TODO
|
320
351
|
|
321
352
|
def ascend(max_depth = nil, &block)
|
322
|
-
max_depth
|
323
|
-
(1..max_depth).reverse_each do |limit|
|
324
|
-
frags = @fragments.take(limit)
|
325
|
-
yield FilePath.join(frags)
|
326
|
-
end
|
353
|
+
iterate(max_depth, :reverse_each, &block)
|
327
354
|
end
|
328
355
|
|
329
356
|
# Iterates over all the directory that lead to the current path.
|
@@ -334,8 +361,13 @@ class FilePath
|
|
334
361
|
# @yield [path] TODO
|
335
362
|
|
336
363
|
def descend(max_depth = nil, &block)
|
364
|
+
iterate(max_depth, :each, &block)
|
365
|
+
end
|
366
|
+
|
367
|
+
# @private
|
368
|
+
def iterate(max_depth, method, &block)
|
337
369
|
max_depth ||= @fragments.length
|
338
|
-
(1..max_depth).
|
370
|
+
(1..max_depth).send(method) do |limit|
|
339
371
|
frags = @fragments.take(limit)
|
340
372
|
yield FilePath.join(frags)
|
341
373
|
end
|
@@ -355,19 +387,25 @@ class FilePath
|
|
355
387
|
|
356
388
|
# @return [String] this path converted to a String
|
357
389
|
#
|
358
|
-
# @note this method operates on the normalized
|
390
|
+
# @note this method operates on the normalized path
|
359
391
|
|
360
392
|
def to_s
|
361
393
|
return self.normalized_fragments.join(SEPARATOR).sub(%r{^//}, SEPARATOR)
|
362
394
|
end
|
363
395
|
|
364
396
|
|
397
|
+
# @return [FilePath] the path itself.
|
398
|
+
def as_path
|
399
|
+
self
|
400
|
+
end
|
401
|
+
|
402
|
+
|
365
403
|
def inspect
|
366
404
|
return '<' + self.to_raw_string + '>'
|
367
405
|
end
|
368
406
|
|
369
407
|
def ==(other)
|
370
|
-
return self.to_s ==
|
408
|
+
return self.to_s == other.as_path.to_s
|
371
409
|
end
|
372
410
|
|
373
411
|
# @private
|
@@ -403,7 +441,7 @@ class FilePath
|
|
403
441
|
# remove '..' fragments following a root delimiter
|
404
442
|
frags.delete_at(i)
|
405
443
|
i -= 1
|
406
|
-
elsif frags[i] == '..' && frags[i-1] != '..'
|
444
|
+
elsif frags[i] == '..' && frags[i-1] != '..' && i >= 1
|
407
445
|
# remove every fragment followed by a ".." marker
|
408
446
|
frags.delete_at(i)
|
409
447
|
frags.delete_at(i-1)
|
@@ -420,63 +458,52 @@ class FilePath
|
|
420
458
|
path = if !self.absolute?
|
421
459
|
self
|
422
460
|
else
|
423
|
-
|
461
|
+
base_dir.as_path / self
|
424
462
|
end
|
425
463
|
|
426
464
|
return path.resolve_link
|
427
465
|
end
|
428
466
|
|
429
467
|
def resolve_link
|
430
|
-
return
|
468
|
+
return File.readlink(self.to_s).as_path
|
431
469
|
end
|
432
470
|
end
|
433
471
|
|
434
472
|
module FileInfo
|
435
|
-
|
436
|
-
|
473
|
+
# @private
|
474
|
+
def self.define_filetest_method(filepath_method, filetest_method = nil)
|
475
|
+
filetest_method ||= filepath_method
|
476
|
+
define_method(filepath_method) do
|
477
|
+
return FileTest.send(filetest_method, self.to_s)
|
478
|
+
end
|
437
479
|
end
|
438
480
|
|
439
|
-
|
440
|
-
FileTest.symlink?(self.to_s)
|
441
|
-
end
|
442
|
-
alias symlink? link?
|
481
|
+
define_filetest_method :file?
|
443
482
|
|
444
|
-
|
445
|
-
|
446
|
-
end
|
483
|
+
define_filetest_method :link?, :symlink?
|
484
|
+
alias :symlink? :link?
|
447
485
|
|
448
|
-
|
449
|
-
FileTest.exists?(self.to_s)
|
450
|
-
end
|
451
|
-
alias exist? exists?
|
486
|
+
define_filetest_method :directory?
|
452
487
|
|
453
|
-
|
454
|
-
|
455
|
-
end
|
488
|
+
define_filetest_method :exists?
|
489
|
+
alias :exist? :exists?
|
456
490
|
|
457
|
-
|
458
|
-
FileTest.writable?(self.to_s)
|
459
|
-
end
|
491
|
+
define_filetest_method :readable?
|
460
492
|
|
461
|
-
|
462
|
-
FileTest.executable?(self.to_s)
|
463
|
-
end
|
493
|
+
define_filetest_method :writeable?
|
464
494
|
|
465
|
-
|
466
|
-
FileTest.setgid?(self.to_s)
|
467
|
-
end
|
495
|
+
define_filetest_method :executable?
|
468
496
|
|
469
|
-
|
470
|
-
|
471
|
-
|
497
|
+
define_filetest_method :setgid?
|
498
|
+
|
499
|
+
define_filetest_method :setuid?
|
500
|
+
|
501
|
+
define_filetest_method :empty?, :zero?
|
502
|
+
alias :zero? :empty?
|
472
503
|
|
473
504
|
def hidden?
|
474
505
|
@fragments.last.start_with('.') # FIXME: windows, mac
|
475
506
|
end
|
476
|
-
|
477
|
-
def empty?
|
478
|
-
FileTest.zero?(self.to_s)
|
479
|
-
end
|
480
507
|
end
|
481
508
|
|
482
509
|
module FileManipulationMethods
|
@@ -489,7 +516,7 @@ class FilePath
|
|
489
516
|
end
|
490
517
|
end
|
491
518
|
|
492
|
-
module
|
519
|
+
module DirectoryMethods
|
493
520
|
def entries(pattern = '*')
|
494
521
|
if !self.directory?
|
495
522
|
raise Errno::ENOTDIR.new(self.to_s)
|
@@ -518,7 +545,7 @@ class FilePath
|
|
518
545
|
include PathResolution
|
519
546
|
include FileInfo
|
520
547
|
include FileManipulationMethods
|
521
|
-
include
|
548
|
+
include DirectoryMethods
|
522
549
|
end
|
523
550
|
|
524
551
|
class String
|
data/spec/filepath_spec.rb
CHANGED
@@ -81,12 +81,15 @@ describe FilePath do
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
describe "#relative_to
|
84
|
+
describe "#relative_to" do
|
85
85
|
test_data = [
|
86
86
|
['/a/b/c', '/a/b', 'c'],
|
87
87
|
['/a/b/c', '/a/d', '../b/c'],
|
88
88
|
['/a/b/c', '/a/b/c/d', '..'],
|
89
89
|
['/a/b/c', '/a/b/c', '.'],
|
90
|
+
['a/d', 'a/b/c', '../../d'],
|
91
|
+
['a/e/f', 'a/b/c/d', '../../../e/f'],
|
92
|
+
['a/c', 'a/b/..', 'c'],
|
90
93
|
]
|
91
94
|
test_data.each do |path, base, result|
|
92
95
|
it "says that `#{path}` relative to `#{base}` is `#{result}`" do
|
@@ -109,6 +112,22 @@ describe FilePath do
|
|
109
112
|
end
|
110
113
|
end
|
111
114
|
|
115
|
+
describe "#relative_to_file" do
|
116
|
+
test_data = [
|
117
|
+
['/a/b/c', '/a/d', 'b/c'],
|
118
|
+
['/a/b/c', '/a/b/c/d', '.'],
|
119
|
+
['/a/b/c', '/a/b/c', 'c'],
|
120
|
+
['a/d', 'a/b/c', '../d'],
|
121
|
+
['a/e/f', 'a/b/c/d', '../../e/f'],
|
122
|
+
]
|
123
|
+
test_data.each do |path, base, result|
|
124
|
+
it "says that `#{path}` relative to the file `#{base}` is `#{result}`" do
|
125
|
+
p = FilePath.new(path)
|
126
|
+
p.relative_to_file(base).should == result
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
112
131
|
describe "#replace_filename" do
|
113
132
|
test_data = [
|
114
133
|
['foo/bar', 'quux', 'foo/quux'],
|
@@ -220,6 +239,10 @@ describe FilePath do
|
|
220
239
|
it "matches `/foo/bar` with /o\\/ba" do
|
221
240
|
FilePath.new('/foo/bar').should =~ /o\/b/
|
222
241
|
end
|
242
|
+
|
243
|
+
it "matches `/foo/bar/../quux` with /foo\\/quux/" do
|
244
|
+
FilePath.new('/foo/bar/../quux').should =~ /foo\/quux/
|
245
|
+
end
|
223
246
|
end
|
224
247
|
|
225
248
|
describe "#absolute?" do
|
@@ -232,6 +255,26 @@ describe FilePath do
|
|
232
255
|
end
|
233
256
|
end
|
234
257
|
|
258
|
+
describe "#normalized" do
|
259
|
+
test_data = [
|
260
|
+
['a', 'a'],
|
261
|
+
['a/b/c', 'a/b/c'],
|
262
|
+
['a/../c', 'c'],
|
263
|
+
['a/b/..', 'a'],
|
264
|
+
['../a', '../a'],
|
265
|
+
['../../a', '../../a'],
|
266
|
+
['../a/..', '..'],
|
267
|
+
['/', '/'],
|
268
|
+
['/..', '/'],
|
269
|
+
['/../../../a', '/a'],
|
270
|
+
]
|
271
|
+
test_data.each do |path, result|
|
272
|
+
it "turns `#{path}` into `#{result}`" do
|
273
|
+
FilePath.new(path).normalized.to_raw_string.should == result
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
235
278
|
describe "#ascend" do
|
236
279
|
it "goes through all the fragments of an absolute path" do
|
237
280
|
steps = []
|
@@ -300,6 +343,12 @@ describe FilePath do
|
|
300
343
|
end
|
301
344
|
end
|
302
345
|
|
346
|
+
describe "#as_path" do
|
347
|
+
it "returns the path itself" do
|
348
|
+
@root.as_path.should be(@root)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
303
352
|
describe "#==(String)" do
|
304
353
|
test_data = [
|
305
354
|
['./', '.'],
|
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: '0.
|
4
|
+
version: '0.2'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-01-
|
12
|
+
date: 2012-01-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bones-rspec
|
16
|
+
requirement: &15445240 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.0.1
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *15445240
|
14
25
|
- !ruby/object:Gem::Dependency
|
15
26
|
name: bones
|
16
|
-
requirement: &
|
27
|
+
requirement: &15444620 !ruby/object:Gem::Requirement
|
17
28
|
none: false
|
18
29
|
requirements:
|
19
30
|
- - ! '>='
|
@@ -21,11 +32,11 @@ dependencies:
|
|
21
32
|
version: 3.7.3
|
22
33
|
type: :development
|
23
34
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
-
description: ! '
|
26
|
-
|
35
|
+
version_requirements: *15444620
|
36
|
+
description: ! 'filepath is a small library that helps dealing with files, directories
|
37
|
+
and
|
27
38
|
|
28
|
-
general; a modern replacement for the standard Pathname.'
|
39
|
+
paths in general; a modern replacement for the standard Pathname.'
|
29
40
|
email: gioele@svario.it
|
30
41
|
executables: []
|
31
42
|
extensions: []
|
@@ -67,6 +78,6 @@ rubyforge_project: filepath
|
|
67
78
|
rubygems_version: 1.8.12
|
68
79
|
signing_key:
|
69
80
|
specification_version: 3
|
70
|
-
summary:
|
71
|
-
in general; a modern replacement for the standard Pathname.
|
81
|
+
summary: filepath is a small library that helps dealing with files, directories and
|
82
|
+
paths in general; a modern replacement for the standard Pathname.
|
72
83
|
test_files: []
|