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 +12 -3
- data/lib/epath.rb +26 -11
- data/lib/epath/dir.rb +34 -14
- data/lib/epath/file.rb +20 -14
- data/lib/epath/file_predicates.rb +3 -1
- data/lib/epath/fileutils.rb +65 -4
- data/lib/epath/find.rb +3 -1
- data/lib/epath/identity.rb +14 -1
- data/lib/epath/implementation.rb +32 -15
- data/lib/epath/io.rb +10 -4
- data/lib/epath/load.rb +4 -1
- data/lib/epath/parts.rb +31 -8
- data/lib/epath/predicates.rb +2 -0
- data/lib/epath/require_tree.rb +9 -1
- data/lib/epath/version.rb +3 -1
- metadata +32 -49
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
|
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
|
-
*
|
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
|
data/lib/epath.rb
CHANGED
@@ -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
|
-
|
22
|
-
|
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
|
-
|
26
|
-
|
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
|
-
|
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?
|
data/lib/epath/dir.rb
CHANGED
@@ -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
|
-
|
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
|
-
#
|
21
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
50
|
-
#
|
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
|
data/lib/epath/file.rb
CHANGED
@@ -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)
|
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
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
86
|
+
# Truncates the file to +length+ bytes. See +File.truncate+.
|
83
87
|
def truncate(length) File.truncate(@path, length) end
|
84
88
|
|
85
|
-
#
|
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
|
-
#
|
89
|
-
|
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 :
|
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
|
data/lib/epath/fileutils.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/epath/find.rb
CHANGED
@@ -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
|
-
|
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 == '.'
|
data/lib/epath/identity.rb
CHANGED
@@ -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
|
-
#
|
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
|
data/lib/epath/implementation.rb
CHANGED
@@ -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
|
74
|
+
# Path#/ appends a path fragment to this one to produce a new Path.
|
63
75
|
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
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
|
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
|
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
|
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
|
215
|
-
$` + File.dirname(path)[
|
231
|
+
elsif %r{/+\z} =~ path
|
232
|
+
$` + File.dirname(path)[%r{/*\z}]
|
216
233
|
else
|
217
234
|
path
|
218
235
|
end
|
data/lib/epath/io.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
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
|
data/lib/epath/load.rb
CHANGED
@@ -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)
|
data/lib/epath/parts.rb
CHANGED
@@ -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.
|
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
|
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
|
-
|
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
|
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
|
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
|
data/lib/epath/predicates.rb
CHANGED
data/lib/epath/require_tree.rb
CHANGED
@@ -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
|
data/lib/epath/version.rb
CHANGED
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
|
-
|
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
|
-
|
19
|
-
|
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
|
-
|
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
|
-
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
32
22
|
type: :development
|
33
|
-
|
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
|
-
|
75
|
-
|
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
|
-
|
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.
|
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:
|