epath 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +60 -21
- data/epath.gemspec +3 -1
- data/lib/epath.rb +37 -126
- data/lib/epath/dir.rb +123 -0
- data/lib/epath/file.rb +109 -0
- data/lib/epath/file_dir.rb +12 -0
- data/lib/epath/file_predicates.rb +149 -0
- data/lib/epath/fileutils.rb +45 -0
- data/lib/epath/find.rb +21 -0
- data/lib/epath/identity.rb +58 -0
- data/lib/epath/implementation.rb +148 -666
- data/lib/epath/io.rb +59 -0
- data/lib/epath/load.rb +26 -0
- data/lib/epath/parts.rb +115 -0
- data/lib/epath/predicates.rb +35 -0
- data/lib/epath/require_tree.rb +14 -0
- metadata +65 -19
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Path -
|
1
|
+
# Path - a Path manipulation library
|
2
2
|
|
3
3
|
Path is a library to manage paths.
|
4
4
|
It is similar to Pathname, but has some extra goodness.
|
@@ -9,6 +9,11 @@ Paths are naturally the subject of their methods and even if they are simple Str
|
|
9
9
|
|
10
10
|
Also, using a path library like this avoid to remember in which class the functionality is implemented, everything is in one place (if not, please open an issue!).
|
11
11
|
|
12
|
+
## Links
|
13
|
+
|
14
|
+
* [GitHub](https://github.com/eregon/epath)
|
15
|
+
* [YARD Documentation](http://rubydoc.info/github/eregon/epath/master/file/README.md)
|
16
|
+
|
12
17
|
## API
|
13
18
|
|
14
19
|
All the useful methods of `File` (and so `IO`) and `Dir` should be included.
|
@@ -26,10 +31,10 @@ Path.new('/usr', 'bin')
|
|
26
31
|
```
|
27
32
|
|
28
33
|
``` ruby
|
29
|
-
Path.
|
30
|
-
Path.dir
|
34
|
+
Path.file # == Path(__FILE__).expand
|
35
|
+
Path.dir # == Path(File.dirname(__FILE__)).expand
|
31
36
|
Path.relative(path) # == Path(File.expand_path("../#{path}", __FILE__))
|
32
|
-
Path.home
|
37
|
+
Path.home # == Path(File.expand_path('~'))
|
33
38
|
```
|
34
39
|
|
35
40
|
### temporary
|
@@ -41,24 +46,55 @@ Path.tmpdir
|
|
41
46
|
|
42
47
|
### aliases
|
43
48
|
|
44
|
-
* expand =>
|
45
|
-
*
|
49
|
+
* expand => expand\_path
|
50
|
+
* relative\_to => relative\_path\_from
|
46
51
|
|
47
52
|
### parts
|
48
53
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
54
|
+
Path can split a path in two ways:
|
55
|
+
|
56
|
+
The first way is the one done by File methods (dirname, basename, extname).
|
57
|
+
|
58
|
+
The second is Path's own way in which the base is given without the extension and the extension is given without the leading dot.
|
59
|
+
The rationale behind this is to have a true three-components path, splitting on the / and the . (See [this issue](https://github.com/eregon/epath/pull/8#issuecomment-3499030) for details)
|
60
|
+
|
61
|
+
dirname basename
|
62
|
+
____________ ______
|
63
|
+
/ \ / \
|
64
|
+
/some/path/dir/file.ext
|
65
|
+
\____________/ \__/ \_/
|
66
|
+
dir base ext
|
67
|
+
|
68
|
+
path = dirname / basename
|
69
|
+
path = dirname / basename(extname) extname
|
70
|
+
path = dir / base [. ext]
|
71
|
+
|
72
|
+
* dirname: "/some/path/dir"
|
73
|
+
* basename: "file.ext"
|
74
|
+
* extname: ".ext"
|
75
|
+
|
76
|
+
<!-- -->
|
77
|
+
|
78
|
+
* dir: alias of dirname: "/some/paths/dir"
|
79
|
+
* base: basename(extname), the basename without the extension: "file"
|
80
|
+
* ext: extname without the leading dot: "ext"
|
81
|
+
|
82
|
+
<!-- -->
|
83
|
+
|
84
|
+
### join
|
85
|
+
|
86
|
+
* join(*parts)
|
87
|
+
* /: join paths (as Pathname#+)
|
53
88
|
|
54
89
|
```ruby
|
55
90
|
Path('/usr')/'bin'
|
56
91
|
```
|
57
92
|
|
58
|
-
|
93
|
+
### extensions
|
59
94
|
|
60
|
-
*
|
61
|
-
*
|
95
|
+
* add\_ext / add\_extension
|
96
|
+
* rm\_ext / without\_extension
|
97
|
+
* sub\_ext(new\_ext) / replace\_extension(new\_ext)
|
62
98
|
|
63
99
|
### glob
|
64
100
|
|
@@ -67,13 +103,15 @@ These method names are too long, any idea to make them shorter and clear?
|
|
67
103
|
|
68
104
|
### structure
|
69
105
|
|
70
|
-
* ancestors: self and all the parent directories
|
106
|
+
* ascend, ancestors: self and all the parent directories
|
107
|
+
* descend: in the reverse order
|
71
108
|
* backfind: ascends the parents until it finds the given path
|
72
109
|
|
73
110
|
``` ruby
|
74
111
|
# Path.backfind is Path.here.backfind
|
75
112
|
Path.backfind('lib') # => Path's lib folder
|
76
|
-
|
113
|
+
|
114
|
+
# It accepts XPath-like context
|
77
115
|
Path.backfind('.[.git]') # => the root of this repository
|
78
116
|
```
|
79
117
|
|
@@ -86,12 +124,12 @@ Path.backfind('.[.git]') # => the root of this repository
|
|
86
124
|
### management
|
87
125
|
|
88
126
|
* mkdir
|
89
|
-
*
|
90
|
-
*
|
127
|
+
* mkdir\_p
|
128
|
+
* rm\_rf
|
91
129
|
|
92
|
-
###
|
130
|
+
### require
|
93
131
|
|
94
|
-
*
|
132
|
+
* Path.require\_tree: require all .rb files recursively (in alphabetic order)
|
95
133
|
|
96
134
|
## Status
|
97
135
|
|
@@ -101,6 +139,7 @@ This is still in the early development stage, you should expect many additions a
|
|
101
139
|
|
102
140
|
Benoit Daloze - eregon
|
103
141
|
|
104
|
-
|
142
|
+
## Contributors
|
105
143
|
|
106
|
-
Bernard Lambeau - blambeau
|
144
|
+
Bernard Lambeau - blambeau
|
145
|
+
Ravil Bayramgalin - brainopia
|
data/epath.gemspec
CHANGED
@@ -5,5 +5,7 @@ Gem::Specification.new do |s|
|
|
5
5
|
s.email = 'eregontp@gmail.com'
|
6
6
|
s.homepage = 'https://github.com/eregon/epath'
|
7
7
|
s.files = Dir['lib/**/*.rb'] + %w[README.md LICENSE epath.gemspec]
|
8
|
-
s.version = '0.0
|
8
|
+
s.version = '0.1.0'
|
9
|
+
|
10
|
+
s.add_development_dependency 'rspec'
|
9
11
|
end
|
data/lib/epath.rb
CHANGED
@@ -1,34 +1,20 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# Path - a Path manipulation library
|
2
|
+
|
3
|
+
Dir.glob(File.expand_path('../epath/*.rb',__FILE__)) { |file| require file }
|
3
4
|
|
4
|
-
require File.expand_path('../epath/implementation', __FILE__)
|
5
5
|
require 'tempfile'
|
6
6
|
|
7
7
|
class Path
|
8
|
-
DOTS = %w[. ..]
|
9
|
-
|
10
8
|
class << self
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
super(*args)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
alias_method :[], :new
|
19
|
-
|
20
|
-
def to_proc
|
21
|
-
lambda { |path| new(path) }
|
22
|
-
end
|
23
|
-
|
24
|
-
def here(from = nil)
|
25
|
-
from ||= caller
|
26
|
-
new(from.first.split(/:\d+(?:$|:in)/).first).expand
|
9
|
+
def file(from = nil)
|
10
|
+
from ||= caller # this can not be moved as a default argument, JRuby optimizes it
|
11
|
+
# v This : is there to define a group without capturing
|
12
|
+
new(from.first.rpartition(/:\d+(?:$|:in )/).first).expand
|
27
13
|
end
|
28
|
-
|
14
|
+
alias :here :file
|
29
15
|
|
30
16
|
def dir(from = nil)
|
31
|
-
from ||= caller
|
17
|
+
from ||= caller # this can not be moved as a default argument, JRuby optimizes it
|
32
18
|
file(from).dir
|
33
19
|
end
|
34
20
|
|
@@ -41,7 +27,7 @@ class Path
|
|
41
27
|
end
|
42
28
|
|
43
29
|
def backfind(path)
|
44
|
-
|
30
|
+
file(caller).backfind(path)
|
45
31
|
end
|
46
32
|
|
47
33
|
def tmpfile(basename = '', tmpdir = nil, options = nil)
|
@@ -57,7 +43,7 @@ class Path
|
|
57
43
|
end
|
58
44
|
file
|
59
45
|
end
|
60
|
-
|
46
|
+
alias :tempfile :tmpfile
|
61
47
|
|
62
48
|
def tmpdir(prefix_suffix = nil, *rest)
|
63
49
|
require 'tmpdir'
|
@@ -71,108 +57,27 @@ class Path
|
|
71
57
|
end
|
72
58
|
dir
|
73
59
|
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def initialize(*parts)
|
77
|
-
path = parts.size > 1 ? parts.join(File::SEPARATOR) : parts.first
|
78
|
-
if Tempfile === path
|
79
|
-
@_tmpfile = path # We would not want it to be GC'd
|
80
|
-
@path = path.path
|
81
|
-
elsif String === path
|
82
|
-
@path = path.dup
|
83
|
-
else
|
84
|
-
@path = path.to_s
|
85
|
-
end
|
86
|
-
taint if @path.tainted?
|
87
|
-
end
|
88
|
-
|
89
|
-
def / part
|
90
|
-
join part.to_s
|
91
|
-
end
|
92
|
-
|
93
|
-
def base # basename(extname)
|
94
|
-
basename(extname)
|
95
|
-
end
|
96
|
-
|
97
|
-
def ext # extname without leading .
|
98
|
-
ext = extname
|
99
|
-
ext.empty? ? ext : ext[1..-1]
|
100
|
-
end
|
101
|
-
|
102
|
-
def without_extension # rm_ext
|
103
|
-
dir / base
|
104
|
-
end
|
105
|
-
|
106
|
-
# NOTE: Pathname has a similar feature named sub_ext
|
107
|
-
# It might be a better method name
|
108
|
-
def replace_extension(ext)
|
109
|
-
ext = ".#{ext}" unless ext.start_with? '.'
|
110
|
-
Path.new(without_extension.to_s + ext)
|
111
|
-
end
|
112
60
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
120
|
-
|
121
|
-
def rm
|
122
|
-
FileUtils.rm(@path)
|
123
|
-
self
|
124
|
-
end
|
125
|
-
|
126
|
-
def rm_f
|
127
|
-
FileUtils.rm_f(@path)
|
128
|
-
self
|
129
|
-
end
|
130
|
-
|
131
|
-
def rm_rf
|
132
|
-
FileUtils.rm_rf(@path)
|
133
|
-
self
|
134
|
-
end
|
135
|
-
|
136
|
-
def mkdir_p
|
137
|
-
FileUtils.mkdir_p(@path)
|
138
|
-
self
|
139
|
-
end
|
140
|
-
|
141
|
-
def write(contents, open_args = nil)
|
142
|
-
if IO.respond_to? :write
|
143
|
-
IO.write(@path, contents, *[open_args].compact)
|
144
|
-
else
|
145
|
-
open('w', *[open_args].compact) { |f| f.write(contents) }
|
61
|
+
def tmpchdir(prefix_suffix = nil, *rest)
|
62
|
+
tmpdir do |dir|
|
63
|
+
dir.chdir do
|
64
|
+
yield dir
|
65
|
+
end
|
66
|
+
end
|
146
67
|
end
|
147
68
|
end
|
148
69
|
|
149
|
-
|
150
|
-
if IO.respond_to? :write
|
151
|
-
open_args = (Array(open_args) << {:mode => 'a'})
|
152
|
-
IO.write(@path, contents, *open_args.compact)
|
153
|
-
else
|
154
|
-
open('a', *[open_args].compact) { |f| f.write(contents) }
|
155
|
-
end
|
156
|
-
end
|
70
|
+
alias :/ :+
|
157
71
|
|
158
|
-
|
159
|
-
|
160
|
-
end
|
72
|
+
alias :relative_to :relative_path_from
|
73
|
+
alias :% :relative_path_from
|
161
74
|
|
162
|
-
def
|
163
|
-
|
75
|
+
def inside? ancestor
|
76
|
+
@path == ancestor.to_s or @path.start_with?("#{ancestor}/")
|
164
77
|
end
|
165
|
-
alias_method :%, :relative_to
|
166
78
|
|
167
|
-
def
|
168
|
-
|
169
|
-
y << path = expand
|
170
|
-
until (path = path.parent).root?
|
171
|
-
y << path
|
172
|
-
end
|
173
|
-
y << path
|
174
|
-
end
|
175
|
-
RUBY_VERSION > '1.9' ? Enumerator.new(&ancestors) : ancestors.call([])
|
79
|
+
def outside? ancestor
|
80
|
+
!inside?(ancestor)
|
176
81
|
end
|
177
82
|
|
178
83
|
def backfind(path)
|
@@ -183,14 +88,20 @@ class Path
|
|
183
88
|
result/path if result
|
184
89
|
end
|
185
90
|
|
186
|
-
|
187
|
-
|
188
|
-
|
91
|
+
# Setup
|
92
|
+
register_loader 'yml', 'yaml' do |path|
|
93
|
+
require 'yaml'
|
94
|
+
YAML.load_file(path)
|
95
|
+
end
|
189
96
|
|
190
|
-
|
97
|
+
register_loader 'json' do |path|
|
98
|
+
require 'json'
|
99
|
+
JSON.load(path.read)
|
100
|
+
end
|
191
101
|
|
192
|
-
|
193
|
-
|
194
|
-
Path.new(*args)
|
102
|
+
register_loader 'gemspec' do |path|
|
103
|
+
eval path.read
|
195
104
|
end
|
196
105
|
end
|
106
|
+
|
107
|
+
EPath = Path # to meet everyone's expectations
|
data/lib/epath/dir.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
class Path
|
2
|
+
class << self
|
3
|
+
# Returns or yields Path objects. See +Dir.glob+.
|
4
|
+
def glob(*args) # :yield: path
|
5
|
+
if block_given?
|
6
|
+
Dir.glob(*args) { |f| yield new(f) }
|
7
|
+
else
|
8
|
+
Dir.glob(*args).map(&Path)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the current working directory as a Path. See +Dir.getwd+.
|
13
|
+
def Path.getwd
|
14
|
+
new Dir.getwd
|
15
|
+
end
|
16
|
+
alias :pwd :getwd
|
17
|
+
end
|
18
|
+
|
19
|
+
# 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
|
22
|
+
Dir.foreach(@path) { |f| yield Path.new(f) }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create the referenced directory and returns self. See +Dir.mkdir+.
|
26
|
+
def mkdir(*args)
|
27
|
+
Dir.mkdir(@path, *args)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
# Remove the referenced directory. See +Dir.rmdir+.
|
32
|
+
def rmdir
|
33
|
+
Dir.rmdir(@path)
|
34
|
+
end
|
35
|
+
|
36
|
+
# See +Dir.open+.
|
37
|
+
def opendir(&block) # :yield: dir
|
38
|
+
Dir.open(@path, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def glob(pattern, flags = 0)
|
42
|
+
Dir.glob(join(pattern), flags).map(&Path)
|
43
|
+
end
|
44
|
+
|
45
|
+
# [DEPRECATED] Return the entries (files and subdirectories) in the directory.
|
46
|
+
# Each Path only contains the filename.
|
47
|
+
# The result may contain the current directory #<Path .> and the parent directory #<Path ..>.
|
48
|
+
#
|
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)
|
54
|
+
#
|
55
|
+
# Use #children instead.
|
56
|
+
def entries
|
57
|
+
Dir.entries(@path).map(&Path)
|
58
|
+
end
|
59
|
+
|
60
|
+
def chdir(&block)
|
61
|
+
Dir.chdir(@path, &block)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the children of the directory (files and subdirectories, not
|
65
|
+
# recursive) as an array of Path objects. By default, the returned
|
66
|
+
# paths will have enough information to access the files. If you set
|
67
|
+
# +with_directory+ to +false+, then the returned paths will contain the
|
68
|
+
# filename only.
|
69
|
+
#
|
70
|
+
# For example:
|
71
|
+
# pn = Path("/usr/lib/ruby/1.8")
|
72
|
+
# pn.children
|
73
|
+
# # -> [ #<Path /usr/lib/ruby/1.8/English.rb>,
|
74
|
+
# #<Path /usr/lib/ruby/1.8/Env.rb>,
|
75
|
+
# #<Path /usr/lib/ruby/1.8/abbrev.rb>, ... ]
|
76
|
+
# pn.children(false)
|
77
|
+
# # -> [ #<Path English.rb>, #<Path Env.rb>, #<Path abbrev.rb>, ... ]
|
78
|
+
#
|
79
|
+
# Note that the results never contain the entries +.+ and +..+ in
|
80
|
+
# the directory because they are not children.
|
81
|
+
def children(with_directory=true)
|
82
|
+
with_directory = false if @path == '.'
|
83
|
+
result = []
|
84
|
+
Dir.foreach(@path) { |e|
|
85
|
+
next if e == '.' || e == '..'
|
86
|
+
if with_directory
|
87
|
+
result << Path.new(@path, e)
|
88
|
+
else
|
89
|
+
result << Path.new(e)
|
90
|
+
end
|
91
|
+
}
|
92
|
+
result
|
93
|
+
end
|
94
|
+
|
95
|
+
# Iterates over the children of the directory
|
96
|
+
# (files and subdirectories, not recursive).
|
97
|
+
# It yields Path object for each child.
|
98
|
+
# By default, the yielded paths will have enough information to access the files.
|
99
|
+
# If you set +with_directory+ to +false+, then the returned paths will contain the filename only.
|
100
|
+
#
|
101
|
+
# Path("/usr/local").each_child { |f| p f } # =>
|
102
|
+
# #<Path /usr/local/share>
|
103
|
+
# #<Path /usr/local/bin>
|
104
|
+
# #<Path /usr/local/games>
|
105
|
+
# #<Path /usr/local/lib>
|
106
|
+
# #<Path /usr/local/include>
|
107
|
+
# #<Path /usr/local/sbin>
|
108
|
+
# #<Path /usr/local/src>
|
109
|
+
# #<Path /usr/local/man>
|
110
|
+
#
|
111
|
+
# Path("/usr/local").each_child(false) { |f| p f } # =>
|
112
|
+
# #<Path share>
|
113
|
+
# #<Path bin>
|
114
|
+
# #<Path games>
|
115
|
+
# #<Path lib>
|
116
|
+
# #<Path include>
|
117
|
+
# #<Path sbin>
|
118
|
+
# #<Path src>
|
119
|
+
# #<Path man>
|
120
|
+
def each_child(with_directory=true, &b)
|
121
|
+
children(with_directory).each(&b)
|
122
|
+
end
|
123
|
+
end
|