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/lib/epitools/iter.rb CHANGED
@@ -160,4 +160,4 @@ class Iter
160
160
  end
161
161
  end
162
162
 
163
- end
163
+ end
@@ -1,4 +1,4 @@
1
- require 'epitools'
1
+ #require 'epitools'
2
2
 
3
3
  #
4
4
  # Allow numbers to be converted into words, or exprssed more easily with words.
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 path
272
+ lstat.ctime
262
273
  end
263
-
274
+
264
275
  def atime
265
- lstat.atime path
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