epath 0.1.1 → 0.2.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Path - a Path manipulation library
2
2
 
3
- Path is a library to manage paths.
3
+ [Path](http://rubydoc.info/github/eregon/epath/master/Path) is a library to manage paths.
4
4
  It is similar to Pathname, but has some extra goodness.
5
5
  The method names are intended to be short and explicit, and avoid too much duplication like having 'name' or 'path' in the method name.
6
6
 
@@ -20,6 +20,8 @@ Also, using a path library like this avoid to remember in which class the functi
20
20
 
21
21
  ## API
22
22
 
23
+ See the [Path](http://rubydoc.info/github/eregon/epath/master/Path) class documentation for details.
24
+
23
25
  All the useful methods of `File` (and so `IO`) and `Dir` should be included.
24
26
  Most methods of `FileUtils` should be there too.
25
27
 
@@ -30,6 +32,11 @@ Path.new('/usr/bin')
30
32
  Path['/usr/bin']
31
33
  Path('/usr/bin') # unless NO_EPATH_GLOBAL_FUNCTION is defined
32
34
 
35
+ Path.new('~myuser/path') # expanded if it begins with ~
36
+
37
+ # Separators are replaced by / on systems having File::ALT_SEPARATOR
38
+ Path.new('win\sepa\rator') # => #<Path win/sepa/rator>
39
+
33
40
  Path.new('/usr', 'bin')
34
41
  %w[foo bar].map(&Path) # => [Path('foo'), Path('bar')]
35
42
  ```
@@ -38,7 +45,8 @@ Path.new('/usr', 'bin')
38
45
  Path.file # == Path(__FILE__).expand
39
46
  Path.dir # == Path(File.dirname(__FILE__)).expand
40
47
  Path.relative(path) # == Path(File.expand_path("../#{path}", __FILE__))
41
- Path.home # == Path(File.expand_path('~'))
48
+ Path.home or Path.~ # == Path(File.expand_path('~'))
49
+ Path.~(user) # == Path(File.expand_path("~#{user}"))
42
50
  ```
43
51
 
44
52
  ### temporary
@@ -102,11 +110,12 @@ Path('/usr')/'bin'
102
110
 
103
111
  ### glob
104
112
 
105
- * entries: files under self, without . and ..
113
+ * children: files under self, without . and ..
106
114
  * glob: relative glob to self, yield absolute paths
107
115
 
108
116
  ### structure
109
117
 
118
+ * parent: parent directory (don't use #dirname more than once, use #parent instead)
110
119
  * ascend, ancestors: self and all the parent directories
111
120
  * descend: in the reverse order
112
121
  * backfind: ascends the parents until it finds the given path
@@ -6,6 +6,7 @@ require 'tempfile'
6
6
 
7
7
  class Path
8
8
  class << self
9
+ # {Path} to the current file +Path(__FILE__)+.
9
10
  def file(from = nil)
10
11
  from ||= caller # this can not be moved as a default argument, JRuby optimizes it
11
12
  # v This : is there to define a group without capturing
@@ -13,23 +14,31 @@ class Path
13
14
  end
14
15
  alias :here :file
15
16
 
17
+ # {Path} to the directory of this file: +Path(__FILE__).dir+.
16
18
  def dir(from = nil)
17
19
  from ||= caller # this can not be moved as a default argument, JRuby optimizes it
18
20
  file(from).dir
19
21
  end
20
22
 
21
- def home
22
- new(Dir.respond_to?(:home) ? Dir.home : new("~").expand)
23
+ # {Path} relative to the directory of this file.
24
+ def relative(path, from = nil)
25
+ from ||= caller # this can not be moved as a default argument, JRuby optimizes it
26
+ new(path).expand dir(from)
23
27
  end
24
28
 
25
- def relative(path)
26
- new(path).expand dir(caller)
29
+ # A {Path} to the home directory of +user+ (defaults to the current user).
30
+ # The form with an argument (+user+) is not supported on Windows.
31
+ def ~(user = '')
32
+ new("~#{user}")
27
33
  end
34
+ alias :home :~
28
35
 
36
+ # Same as +Path.file.backfind(path)+. See {#backfind}.
29
37
  def backfind(path)
30
38
  file(caller).backfind(path)
31
39
  end
32
40
 
41
+ # @yieldparam [Path] tmpfile
33
42
  def tmpfile(basename = '', tmpdir = nil, options = nil)
34
43
  tempfile = Tempfile.new(basename, *[tmpdir, options].compact)
35
44
  file = new tempfile
@@ -37,14 +46,14 @@ class Path
37
46
  begin
38
47
  yield file
39
48
  ensure
40
- tempfile.close
41
- tempfile.unlink if file.exist?
49
+ tempfile.close!
42
50
  end
43
51
  end
44
52
  file
45
53
  end
46
54
  alias :tempfile :tmpfile
47
55
 
56
+ # @yieldparam [Path] tmpdir
48
57
  def tmpdir(prefix_suffix = nil, *rest)
49
58
  require 'tmpdir'
50
59
  dir = new Dir.mktmpdir(prefix_suffix, *rest)
@@ -58,6 +67,7 @@ class Path
58
67
  dir
59
68
  end
60
69
 
70
+ # @yieldparam [Path] tmpdir
61
71
  def tmpchdir(prefix_suffix = nil, *rest)
62
72
  tmpdir do |dir|
63
73
  dir.chdir do
@@ -67,19 +77,24 @@ class Path
67
77
  end
68
78
  end
69
79
 
70
- alias :/ :+
71
-
72
- alias :relative_to :relative_path_from
73
- alias :% :relative_path_from
74
-
80
+ # Whether +self+ is inside +ancestor+, such that +ancestor+ is an ancestor of +self+.
81
+ # This is pure String manipulation. Paths should be absolute.
75
82
  def inside? ancestor
76
83
  @path == ancestor.to_s or @path.start_with?("#{ancestor}/")
77
84
  end
78
85
 
86
+ # The opposite of {#inside?}.
79
87
  def outside? ancestor
80
88
  !inside?(ancestor)
81
89
  end
82
90
 
91
+ # Ascends the parents until it finds the given +path+.
92
+ #
93
+ # Path.backfind('lib') # => the lib folder
94
+ #
95
+ # It accepts an XPath-like context:
96
+ #
97
+ # Path.backfind('.[.git]') # => the root of the repository
83
98
  def backfind(path)
84
99
  condition = path[/\[(.*)\]$/, 1] || ''
85
100
  path = $` unless condition.empty?
@@ -1,7 +1,10 @@
1
1
  class Path
2
2
  class << self
3
+ # @!group Directory
4
+
3
5
  # Returns or yields Path objects. See +Dir.glob+.
4
- def glob(*args) # :yield: path
6
+ # @yieldparam [Path] path
7
+ def glob(*args)
5
8
  if block_given?
6
9
  Dir.glob(*args) { |f| yield new(f) }
7
10
  else
@@ -16,9 +19,22 @@ class Path
16
19
  alias :pwd :getwd
17
20
  end
18
21
 
22
+ # @!group Directory
23
+
19
24
  # Iterates over the entries (files and subdirectories) in the directory.
20
- # It yields a Path object for each entry.
21
- def each_entry(&block) # :yield: path
25
+ #
26
+ # Path("/usr/local").each_entry { |entry| p entry } # =>
27
+ # #<Path .>
28
+ # #<Path ..>
29
+ # #<Path lib>
30
+ # #<Path share>
31
+ # # ...
32
+ #
33
+ # @deprecated Use {#each_child} instead.
34
+ # This method is deprecated since it is too low level and likely useless in Ruby.
35
+ # But it is there for the sake of compatibility with Dir.foreach and Pathname#each_entry.
36
+ # @yieldparam [Path] entry
37
+ def each_entry(&block)
22
38
  Dir.foreach(@path) { |f| yield Path.new(f) }
23
39
  end
24
40
 
@@ -34,29 +50,33 @@ class Path
34
50
  end
35
51
 
36
52
  # See +Dir.open+.
37
- def opendir(&block) # :yield: dir
53
+ # @yieldparam [Dir] dir
54
+ def opendir(&block)
38
55
  Dir.open(@path, &block)
39
56
  end
40
57
 
58
+ # Returns or yields Path objects. See +Dir.glob+.
59
+ # @yieldparam [Path] path
41
60
  def glob(pattern, flags = 0)
42
61
  Dir.glob(join(pattern), flags).map(&Path)
43
62
  end
44
63
 
45
- # [DEPRECATED] Return the entries (files and subdirectories) in the directory.
64
+ # Return the entries (files and subdirectories) in the directory.
46
65
  # Each Path only contains the filename.
47
66
  # The result may contain the current directory #<Path .> and the parent directory #<Path ..>.
48
67
  #
49
- # Path('/usr/local').entries
50
- # # => [#<Path share>, #<Path lib>, #<Path .>, #<Path ..>, <Path bin>, ...]
51
- #
52
- # This method is deprecated, since it is too low level and likely useless in Ruby.
53
- # But it is there for the sake of compatibility with Dir.entries (and Pathname#entries)
68
+ # Path('/usr/local').entries
69
+ # # => [#<Path share>, #<Path lib>, #<Path .>, #<Path ..>, <Path bin>, ...]
54
70
  #
55
- # Use #children instead.
71
+ # @deprecated Use {#children} instead.
72
+ # This method is deprecated since it is too low level and likely useless in Ruby.
73
+ # But it is there for the sake of compatibility with Dir.entries (and Pathname#entries).
56
74
  def entries
57
75
  Dir.entries(@path).map(&Path)
58
76
  end
59
77
 
78
+ # Changes the current working directory of the process to self. See Dir.chdir.
79
+ # The recommended way to use it is to use the block form, or not use it all!
60
80
  def chdir(&block)
61
81
  Dir.chdir(@path, &block)
62
82
  end
@@ -92,9 +112,7 @@ class Path
92
112
  result
93
113
  end
94
114
 
95
- # Iterates over the children of the directory
96
- # (files and subdirectories, not recursive).
97
- # It yields Path object for each child.
115
+ # Iterates over the children of the directory (files and subdirectories, not recursive).
98
116
  # By default, the yielded paths will have enough information to access the files.
99
117
  # If you set +with_directory+ to +false+, then the returned paths will contain the filename only.
100
118
  #
@@ -117,6 +135,8 @@ class Path
117
135
  # #<Path sbin>
118
136
  # #<Path src>
119
137
  # #<Path man>
138
+ #
139
+ # @yieldparam [Path] child
120
140
  def each_child(with_directory=true, &b)
121
141
  children(with_directory).each(&b)
122
142
  end
@@ -1,10 +1,13 @@
1
1
  class Path
2
+ # @!group File
3
+
2
4
  # Returns last access time. See +File.atime+.
3
5
  def atime
4
6
  File.atime(@path)
5
7
  end
6
8
 
7
- # Returns last (directory entry, not file) change time. See +File.ctime+.
9
+ # Returns last change time (of the directory entry, not the file itself).
10
+ # See +File.ctime+.
8
11
  def ctime
9
12
  File.ctime(@path)
10
13
  end
@@ -14,17 +17,18 @@ class Path
14
17
  File.mtime(@path)
15
18
  end
16
19
 
17
- # Changes permissions. See +File.chmod+.
20
+ # Changes permissions of +path+. See +File.chmod+.
18
21
  def chmod(mode) File.chmod(mode, @path) end
19
22
 
20
- # See +File.lchmod+.
23
+ # Changes permissions of +path+, not following symlink. See +File.lchmod+.
21
24
  def lchmod(mode) File.lchmod(mode, @path) end
22
25
 
23
- # Change owner and group of file. See +File.chown+.
26
+ # Changes the owner and group of the file. See +File.chown+.
24
27
  def chown(owner, group)
25
28
  File.chown(owner, group, @path)
26
29
  end
27
30
 
31
+ # Changes the owner and group of +path+, not following symlink.
28
32
  # See +File.lchown+.
29
33
  def lchown(owner, group)
30
34
  File.lchown(owner, group, @path)
@@ -44,28 +48,28 @@ class Path
44
48
  self
45
49
  end
46
50
 
47
- # Read symbolic link. See +File.readlink+.
51
+ # Reads the symbolic link. See +File.readlink+.
48
52
  def readlink
49
53
  Path.new(File.readlink(@path))
50
54
  end
51
55
 
52
- # Rename the file and returns the new Path. See +File.rename+.
56
+ # Renames the file and returns the new Path. See +File.rename+.
53
57
  def rename(to)
54
58
  File.rename(@path, to)
55
59
  Path(to)
56
60
  end
57
61
 
58
- # Returns a +File::Stat+ object. See +File.stat+.
62
+ # Returns the stat of +path+ as a +File::Stat+ object. See +File.stat+.
59
63
  def stat
60
64
  File.stat(@path)
61
65
  end
62
66
 
63
- # See +File.lstat+.
67
+ # Returns the stat of +path+ as a +File::Stat+ object, not following symlink. See +File.lstat+.
64
68
  def lstat
65
69
  File.lstat(@path)
66
70
  end
67
71
 
68
- # See +File.size+.
72
+ # Returns the file size in bytes. See +File.size+.
69
73
  def size
70
74
  File.size(@path)
71
75
  end
@@ -79,17 +83,19 @@ class Path
79
83
  self
80
84
  end
81
85
 
82
- # Truncate the file to +length+ bytes. See +File.truncate+.
86
+ # Truncates the file to +length+ bytes. See +File.truncate+.
83
87
  def truncate(length) File.truncate(@path, length) end
84
88
 
85
- # Update the access and modification times. See +File.utime+.
89
+ # Updates the access and modification times. See +File.utime+.
86
90
  def utime(atime, mtime) File.utime(atime, mtime, @path) end
87
91
 
88
- # See +File.expand_path+.
89
- def expand_path(*args)
92
+ # Expands +path+, making it absolute.
93
+ # If the path is relative, it is expanded with the current working directory,
94
+ # unless +dir+ is given as an argument. See +File.expand_path+.
95
+ def expand(*args)
90
96
  Path.new(File.expand_path(@path, *args))
91
97
  end
92
- alias :expand :expand_path
98
+ alias :expand_path :expand
93
99
 
94
100
  # Returns the real (absolute) path of +self+ in the actual
95
101
  # filesystem not containing symlinks or useless dots.
@@ -1,6 +1,8 @@
1
1
  # All methods from FileTest and all predicates from File are included
2
2
 
3
3
  class Path
4
+ # @!group File predicates
5
+
4
6
  # See +File.blockdev?+.
5
7
  def blockdev?
6
8
  File.blockdev?(@path)
@@ -128,7 +130,7 @@ class Path
128
130
  end
129
131
 
130
132
  # See +File.zero?+.
131
- # empty? is not defined in File/FileTest, but is is clearer
133
+ # empty? is not defined in File/FileTest, but is is clearer.
132
134
  def zero?
133
135
  File.zero?(@path)
134
136
  end
@@ -1,6 +1,8 @@
1
1
  require 'fileutils'
2
2
 
3
3
  class Path
4
+ # @!group File utilities
5
+
4
6
  # Creates a full path, including any intermediate directories that don't yet exist.
5
7
  # See +FileUtils.mkpath+.
6
8
  def mkpath
@@ -18,37 +20,96 @@ class Path
18
20
  end
19
21
  alias :rm_r :rmtree
20
22
 
23
+ # Removes the file using +FileUtils.rm+.
21
24
  def rm
22
25
  FileUtils.rm(@path)
23
26
  self
24
27
  end
25
28
 
29
+ # Removes the file, ignoring errors, using +FileUtils.rm_f+.
26
30
  def rm_f
27
31
  FileUtils.rm_f(@path)
28
32
  self
29
33
  end
34
+ alias :safe_unlink :rm_f
35
+
36
+ # Removes the file or directory recursively, using +FileUtils.rm_r+.
37
+ def rm_r
38
+ FileUtils.rm_r(@path)
39
+ self
40
+ end
30
41
 
42
+ # Removes the file or directory recursively, ignoring errors,
43
+ # using +FileUtils.rm_f+.
31
44
  def rm_rf
32
45
  FileUtils.rm_rf(@path)
33
46
  self
34
47
  end
35
48
 
49
+ # Copies the file to +to+. See +FileUtils.cp+.
36
50
  def cp(to)
37
- FileUtils.cp(@path, to)
51
+ # TODO: remove :preserve when all implement it correctly (r31123)
52
+ FileUtils.cp(@path, to, :preserve => true)
38
53
  end
39
- alias copy cp
54
+ alias :copy :cp
40
55
 
56
+ # Copies the file or directory recursively to the directory +to+.
57
+ # See +FileUtils.cp_r+.
41
58
  def cp_r(to)
42
59
  FileUtils.cp_r(@path, to)
43
60
  end
44
61
 
62
+ # Updates access and modification time or create an empty file.
45
63
  def touch
46
- FileUtils.touch(@path)
64
+ if exist?
65
+ now = Time.now
66
+ File.utime(now, now, @path)
67
+ else
68
+ open('w') {}
69
+ end
47
70
  self
48
71
  end
49
72
 
73
+ # {#touch} preceded by +dir.+{#mkpath}.
50
74
  def touch!
51
- dirname.mkpath
75
+ dir.mkpath
52
76
  touch
53
77
  end
78
+
79
+ # Moves +self+ to the +to+ directory.
80
+ def mv(to)
81
+ FileUtils.mv(@path, to)
82
+ to
83
+ end
84
+ alias :move :mv
85
+
86
+ # Install +file+ into +path+ (the "prefix", which should be a directory).
87
+ # If +file+ is not same as +path/file+, replaces it.
88
+ # See +FileUtils.install+ (arguments are swapped).
89
+ def install(file, options = {})
90
+ FileUtils.install(file, @path, options)
91
+ end
92
+
93
+ # Recusively changes permissions. See +FileUtils.chmod_R+ and +File.chmod+.
94
+ def chmod_r(mode)
95
+ FileUtils.chmod_R(mode, @path)
96
+ end
97
+
98
+ # Recusively changes owner and group. See +FileUtils.chown_R+ and +File.chown+.
99
+ def chown_r(owner, group)
100
+ FileUtils.chown_R(owner, group, @path)
101
+ end
102
+
103
+ # Whether the contents of +path+ and +file+ are identical.
104
+ # See +FileUtils.compare_file+.
105
+ def has_same_contents?(file)
106
+ FileUtils.compare_file(@path, file)
107
+ end
108
+
109
+ # Returns whether +self+ is newer than all +others+.
110
+ # Non-existent files are older than any file.
111
+ # See +FileUtils.uptodate?+.
112
+ def uptodate?(*others)
113
+ FileUtils.uptodate?(@path, others)
114
+ end
54
115
  end
@@ -9,7 +9,9 @@ class Path
9
9
  #
10
10
  # If +self+ is +.+, yielded paths begin with a filename in the
11
11
  # current directory, not +./+.
12
- def find # :yield: path
12
+ #
13
+ # @yieldparam [Path] path
14
+ def find
13
15
  return to_enum(__method__) unless block_given?
14
16
  require 'find'
15
17
  if @path == '.'
@@ -1,5 +1,8 @@
1
1
  class Path
2
2
  class << self
3
+ # @!group Identity
4
+
5
+ # Creates a new Path. See {#initialize}.
3
6
  def new(*args)
4
7
  if args.size == 1 and Path === args[0]
5
8
  args[0]
@@ -9,11 +12,16 @@ class Path
9
12
  end
10
13
  alias :[] :new
11
14
 
15
+ # A class constructor.
16
+ #
17
+ # %w[foo bar].map(&Path) # == [Path('foo'), Path('bar')]
12
18
  def to_proc
13
19
  lambda { |path| new(path) }
14
20
  end
15
21
  end
16
22
 
23
+ # @!group Identity
24
+
17
25
  # Compare this path with +other+. The comparison is string-based.
18
26
  # Be aware that two different paths (+foo.txt+ and +./foo.txt+)
19
27
  # can refer to the same file.
@@ -28,11 +36,12 @@ class Path
28
36
  @path.tr('/', "\0") <=> other.to_s.tr('/', "\0")
29
37
  end
30
38
 
39
+ # The hash value of the +path+.
31
40
  def hash
32
41
  @path.hash
33
42
  end
34
43
 
35
- # Return the path as a String.
44
+ # Returns the +path+ as a String.
36
45
  def to_s
37
46
  @path
38
47
  end
@@ -40,18 +49,22 @@ class Path
40
49
  # to_path is implemented so Path objects are usable with File.open, etc.
41
50
  alias :to_path :to_s
42
51
 
52
+ # to_str is implemented so Path objects are usable with File.open, etc in Ruby 1.8.
43
53
  alias :to_str :to_s if RUBY_VERSION < '1.9'
44
54
 
55
+ # Returns the +path+ as a Symbol.
45
56
  def to_sym
46
57
  @path.to_sym
47
58
  end
48
59
 
60
+ # A representation of the +path+.
49
61
  def inspect
50
62
  "#<Path #{@path}>"
51
63
  end
52
64
  end
53
65
 
54
66
  unless defined? NO_EPATH_GLOBAL_FUNCTION
67
+ # A shorthand method to create a {Path}. Same as {Path.new}.
55
68
  def Path(*args)
56
69
  Path.new(*args)
57
70
  end
@@ -8,8 +8,12 @@ class Path
8
8
  lambda { |a,b| a == b }
9
9
  end
10
10
 
11
+ # Creates a new Path.
12
+ # If multiple arguments are given, they are joined with File.join.
13
+ # The path will have File::ALT_SEPARATOR replaced with '/' and
14
+ # if it begins with a '~', it will be expanded (using File.expand_path).
11
15
  def initialize(*parts)
12
- path = parts.size > 1 ? File.join(parts) : parts.first
16
+ path = parts.size > 1 ? File.join(*parts) : parts.first
13
17
  @path = case path
14
18
  when Tempfile
15
19
  @_tmpfile = path # We would not want it to be GC'd
@@ -23,15 +27,24 @@ class Path
23
27
  init
24
28
  end
25
29
 
30
+ # YAML loading.
26
31
  def yaml_initialize(tag, ivars)
27
32
  @path = ivars['path']
28
33
  init
29
34
  end
30
35
 
36
+ # Psych loading.
37
+ def init_with(coder)
38
+ @path = coder['path']
39
+ init
40
+ end
41
+
42
+ # Marshal dumping.
31
43
  def marshal_dump
32
44
  @path
33
45
  end
34
46
 
47
+ # Marshal loading.
35
48
  def marshal_load path
36
49
  @path = path
37
50
  init
@@ -43,7 +56,7 @@ class Path
43
56
  # If +consider_symlink+ is +true+, then a more conservative algorithm is used
44
57
  # to avoid breaking symbolic linkages. This may retain more +..+
45
58
  # entries than absolutely necessary, but without accessing the filesystem,
46
- # this can't be avoided. See #realpath.
59
+ # this can't be avoided. See {#realpath}.
47
60
  def cleanpath(consider_symlink=false)
48
61
  if consider_symlink
49
62
  cleanpath_conservative
@@ -53,34 +66,34 @@ class Path
53
66
  end
54
67
 
55
68
  # #parent returns the parent directory.
56
- #
57
- # This is same as <tt>self + '..'</tt>.
69
+ # This can be chained.
58
70
  def parent
59
- self + '..'
71
+ self / '..'
60
72
  end
61
73
 
62
- # Path#+ appends a path fragment to this one to produce a new Path.
74
+ # Path#/ appends a path fragment to this one to produce a new Path.
63
75
  #
64
- # p1 = Path.new("/usr") # => #<Path /usr>
65
- # p2 = p1 + "bin/ruby" # => #<Path /usr/bin/ruby>
66
- # p3 = p1 + "/etc/passwd" # => #<Path /etc/passwd>
76
+ # p = Path.new("/usr") # => #<Path /usr>
77
+ # p / "bin/ruby" # => #<Path /usr/bin/ruby>
78
+ # p / "/etc/passwd" # => #<Path /etc/passwd>
67
79
  #
68
80
  # This method doesn't access the file system, it is pure string manipulation.
69
- def +(other)
81
+ def /(other)
70
82
  Path.new(plus(@path, other.to_s))
71
83
  end
84
+ alias :+ :/
72
85
 
73
86
  # Path#join joins paths.
74
87
  #
75
88
  # <tt>path0.join(path1, ..., pathN)</tt> is the same as
76
- # <tt>path0 + path1 + ... + pathN</tt>.
89
+ # <tt>path0 / path1 / ... / pathN</tt>.
77
90
  def join(*args)
78
91
  args.unshift self
79
92
  result = Path.new(args.pop)
80
93
  return result if result.absolute?
81
94
  args.reverse_each { |arg|
82
95
  arg = Path.new(arg)
83
- result = arg + result
96
+ result = arg / result
84
97
  return result if result.absolute?
85
98
  }
86
99
  result
@@ -126,6 +139,8 @@ class Path
126
139
  Path.new(*relpath_names)
127
140
  end
128
141
  end
142
+ alias :relative_to :relative_path_from
143
+ alias :% :relative_path_from
129
144
 
130
145
  # @private
131
146
  module Helpers
@@ -148,7 +163,7 @@ class Path
148
163
  private
149
164
 
150
165
  def init
151
- validate(@path)
166
+ @path = validate(@path)
152
167
 
153
168
  taint if @path.tainted?
154
169
  @path.freeze
@@ -158,6 +173,8 @@ class Path
158
173
  def validate(path)
159
174
  raise ArgumentError, "path contains a null byte: #{path.inspect}" if path.include? "\0"
160
175
  path.gsub!(File::ALT_SEPARATOR, '/') if File::ALT_SEPARATOR
176
+ path = File.expand_path(path) if path.start_with? '~'
177
+ path
161
178
  end
162
179
 
163
180
  # chop_basename(path) -> [pre-basename, basename] or nil
@@ -211,8 +228,8 @@ class Path
211
228
  if r = chop_basename(path)
212
229
  pre, basename = r
213
230
  pre + basename
214
- elsif /\/+\z/o =~ path
215
- $` + File.dirname(path)[/\/*\z/o]
231
+ elsif %r{/+\z} =~ path
232
+ $` + File.dirname(path)[%r{/*\z}]
216
233
  else
217
234
  path
218
235
  end
@@ -1,11 +1,15 @@
1
1
  class Path
2
+ # @!group IO
3
+
2
4
  # Opens the file for reading or writing. See +File.open+.
3
- def open(*args, &block) # :yield: file
5
+ # @yieldparam [File] file
6
+ def open(*args, &block)
4
7
  File.open(@path, *args, &block)
5
8
  end
6
9
 
7
10
  # Iterates over the lines in the file. See +IO.foreach+.
8
- def each_line(*args, &block) # :yield: line
11
+ # @yieldparam [String] line
12
+ def each_line(*args, &block)
9
13
  IO.foreach(@path, *args, &block)
10
14
  end
11
15
  alias :lines :each_line
@@ -37,6 +41,7 @@ class Path
37
41
  end
38
42
 
39
43
  if IO.respond_to? :write
44
+ # Writes +contents+ to +self+. See +IO.write+ or +IO#write+.
40
45
  def write(contents, *open_args)
41
46
  IO.write(@path, contents, *open_args)
42
47
  end
@@ -47,13 +52,14 @@ class Path
47
52
  end
48
53
 
49
54
  if IO.respond_to? :write and !RUBY_DESCRIPTION.start_with?('jruby')
55
+ # Appends +contents+ to +self+. See +IO.write+ or +IO#write+.
50
56
  def append(contents, open_args = {})
51
57
  open_args[:mode] = 'a'
52
58
  IO.write(@path, contents, open_args)
53
59
  end
54
60
  else
55
- def append(contents, open_args = nil)
56
- open('a') { |f| f.write(contents) }
61
+ def append(contents, *open_args)
62
+ open('a', *open_args) { |f| f.write(contents) }
57
63
  end
58
64
  end
59
65
  end
@@ -1,4 +1,5 @@
1
1
  class Path
2
+ # @!group Loading
2
3
 
3
4
  # The list of loaders. See {Path.register_loader}.
4
5
  LOADERS = {}
@@ -7,6 +8,8 @@ class Path
7
8
  # for the given extensions (either with the leading dot or not)
8
9
  #
9
10
  # Path.register_loader('.marshal') { |file| Marshal.load file.read }
11
+ #
12
+ # @yieldparam [Path] path
10
13
  def self.register_loader(*extensions, &loader)
11
14
  extensions.each { |ext|
12
15
  LOADERS[pure_ext(ext)] = loader
@@ -15,7 +18,7 @@ class Path
15
18
 
16
19
  # Path#load helps loading data from various files.
17
20
  # JSON and YAML loaders are provided by default.
18
- # See Path.register_loader.
21
+ # See {Path.register_loader}.
19
22
  def load
20
23
  if LOADERS.key? ext
21
24
  LOADERS[ext].call(self)
@@ -1,4 +1,6 @@
1
1
  class Path
2
+ # @!group Path parts
3
+
2
4
  # Returns the last component of the path. See +File.basename+.
3
5
  def basename(*args)
4
6
  Path.new(File.basename(@path, *args))
@@ -9,18 +11,23 @@ class Path
9
11
  basename(extname)
10
12
  end
11
13
 
12
- # Returns all but the last component of the path. See +File.dirname+.
14
+ # Returns all but the last component of the path.
15
+ #
16
+ # Don't chain this when the path is relative:
17
+ # Path('.').dir # => #<Path .>
18
+ # Use #parent instead.
19
+ # See +File.dirname+.
13
20
  def dirname
14
21
  Path.new(File.dirname(@path))
15
22
  end
16
23
  alias :dir :dirname
17
24
 
18
- # Returns the file's extension. See +File.extname+.
25
+ # Returns the extension, with a leading dot. See +File.extname+.
19
26
  def extname
20
27
  File.extname(@path)
21
28
  end
22
29
 
23
- # extname without leading .
30
+ # {#extname} without leading dot.
24
31
  def ext
25
32
  ext = extname
26
33
  ext.empty? ? ext : ext[1..-1]
@@ -31,17 +38,31 @@ class Path
31
38
  File.split(@path).map(&Path)
32
39
  end
33
40
 
41
+ # Adds +ext+ as an extension to +path+.
42
+ # Handle both extensions with or without leading dot.
43
+ # No-op if +ext+ is +empty?+.
44
+ #
45
+ # Path('file').add_extension('txt') # => #<Path file.txt>
34
46
  def add_extension(ext)
35
47
  return self if ext.empty?
36
48
  Path.new @path+dotted_ext(ext)
37
49
  end
38
50
  alias :add_ext :add_extension
39
51
 
52
+ # Removes the last extension of +path+.
53
+ #
54
+ # Path('script.rb').without_extension # => #<Path script>
55
+ # Path('archive.tar.gz').without_extension # => #<Path archive.tar>
40
56
  def without_extension
41
57
  Path.new @path[0..-extname.size-1]
42
58
  end
43
59
  alias :rm_ext :without_extension
44
60
 
61
+ # Replaces the last extension of +path+ with +ext+.
62
+ # Handle both extensions with or without leading dot.
63
+ # Removes last extension if +ext+ is +empty?+.
64
+ #
65
+ # Path('main.c++').replace_extension('cc') # => #<Path main.cc>
45
66
  def replace_extension(ext)
46
67
  return without_extension if ext.empty?
47
68
  Path.new(@path[0..-extname.size-1] << dotted_ext(ext))
@@ -52,15 +73,16 @@ class Path
52
73
  #
53
74
  # Path.new("/usr/bin/ruby").each_filename { |filename| ... }
54
75
  # # yields "usr", "bin", and "ruby".
55
- def each_filename # :yield: filename
76
+ #
77
+ # @yieldparam [String] filename
78
+ def each_filename
56
79
  return to_enum(__method__) unless block_given?
57
80
  _, names = split_names(@path)
58
81
  names.each { |filename| yield filename }
59
82
  nil
60
83
  end
61
84
 
62
- # Iterates over and yields a new Path object
63
- # for each element in the given path in descending order.
85
+ # Iterates over each element in the given path in descending order.
64
86
  #
65
87
  # Path.new('/path/to/some/file.rb').descend { |v| p v }
66
88
  # #<Path />
@@ -76,6 +98,7 @@ class Path
76
98
  # #<Path path/to/some/file.rb>
77
99
  #
78
100
  # It doesn't access actual filesystem.
101
+ # @yieldparam [Path] path
79
102
  def descend
80
103
  return to_enum(:descend) unless block_given?
81
104
  vs = []
@@ -84,8 +107,7 @@ class Path
84
107
  nil
85
108
  end
86
109
 
87
- # Iterates over and yields a new Path object
88
- # for each element in the given path in ascending order.
110
+ # Iterates over each element in the given path in ascending order.
89
111
  #
90
112
  # Path.new('/path/to/some/file.rb').ascend { |v| p v }
91
113
  # #<Path /path/to/some/file.rb>
@@ -101,6 +123,7 @@ class Path
101
123
  # #<Path path>
102
124
  #
103
125
  # It doesn't access actual filesystem.
126
+ # @yieldparam [Path] path
104
127
  def ascend
105
128
  return to_enum(:ascend) unless block_given?
106
129
  path = @path
@@ -1,4 +1,6 @@
1
1
  class Path
2
+ # @!group Path predicates
3
+
2
4
  # Whether a path is absolute.
3
5
  def absolute?
4
6
  !relative?
@@ -1,4 +1,8 @@
1
1
  class Path
2
+ # @!group Requiring
3
+
4
+ # Requires all .rb files recursively (in alphabetic order)
5
+ # under +directory+ (or this file's directory if not given).
2
6
  def self.require_tree(directory = nil)
3
7
  if directory
4
8
  new(directory).require_tree
@@ -8,7 +12,11 @@ class Path
8
12
  end
9
13
  end
10
14
 
15
+ # @api private
16
+ # See {Path.require_tree}.
17
+ # It is not a real private method because {Path.require_tree}
18
+ # (so the {Path} class) needs to be able to call it.
11
19
  def require_tree(source = nil)
12
- glob('**/*.rb').sort.each { |file| require file.expand(dir) unless file == source }
20
+ glob('**/*.rb').sort.each { |file| require file.expand(dir).to_s unless file == source }
13
21
  end
14
22
  end
@@ -1,3 +1,5 @@
1
1
  class Path
2
- VERSION = '0.1.1'
2
+ # The version of the gem.
3
+ # Set here to avoid duplication and allow introspection.
4
+ VERSION = '0.2.0'
3
5
  end
metadata CHANGED
@@ -1,45 +1,38 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: epath
3
- version: !ruby/object:Gem::Version
4
- hash: 25
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - eregon
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-03-18 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-06-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: rspec
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 3
29
- segments:
30
- - 0
31
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
32
22
  type: :development
33
- version_requirements: *id001
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
34
30
  description:
35
31
  email: eregontp@gmail.com
36
32
  executables: []
37
-
38
33
  extensions: []
39
-
40
34
  extra_rdoc_files: []
41
-
42
- files:
35
+ files:
43
36
  - lib/epath/dir.rb
44
37
  - lib/epath/file.rb
45
38
  - lib/epath/file_dir.rb
@@ -60,37 +53,27 @@ files:
60
53
  - epath.gemspec
61
54
  homepage: https://github.com/eregon/epath
62
55
  licenses: []
63
-
64
56
  post_install_message:
65
57
  rdoc_options: []
66
-
67
- require_paths:
58
+ require_paths:
68
59
  - lib
69
- required_ruby_version: !ruby/object:Gem::Requirement
60
+ required_ruby_version: !ruby/object:Gem::Requirement
70
61
  none: false
71
- requirements:
72
- - - ">="
73
- - !ruby/object:Gem::Version
74
- hash: 3
75
- segments:
76
- - 0
77
- version: "0"
78
- required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
67
  none: false
80
- requirements:
81
- - - ">="
82
- - !ruby/object:Gem::Version
83
- hash: 3
84
- segments:
85
- - 0
86
- version: "0"
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
87
72
  requirements: []
88
-
89
73
  rubyforge_project:
90
- rubygems_version: 1.8.15
74
+ rubygems_version: 1.8.23
91
75
  signing_key:
92
76
  specification_version: 3
93
77
  summary: a Path manipulation library
94
78
  test_files: []
95
-
96
79
  has_rdoc: