epath 0.0.1 → 0.1.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 +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
|