rubypath 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +12 -4
- data/doc/file.README.html +74 -3
- data/lib/rubypath/backend/mock.rb +41 -0
- data/lib/rubypath/backend/sys.rb +20 -0
- data/lib/rubypath/backend.rb +5 -0
- data/lib/rubypath/comparison.rb +4 -7
- data/lib/rubypath/dir_operations.rb +72 -0
- data/lib/rubypath/file_operations.rb +26 -0
- data/lib/rubypath/path_operations.rb +20 -3
- data/lib/rubypath/version.rb +2 -2
- data/spec/rubypath/comparison_spec.rb +32 -1
- data/spec/rubypath/dir_operations_spec.rb +78 -1
- data/spec/rubypath/file_operations_spec.rb +39 -0
- data/spec/rubypath/identity_spec.rb +1 -1
- data/spec/rubypath/path_operations_spec.rb +49 -14
- data/spec/support/with_backend.rb +17 -11
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6a50c0dd1eea579ab534a5b321a38265bd9c4f6
|
4
|
+
data.tar.gz: ad371190f1cc868eeba76d652302445f515dddfb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97edfdceb6a0bea7103f0f1923a36b9c1fdba7a604672910c7162886c701475b6bdb1975875d3d4bd42f75663e7f46ec34114d9a22208fa07b9528b33f40e82b
|
7
|
+
data.tar.gz: c4dbb1749cbc28cb73411cf387f39b10bf78a114f1b3d883b32d8ef13514e17878fc66f4a75b32a947443dbfb639e2a576fade52fb41481bc1e0248ec40581c3
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
[](https://gemnasium.com/jgraichen/rubypath)
|
7
7
|
[](http://rubydoc.info/github/jgraichen/rubypath/master/frames)
|
8
8
|
|
9
|
-
*Ruby Path* introduces a global `Path` class unifying most `File`, `Dir`, `FileUtils`, `Pathname` and `IO` operations with a flexible and powerful Object-Interface and still adding new useful methods and functions like mocking a
|
9
|
+
*Ruby Path* introduces a global `Path` class unifying most `File`, `Dir`, `FileUtils`, `Pathname` and `IO` operations with a flexible and powerful Object-Interface and still adding new useful methods and functions like mocking a whole file system for fast and reliable testing.
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
@@ -79,9 +79,17 @@ See full API documentation here: http://rubydoc.info/gems/rubypath/Path
|
|
79
79
|
|
80
80
|
1. Fork it
|
81
81
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
82
|
-
3.
|
83
|
-
4.
|
84
|
-
5.
|
82
|
+
3. Add specs testing SYS *and* MOCK file system
|
83
|
+
4. Commit your specs (`git commit -am 'Add specs for feature'`)
|
84
|
+
5. Add our changes for SYS *and* MOCK file system
|
85
|
+
6. Commit your changes (`git commit -am 'Add some feature'`)
|
86
|
+
7. Push to the branch (`git push origin my-new-feature`)
|
87
|
+
8. Create new Pull Request
|
88
|
+
|
89
|
+
### ToDos
|
90
|
+
|
91
|
+
* Add missing methods
|
92
|
+
* Improve MOCK FS implementation
|
85
93
|
|
86
94
|
## License
|
87
95
|
|
data/doc/file.README.html
CHANGED
@@ -63,7 +63,13 @@
|
|
63
63
|
|
64
64
|
<div id="content"><div id='filecontents'><h1>Ruby Path</h1>
|
65
65
|
|
66
|
-
<p><
|
66
|
+
<p><a href="http://badge.fury.io/rb/rubypath"><img src="https://badge.fury.io/rb/rubypath.svg" alt="Gem Version"></a>
|
67
|
+
<a href="https://travis-ci.org/jgraichen/rubypath"><img src="http://img.shields.io/travis/jgraichen/rubypath/master.svg" alt="Build Status"></a>
|
68
|
+
<a href="https://coveralls.io/r/jgraichen/rubypath"><img src="http://img.shields.io/coveralls/jgraichen/rubypath/master.svg" alt="Coverage Status"></a>
|
69
|
+
<a href="https://gemnasium.com/jgraichen/rubypath"><img src="http://img.shields.io/gemnasium/jgraichen/rubypath.svg" alt="Dependency Status"></a>
|
70
|
+
<a href="http://rubydoc.info/github/jgraichen/rubypath/master/frames"><img src="http://img.shields.io/badge/rubydoc-here-blue.svg" alt="RubyDoc Documentation"></a></p>
|
71
|
+
|
72
|
+
<p><em>Ruby Path</em> introduces a global <code>Path</code> class unifying most <code>File</code>, <code>Dir</code>, <code>FileUtils</code>, <code>Pathname</code> and <code>IO</code> operations with a flexible and powerful Object-Interface and still adding new useful methods and functions like mocking a whole file system for fast and reliable testing.</p>
|
67
73
|
|
68
74
|
<h2>Installation</h2>
|
69
75
|
|
@@ -71,18 +77,83 @@
|
|
71
77
|
|
72
78
|
<h2>Usage</h2>
|
73
79
|
|
74
|
-
<p>
|
80
|
+
<p>Using <code>Path</code> with file and directory methods:</p>
|
81
|
+
|
82
|
+
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_base'>base</span> <span class='op'>=</span> <span class='const'>Path</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>/path/to/base</span><span class='tstring_end'>'</span></span>
|
83
|
+
<span class='id identifier rubyid_src'>src</span> <span class='op'>=</span> <span class='id identifier rubyid_base'>base</span><span class='period'>.</span><span class='id identifier rubyid_mkpath'>mkpath</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>project/src</span><span class='tstring_end'>'</span></span>
|
84
|
+
<span class='id identifier rubyid_src'>src</span><span class='period'>.</span><span class='id identifier rubyid_touch'>touch</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Rakefile</span><span class='tstring_end'>'</span></span>
|
85
|
+
<span class='id identifier rubyid_src'>src</span><span class='period'>.</span><span class='id identifier rubyid_mkdir'>mkdir</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>lib</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_mkdir'>mkdir</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>mylib</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_touch'>touch</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>version.rb</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span>
|
86
|
+
<span class='comment'>#=> <Path '/path/to/base/project/src/lib/mylib/version.rb'
|
87
|
+
</span></code></pre>
|
88
|
+
|
89
|
+
<p>Using IO:</p>
|
90
|
+
|
91
|
+
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_src'>src</span><span class='period'>.</span><span class='id identifier rubyid_write'>write</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>module Mylib\n VERSION = '0.1.0'\nend</span><span class='tstring_end'>"</span></span>
|
92
|
+
|
93
|
+
<span class='id identifier rubyid_src'>src</span><span class='period'>.</span><span class='id identifier rubyid_lookup'>lookup</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>project.yml</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_read'>read</span>
|
94
|
+
<span class='comment'>#=> "..."
|
95
|
+
</span></code></pre>
|
96
|
+
|
97
|
+
<h3>Mock FS in tests</h3>
|
98
|
+
|
99
|
+
<p>Wrap specific or just all specs in a virtual filesystem:</p>
|
100
|
+
|
101
|
+
<pre class="code ruby"><code class="ruby"><span class='comment'># spec_helper.rb
|
102
|
+
</span>
|
103
|
+
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_around'>around</span><span class='lparen'>(</span><span class='symbol'>:each</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_example'>example</span><span class='op'>|</span>
|
104
|
+
<span class='const'>Path</span><span class='op'>::</span><span class='const'>Backend</span><span class='period'>.</span><span class='id identifier rubyid_mock'>mock</span> <span class='label'>root:</span> <span class='symbol'>:tmp</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_example'>example</span>
|
105
|
+
<span class='kw'>end</span>
|
106
|
+
</code></pre>
|
107
|
+
|
108
|
+
<p>Supported options for <code>:root</code> are <code>:tmp</code> using the real filesystem but scoping all actions into a temporary directory similar to chroot or a custom defined path to use as "chroot" directory. This mode does not allow to stub users, home directories and some attributes.</p>
|
109
|
+
|
110
|
+
<p>If not <code>:root</code> is specified a completely virtual in-memory filesystem will be used. This backend allows to even specify available users and home directories, the current user etc.</p>
|
111
|
+
|
112
|
+
<p>You can then define a specific scenario in your specs:</p>
|
113
|
+
|
114
|
+
<pre class="code ruby"><code class="ruby"> <span class='id identifier rubyid_before'>before</span> <span class='kw'>do</span>
|
115
|
+
<span class='const'>Path</span><span class='period'>.</span><span class='id identifier rubyid_mock'>mock</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_root'>root</span><span class='comma'>,</span> <span class='id identifier rubyid_backend'>backend</span><span class='op'>|</span>
|
116
|
+
<span class='id identifier rubyid_backend'>backend</span><span class='period'>.</span><span class='id identifier rubyid_cwd'>cwd</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>/root</span><span class='tstring_end'>'</span></span>
|
117
|
+
<span class='id identifier rubyid_backend'>backend</span><span class='period'>.</span><span class='id identifier rubyid_current_user'>current_user</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>test</span><span class='tstring_end'>'</span></span>
|
118
|
+
<span class='id identifier rubyid_backend'>backend</span><span class='period'>.</span><span class='id identifier rubyid_homes'>homes</span> <span class='op'>=</span> <span class='lbrace'>{</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>test</span><span class='tstring_end'>'</span></span> <span class='op'>=></span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>/home/test</span><span class='tstring_end'>'</span></span><span class='rbrace'>}</span>
|
119
|
+
|
120
|
+
<span class='id identifier rubyid_home'>home</span> <span class='op'>=</span> <span class='id identifier rubyid_root'>root</span><span class='period'>.</span><span class='id identifier rubyid_mkpath'>mkpath</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>/home/test</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span>
|
121
|
+
<span class='id identifier rubyid_home'>home</span><span class='period'>.</span><span class='id identifier rubyid_mkfile'>mkfile</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>src/test.txt</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_write'>write</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>CONTENT</span><span class='tstring_end'>'</span></span>
|
122
|
+
<span class='id identifier rubyid_home'>home</span><span class='period'>.</span><span class='id identifier rubyid_mkfile'>mkfile</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>src/test.html</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_write'>write</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'><html><head><title></title>...</span><span class='tstring_end'>'</span></span>
|
123
|
+
<span class='kw'>end</span>
|
124
|
+
<span class='kw'>end</span>
|
125
|
+
|
126
|
+
<span class='id identifier rubyid_it'>it</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>should mock all FS</span><span class='tstring_end'>'</span></span> <span class='kw'>do</span>
|
127
|
+
<span class='id identifier rubyid_base'>base</span> <span class='op'>=</span> <span class='const'>Path</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>~test</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_expand'>expand</span>
|
128
|
+
<span class='id identifier rubyid_expect'>expect</span><span class='lparen'>(</span><span class='id identifier rubyid_base'>base</span><span class='period'>.</span><span class='id identifier rubyid_join'>join</span><span class='lparen'>(</span><span class='qwords_beg'>%w(</span><span class='tstring_content'>src</span><span class='words_sep'> </span><span class='tstring_content'>test.txt</span><span class='words_sep'>)</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_read'>read</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_to'>to</span> <span class='id identifier rubyid_eq'>eq</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>CONTENT</span><span class='tstring_end'>'</span></span>
|
129
|
+
|
130
|
+
<span class='id identifier rubyid_files'>files</span> <span class='op'>=</span> <span class='id identifier rubyid_base'>base</span><span class='period'>.</span><span class='id identifier rubyid_glob'>glob</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>**/*</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_select'>select</span><span class='lbrace'>{</span><span class='op'>|</span><span class='id identifier rubyid_p'>p</span><span class='op'>|</span> <span class='id identifier rubyid_p'>p</span><span class='period'>.</span><span class='id identifier rubyid_file?'>file?</span> <span class='rbrace'>}</span>
|
131
|
+
<span class='id identifier rubyid_expect'>expect</span><span class='lparen'>(</span><span class='id identifier rubyid_files'>files</span><span class='period'>.</span><span class='id identifier rubyid_size'>size</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_to'>to</span> <span class='id identifier rubyid_eq'>eq</span> <span class='int'>2</span>
|
132
|
+
<span class='kw'>end</span>
|
133
|
+
</code></pre>
|
134
|
+
|
135
|
+
<p>See full API documentation here: <a href="http://rubydoc.info/gems/rubypath/Path">http://rubydoc.info/gems/rubypath/Path</a></p>
|
75
136
|
|
76
137
|
<h2>Contributing</h2>
|
77
138
|
|
78
139
|
<ol>
|
79
140
|
<li>Fork it</li>
|
80
141
|
<li>Create your feature branch (<code>git checkout -b my-new-feature</code>)</li>
|
142
|
+
<li>Add specs testing SYS <em>and</em> MOCK file system</li>
|
143
|
+
<li>Commit your specs (<code>git commit -am 'Add specs for feature'</code>)</li>
|
144
|
+
<li>Add our changes for SYS <em>and</em> MOCK file system</li>
|
81
145
|
<li>Commit your changes (<code>git commit -am 'Add some feature'</code>)</li>
|
82
146
|
<li>Push to the branch (<code>git push origin my-new-feature</code>)</li>
|
83
147
|
<li>Create new Pull Request</li>
|
84
148
|
</ol>
|
85
149
|
|
150
|
+
<h3>ToDos</h3>
|
151
|
+
|
152
|
+
<ul>
|
153
|
+
<li>Add missing methods</li>
|
154
|
+
<li>Improve MOCK FS implementation</li>
|
155
|
+
</ul>
|
156
|
+
|
86
157
|
<h2>License</h2>
|
87
158
|
|
88
159
|
<p>Copyright (C) 2014 Jan Graichen</p>
|
@@ -95,7 +166,7 @@
|
|
95
166
|
</div></div>
|
96
167
|
|
97
168
|
<div id="footer">
|
98
|
-
Generated on
|
169
|
+
Generated on Sun Apr 27 13:05:43 2014 by
|
99
170
|
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
100
171
|
0.8.7.3 (ruby-2.1.1).
|
101
172
|
</div>
|
@@ -164,6 +164,47 @@ class Path::Backend
|
|
164
164
|
lookup!(path).mode
|
165
165
|
end
|
166
166
|
|
167
|
+
def unlink(path)
|
168
|
+
node = lookup_parent!(path)
|
169
|
+
file = node.lookup ::File.basename path
|
170
|
+
case file
|
171
|
+
when Dir
|
172
|
+
raise Errno::EISDIR.new path
|
173
|
+
when File
|
174
|
+
node.children.delete(file)
|
175
|
+
when nil
|
176
|
+
raise Errno::ENOENT.new path
|
177
|
+
else
|
178
|
+
raise ArgumentError.new "Unknown node #{node.inspect} for #unlink."
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def rmtree(path)
|
183
|
+
node = lookup path
|
184
|
+
case node
|
185
|
+
when Dir, File
|
186
|
+
lookup_parent!(path).children.delete(node)
|
187
|
+
when nil
|
188
|
+
nil
|
189
|
+
else
|
190
|
+
raise ArgumentError.new "Unknown node #{node.inspect} for #rmtree."
|
191
|
+
end
|
192
|
+
end
|
193
|
+
alias_method :safe_rmtree, :rmtree
|
194
|
+
|
195
|
+
def rmtree!(path)
|
196
|
+
node = lookup path
|
197
|
+
case node
|
198
|
+
when Dir, File
|
199
|
+
lookup_parent!(path).children.delete(node)
|
200
|
+
when nil
|
201
|
+
raise Errno::ENOENT.new path
|
202
|
+
else
|
203
|
+
raise ArgumentError.new "Unknown node #{node.inspect} for #rmtree."
|
204
|
+
end
|
205
|
+
end
|
206
|
+
alias_method :safe_rmtree!, :rmtree!
|
207
|
+
|
167
208
|
# @!group Internal Virtual File System
|
168
209
|
|
169
210
|
# Return root node.
|
data/lib/rubypath/backend/sys.rb
CHANGED
@@ -137,5 +137,25 @@ class Path::Backend
|
|
137
137
|
def chmod(path, mode)
|
138
138
|
fs path, ::File, :chmod, mode, r(path)
|
139
139
|
end
|
140
|
+
|
141
|
+
def unlink(path)
|
142
|
+
fs path, ::File, :unlink, r(path)
|
143
|
+
end
|
144
|
+
|
145
|
+
def rmtree(path)
|
146
|
+
fs path, ::FileUtils, :rm_r, r(path), force: true
|
147
|
+
end
|
148
|
+
|
149
|
+
def rmtree!(path)
|
150
|
+
fs path, ::FileUtils, :rm_r, r(path)
|
151
|
+
end
|
152
|
+
|
153
|
+
def safe_rmtree(path)
|
154
|
+
fs path, ::FileUtils, :rm_r, r(path), force: true, secure: true
|
155
|
+
end
|
156
|
+
|
157
|
+
def safe_rmtree!(path)
|
158
|
+
fs path, ::FileUtils, :rm_r, r(path), secure: true
|
159
|
+
end
|
140
160
|
end
|
141
161
|
end
|
data/lib/rubypath/backend.rb
CHANGED
data/lib/rubypath/comparison.rb
CHANGED
@@ -9,13 +9,10 @@ class Path
|
|
9
9
|
# @return [Boolean] True if object represents same path.
|
10
10
|
#
|
11
11
|
def eql?(other)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
internal_path.eql? other.path
|
17
|
-
else
|
18
|
-
Path.new(other).eql?(self) if Path.like? other
|
12
|
+
if other.is_a?(Path)
|
13
|
+
cleanpath.internal_path == other.cleanpath.internal_path
|
14
|
+
else
|
15
|
+
Path.new(other).eql?(self) if Path.like?(other)
|
19
16
|
end
|
20
17
|
end
|
21
18
|
alias_method :==, :eql?
|
@@ -31,6 +31,8 @@ class Path
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
# @!group Directory Operations
|
35
|
+
|
34
36
|
# Create directory.
|
35
37
|
#
|
36
38
|
# Given arguments will be joined with current path before directory is
|
@@ -80,6 +82,76 @@ class Path
|
|
80
82
|
Path.glob(::File.join(escaped_glob_path, pattern), flags, &block)
|
81
83
|
end
|
82
84
|
|
85
|
+
# Removes file or directory. If it's a directory it will be removed
|
86
|
+
# recursively.
|
87
|
+
#
|
88
|
+
# WARNING: This method causes local vulnerability if one of parent
|
89
|
+
# directories or removing directory tree are world writable (including
|
90
|
+
# `/tmp`, whose permission is 1777), and the current process has strong
|
91
|
+
# privilege such as Unix super user (root), and the system has symbolic link.
|
92
|
+
# For secure removing see {#safe_rmtree}.
|
93
|
+
#
|
94
|
+
# @return [Path] Path to removed file or directory.
|
95
|
+
#
|
96
|
+
def rmtree(*args)
|
97
|
+
with_path(*args) do |path|
|
98
|
+
invoke_backend :rmtree, internal_path
|
99
|
+
Path path
|
100
|
+
end
|
101
|
+
end
|
102
|
+
alias_method :rm_rf, :rmtree
|
103
|
+
|
104
|
+
# Removes file or directory. If it's a directory it will be removed
|
105
|
+
# recursively.
|
106
|
+
#
|
107
|
+
# This method uses #{FileUtils#remove_entry_secure} to avoid TOCTTOU
|
108
|
+
# (time-of-check-to-time-of-use) local security vulnerability of {#rmtree}.
|
109
|
+
# {#rmtree} causes security hole when:
|
110
|
+
#
|
111
|
+
# * Parent directory is world writable (including `/tmp`).
|
112
|
+
# * Removing directory tree includes world writable directory.
|
113
|
+
# * The system has symbolic link.
|
114
|
+
#
|
115
|
+
# @return [Path] Path to removed file or directory.
|
116
|
+
#
|
117
|
+
def safe_rmtree(*args)
|
118
|
+
with_path(*args) do |path|
|
119
|
+
invoke_backend :safe_rmtree, internal_path
|
120
|
+
Path path
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Removes file or directory. If it's a directory it will be removed
|
125
|
+
# recursively.
|
126
|
+
#
|
127
|
+
# This method behaves exactly like {#rmtree} but will raise exceptions
|
128
|
+
# e.g. when file does not exist.
|
129
|
+
#
|
130
|
+
# @return [Path] Path to removed file or directory.
|
131
|
+
#
|
132
|
+
def rmtree!(*args)
|
133
|
+
with_path(*args) do |path|
|
134
|
+
invoke_backend :rmtree!, internal_path
|
135
|
+
Path path
|
136
|
+
end
|
137
|
+
end
|
138
|
+
alias_method :rm_r, :rmtree!
|
139
|
+
|
140
|
+
# Removes file or directory. If it's a directory it will be removed
|
141
|
+
# recursively.
|
142
|
+
#
|
143
|
+
# This method behaves exactly like {#safe_rmtree} but will raise exceptions
|
144
|
+
# e.g. when file does not exist.
|
145
|
+
#
|
146
|
+
# @return [Path] Path to removed file or directory.
|
147
|
+
#
|
148
|
+
def safe_rmtree!(*args)
|
149
|
+
with_path(*args) do |path|
|
150
|
+
invoke_backend :safe_rmtree!, internal_path
|
151
|
+
Path path
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
83
155
|
private
|
84
156
|
|
85
157
|
def escaped_glob_path
|
@@ -17,6 +17,10 @@ class Path
|
|
17
17
|
# Path('/path/to/file.txt').touch
|
18
18
|
# #=> <Path:"/path/to/file.txt">
|
19
19
|
#
|
20
|
+
# @example
|
21
|
+
# Path('/path/to').touch('file.txt')
|
22
|
+
# #=> <Path:"/path/to/file.txt">
|
23
|
+
#
|
20
24
|
# @return [Path] Path to touched file.
|
21
25
|
#
|
22
26
|
def touch(*args)
|
@@ -26,6 +30,28 @@ class Path
|
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
33
|
+
# Removes file at current path.
|
34
|
+
#
|
35
|
+
# Raise an error if file does not exists or is a directory.
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# Path('/file.txt').touch.unlink
|
39
|
+
# #=> <Path /file.txt>
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# Path('/file.txt').touch
|
43
|
+
# Path('/').unlink('file.txt')
|
44
|
+
# #=> <Path /file.txt>
|
45
|
+
#
|
46
|
+
# @return [Path] Unlinked path.
|
47
|
+
#
|
48
|
+
def unlink(*args)
|
49
|
+
with_path(*args) do |path|
|
50
|
+
invoke_backend :unlink, path
|
51
|
+
Path path
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
29
55
|
# Create a file at pointed location and all missing parent directories.
|
30
56
|
#
|
31
57
|
# Given arguments will be joined with current path before directories and
|
@@ -280,9 +280,26 @@ class Path
|
|
280
280
|
end
|
281
281
|
alias_method :relative_path_from, :relative_from
|
282
282
|
|
283
|
-
|
284
|
-
|
283
|
+
# Return cleaned path with all dot components removed.
|
284
|
+
#
|
285
|
+
# No file system will accessed and not symlinks will be resolved.
|
286
|
+
#
|
287
|
+
# @example
|
288
|
+
# Path('./file.txt').cleanpath
|
289
|
+
# #=> <Path file.txt>
|
290
|
+
#
|
291
|
+
# @example
|
292
|
+
# Path('path/to/another/../file/../../txt').cleanpath
|
293
|
+
# #=> <Path path/txt>
|
294
|
+
#
|
295
|
+
# @return [Path] Cleaned path.
|
296
|
+
#
|
285
297
|
def cleanpath
|
286
|
-
|
298
|
+
path = Pathname.new(self).cleanpath
|
299
|
+
if path == internal_path
|
300
|
+
self
|
301
|
+
else
|
302
|
+
Path path
|
303
|
+
end
|
287
304
|
end
|
288
305
|
end
|
data/lib/rubypath/version.rb
CHANGED
@@ -11,10 +11,20 @@ describe Path do
|
|
11
11
|
expect(res).to be true
|
12
12
|
end
|
13
13
|
|
14
|
-
it 'should compare paths (
|
14
|
+
it 'should compare paths (2)' do
|
15
15
|
res = path.send described_method, Path('/path/to/another/file')
|
16
16
|
expect(res).to be false
|
17
17
|
end
|
18
|
+
|
19
|
+
it 'should compare clean paths (1)' do
|
20
|
+
res = path.send described_method, Path('/path/to/./file')
|
21
|
+
expect(res).to be true
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should compare clean paths (2)' do
|
25
|
+
res = path.send described_method, Path('/path/to/another/../file')
|
26
|
+
expect(res).to be true
|
27
|
+
end
|
18
28
|
end
|
19
29
|
|
20
30
|
context 'with String object' do
|
@@ -27,6 +37,16 @@ describe Path do
|
|
27
37
|
res = path.send described_method, '/path/to/another/file'
|
28
38
|
expect(res).to be false
|
29
39
|
end
|
40
|
+
|
41
|
+
it 'should compare clean paths (1)' do
|
42
|
+
res = path.send described_method, '/path/to/./file'
|
43
|
+
expect(res).to be true
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should compare clean paths (2)' do
|
47
|
+
res = path.send described_method, '/path/to/another/../file'
|
48
|
+
expect(res).to be true
|
49
|
+
end
|
30
50
|
end
|
31
51
|
|
32
52
|
context 'with Pathname object' do
|
@@ -40,6 +60,17 @@ describe Path do
|
|
40
60
|
Pathname.new('/path/to/another/file')
|
41
61
|
expect(res).to be false
|
42
62
|
end
|
63
|
+
|
64
|
+
it 'should compare clean paths (1)' do
|
65
|
+
res = path.send described_method, Pathname.new('/path/to/./file')
|
66
|
+
expect(res).to be true
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should compare clean paths (2)' do
|
70
|
+
res = path.send described_method,
|
71
|
+
Pathname.new('/path/to/another/../file')
|
72
|
+
expect(res).to be true
|
73
|
+
end
|
43
74
|
end
|
44
75
|
end
|
45
76
|
end
|
@@ -39,6 +39,83 @@ describe Path do
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
shared_examples '#remove_recursive' do
|
43
|
+
context 'on existent file' do
|
44
|
+
before { path.mkfile }
|
45
|
+
it{ expect{ subject }.to change(path, :exist?).from(true).to(false) }
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'on existent directory' do
|
49
|
+
before { path.mkpath }
|
50
|
+
it{ expect{ subject }.to change(path, :exist?).from(true).to(false) }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'on existent directory with children' do
|
54
|
+
before { path.mkfile('subdir/file') }
|
55
|
+
it{ expect{ subject }.to change(path, :exist?).from(true).to(false) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe_method :rmtree, aliases: [:rm_rf] do
|
60
|
+
let(:path) { Path '/path' }
|
61
|
+
subject { path.send(described_method) }
|
62
|
+
|
63
|
+
context 'on non-existent file' do
|
64
|
+
it { expect{ subject }.to_not raise_error }
|
65
|
+
end
|
66
|
+
|
67
|
+
it_behaves_like '#remove_recursive'
|
68
|
+
end
|
69
|
+
|
70
|
+
describe_method :safe_rmtree do
|
71
|
+
let(:path) { Path '/path' }
|
72
|
+
subject { path.send(described_method) }
|
73
|
+
|
74
|
+
it 'should use #remove_entry_secure' do
|
75
|
+
if backend_type == :sys
|
76
|
+
path.mkfile
|
77
|
+
expect(FileUtils).to receive(:remove_entry_secure).and_call_original
|
78
|
+
subject
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'on non-existent file' do
|
83
|
+
it { expect{ subject }.to_not raise_error }
|
84
|
+
end
|
85
|
+
|
86
|
+
it_behaves_like '#remove_recursive'
|
87
|
+
end
|
88
|
+
|
89
|
+
describe_method :rmtree!, aliases: [:rm_r] do
|
90
|
+
let(:path) { Path '/path' }
|
91
|
+
subject { path.send(described_method) }
|
92
|
+
|
93
|
+
context 'on non-existent file' do
|
94
|
+
it { expect{ subject }.to raise_error Errno::ENOENT }
|
95
|
+
end
|
96
|
+
|
97
|
+
it_behaves_like '#remove_recursive'
|
98
|
+
end
|
99
|
+
|
100
|
+
describe_method :safe_rmtree! do
|
101
|
+
let(:path) { Path '/path' }
|
102
|
+
subject { path.send(described_method) }
|
103
|
+
|
104
|
+
it 'should use #remove_entry_secure' do
|
105
|
+
if backend_type == :sys
|
106
|
+
path.mkfile
|
107
|
+
expect(FileUtils).to receive(:remove_entry_secure).and_call_original
|
108
|
+
subject
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'on non-existent file' do
|
113
|
+
it { expect{ subject }.to raise_error Errno::ENOENT }
|
114
|
+
end
|
115
|
+
|
116
|
+
it_behaves_like '#remove_recursive'
|
117
|
+
end
|
118
|
+
|
42
119
|
describe '#glob' do
|
43
120
|
it 'should delegate to class#glob' do
|
44
121
|
expect(Path).to receive(:glob)
|
@@ -130,7 +207,7 @@ describe Path do
|
|
130
207
|
end
|
131
208
|
|
132
209
|
it 'should return list of Path objects' do
|
133
|
-
subject.each{|e| expect(e).to be_a Path }
|
210
|
+
subject.each{ |e| expect(e).to be_a Path }
|
134
211
|
end
|
135
212
|
end
|
136
213
|
|
@@ -15,6 +15,45 @@ describe Path do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
describe_method :unlink do
|
19
|
+
subject { path.send described_method }
|
20
|
+
|
21
|
+
context 'on non-existent file' do
|
22
|
+
it { expect{ subject }.to raise_error Errno::ENOENT }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'on existent file' do
|
26
|
+
before { path.mkfile }
|
27
|
+
|
28
|
+
it 'should unlink file' do
|
29
|
+
expect{ subject }.to change(path, :exist?).from(true).to(false)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'on existent directory' do
|
34
|
+
before { path.mkpath }
|
35
|
+
|
36
|
+
it { expect{ subject }.to raise_error Errno::EISDIR }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with args' do
|
40
|
+
subject { path.send(described_method, 'file') }
|
41
|
+
|
42
|
+
context 'on non-existent file' do
|
43
|
+
it { expect{ subject }.to raise_error Errno::ENOENT }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'on existent file' do
|
47
|
+
before { path.mkfile('file') }
|
48
|
+
|
49
|
+
it 'should unlink file' do
|
50
|
+
expect{ subject }
|
51
|
+
.to change(path.join('file'), :exist?).from(true).to(false)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
18
57
|
describe_method :touch do
|
19
58
|
let(:path) { Path '/rubypath' }
|
20
59
|
let(:args) { Array.new }
|
@@ -4,7 +4,7 @@ describe Path do
|
|
4
4
|
describe 'Identity' do
|
5
5
|
let(:str) { '/path/to/file' }
|
6
6
|
let(:args) { [str] }
|
7
|
-
let(:path) { described_class.new
|
7
|
+
let(:path) { described_class.new(*args) }
|
8
8
|
subject { path }
|
9
9
|
|
10
10
|
describe_method :path, aliases: [:to_path, :to_str, :to_s] do
|
@@ -8,7 +8,7 @@ describe Path do
|
|
8
8
|
subject { path }
|
9
9
|
|
10
10
|
describe_method :join do
|
11
|
-
subject { path.send
|
11
|
+
subject { path.send(described_method, *join_args) }
|
12
12
|
|
13
13
|
context 'with single string' do
|
14
14
|
let(:join_args) { ['to/file.txt'] }
|
@@ -36,6 +36,35 @@ describe Path do
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
describe_method :cleanpath do
|
40
|
+
subject { path.send(described_method) }
|
41
|
+
|
42
|
+
context 'with out dot components' do
|
43
|
+
let(:path) { Path 'path/to/file.txt' }
|
44
|
+
it { should eq 'path/to/file.txt' }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with leading dot' do
|
48
|
+
let(:path) { Path './file.txt' }
|
49
|
+
it { should eq 'file.txt' }
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'with including dot' do
|
53
|
+
let(:path) { Path 'path/to/./file.txt' }
|
54
|
+
it { should eq 'path/to/file.txt' }
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'with double dot' do
|
58
|
+
let(:path) { Path 'path/to/../file.txt' }
|
59
|
+
it { should eq 'path/file.txt' }
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'with multiple dots' do
|
63
|
+
let(:path) { Path 'path/to/../../opath/to/./../file.txt' }
|
64
|
+
it { should eq 'opath/file.txt' }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
39
68
|
describe_method :each_component do
|
40
69
|
let(:block) { nil }
|
41
70
|
let(:opts) { Hash.new }
|
@@ -52,17 +81,17 @@ describe Path do
|
|
52
81
|
let(:opts) { {empty: true} }
|
53
82
|
|
54
83
|
it 'should also return empty path components' do
|
55
|
-
expect(subject.to_a).to eq
|
84
|
+
expect(subject.to_a).to eq([''] + %w(path to templates dir) + [''])
|
56
85
|
end
|
57
86
|
end
|
58
87
|
|
59
88
|
context 'with block' do
|
60
|
-
let(:block) { proc{|fn| fn} }
|
89
|
+
let(:block) { proc{|fn| fn } }
|
61
90
|
|
62
91
|
it 'should yield components' do
|
63
|
-
expect
|
92
|
+
expect do |b|
|
64
93
|
path.send described_method, &b
|
65
|
-
|
94
|
+
end.to yield_successive_args(*%w(path to templates dir))
|
66
95
|
end
|
67
96
|
|
68
97
|
it { should eq path }
|
@@ -80,7 +109,8 @@ describe Path do
|
|
80
109
|
describe_method :dirname, aliases: [:parent] do
|
81
110
|
shared_examples 'dirname' do
|
82
111
|
it 'should return parent directory' do
|
83
|
-
expect(Path(base, 'path/to/file').send(described_method))
|
112
|
+
expect(Path(base, 'path/to/file').send(described_method))
|
113
|
+
.to eq "#{base}path/to"
|
84
114
|
end
|
85
115
|
|
86
116
|
context 'when hitting root' do
|
@@ -108,7 +138,8 @@ describe Path do
|
|
108
138
|
end
|
109
139
|
|
110
140
|
with_backends :mock, :sys do
|
111
|
-
describe_method :expand, aliases: [:expand_path,
|
141
|
+
describe_method :expand, aliases: [:expand_path,
|
142
|
+
:absolute, :absolute_path] do
|
112
143
|
let(:cwd) { '/working/dir' }
|
113
144
|
let(:base) { cwd }
|
114
145
|
let(:args) { Array.new }
|
@@ -120,7 +151,7 @@ describe Path do
|
|
120
151
|
end
|
121
152
|
end
|
122
153
|
|
123
|
-
around{|example| Path::Backend.mock
|
154
|
+
around{|example| Path::Backend.mock(&example) }
|
124
155
|
|
125
156
|
shared_examples '#expand' do
|
126
157
|
subject { Path(path).send(described_method, *args) }
|
@@ -311,11 +342,13 @@ describe Path do
|
|
311
342
|
it { should eq path }
|
312
343
|
|
313
344
|
it 'should yield part paths' do
|
314
|
-
expect{|b| path.send
|
345
|
+
expect{|b| path.send(described_method, &b) }
|
346
|
+
.to yield_successive_args(*expected_paths)
|
315
347
|
end
|
316
348
|
|
317
349
|
it 'should yield Path objects' do
|
318
|
-
expect{|b| path.send
|
350
|
+
expect{|b| path.send(described_method, &b) }
|
351
|
+
.to yield_successive_args(*expected_paths.map{ Path })
|
319
352
|
end
|
320
353
|
end
|
321
354
|
|
@@ -325,24 +358,26 @@ describe Path do
|
|
325
358
|
it { should be_a Enumerator }
|
326
359
|
|
327
360
|
it 'should yield part paths' do
|
328
|
-
expect{|b| subject.each
|
361
|
+
expect{|b| subject.each(&b) }
|
362
|
+
.to yield_successive_args(*expected_paths)
|
329
363
|
end
|
330
364
|
|
331
365
|
it 'should yield path objects' do
|
332
|
-
expect{|b| subject.each
|
366
|
+
expect{|b| subject.each(&b) }
|
367
|
+
.to yield_successive_args(*expected_paths.map{ Path })
|
333
368
|
end
|
334
369
|
end
|
335
370
|
end
|
336
371
|
|
337
372
|
context 'with absolute path' do
|
338
373
|
let(:path) { Path '/path/to/file.txt' }
|
339
|
-
let(:expected_paths) { %w(/path/to/file.txt /path/to /path /)}
|
374
|
+
let(:expected_paths) { %w(/path/to/file.txt /path/to /path /) }
|
340
375
|
it_behaves_like 'ascend'
|
341
376
|
end
|
342
377
|
|
343
378
|
context 'with relative path' do
|
344
379
|
let(:path) { Path 'path/to/file.txt' }
|
345
|
-
let(:expected_paths) { %w(path/to/file.txt path/to path .)}
|
380
|
+
let(:expected_paths) { %w(path/to/file.txt path/to path .) }
|
346
381
|
it_behaves_like 'ascend'
|
347
382
|
end
|
348
383
|
end
|
@@ -1,15 +1,17 @@
|
|
1
|
+
#
|
1
2
|
module WithBackend
|
2
|
-
|
3
3
|
def with_backends(*args, &block)
|
4
4
|
args.each do |backend|
|
5
5
|
be = case backend
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
6
|
+
when :mock
|
7
|
+
->(ex){ Path::Backend.mock(&ex) }
|
8
|
+
when :sys
|
9
|
+
->(ex){ Path::Backend.mock(root: :tmp, &ex) }
|
10
|
+
else
|
11
|
+
raise ArgumentError.new 'Unknown backend.'
|
12
|
+
end
|
13
|
+
|
14
|
+
next if ENV["FS_#{backend.upcase}"] == '0'
|
13
15
|
|
14
16
|
describe "with #{backend.upcase} FS" do
|
15
17
|
let(:backend_type) { backend }
|
@@ -17,15 +19,19 @@ module WithBackend
|
|
17
19
|
be.call(example)
|
18
20
|
end
|
19
21
|
|
20
|
-
module_eval
|
22
|
+
module_eval(&block)
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
24
26
|
alias_method :with_backend, :with_backends
|
25
27
|
|
26
28
|
def pending_backend(*args)
|
27
|
-
before
|
29
|
+
before do
|
30
|
+
if args.include? backend_type
|
31
|
+
pending "Pending on #{backend_type} backend."
|
32
|
+
end
|
33
|
+
end
|
28
34
|
end
|
29
35
|
|
30
|
-
RSpec.configure{|c| c.extend WithBackend }
|
36
|
+
RSpec.configure{ |c| c.extend WithBackend }
|
31
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubypath
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Graichen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|