filepath 0.1 → 0.2

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/.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: []