epitools 0.5.7 → 0.5.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|