epitools 0.5.7 → 0.5.8
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/VERSION +1 -1
- data/epitools.gemspec +3 -3
- data/lib/epitools/autoloads.rb +1 -0
- data/lib/epitools/core_ext.rb +85 -33
- data/lib/epitools/core_ext/array.rb +16 -13
- data/lib/epitools/core_ext/enumerable.rb +27 -0
- data/lib/epitools/hexdump.rb +47 -38
- data/lib/epitools/iter.rb +1 -1
- data/lib/epitools/numwords.rb +1 -1
- data/lib/epitools/path.rb +215 -185
- data/lib/epitools/sys.rb +25 -1
- data/lib/epitools/term.rb +37 -31
- data/lib/epitools/wm.rb +293 -2
- data/spec/core_ext_spec.rb +20 -1
- data/spec/path_spec.rb +95 -87
- data/spec/wm_spec.rb +10 -0
- metadata +3 -3
data/lib/epitools/iter.rb
CHANGED
data/lib/epitools/numwords.rb
CHANGED
data/lib/epitools/path.rb
CHANGED
@@ -21,7 +21,7 @@ require 'epitools'
|
|
21
21
|
#
|
22
22
|
# Note: all of the above attributes can be modified to produce new paths!
|
23
23
|
# Here's a useful example:
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# # Check if there's a '.git' directory in the current or parent directories.
|
26
26
|
# def inside_a_git_repository?
|
27
27
|
# path = Path.pwd # get the current directory
|
@@ -31,7 +31,7 @@ require 'epitools'
|
|
31
31
|
# end
|
32
32
|
# false
|
33
33
|
# end
|
34
|
-
#
|
34
|
+
#
|
35
35
|
# More examples:
|
36
36
|
#
|
37
37
|
# Path["*.jpeg"].each { |path| path.rename(:ext=>"jpg") }
|
@@ -55,7 +55,7 @@ require 'epitools'
|
|
55
55
|
#
|
56
56
|
#
|
57
57
|
class Path
|
58
|
-
|
58
|
+
|
59
59
|
## initializers
|
60
60
|
|
61
61
|
def initialize(newpath, hints={})
|
@@ -65,13 +65,13 @@ class Path
|
|
65
65
|
def self.glob(str)
|
66
66
|
Dir[str].map { |entry| new(entry) }
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
def self.[](path)
|
70
70
|
case path
|
71
71
|
when Path
|
72
72
|
path
|
73
73
|
when String
|
74
|
-
|
74
|
+
|
75
75
|
if path =~ %r{^[a-z\-]+://}i # URL?
|
76
76
|
Path::URL.new(path)
|
77
77
|
|
@@ -81,27 +81,27 @@ class Path
|
|
81
81
|
else
|
82
82
|
# todo: highlight backgrounds of codeblocks to show indent level & put boxes (or rules?) around (between?) double-spaced regions
|
83
83
|
path = Path.expand_path(path)
|
84
|
-
if path =~ /(^|[^\\])[\?\*\{\}]/ # contains unescaped glob chars?
|
84
|
+
if path =~ /(^|[^\\])[\?\*\{\}]/ # contains unescaped glob chars?
|
85
85
|
glob(path)
|
86
86
|
else
|
87
87
|
new(path)
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
95
|
## setters
|
96
|
-
|
96
|
+
|
97
97
|
attr_writer :base
|
98
98
|
attr_writer :dirs
|
99
|
-
|
99
|
+
|
100
100
|
#
|
101
101
|
# This is the core that initializes the whole class.
|
102
102
|
#
|
103
103
|
# Note: The `hints` parameter contains options so `path=` doesn't have to touch the filesytem as much.
|
104
|
-
#
|
104
|
+
#
|
105
105
|
def path=(newpath, hints={})
|
106
106
|
if hints[:type] or File.exists? newpath
|
107
107
|
if hints[:type] == :dir or File.directory? newpath
|
@@ -112,18 +112,18 @@ class Path
|
|
112
112
|
else
|
113
113
|
if newpath.endswith(File::SEPARATOR) # ends in '/'
|
114
114
|
self.dir = newpath
|
115
|
-
else
|
115
|
+
else
|
116
116
|
self.dir, self.filename = File.split(newpath)
|
117
117
|
end
|
118
118
|
end
|
119
119
|
end
|
120
|
-
|
120
|
+
|
121
121
|
def filename=(newfilename)
|
122
122
|
if newfilename.nil?
|
123
123
|
@ext, @base = nil, nil
|
124
124
|
else
|
125
125
|
ext = File.extname(newfilename)
|
126
|
-
|
126
|
+
|
127
127
|
if ext.blank?
|
128
128
|
@ext = nil
|
129
129
|
@base = newfilename
|
@@ -135,16 +135,16 @@ class Path
|
|
135
135
|
end
|
136
136
|
end
|
137
137
|
end
|
138
|
-
|
138
|
+
|
139
139
|
def dir=(newdir)
|
140
140
|
dirs = File.expand_path(newdir).split(File::SEPARATOR)
|
141
141
|
dirs = dirs[1..-1] if dirs.size > 0
|
142
|
-
|
142
|
+
|
143
143
|
@dirs = dirs
|
144
144
|
end
|
145
|
-
|
145
|
+
|
146
146
|
# TODO: Figure out how to fix the 'path.with(:ext=>ext+".other")' problem (when 'ext == nil')...
|
147
|
-
|
147
|
+
|
148
148
|
def ext=(newext)
|
149
149
|
if newext.blank?
|
150
150
|
@ext = nil
|
@@ -156,17 +156,17 @@ class Path
|
|
156
156
|
end
|
157
157
|
|
158
158
|
## getters
|
159
|
-
|
159
|
+
|
160
160
|
# The directories in the path, split into an array. (eg: ['usr', 'src', 'linux'])
|
161
|
-
attr_reader :dirs
|
162
|
-
|
163
|
-
# The filename without an extension
|
161
|
+
attr_reader :dirs
|
162
|
+
|
163
|
+
# The filename without an extension
|
164
164
|
attr_reader :base
|
165
|
-
|
166
|
-
# The file extension, including the . (eg: ".mp3")
|
165
|
+
|
166
|
+
# The file extension, including the . (eg: ".mp3")
|
167
167
|
attr_reader :ext
|
168
168
|
|
169
|
-
# Joins and returns the full path
|
169
|
+
# Joins and returns the full path
|
170
170
|
def path
|
171
171
|
if d = dir
|
172
172
|
File.join(d, (filename || "") )
|
@@ -193,38 +193,38 @@ class Path
|
|
193
193
|
fname = from.pop
|
194
194
|
join(*(from.map { RELATIVE_PARENTDIR } + to))
|
195
195
|
end
|
196
|
-
|
196
|
+
|
197
197
|
def relative_to2(anchor=nil)
|
198
|
-
anchor ||= Path.pwd
|
199
|
-
|
198
|
+
anchor ||= Path.pwd
|
199
|
+
|
200
200
|
# operations to transform anchor into self
|
201
|
-
|
201
|
+
|
202
202
|
# stage 1: go ".." until we find a common dir prefix
|
203
203
|
# (discard everything and go '/' if there's no common dir)
|
204
|
-
# stage 2: append the rest of the other path
|
205
|
-
|
204
|
+
# stage 2: append the rest of the other path
|
205
|
+
|
206
206
|
# find common prefix
|
207
207
|
smaller, bigger = [ anchor.dirs, self.dirs ].sort_by(&:size)
|
208
208
|
common_prefix_end = bigger.zip(smaller).index { |a,b | a != b }
|
209
|
-
common_prefix = bigger[0...common_prefix_end]
|
210
|
-
|
209
|
+
common_prefix = bigger[0...common_prefix_end]
|
210
|
+
|
211
211
|
if common_prefix.any?
|
212
212
|
dots = nil
|
213
213
|
end
|
214
|
-
|
214
|
+
|
215
215
|
self.dirs & anchor.dirs
|
216
|
-
|
216
|
+
|
217
217
|
end
|
218
|
-
|
218
|
+
|
219
219
|
# The current directory (with a trailing /)
|
220
220
|
def dir
|
221
|
-
if dirs
|
221
|
+
if dirs
|
222
222
|
File::SEPARATOR + File.join(*dirs)
|
223
223
|
else
|
224
224
|
nil
|
225
225
|
end
|
226
226
|
end
|
227
|
-
|
227
|
+
|
228
228
|
def filename
|
229
229
|
if base
|
230
230
|
if ext
|
@@ -237,14 +237,16 @@ class Path
|
|
237
237
|
end
|
238
238
|
end
|
239
239
|
|
240
|
+
alias_method :name, :filename
|
241
|
+
|
240
242
|
def exts
|
241
243
|
extensions = basename.split('.')[1..-1]
|
242
244
|
extensions += [@ext] if @ext
|
243
245
|
extensions
|
244
246
|
end
|
245
|
-
|
247
|
+
|
246
248
|
## fstat info
|
247
|
-
|
249
|
+
|
248
250
|
def exists?
|
249
251
|
File.exists? path
|
250
252
|
end
|
@@ -252,61 +254,88 @@ class Path
|
|
252
254
|
def size
|
253
255
|
File.size path
|
254
256
|
end
|
255
|
-
|
257
|
+
|
258
|
+
def lstat
|
259
|
+
@lstat ||= File.lstat self # to cache or not to cache -- that is the question.
|
260
|
+
#File.lstat self
|
261
|
+
end
|
262
|
+
|
263
|
+
def mode
|
264
|
+
lstat.mode
|
265
|
+
end
|
266
|
+
|
256
267
|
def mtime
|
257
268
|
lstat.mtime
|
258
269
|
end
|
259
|
-
|
270
|
+
|
260
271
|
def ctime
|
261
|
-
lstat.ctime
|
272
|
+
lstat.ctime
|
262
273
|
end
|
263
|
-
|
274
|
+
|
264
275
|
def atime
|
265
|
-
lstat.atime
|
276
|
+
lstat.atime
|
266
277
|
end
|
267
|
-
|
278
|
+
|
279
|
+
# FIXME: Does the current user own this file?
|
280
|
+
def owner?
|
281
|
+
raise "STUB"
|
282
|
+
end
|
283
|
+
|
284
|
+
def executable?
|
285
|
+
mode & 0o111 > 0
|
286
|
+
end
|
287
|
+
alias_method :exe?, :executable?
|
288
|
+
|
289
|
+
def writable?
|
290
|
+
mode & 0o222 > 0
|
291
|
+
end
|
292
|
+
|
293
|
+
def readable?
|
294
|
+
mode & 0o444 > 0
|
295
|
+
end
|
296
|
+
|
268
297
|
def dir?
|
269
298
|
File.directory? path
|
270
299
|
end
|
271
|
-
|
300
|
+
|
272
301
|
def file?
|
273
302
|
File.file? path
|
274
303
|
end
|
275
|
-
|
304
|
+
|
276
305
|
def symlink?
|
277
306
|
File.symlink? path
|
278
307
|
end
|
279
|
-
|
308
|
+
|
280
309
|
def broken_symlink?
|
281
310
|
File.symlink?(path) and not File.exists?(path)
|
282
311
|
end
|
283
|
-
|
312
|
+
|
284
313
|
def symlink_target
|
285
|
-
Path.new File.readlink(path)
|
314
|
+
Path.new File.readlink(path)
|
286
315
|
end
|
287
316
|
alias_method :readlink, :symlink_target
|
288
|
-
|
317
|
+
|
289
318
|
def uri?
|
290
319
|
false
|
291
320
|
end
|
292
|
-
|
321
|
+
|
293
322
|
def url?
|
294
323
|
uri?
|
295
324
|
end
|
296
|
-
|
325
|
+
|
297
326
|
def child_of?(parent)
|
298
327
|
parent.parent_of? self
|
299
328
|
end
|
300
|
-
|
329
|
+
|
301
330
|
def parent_of?(child)
|
302
331
|
# If `self` is a parent of `child`, it's a prefix.
|
303
332
|
child.path[/^#{Regexp.escape self.path}\/.+/] != nil
|
304
333
|
end
|
305
|
-
|
334
|
+
|
306
335
|
## comparisons
|
307
336
|
|
308
337
|
include Comparable
|
309
|
-
|
338
|
+
|
310
339
|
def <=>(other)
|
311
340
|
case other
|
312
341
|
when Path
|
@@ -317,26 +346,33 @@ class Path
|
|
317
346
|
raise "Invalid comparison: Path to #{other.class}"
|
318
347
|
end
|
319
348
|
end
|
320
|
-
|
349
|
+
|
321
350
|
def ==(other)
|
322
351
|
self.path == other.to_s
|
323
352
|
end
|
324
353
|
|
325
|
-
|
354
|
+
|
326
355
|
## appending
|
327
|
-
|
356
|
+
|
357
|
+
#
|
358
|
+
# Path["/etc"].join("anything{}").path == "/etc/anything{}"
|
359
|
+
#
|
360
|
+
def join(other)
|
361
|
+
Path.new File.join(self, other)
|
362
|
+
end
|
363
|
+
|
328
364
|
#
|
329
365
|
# Path["/etc"]/"passwd" == Path["/etc/passwd"]
|
330
366
|
#
|
331
367
|
def /(other)
|
332
368
|
# / <- fixes jedit syntax highlighting bug.
|
333
|
-
# TODO: make it work for "/dir/dir"/"/dir/file"
|
369
|
+
# TODO: make it work for "/dir/dir"/"/dir/file"
|
334
370
|
#Path.new( File.join(self, other) )
|
335
371
|
Path[ File.join(self, other) ]
|
336
|
-
end
|
337
|
-
|
372
|
+
end
|
373
|
+
|
338
374
|
## opening/reading files
|
339
|
-
|
375
|
+
|
340
376
|
def open(mode="rb", &block)
|
341
377
|
if block_given?
|
342
378
|
File.open(path, mode, &block)
|
@@ -346,31 +382,31 @@ class Path
|
|
346
382
|
end
|
347
383
|
alias_method :io, :open
|
348
384
|
alias_method :stream, :open
|
349
|
-
|
385
|
+
|
350
386
|
def read(length=nil, offset=nil)
|
351
387
|
File.read(path, length, offset)
|
352
388
|
end
|
353
|
-
|
389
|
+
|
354
390
|
#
|
355
391
|
# All the lines in this file, chomped.
|
356
|
-
#
|
392
|
+
#
|
357
393
|
def lines
|
358
394
|
io.lines.map(&:chomp)
|
359
395
|
end
|
360
|
-
|
396
|
+
|
361
397
|
def unmarshal
|
362
398
|
read.unmarshal
|
363
399
|
end
|
364
|
-
|
400
|
+
|
365
401
|
def ls; Path[File.join(path, "*")]; end
|
366
402
|
|
367
403
|
def ls_r; Path[File.join(path, "**/*")]; end
|
368
|
-
|
404
|
+
|
369
405
|
def ls_dirs
|
370
406
|
ls.select(&:dir?)
|
371
407
|
#Dir.glob("#{path}*/", File::FNM_DOTMATCH).map { |s| Path.new(s, :type=>:dir) }
|
372
408
|
end
|
373
|
-
|
409
|
+
|
374
410
|
def ls_files
|
375
411
|
ls.select(&:file?)
|
376
412
|
#Dir.glob("#{path}*", File::FNM_DOTMATCH).map { |s| Path.new(s, :type=>:file) }
|
@@ -379,12 +415,12 @@ class Path
|
|
379
415
|
def siblings
|
380
416
|
ls - [self]
|
381
417
|
end
|
382
|
-
|
418
|
+
|
383
419
|
def touch
|
384
420
|
open("a") { }
|
385
421
|
self
|
386
422
|
end
|
387
|
-
|
423
|
+
|
388
424
|
## modifying files
|
389
425
|
|
390
426
|
#
|
@@ -405,7 +441,7 @@ class Path
|
|
405
441
|
self
|
406
442
|
end
|
407
443
|
alias_method :<<, :append
|
408
|
-
|
444
|
+
|
409
445
|
#
|
410
446
|
# Overwrite the data in this file (accepts a string, an IO, or it can yield the file handle to a block.)
|
411
447
|
#
|
@@ -420,7 +456,7 @@ class Path
|
|
420
456
|
else
|
421
457
|
yield f
|
422
458
|
end
|
423
|
-
end
|
459
|
+
end
|
424
460
|
end
|
425
461
|
|
426
462
|
#
|
@@ -450,7 +486,7 @@ class Path
|
|
450
486
|
def read_json
|
451
487
|
JSON.load(io)
|
452
488
|
end
|
453
|
-
|
489
|
+
|
454
490
|
# Convert the object to JSON and write it to the file (overwriting the existing file).
|
455
491
|
def write_json(object)
|
456
492
|
write object.to_json
|
@@ -490,64 +526,64 @@ class Path
|
|
490
526
|
def read_bson
|
491
527
|
BSON.deserialize(read)
|
492
528
|
end
|
493
|
-
|
529
|
+
|
494
530
|
def write_bson(object)
|
495
531
|
write BSON.serialize(object)
|
496
532
|
end
|
497
533
|
|
498
|
-
|
534
|
+
|
499
535
|
#
|
500
536
|
# Examples:
|
501
537
|
# Path["SongySong.mp3"].rename(:basename=>"Songy Song")
|
502
538
|
# Path["Songy Song.mp3"].rename(:ext=>"aac")
|
503
539
|
# Path["Songy Song.aac"].rename(:dir=>"/music2")
|
504
540
|
# Path["/music2/Songy Song.aac"].exists? #=> true
|
505
|
-
#
|
541
|
+
#
|
506
542
|
def rename!(options)
|
507
543
|
raise "Broken!"
|
508
|
-
|
544
|
+
|
509
545
|
dest = rename(options)
|
510
546
|
self.path = dest.path # become dest
|
511
547
|
self
|
512
548
|
end
|
513
|
-
|
549
|
+
|
514
550
|
def rename(options)
|
515
551
|
raise "Broken!"
|
516
|
-
|
552
|
+
|
517
553
|
raise "Options must be a Hash" unless options.is_a? Hash
|
518
554
|
dest = self.with(options)
|
519
|
-
|
555
|
+
|
520
556
|
raise "Error: destination (#{dest.inspect}) already exists" if dest.exists?
|
521
557
|
File.rename(path, dest)
|
522
|
-
|
558
|
+
|
523
559
|
dest
|
524
560
|
end
|
525
561
|
|
526
562
|
#
|
527
563
|
# Renames the file the specified full path (like Dir.rename.)
|
528
|
-
#
|
564
|
+
#
|
529
565
|
def rename_to(path)
|
530
566
|
raise "Broken!"
|
531
|
-
|
567
|
+
|
532
568
|
rename :path=>path.to_s
|
533
569
|
end
|
534
570
|
alias_method :mv, :rename_to
|
535
|
-
|
571
|
+
|
536
572
|
def rename_to!(path)
|
537
573
|
raise "Broken!"
|
538
574
|
rename! :path=>path.to_s
|
539
575
|
end
|
540
576
|
alias_method :mv!, :rename_to!
|
541
|
-
|
577
|
+
|
542
578
|
def reload!
|
543
579
|
self.path = to_s
|
544
580
|
end
|
545
|
-
|
581
|
+
|
546
582
|
#
|
547
|
-
# Generate two almost identical methods: mkdir and mkdir_p
|
583
|
+
# Generate two almost identical methods: mkdir and mkdir_p
|
548
584
|
#
|
549
585
|
{
|
550
|
-
:mkdir => "Dir.mkdir",
|
586
|
+
:mkdir => "Dir.mkdir",
|
551
587
|
:mkdir_p =>"FileUtils.mkdir_p"
|
552
588
|
}.each do |method, command|
|
553
589
|
class_eval %{
|
@@ -573,7 +609,7 @@ raise "Broken!"
|
|
573
609
|
def cp_r(dest)
|
574
610
|
FileUtils.cp_r(path, dest) #if Path[dest].exists?
|
575
611
|
end
|
576
|
-
|
612
|
+
|
577
613
|
def mv(dest)
|
578
614
|
FileUtils.mv(path, dest)
|
579
615
|
end
|
@@ -588,22 +624,22 @@ raise "Broken!"
|
|
588
624
|
|
589
625
|
def ln_s(dest)
|
590
626
|
dest = Path[dest]
|
591
|
-
FileUtils.ln_s self, dest
|
627
|
+
FileUtils.ln_s self, dest
|
592
628
|
end
|
593
629
|
|
594
630
|
## Owners and permissions
|
595
|
-
|
631
|
+
|
596
632
|
def chmod(mode)
|
597
633
|
FileUtils.chmod(mode, self)
|
598
634
|
self
|
599
635
|
end
|
600
|
-
|
636
|
+
|
601
637
|
def chown(usergroup)
|
602
638
|
user, group = usergroup.split(":")
|
603
639
|
FileUtils.chown(user, group, self)
|
604
640
|
self
|
605
641
|
end
|
606
|
-
|
642
|
+
|
607
643
|
def chmod_R(mode)
|
608
644
|
if directory?
|
609
645
|
FileUtils.chmod_R(mode, self)
|
@@ -612,7 +648,7 @@ raise "Broken!"
|
|
612
648
|
raise "Not a directory."
|
613
649
|
end
|
614
650
|
end
|
615
|
-
|
651
|
+
|
616
652
|
def chown_R(usergroup)
|
617
653
|
user, group = usergroup.split(":")
|
618
654
|
if directory?
|
@@ -622,36 +658,9 @@ raise "Broken!"
|
|
622
658
|
raise "Not a directory."
|
623
659
|
end
|
624
660
|
end
|
625
|
-
|
626
|
-
def lstat
|
627
|
-
@lstat ||= File.lstat self # to cache or not to cache -- that is the question.
|
628
|
-
#File.lstat self
|
629
|
-
end
|
630
|
-
|
631
|
-
def mode
|
632
|
-
lstat.mode
|
633
|
-
end
|
634
|
-
|
635
|
-
# TODO: Unstub
|
636
|
-
def owner?
|
637
|
-
raise "STUB"
|
638
|
-
end
|
639
|
-
|
640
|
-
def executable?
|
641
|
-
mode & 0o111 > 0
|
642
|
-
end
|
643
|
-
alias_method :exe?, :executable?
|
644
|
-
|
645
|
-
def writable?
|
646
|
-
mode & 0o222 > 0
|
647
|
-
end
|
648
|
-
|
649
|
-
def readable?
|
650
|
-
mode & 0o444 > 0
|
651
|
-
end
|
652
661
|
|
653
662
|
## Dangerous methods.
|
654
|
-
|
663
|
+
|
655
664
|
def rm
|
656
665
|
if directory? and not symlink?
|
657
666
|
Dir.rmdir(self) == 0
|
@@ -662,61 +671,61 @@ raise "Broken!"
|
|
662
671
|
alias_method :"delete!", :rm
|
663
672
|
alias_method :"unlink!", :rm
|
664
673
|
alias_method :"remove!", :rm
|
665
|
-
|
674
|
+
|
666
675
|
def truncate(offset=0)
|
667
676
|
File.truncate(self, offset) if exists?
|
668
677
|
end
|
669
678
|
|
670
|
-
|
679
|
+
|
671
680
|
## Checksums
|
672
|
-
|
681
|
+
|
673
682
|
def sha1
|
674
683
|
Digest::SHA1.file(self).hexdigest
|
675
684
|
end
|
676
|
-
|
685
|
+
|
677
686
|
def sha2
|
678
687
|
Digest::SHA2.file(self).hexdigest
|
679
688
|
end
|
680
|
-
|
689
|
+
|
681
690
|
def md5
|
682
691
|
Digest::MD5.file(self).hexdigest
|
683
692
|
end
|
684
693
|
alias_method :md5sum, :md5
|
685
694
|
|
686
|
-
|
695
|
+
|
687
696
|
# http://ruby-doc.org/stdlib/libdoc/zlib/rdoc/index.html
|
688
|
-
|
697
|
+
|
689
698
|
def gzip(level=nil)
|
690
699
|
gz_filename = self.with(:filename=>filename+".gz")
|
691
|
-
|
692
|
-
raise "#{gz_filename} already exists" if gz_filename.exists?
|
693
|
-
|
694
|
-
open("rb") do |input|
|
700
|
+
|
701
|
+
raise "#{gz_filename} already exists" if gz_filename.exists?
|
702
|
+
|
703
|
+
open("rb") do |input|
|
695
704
|
Zlib::GzipWriter.open(gz_filename) do |gzip|
|
696
705
|
IO.copy_stream(input, gzip)
|
697
706
|
end
|
698
707
|
end
|
699
|
-
|
708
|
+
|
700
709
|
gz_filename
|
701
710
|
end
|
702
|
-
|
711
|
+
|
703
712
|
def gzip!(level=nil)
|
704
713
|
gzipped = self.gzip(level)
|
705
714
|
self.rm
|
706
715
|
self.path = gzipped.path
|
707
716
|
end
|
708
|
-
|
717
|
+
|
709
718
|
def gunzip
|
710
719
|
raise "Not a .gz file" unless ext == "gz"
|
711
720
|
|
712
721
|
gunzipped = self.with(:ext=>nil)
|
713
|
-
|
722
|
+
|
714
723
|
gunzipped.open("wb") do |out|
|
715
724
|
Zlib::GzipReader.open(self) do |gunzip|
|
716
725
|
IO.copy_stream(gunzip, out)
|
717
726
|
end
|
718
727
|
end
|
719
|
-
|
728
|
+
|
720
729
|
gunzipped
|
721
730
|
end
|
722
731
|
|
@@ -740,7 +749,7 @@ raise "Broken!"
|
|
740
749
|
with(:dirs=>dirs[0...-1])
|
741
750
|
end
|
742
751
|
end
|
743
|
-
|
752
|
+
|
744
753
|
#
|
745
754
|
# Follows all symlinks to give the true location of a path.
|
746
755
|
#
|
@@ -755,29 +764,29 @@ raise "Broken!"
|
|
755
764
|
end
|
756
765
|
end
|
757
766
|
|
758
|
-
|
767
|
+
|
759
768
|
#
|
760
769
|
# Find the file's mimetype (first from file extension, then by magic)
|
761
|
-
#
|
770
|
+
#
|
762
771
|
def mimetype
|
763
772
|
mimetype_from_ext || magic
|
764
773
|
end
|
765
774
|
alias_method :identify, :mimetype
|
766
|
-
|
775
|
+
|
767
776
|
#
|
768
777
|
# Find the file's mimetype (only using the file extension)
|
769
|
-
#
|
778
|
+
#
|
770
779
|
def mimetype_from_ext
|
771
780
|
MimeMagic.by_extension(ext)
|
772
781
|
end
|
773
782
|
|
774
783
|
#
|
775
784
|
# Find the file's mimetype (by magic)
|
776
|
-
#
|
785
|
+
#
|
777
786
|
def magic
|
778
787
|
open { |io| MimeMagic.by_magic(io) }
|
779
788
|
end
|
780
|
-
|
789
|
+
|
781
790
|
#
|
782
791
|
# Returns the filetype (as a standard file extension), verified with Magic.
|
783
792
|
#
|
@@ -787,15 +796,15 @@ raise "Broken!"
|
|
787
796
|
# Note: Prefers long extensions (eg: jpeg over jpg)
|
788
797
|
#
|
789
798
|
# TODO: rename type => magicext?
|
790
|
-
#
|
799
|
+
#
|
791
800
|
def type
|
792
801
|
@cached_type ||= begin
|
793
|
-
|
802
|
+
|
794
803
|
if file? or symlink?
|
795
|
-
|
804
|
+
|
796
805
|
ext = self.ext
|
797
806
|
magic = self.magic
|
798
|
-
|
807
|
+
|
799
808
|
if ext and magic
|
800
809
|
if magic.extensions.include? ext
|
801
810
|
ext
|
@@ -809,17 +818,17 @@ raise "Broken!"
|
|
809
818
|
else # !ext and !magic
|
810
819
|
:unknown
|
811
820
|
end
|
812
|
-
|
821
|
+
|
813
822
|
elsif dir?
|
814
823
|
:directory
|
815
824
|
end
|
816
|
-
|
825
|
+
|
817
826
|
end
|
818
827
|
end
|
819
828
|
|
820
829
|
|
821
830
|
## aliases
|
822
|
-
|
831
|
+
|
823
832
|
alias_method :to_path, :path
|
824
833
|
alias_method :to_str, :path
|
825
834
|
alias_method :to_s, :path
|
@@ -837,9 +846,9 @@ raise "Broken!"
|
|
837
846
|
alias_method :directory=, :dir=
|
838
847
|
|
839
848
|
alias_method :directory?, :dir?
|
840
|
-
|
849
|
+
|
841
850
|
alias_method :exist?, :exists?
|
842
|
-
|
851
|
+
|
843
852
|
############################################################################
|
844
853
|
## Class Methods
|
845
854
|
|
@@ -852,9 +861,9 @@ raise "Broken!"
|
|
852
861
|
#
|
853
862
|
AUTOGENERATED_CLASS_METHODS = %w[
|
854
863
|
mkdir
|
855
|
-
mkdir_p
|
856
|
-
sha1
|
857
|
-
sha2
|
864
|
+
mkdir_p
|
865
|
+
sha1
|
866
|
+
sha2
|
858
867
|
md5
|
859
868
|
rm
|
860
869
|
truncate
|
@@ -868,7 +877,7 @@ raise "Broken!"
|
|
868
877
|
].each do |spec|
|
869
878
|
method, cardinality = spec.split("/")
|
870
879
|
cardinality = cardinality.to_i
|
871
|
-
|
880
|
+
|
872
881
|
class_eval %{
|
873
882
|
def self.#{method}(path#{", *args" if cardinality > 0})
|
874
883
|
Path[path].#{method}#{"(*args)" if cardinality > 0}
|
@@ -885,7 +894,7 @@ raise "Broken!"
|
|
885
894
|
new_path << "/" if orig_path.endswith "/"
|
886
895
|
new_path
|
887
896
|
end
|
888
|
-
|
897
|
+
|
889
898
|
#
|
890
899
|
# TODO: Remove the tempfile when the Path object is garbage collected or freed.
|
891
900
|
#
|
@@ -894,39 +903,39 @@ raise "Broken!"
|
|
894
903
|
yield path if block_given?
|
895
904
|
path
|
896
905
|
end
|
897
|
-
alias_class_method :tempfile, :tmpfile
|
898
|
-
alias_class_method :tmp, :tmpfile
|
899
|
-
|
906
|
+
alias_class_method :tempfile, :tmpfile
|
907
|
+
alias_class_method :tmp, :tmpfile
|
908
|
+
|
900
909
|
def self.home
|
901
910
|
Path[ENV['HOME']]
|
902
911
|
end
|
903
|
-
|
912
|
+
|
904
913
|
def self.pwd
|
905
914
|
Path.new expand_path(Dir.pwd)
|
906
915
|
end
|
907
|
-
|
916
|
+
|
908
917
|
def self.pushd
|
909
918
|
@@dir_stack ||= []
|
910
919
|
@@dir_stack.push pwd
|
911
920
|
end
|
912
|
-
|
921
|
+
|
913
922
|
def self.popd
|
914
923
|
@@dir_stack ||= [pwd]
|
915
924
|
@@dir_stack.pop
|
916
925
|
end
|
917
|
-
|
926
|
+
|
918
927
|
def self.cd(dest); Dir.chdir(dest); end
|
919
|
-
|
928
|
+
|
920
929
|
def self.ls(path); Path[path].ls end
|
921
|
-
|
930
|
+
|
922
931
|
def self.ls_r(path); Path[path].ls_r; end
|
923
|
-
|
932
|
+
|
924
933
|
def self.ln_s(src, dest); Path[src].ln_s(dest); end
|
925
934
|
|
926
935
|
## TODO: Verbose mode
|
927
936
|
#def self.verbose=(value); @@verbose = value; end
|
928
937
|
#def self.verbose; @@verbose ||= false; end
|
929
|
-
|
938
|
+
|
930
939
|
if Sys.windows?
|
931
940
|
PATH_SEPARATOR = ";"
|
932
941
|
BINARY_EXTENSION = ".exe"
|
@@ -941,7 +950,7 @@ raise "Broken!"
|
|
941
950
|
#
|
942
951
|
# (Note: If you pass more than one argument, it'll return an array of `Path`s instead of
|
943
952
|
# a single path.)
|
944
|
-
#
|
953
|
+
#
|
945
954
|
def self.which(bin, *extras)
|
946
955
|
if extras.empty?
|
947
956
|
ENV["PATH"].split(PATH_SEPARATOR).find do |path|
|
@@ -952,8 +961,8 @@ raise "Broken!"
|
|
952
961
|
else
|
953
962
|
([bin] + extras).map { |bin| which(bin) }
|
954
963
|
end
|
955
|
-
end
|
956
|
-
|
964
|
+
end
|
965
|
+
|
957
966
|
end
|
958
967
|
|
959
968
|
|
@@ -967,12 +976,12 @@ class Path::URL < Path
|
|
967
976
|
#
|
968
977
|
# TODO: only include certain methods from Path (delegate style)
|
969
978
|
# (eg: remove commands that write)
|
970
|
-
|
979
|
+
|
971
980
|
def initialize(uri, hints={})
|
972
981
|
@uri = URI.parse(uri)
|
973
982
|
self.path = @uri.path
|
974
983
|
end
|
975
|
-
|
984
|
+
|
976
985
|
def uri?
|
977
986
|
true
|
978
987
|
end
|
@@ -985,30 +994,30 @@ class Path::URL < Path
|
|
985
994
|
def to_s
|
986
995
|
uri.to_s
|
987
996
|
end
|
988
|
-
|
997
|
+
|
989
998
|
|
990
999
|
#
|
991
1000
|
# ...this is: 'http'
|
992
|
-
#
|
1001
|
+
#
|
993
1002
|
def scheme
|
994
1003
|
uri.scheme
|
995
1004
|
end
|
996
1005
|
alias_method :protocol, :scheme
|
997
|
-
|
1006
|
+
|
998
1007
|
#
|
999
1008
|
# ...and this is: 'host.com'
|
1000
1009
|
#
|
1001
1010
|
def host
|
1002
1011
|
uri.host
|
1003
1012
|
end
|
1004
|
-
|
1013
|
+
|
1005
1014
|
#
|
1006
1015
|
# ...and this is: 80
|
1007
1016
|
#
|
1008
1017
|
def port
|
1009
1018
|
uri.port
|
1010
1019
|
end
|
1011
|
-
|
1020
|
+
|
1012
1021
|
#
|
1013
1022
|
# ...and this is: {param1: value1, param2: value2, ...etc... }
|
1014
1023
|
#
|
@@ -1019,8 +1028,29 @@ class Path::URL < Path
|
|
1019
1028
|
nil
|
1020
1029
|
end
|
1021
1030
|
end
|
1022
|
-
|
1031
|
+
|
1023
1032
|
# ...and `path` is /path/filename.ext
|
1033
|
+
|
1034
|
+
def open(mode="r", &block)
|
1035
|
+
require 'open-uri'
|
1036
|
+
if block_given?
|
1037
|
+
open(to_s, mode, &block)
|
1038
|
+
else
|
1039
|
+
open(to_s, mode)
|
1040
|
+
end
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
# Note: open is already aliased to io in parent class.
|
1044
|
+
def read(*args)
|
1045
|
+
require 'open-uri'
|
1046
|
+
case scheme
|
1047
|
+
when /https?/i
|
1048
|
+
io.read(*args)
|
1049
|
+
else
|
1050
|
+
raise "No connector for #{scheme} yet. Please fix!"
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
|
1024
1054
|
end
|
1025
1055
|
|
1026
1056
|
|
@@ -1035,6 +1065,6 @@ class String
|
|
1035
1065
|
def to_Path
|
1036
1066
|
Path.new self
|
1037
1067
|
end
|
1038
|
-
|
1068
|
+
|
1039
1069
|
alias_method :to_P, :to_Path
|
1040
1070
|
end
|