epath 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|