filepath 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts CHANGED
@@ -1,3 +1,3 @@
1
- -m none
1
+ -m markdown
2
2
  -
3
3
  UNLICENSE
data/README.md CHANGED
@@ -1,13 +1,15 @@
1
1
  FilePath
2
2
  ========
3
3
 
4
- `FilePath` is a class that helps dealing with files, directories and paths in
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.1'
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| FilePath.new(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
- # FilePath.new("a/b") / "c" #=> <a/b/c>
43
+ # "a/b".as_path / "c" #=> <a/b/c>
44
44
  #
45
45
  # @example Append another FilePath
46
46
  #
47
- # home = FilePath.new(ENV["HOME"] || "/root")
48
- # conf_dir = FilePath.new('.config')
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 another path.
85
+ # Calculates the relative path from a given directory.
86
86
  #
87
- # @param [FilePath, String] base the path to use as a base for 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 = FilePath.new(base) unless base.is_a? FilePath
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.fragments
105
- base_frags = base.fragments.dup
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
- base_frags.delete_at(0) != frag
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 ||= self.fragments.length
115
+ num_same ||= self_frags.length
112
116
 
113
- num_parent_dirs = base.fragments.length - num_same
114
- left_in_self = self.fragments[num_same..-1]
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(frags)
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 FilePath.new('')
149
+ return ''.as_path
132
150
  end
133
151
 
134
152
  filename = self.normalized_fragments.last
135
- return FilePath.new(filename)
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 / FilePath.new(new_path)
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
- path = self.to_s
253
+ new_filename = filename
236
254
  else
237
- path = self.to_s + '.' + new_ext
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
- path = self.to_s.sub(pattern, '')
260
+ new_filename = filename.to_s.sub(pattern, '')
243
261
  else
244
- pattern = '.' + extension
245
- path = self.to_s.sub(pattern, '.' + new_ext)
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
- return FilePath.new(path)
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 ||= @fragments.length
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).each do |limit|
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 the path
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 == FilePath.new(other).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
- FilePath.new(base_dir) / self
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 FilePath.new(File.readlink(self.to_s))
468
+ return File.readlink(self.to_s).as_path
431
469
  end
432
470
  end
433
471
 
434
472
  module FileInfo
435
- def file?
436
- FileTest.file?(self.to_s)
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
- def link?
440
- FileTest.symlink?(self.to_s)
441
- end
442
- alias symlink? link?
481
+ define_filetest_method :file?
443
482
 
444
- def directory?
445
- FileTest.directory?(self.to_s)
446
- end
483
+ define_filetest_method :link?, :symlink?
484
+ alias :symlink? :link?
447
485
 
448
- def exists?
449
- FileTest.exists?(self.to_s)
450
- end
451
- alias exist? exists?
486
+ define_filetest_method :directory?
452
487
 
453
- def readable?
454
- FileTest.readable?(self.to_s)
455
- end
488
+ define_filetest_method :exists?
489
+ alias :exist? :exists?
456
490
 
457
- def writable?
458
- FileTest.writable?(self.to_s)
459
- end
491
+ define_filetest_method :readable?
460
492
 
461
- def executable?
462
- FileTest.executable?(self.to_s)
463
- end
493
+ define_filetest_method :writeable?
464
494
 
465
- def setgid?
466
- FileTest.setgid?(self.to_s)
467
- end
495
+ define_filetest_method :executable?
468
496
 
469
- def setuid?
470
- FileTest.setuid?(self.to_s)
471
- end
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 DirectoyMethods
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 DirectoyMethods
548
+ include DirectoryMethods
522
549
  end
523
550
 
524
551
  class String
@@ -81,12 +81,15 @@ describe FilePath do
81
81
  end
82
82
  end
83
83
 
84
- describe "#relative_to(FilePath)" do
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.1'
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-14 00:00:00.000000000 Z
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: &14096440 !ruby/object:Gem::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: *14096440
25
- description: ! '`FilePath` is a class that helps dealing with files, directories and
26
- paths in
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: ! '`FilePath` is a class that helps dealing with files, directories and paths
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: []