pathutil 0.0.1
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 +7 -0
- data/Gemfile +22 -0
- data/LICENSE +19 -0
- data/Rakefile +65 -0
- data/lib/pathutil.rb +783 -0
- data/lib/pathutil/version.rb +9 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 312093b72daeed797421e82320e9b69dbe8cca1c
|
4
|
+
data.tar.gz: be2774aba80baf6d8ec0638ba69a5014d5ca2bdf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c66c018dc735686f536246dd21ada663d5e676add05f1eca73a351bbd636c49a0aaf1bc0269294266d4922a591feeddeef8114c8d33b032cacc1cfdda1d73004
|
7
|
+
data.tar.gz: 9103cd6a157ae019af799844af46116158500317942575a4f8e1f939fabed3003f8af1bbe06eac26f8f0d1b8758758154bea80fbf7f457bb9aa1a7313a8155ad
|
data/Gemfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Frozen-string-literal: true
|
2
|
+
# Copyright: 2015-2016 Jordon Bedwell - MIT License
|
3
|
+
# Encoding: utf-8
|
4
|
+
|
5
|
+
source "https://rubygems.org"
|
6
|
+
gem "rake", :require => false
|
7
|
+
gemspec
|
8
|
+
|
9
|
+
group :test do
|
10
|
+
gem "codeclimate-test-reporter", :require => false
|
11
|
+
gem "safe_yaml", :require => false
|
12
|
+
end
|
13
|
+
|
14
|
+
group :development do
|
15
|
+
gem "rspec", :require => false
|
16
|
+
gem "rspec-helpers", :require => false
|
17
|
+
gem "rubocop", :github => "bbatsov/rubocop", :require => false
|
18
|
+
gem "luna-rspec-formatters", :require => false
|
19
|
+
gem "benchmark-ips", :require => false
|
20
|
+
gem "simple-ansi", :require => false
|
21
|
+
gem "pry", :require => false
|
22
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2015-2016 Jordon Bedwell
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the "Software"),
|
5
|
+
to deal in the Software without restriction, including without limitation
|
6
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
7
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
8
|
+
Software is furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included
|
11
|
+
in all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
14
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
18
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
19
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Frozen-string-literal: true
|
2
|
+
# Copyright: 2015-2016 Jordon Bedwell - MIT License
|
3
|
+
# Encoding: utf-8
|
4
|
+
|
5
|
+
require "open3"
|
6
|
+
require "rspec/core/rake_task"
|
7
|
+
require_relative "benchmark/support/task"
|
8
|
+
require "simple/ansi"
|
9
|
+
require "pathutil"
|
10
|
+
require "json"
|
11
|
+
|
12
|
+
task :default => [:spec]
|
13
|
+
RSpec::Core::RakeTask.new :spec
|
14
|
+
BenchmarkTask.new :benchmark
|
15
|
+
task :test => :spec
|
16
|
+
|
17
|
+
namespace :diff do
|
18
|
+
desc "List methods we have that Pathname doesn't."
|
19
|
+
task :methods do
|
20
|
+
methods = Pathutil.instance_methods - Pathname.instance_methods - Object.instance_methods
|
21
|
+
methods.each do |method|
|
22
|
+
$stdout.print "- ", "`", method, "`", "\n"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
namespace :missing do
|
28
|
+
desc "List methods we are missing."
|
29
|
+
task :methods do
|
30
|
+
methods = Pathname.instance_methods - Pathutil.instance_methods - Object.instance_methods
|
31
|
+
methods-= [
|
32
|
+
:cleanpath
|
33
|
+
]
|
34
|
+
|
35
|
+
methods.each do |method|
|
36
|
+
$stdout.puts method
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
namespace :pathname do
|
42
|
+
desc "List all of Pathnames methods."
|
43
|
+
task :methods do
|
44
|
+
methods = Pathname.instance_methods - Object.instance_methods
|
45
|
+
methods.each_with_index do |method, index|
|
46
|
+
$stdout.print method
|
47
|
+
unless index == methods.size - 1
|
48
|
+
$stdout.print ", "
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
$stdout.puts
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "List all of Pathutils methods."
|
57
|
+
task :methods do
|
58
|
+
methods = Pathutil.instance_methods - Object.instance_methods
|
59
|
+
methods.each_with_index do |method, index|
|
60
|
+
$stdout.print "`", method, "`"
|
61
|
+
$stdout.print ", " unless index == methods.size - 1
|
62
|
+
end
|
63
|
+
|
64
|
+
$stdout.puts
|
65
|
+
end
|
data/lib/pathutil.rb
ADDED
@@ -0,0 +1,783 @@
|
|
1
|
+
# ----------------------------------------------------------------------------
|
2
|
+
# Frozen-string-literal: true
|
3
|
+
# Copyright: 2015-2016 Jordon Bedwell - MIT License
|
4
|
+
# Encoding: utf-8
|
5
|
+
# ----------------------------------------------------------------------------
|
6
|
+
|
7
|
+
require "forwardable/extended"
|
8
|
+
require "find"
|
9
|
+
|
10
|
+
#
|
11
|
+
|
12
|
+
class Pathutil
|
13
|
+
extend Forwardable::Extended
|
14
|
+
self.class.send(:attr_writer, :encoding)
|
15
|
+
attr_writer :encoding
|
16
|
+
|
17
|
+
#
|
18
|
+
|
19
|
+
class << self
|
20
|
+
|
21
|
+
# ------------------------------------------------------------------------
|
22
|
+
# Aliases the default system encoding to us so that we can do most read
|
23
|
+
# and write operations with that encoding, instead of being crazy.
|
24
|
+
# @note you are encouraged to override this if you need to.
|
25
|
+
# ------------------------------------------------------------------------
|
26
|
+
|
27
|
+
def encoding
|
28
|
+
return @encoding ||= begin
|
29
|
+
Encoding.default_external
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# ------------------------------------------------------------------------
|
34
|
+
# Normalize CRLF -> LF on Windows reads, to ease your troubles.
|
35
|
+
# Normalize LF -> CLRF on Windows write, to ease their troubles.
|
36
|
+
# ------------------------------------------------------------------------
|
37
|
+
|
38
|
+
def normalize
|
39
|
+
return @normalize ||= {
|
40
|
+
:read => Gem.win_platform?,
|
41
|
+
:write => Gem.win_platform?
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# --------------------------------------------------------------------------
|
47
|
+
|
48
|
+
def initialize(path)
|
49
|
+
@path = path.to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
# --------------------------------------------------------------------------
|
53
|
+
|
54
|
+
def to_pathname
|
55
|
+
Pathname.new(
|
56
|
+
self
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
# --------------------------------------------------------------------------
|
61
|
+
|
62
|
+
def read_yaml(safe: true, whitelist_classes: [], throw_missing: false)
|
63
|
+
require "yaml"
|
64
|
+
|
65
|
+
unless safe
|
66
|
+
return YAML.load(
|
67
|
+
read
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
if !YAML.respond_to?(:safe_load)
|
72
|
+
setup_safe_yaml whitelist_classes
|
73
|
+
SafeYAML.load(read, {
|
74
|
+
:raise_on_unknown_tag => true
|
75
|
+
})
|
76
|
+
|
77
|
+
else
|
78
|
+
YAML.safe_load(read, {
|
79
|
+
:whitelist_classes => whitelist_classes
|
80
|
+
})
|
81
|
+
end
|
82
|
+
rescue Errno::ENOENT
|
83
|
+
throw_missing ? raise : (
|
84
|
+
return {}
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
# --------------------------------------------------------------------------
|
89
|
+
|
90
|
+
def read_json(throw_missing: false)
|
91
|
+
JSON.parse(
|
92
|
+
read
|
93
|
+
)
|
94
|
+
|
95
|
+
rescue Errno::ENOENT
|
96
|
+
throw_missing ? raise : (
|
97
|
+
return {}
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
# --------------------------------------------------------------------------
|
102
|
+
# Splits the path into all parts so that you can do step by step comparisons
|
103
|
+
# @note The blank part is intentionally left there so that you can rejoin.
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# Pathutil.new("/my/path").split_path # => [
|
107
|
+
# "", "my", "path"
|
108
|
+
# ]
|
109
|
+
# --------------------------------------------------------------------------
|
110
|
+
|
111
|
+
def split_path
|
112
|
+
@path.split(
|
113
|
+
File::SEPARATOR
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
# --------------------------------------------------------------------------
|
118
|
+
# @see `String#==` for more details.
|
119
|
+
# A stricter version of `==` that also makes sure the object matches.
|
120
|
+
# @param [Pathutil] other the comparee.
|
121
|
+
# @return true, false
|
122
|
+
# --------------------------------------------------------------------------
|
123
|
+
|
124
|
+
def ===(other)
|
125
|
+
other.is_a?(self.class) && @path == other
|
126
|
+
end
|
127
|
+
|
128
|
+
# --------------------------------------------------------------------------
|
129
|
+
# @example Pathutil.new("/hello") >= Pathutil.new("/") # => true
|
130
|
+
# @example Pathutil.new("/hello") >= Pathutil.new("/hello") # => true
|
131
|
+
# Checks to see if a path falls within a path and deeper or is the other.
|
132
|
+
# @param path the path that should be above the object.
|
133
|
+
# @return true, false
|
134
|
+
# --------------------------------------------------------------------------
|
135
|
+
|
136
|
+
def >=(other)
|
137
|
+
mine, other = expanded_paths(other)
|
138
|
+
return true if other == mine
|
139
|
+
mine.in_path?(other)
|
140
|
+
end
|
141
|
+
|
142
|
+
# --------------------------------------------------------------------------
|
143
|
+
# @example Pathutil.new("/hello/world") > Pathutil.new("/hello") # => true
|
144
|
+
# Strictly checks to see if a path is deeper but within the path of the other.
|
145
|
+
# @param path the path that should be above the object.
|
146
|
+
# @return true, false
|
147
|
+
# --------------------------------------------------------------------------
|
148
|
+
|
149
|
+
def >(other)
|
150
|
+
mine, other = expanded_paths(other)
|
151
|
+
return false if other == mine
|
152
|
+
mine.in_path?(other)
|
153
|
+
end
|
154
|
+
|
155
|
+
# --------------------------------------------------------------------------
|
156
|
+
# @example Pathutil.new("/") < Pathutil.new("/hello") # => true
|
157
|
+
# Strictly check to see if a path is behind other path but within it.
|
158
|
+
# @param path the path that should be below the object.
|
159
|
+
# @return true, false
|
160
|
+
# --------------------------------------------------------------------------
|
161
|
+
|
162
|
+
def <(other)
|
163
|
+
mine, other = expanded_paths(other)
|
164
|
+
return false if other == mine
|
165
|
+
other.in_path?(mine)
|
166
|
+
end
|
167
|
+
|
168
|
+
# --------------------------------------------------------------------------
|
169
|
+
# Check to see if a path is behind the other path butt within it.
|
170
|
+
# @example Pathutil.new("/hello") < Pathutil.new("/hello") # => true
|
171
|
+
# @example Pathutil.new("/") < Pathutil.new("/hello") # => true
|
172
|
+
# @param path the path that should be below the object.
|
173
|
+
# @return true, false
|
174
|
+
# --------------------------------------------------------------------------
|
175
|
+
|
176
|
+
def <=(other)
|
177
|
+
mine, other = expanded_paths(other)
|
178
|
+
return true if other == mine
|
179
|
+
other.in_path?(mine)
|
180
|
+
end
|
181
|
+
|
182
|
+
# --------------------------------------------------------------------------
|
183
|
+
# @note "./" is considered relative.
|
184
|
+
# Check to see if the path is absolute, as in: starts with "/"
|
185
|
+
# @return true, false
|
186
|
+
# --------------------------------------------------------------------------
|
187
|
+
|
188
|
+
def absolute?
|
189
|
+
@path.start_with?("/")
|
190
|
+
end
|
191
|
+
|
192
|
+
# --------------------------------------------------------------------------
|
193
|
+
# Break apart the path and yield each with the previous parts.
|
194
|
+
# @return Enumerator if no block is given.
|
195
|
+
#
|
196
|
+
# @example
|
197
|
+
# Pathutil.new("/hello/world").ascend.to_a # => [
|
198
|
+
# "/", "/hello", "/hello/world"
|
199
|
+
# ]
|
200
|
+
#
|
201
|
+
# @example
|
202
|
+
# Pathutil.new("/hello/world").ascend do |path|
|
203
|
+
# $stdout.puts path
|
204
|
+
# end
|
205
|
+
#
|
206
|
+
# /
|
207
|
+
# /hello
|
208
|
+
# /hello/world
|
209
|
+
# --------------------------------------------------------------------------
|
210
|
+
|
211
|
+
def ascend
|
212
|
+
unless block_given?
|
213
|
+
return to_enum(
|
214
|
+
__method__
|
215
|
+
)
|
216
|
+
end
|
217
|
+
|
218
|
+
yield(
|
219
|
+
path = self
|
220
|
+
)
|
221
|
+
|
222
|
+
while (new_path = path.dirname)
|
223
|
+
if path == new_path || new_path == "."
|
224
|
+
break
|
225
|
+
else
|
226
|
+
path = new_path
|
227
|
+
yield new_path
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
nil
|
232
|
+
end
|
233
|
+
|
234
|
+
# --------------------------------------------------------------------------
|
235
|
+
# Break apart the path in reverse order and descend into the path.
|
236
|
+
# @return Enumerator if no block is given.
|
237
|
+
#
|
238
|
+
# @example
|
239
|
+
# Pathutil.new("/hello/world").descend.to_a # => [
|
240
|
+
# "/hello/world", "/hello", "/"
|
241
|
+
# ]
|
242
|
+
#
|
243
|
+
# @example
|
244
|
+
# Pathutil.new("/hello/world").descend do |path|
|
245
|
+
# $stdout.puts path
|
246
|
+
# end
|
247
|
+
#
|
248
|
+
# /hello/world
|
249
|
+
# /hello
|
250
|
+
# /
|
251
|
+
# --------------------------------------------------------------------------
|
252
|
+
|
253
|
+
def descend
|
254
|
+
return ascend.to_a.reverse.to_enum unless block_given?
|
255
|
+
ascend.to_a.reverse_each do |val|
|
256
|
+
yield val
|
257
|
+
end
|
258
|
+
|
259
|
+
nil
|
260
|
+
end
|
261
|
+
|
262
|
+
# --------------------------------------------------------------------------
|
263
|
+
# Wraps `readlines` and allows you to yield on the result.
|
264
|
+
#
|
265
|
+
# @example
|
266
|
+
# Pathutil.new("/hello/world").each_line do |line|
|
267
|
+
# $stdout.puts line
|
268
|
+
# end
|
269
|
+
#
|
270
|
+
# Hello
|
271
|
+
# World
|
272
|
+
# --------------------------------------------------------------------------
|
273
|
+
|
274
|
+
def each_line
|
275
|
+
return to_enum(__method__) unless block_given?
|
276
|
+
readlines.each do |line|
|
277
|
+
yield line
|
278
|
+
end
|
279
|
+
|
280
|
+
nil
|
281
|
+
end
|
282
|
+
|
283
|
+
# --------------------------------------------------------------------------
|
284
|
+
# @see `File#fnmatch` for more information.
|
285
|
+
# Unlike traditional `fnmatch`, with this one `Regexp` is allowed.
|
286
|
+
# @param [String, Regexp] matcher the matcher used, can be a `Regexp`
|
287
|
+
# @example Pathutil.new("/hello").fnmatch?("/hello") # => true
|
288
|
+
# @example Pathutil.new("/hello").fnmatch?(/h/) # => true
|
289
|
+
# @return true, false
|
290
|
+
# --------------------------------------------------------------------------
|
291
|
+
|
292
|
+
def fnmatch?(matcher)
|
293
|
+
matcher.is_a?(Regexp) ? !!(self =~ matcher) : \
|
294
|
+
File.fnmatch(self, matcher)
|
295
|
+
end
|
296
|
+
|
297
|
+
# --------------------------------------------------------------------------
|
298
|
+
# Allows you to quickly determine if the file is the root folder.
|
299
|
+
# @return true, false
|
300
|
+
# --------------------------------------------------------------------------
|
301
|
+
|
302
|
+
def root?
|
303
|
+
self == File::SEPARATOR
|
304
|
+
end
|
305
|
+
|
306
|
+
# --------------------------------------------------------------------------
|
307
|
+
# @param [Pathutil, String] path the reference.
|
308
|
+
# Allows you to check if the current path is in the path you want.
|
309
|
+
# @return true, false
|
310
|
+
# --------------------------------------------------------------------------
|
311
|
+
|
312
|
+
def in_path?(path)
|
313
|
+
path = self.class.new(path).expand_path.split_path
|
314
|
+
mine = (symlink?? expand_path.realpath : expand_path).split_path
|
315
|
+
path.each_with_index { |part, index| return false if mine[index] != part }
|
316
|
+
true
|
317
|
+
end
|
318
|
+
|
319
|
+
# --------------------------------------------------------------------------
|
320
|
+
|
321
|
+
def inspect
|
322
|
+
"#<#{self.class}:#{@path}>"
|
323
|
+
end
|
324
|
+
|
325
|
+
# --------------------------------------------------------------------------
|
326
|
+
# Grab all of the children from the current directory, including hidden.
|
327
|
+
# @return Array<Pathutils>
|
328
|
+
# --------------------------------------------------------------------------
|
329
|
+
|
330
|
+
def children
|
331
|
+
ary = []
|
332
|
+
|
333
|
+
Dir.foreach(@path) do |path|
|
334
|
+
if path == "." || path == ".."
|
335
|
+
next
|
336
|
+
else
|
337
|
+
path = self.class.new(File.join(@path, path))
|
338
|
+
yield path if block_given?
|
339
|
+
ary.push(
|
340
|
+
path
|
341
|
+
)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
ary
|
346
|
+
end
|
347
|
+
|
348
|
+
# --------------------------------------------------------------------------
|
349
|
+
# @see `File::Constants` for a list of flags.
|
350
|
+
# Allows you to glob however you wish to glob in the current `Pathutils`
|
351
|
+
# @param [String] flags the flags you want to ship to the glob.
|
352
|
+
# @param [String] pattern the pattern A.K.A: "**/*"
|
353
|
+
# @return Enumerator unless a block is given.
|
354
|
+
# --------------------------------------------------------------------------
|
355
|
+
|
356
|
+
def glob(pattern, flags = 0)
|
357
|
+
unless block_given?
|
358
|
+
return to_enum(
|
359
|
+
__method__, pattern, flags
|
360
|
+
)
|
361
|
+
end
|
362
|
+
|
363
|
+
chdir do
|
364
|
+
Dir.glob(pattern, flags).each do |file|
|
365
|
+
yield self.class.new(
|
366
|
+
File.join(@path, file)
|
367
|
+
)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
nil
|
372
|
+
end
|
373
|
+
|
374
|
+
# --------------------------------------------------------------------------
|
375
|
+
# @note you do not need to ship a block at all.
|
376
|
+
# Move to the current directory temporarily (or for good) and do work son.
|
377
|
+
# @return 0, 1 if no block given
|
378
|
+
# --------------------------------------------------------------------------
|
379
|
+
|
380
|
+
def chdir
|
381
|
+
if !block_given?
|
382
|
+
Dir.chdir(
|
383
|
+
@path
|
384
|
+
)
|
385
|
+
|
386
|
+
else
|
387
|
+
Dir.chdir @path do
|
388
|
+
yield
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
# --------------------------------------------------------------------------
|
394
|
+
# @return Enumerator if no block is given.
|
395
|
+
# Find all files without care and yield the given block.
|
396
|
+
# @see Find.find
|
397
|
+
# --------------------------------------------------------------------------
|
398
|
+
|
399
|
+
def find
|
400
|
+
return to_enum(__method__) unless block_given?
|
401
|
+
Find.find @path do |val|
|
402
|
+
yield self.class.new(val)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
# --------------------------------------------------------------------------
|
407
|
+
# Splits the path returning each part (filename) back to you.
|
408
|
+
# @return Enumerator if no block is given.
|
409
|
+
# --------------------------------------------------------------------------
|
410
|
+
|
411
|
+
def each_filename
|
412
|
+
return to_enum(__method__) unless block_given?
|
413
|
+
@path.split(File::SEPARATOR).delete_if(&:empty?).each do |file|
|
414
|
+
yield file
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
# --------------------------------------------------------------------------
|
419
|
+
|
420
|
+
def parent
|
421
|
+
self.class.new(File.join(
|
422
|
+
@path, ".."
|
423
|
+
))
|
424
|
+
end
|
425
|
+
|
426
|
+
# --------------------------------------------------------------------------
|
427
|
+
# Split the file into its dirname and basename, so you can do stuff.
|
428
|
+
# @return File.dirname, File.basename
|
429
|
+
# --------------------------------------------------------------------------
|
430
|
+
|
431
|
+
def split
|
432
|
+
File.split(@path).collect! do |path|
|
433
|
+
self.class.new(path)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
# --------------------------------------------------------------------------
|
438
|
+
# Replace a files extension with your given extension.
|
439
|
+
# --------------------------------------------------------------------------
|
440
|
+
|
441
|
+
def sub_ext(ext)
|
442
|
+
self.class.new(
|
443
|
+
"#{@path.gsub(/\..+$/, "")}#{ext}"
|
444
|
+
)
|
445
|
+
end
|
446
|
+
|
447
|
+
# --------------------------------------------------------------------------
|
448
|
+
# A less complex version of `relative_path_from` that simply uses a
|
449
|
+
# `Regexp` and returns the full path if it cannot be relatively determined.
|
450
|
+
# @return Pathutils the relative path if it can be determined or is relative.
|
451
|
+
# @return Pathutils the full path if relative path cannot be determined
|
452
|
+
# --------------------------------------------------------------------------
|
453
|
+
|
454
|
+
def relative_path_from(from)
|
455
|
+
from = self.class.new(from).expand_path.gsub(%r!/$!, "")
|
456
|
+
self.class.new(expand_path.gsub(%r!^#{from.regexp_escape}/!, ""))
|
457
|
+
end
|
458
|
+
|
459
|
+
# --------------------------------------------------------------------------
|
460
|
+
# Expands the path and left joins the root to the path.
|
461
|
+
# @param [String, Pathutil] root the root you wish to enforce on it.
|
462
|
+
# @return Pathutil the enforced path with given root.
|
463
|
+
# --------------------------------------------------------------------------
|
464
|
+
|
465
|
+
def enforce_root(root)
|
466
|
+
curr, root = expanded_paths(root)
|
467
|
+
if curr.in_path?(root)
|
468
|
+
return curr
|
469
|
+
|
470
|
+
else
|
471
|
+
File.join(
|
472
|
+
root, curr
|
473
|
+
)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
# --------------------------------------------------------------------------
|
478
|
+
# Copy a directory, allowing symlinks if the link falls inside of the root.
|
479
|
+
# --------------------------------------------------------------------------
|
480
|
+
|
481
|
+
def safe_copy(to, root: nil)
|
482
|
+
raise ArgumentError, "must give a root" unless root
|
483
|
+
to = self.class.new(to)
|
484
|
+
|
485
|
+
root = self.class.new(root)
|
486
|
+
return safe_copy_directory(to, :root => root) if directory?
|
487
|
+
safe_copy_file(to, :root => root)
|
488
|
+
end
|
489
|
+
|
490
|
+
# --------------------------------------------------------------------------
|
491
|
+
# @see `self.class.normalize` as this is an alias.
|
492
|
+
# --------------------------------------------------------------------------
|
493
|
+
|
494
|
+
def normalize
|
495
|
+
return @normalize ||= begin
|
496
|
+
self.class.normalize
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
# --------------------------------------------------------------------------
|
501
|
+
# @see `self.class.encoding` as this is an alias.
|
502
|
+
# --------------------------------------------------------------------------
|
503
|
+
|
504
|
+
def encoding
|
505
|
+
return @encoding ||= begin
|
506
|
+
self.class.encoding
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
# --------------------------------------------------------------------------
|
511
|
+
# Read took two steroid shots: it can normalize your string, and encode.
|
512
|
+
# --------------------------------------------------------------------------
|
513
|
+
|
514
|
+
def read(*args, **kwd)
|
515
|
+
kwd[:encoding] ||= encoding
|
516
|
+
|
517
|
+
if normalize[:read]
|
518
|
+
File.read(self, *args, kwd).encode({
|
519
|
+
:universal_newline => true
|
520
|
+
})
|
521
|
+
|
522
|
+
else
|
523
|
+
File.read(
|
524
|
+
self, *args, kwd
|
525
|
+
)
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
# --------------------------------------------------------------------------
|
530
|
+
# Binread took two steroid shots: it can normalize your string, and encode.
|
531
|
+
# --------------------------------------------------------------------------
|
532
|
+
|
533
|
+
def binread(*args, **kwd)
|
534
|
+
kwd[:encoding] ||= encoding
|
535
|
+
|
536
|
+
if normalize[:read]
|
537
|
+
File.binread(self, *args, kwd).encode({
|
538
|
+
:universal_newline => true
|
539
|
+
})
|
540
|
+
|
541
|
+
else
|
542
|
+
File.read(
|
543
|
+
self, *args, kwd
|
544
|
+
)
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
# --------------------------------------------------------------------------
|
549
|
+
# Readlines took two steroid shots: it can normalize your string, and encode.
|
550
|
+
# --------------------------------------------------------------------------
|
551
|
+
|
552
|
+
def readlines(*args, **kwd)
|
553
|
+
kwd[:encoding] ||= encoding
|
554
|
+
|
555
|
+
if normalize[:read]
|
556
|
+
File.readlines(self, *args, kwd).encode({
|
557
|
+
:universal_newline => true
|
558
|
+
})
|
559
|
+
|
560
|
+
else
|
561
|
+
File.readlines(
|
562
|
+
self, *args, kwd
|
563
|
+
)
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
# --------------------------------------------------------------------------
|
568
|
+
# Write took two steroid shots: it can normalize your string, and encode.
|
569
|
+
# --------------------------------------------------------------------------
|
570
|
+
|
571
|
+
def write(data, *args, **kwd)
|
572
|
+
kwd[:encoding] ||= encoding
|
573
|
+
|
574
|
+
if normalize[:write]
|
575
|
+
File.write(self, data.encode(
|
576
|
+
:crlf_newline => true
|
577
|
+
), *args, kwd)
|
578
|
+
|
579
|
+
else
|
580
|
+
File.write(
|
581
|
+
self, data, *args, kwd
|
582
|
+
)
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
# --------------------------------------------------------------------------
|
587
|
+
# Binwrite took two steroid shots: it can normalize your string, and encode.
|
588
|
+
# --------------------------------------------------------------------------
|
589
|
+
|
590
|
+
def binwrite(data, *args, **kwd)
|
591
|
+
kwd[:encoding] ||= encoding
|
592
|
+
|
593
|
+
if normalize[:write]
|
594
|
+
File.binwrite(self, data.encode(
|
595
|
+
:crlf_newline => true
|
596
|
+
), *args, kwd)
|
597
|
+
|
598
|
+
else
|
599
|
+
File.binwrite(
|
600
|
+
self, data, *args, kwd
|
601
|
+
)
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
# --------------------------------------------------------------------------
|
606
|
+
# @api returns the current objects expanded path and their expanded path.
|
607
|
+
# --------------------------------------------------------------------------
|
608
|
+
|
609
|
+
private
|
610
|
+
def expanded_paths(path)
|
611
|
+
return expand_path, self.class.new(path).expand_path
|
612
|
+
end
|
613
|
+
|
614
|
+
# --------------------------------------------------------------------------
|
615
|
+
|
616
|
+
private
|
617
|
+
def safe_copy_file(to, root: nil)
|
618
|
+
raise Errno::EPERM, "#{self} not in #{root}" unless in_path?(root)
|
619
|
+
FileUtils.cp(self, to, {
|
620
|
+
:preserve => true
|
621
|
+
})
|
622
|
+
end
|
623
|
+
|
624
|
+
# --------------------------------------------------------------------------
|
625
|
+
|
626
|
+
private
|
627
|
+
def safe_copy_directory(to, root: nil)
|
628
|
+
if !in_path?(root)
|
629
|
+
raise Errno::EPERM, "#{self} not in #{
|
630
|
+
root
|
631
|
+
}"
|
632
|
+
|
633
|
+
else
|
634
|
+
to.mkdir_p unless to.exist?
|
635
|
+
children do |file|
|
636
|
+
if !file.in_path?(root)
|
637
|
+
raise Errno::EPERM, "#{file} not in #{
|
638
|
+
root
|
639
|
+
}"
|
640
|
+
|
641
|
+
elsif file.file?
|
642
|
+
FileUtils.cp(file, to, {
|
643
|
+
:preserve => true
|
644
|
+
})
|
645
|
+
|
646
|
+
else
|
647
|
+
path = file.realpath
|
648
|
+
path.safe_copy(to.join(file.basename), {
|
649
|
+
:root => root
|
650
|
+
})
|
651
|
+
end
|
652
|
+
end
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
# --------------------------------------------------------------------------
|
657
|
+
|
658
|
+
private
|
659
|
+
def setup_safe_yaml(whitelist_classes)
|
660
|
+
warn "WARN: SafeYAML will be removed when Ruby 2.0 goes EOL."
|
661
|
+
require "safe_yaml/load"
|
662
|
+
|
663
|
+
SafeYAML.restore_defaults!
|
664
|
+
whitelist_classes.map(&SafeYAML.method(
|
665
|
+
:whitelist_class!
|
666
|
+
))
|
667
|
+
end
|
668
|
+
|
669
|
+
# --------------------------------------------------------------------------
|
670
|
+
|
671
|
+
rb_delegate :sub, :to => :@path, :wrap => true
|
672
|
+
rb_delegate :chomp, :to => :@path, :wrap => true
|
673
|
+
rb_delegate :gsub, :to => :@path, :wrap => true
|
674
|
+
rb_delegate :=~, :to => :@path
|
675
|
+
rb_delegate :==, :to => :@path
|
676
|
+
rb_delegate :to_s, :to => :@path
|
677
|
+
rb_delegate :freeze, :to => :@path
|
678
|
+
rb_delegate :frozen?, :to => :@path
|
679
|
+
rb_delegate :to_str, :to => :@path
|
680
|
+
rb_delegate :"!~", :to => :@path
|
681
|
+
rb_delegate :<=>, :to => :@path
|
682
|
+
|
683
|
+
# --------------------------------------------------------------------------
|
684
|
+
|
685
|
+
rb_delegate :basename, :to => :File, :args => :@path, :wrap => true
|
686
|
+
rb_delegate :dirname, :to => :File, :args => :@path, :wrap => true
|
687
|
+
rb_delegate :readlink, :to => :File, :args => :@path, :wrap => true
|
688
|
+
rb_delegate :expand_path, :to => :File, :args => :@path, :wrap => true
|
689
|
+
rb_delegate :realdirpath, :to => :File, :args => :@path, :wrap => true
|
690
|
+
rb_delegate :realpath, :to => :File, :args => :@path, :wrap => true
|
691
|
+
rb_delegate :rename, :to => :File, :args => :@path, :wrap => true
|
692
|
+
rb_delegate :join, :to => :File, :args => :@path, :wrap => true
|
693
|
+
rb_delegate :size, :to => :File, :args => :@path
|
694
|
+
rb_delegate :link, :to => :File, :args => :@path
|
695
|
+
rb_delegate :atime, :to => :File, :args => :@path
|
696
|
+
rb_delegate :chown, :to => :File, :args => :@path
|
697
|
+
rb_delegate :ctime, :to => :File, :args => :@path
|
698
|
+
rb_delegate :lstat, :to => :File, :args => :@path
|
699
|
+
rb_delegate :utime, :to => :File, :args => :@path
|
700
|
+
rb_delegate :lchmod, :to => :File, :args => :@path
|
701
|
+
rb_delegate :sysopen, :to => :File, :args => :@path
|
702
|
+
rb_delegate :birthtime, :to => :File, :args => :@path
|
703
|
+
rb_delegate :mountpoint?, :to => :File, :args => :@path
|
704
|
+
rb_delegate :truncate, :to => :File, :args => :@path
|
705
|
+
rb_delegate :symlink, :to => :File, :args => :@path
|
706
|
+
rb_delegate :extname, :to => :File, :args => :@path
|
707
|
+
rb_delegate :lchown, :to => :File, :args => :@path
|
708
|
+
rb_delegate :zero?, :to => :File, :args => :@path
|
709
|
+
rb_delegate :ftype, :to => :File, :args => :@path
|
710
|
+
rb_delegate :chmod, :to => :File, :args => :@path
|
711
|
+
rb_delegate :mtime, :to => :File, :args => :@path
|
712
|
+
rb_delegate :open, :to => :File, :args => :@path
|
713
|
+
rb_delegate :stat, :to => :File, :args => :@path
|
714
|
+
|
715
|
+
# --------------------------------------------------------------------------
|
716
|
+
|
717
|
+
rb_delegate :pipe?, :to => :FileTest, :args => :@path
|
718
|
+
rb_delegate :file?, :to => :FileTest, :args => :@path
|
719
|
+
rb_delegate :owned?, :to => :FileTest, :args => :@path
|
720
|
+
rb_delegate :setgid?, :to => :FileTest, :args => :@path
|
721
|
+
rb_delegate :socket?, :to => :FileTest, :args => :@path
|
722
|
+
rb_delegate :readable?, :to => :FileTest, :args => :@path
|
723
|
+
rb_delegate :blockdev?, :to => :FileTest, :args => :@path
|
724
|
+
rb_delegate :directory?, :to => :FileTest, :args => :@path
|
725
|
+
rb_delegate :readable_real?, :to => :FileTest, :args => :@path
|
726
|
+
rb_delegate :world_readable?, :to => :FileTest, :args => :@path
|
727
|
+
rb_delegate :executable_real?, :to => :FileTest, :args => :@path
|
728
|
+
rb_delegate :world_writable?, :to => :FileTest, :args => :@path
|
729
|
+
rb_delegate :writable_real?, :to => :FileTest, :args => :@path
|
730
|
+
rb_delegate :executable?, :to => :FileTest, :args => :@path
|
731
|
+
rb_delegate :writable?, :to => :FileTest, :args => :@path
|
732
|
+
rb_delegate :grpowned?, :to => :FileTest, :args => :@path
|
733
|
+
rb_delegate :chardev?, :to => :FileTest, :args => :@path
|
734
|
+
rb_delegate :symlink?, :to => :FileTest, :args => :@path
|
735
|
+
rb_delegate :sticky?, :to => :FileTest, :args => :@path
|
736
|
+
rb_delegate :setuid?, :to => :FileTest, :args => :@path
|
737
|
+
rb_delegate :exist?, :to => :FileTest, :args => :@path
|
738
|
+
rb_delegate :size?, :to => :FileTest, :args => :@path
|
739
|
+
|
740
|
+
# --------------------------------------------------------------------------
|
741
|
+
|
742
|
+
rb_delegate :rm_rf, :to => :FileUtils, :args => :@path
|
743
|
+
rb_delegate :rm_r, :to => :FileUtils, :args => :@path
|
744
|
+
rb_delegate :rm, :to => :FileUtils, :args => :@path
|
745
|
+
rb_delegate :cp_r, :to => :FileUtils, :args => :@path
|
746
|
+
rb_delegate :touch, :to => :FileUtils, :args => :@path
|
747
|
+
rb_delegate :mkdir_p, :to => :FileUtils, :args => :@path
|
748
|
+
rb_delegate :mkpath, :to => :FileUtils, :args => :@path
|
749
|
+
rb_delegate :cp, :to => :FileUtils, :args => :@path
|
750
|
+
|
751
|
+
# --------------------------------------------------------------------------
|
752
|
+
|
753
|
+
rb_delegate :each_child, :to => :children
|
754
|
+
rb_delegate :each_entry, :to => :children
|
755
|
+
rb_delegate :to_a, :to => :children
|
756
|
+
|
757
|
+
# --------------------------------------------------------------------------
|
758
|
+
|
759
|
+
rb_delegate :opendir, :to => :Dir, :alias_of => :open
|
760
|
+
rb_delegate :relative?, :to => :self, :alias_of => :absolute?, :bool => :reverse
|
761
|
+
rb_delegate :regexp_escape, :to => :Regexp, :args => :@path, :alias_of => :escape
|
762
|
+
rb_delegate :to_regexp, :to => :Regexp, :args => :@path, :alias_of => :new
|
763
|
+
rb_delegate :shellescape, :to => :Shellwords, :args => :@path
|
764
|
+
rb_delegate :mkdir, :to => :Dir, :args => :@path
|
765
|
+
|
766
|
+
# --------------------------------------------------------------------------
|
767
|
+
# alias last basename, alias first dirname, alias ext extname
|
768
|
+
# --------------------------------------------------------------------------
|
769
|
+
|
770
|
+
alias + join
|
771
|
+
alias delete rm
|
772
|
+
alias rmtree rm_r
|
773
|
+
alias to_path to_s
|
774
|
+
alias last basename
|
775
|
+
alias entries children
|
776
|
+
alias make_symlink symlink
|
777
|
+
alias fnmatch fnmatch?
|
778
|
+
alias make_link link
|
779
|
+
alias first dirname
|
780
|
+
alias rmdir rm_r
|
781
|
+
alias unlink rm
|
782
|
+
alias / join
|
783
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# ----------------------------------------------------------------------------
|
2
|
+
# Frozen-string-literal: true
|
3
|
+
# Copyright: 2015-2016 Jordon Bedwell - MIT License
|
4
|
+
# Encoding: utf-8
|
5
|
+
# ----------------------------------------------------------------------------
|
6
|
+
|
7
|
+
class Pathutil
|
8
|
+
VERSION = "0.0.1"
|
9
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pathutil
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jordon Bedwell
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: forwardable-extended
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.4'
|
27
|
+
description: Like Pathname but a little less insane.
|
28
|
+
email:
|
29
|
+
- jordon@envygeeks.io
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- Gemfile
|
35
|
+
- LICENSE
|
36
|
+
- Rakefile
|
37
|
+
- lib/pathutil.rb
|
38
|
+
- lib/pathutil/version.rb
|
39
|
+
homepage: http://github.com/envygeeks/pathutils
|
40
|
+
licenses:
|
41
|
+
- MIT
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.5.1
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: Almost like Pathname but just a little less insane.
|
63
|
+
test_files: []
|