epitools 0.4.49 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  require 'epitools'
2
+ gem 'mechanize', '~> 1.0'
2
3
  require 'mechanize'
3
4
  require 'epitools/browser/cache'
4
5
  require 'epitools/browser/mechanize_progressbar'
@@ -1,4 +1,4 @@
1
- require 'epitools'
1
+ #require 'epitools'
2
2
 
3
3
  class String
4
4
 
@@ -67,8 +67,9 @@ def lesspipe(*args)
67
67
  yield less
68
68
  end
69
69
  end
70
-
71
- rescue Errno::EPIPE
70
+
71
+ rescue Errno::EPIPE, Interrupt
72
+ # less just quit -- eat the exception.
72
73
  end
73
74
 
74
75
 
@@ -175,3 +176,6 @@ def autoinstall(*packages)
175
176
  cmd(["sudo apt-get install ?", packages.join(' ')])
176
177
  end
177
178
  end
179
+
180
+
181
+
@@ -1,28 +1,49 @@
1
- require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /win32/
2
- require 'set'
3
-
4
1
  #
5
- # ANSI Colour-coding for terminals that support it.
2
+ # ANSI Colour-coding (for terminals that support it.)
3
+ #
6
4
  # Originally by defunkt (Chris Wanstrath)
5
+ # Enhanced by epitron (Chris Gahan)
6
+ #
7
+ # It adds methods to String to allow easy coloring.
8
+ #
9
+ # (Note: Colors are automatically disabled if your program is piped to another program,
10
+ # ie: if STDOUT is not a TTY)
11
+ #
12
+ # Basic examples:
7
13
  #
8
- # It extends String with methods that insert terminal codes.
14
+ # >> "this is red".red
15
+ # >> "this is red with a blue background (read: ugly)".red_on_blue
16
+ # >> "this is light blue".light_blue
17
+ # >> "this is red with an underline".red.underline
18
+ # >> "this is really bold and really blue".bold.blue
9
19
  #
10
- # Examples:
20
+ # Color tags:
21
+ # (Note: You don't *need* to close color tags, but you can!)
11
22
  #
12
- # >> "this is red".red
13
- #
14
- # >> "this is red with a blue background (read: ugly)".red_on_blue
23
+ # >> "<yellow>This is using <green>color tags</green> to colorize.".colorize
24
+ # >> "<1>N<9>u<11>m<15>eric tags!".colorize
25
+ # (For those who still remember the DOS color palette and want more terse tagged-colors.)
15
26
  #
16
- # >> "this is light blue".light_blue
27
+ # Highlight search results:
17
28
  #
18
- # >> "<yellow>This is using <green>tags</green> to colorize.".colorize
29
+ # >> string.gsub(pattern) { |match| "<yellow>#{match}</yellow>" }.colorize
19
30
  #
20
- # >> "this is red with an underline".red.underline
31
+ # Forcing colors:
21
32
  #
22
- # >> "this is really bold and really blue".bold.blue
33
+ # Since the presence of a terminal is detected automatically, the colors will be
34
+ # disabled when you pipe your program to another program. However, if you want to
35
+ # show colors when piped (eg: when you pipe to `less -R`), you can force it:
23
36
  #
24
- # >> Colored.red "This is red" # but this part is mostly untested
37
+ # >> Colored.enable!
38
+ # >> Colored.disable!
39
+ # >> Colored.enable_temporarily { puts "whee!".red }
25
40
  #
41
+
42
+ require 'set'
43
+ require 'rbconfig'
44
+ require 'Win32/Console/ANSI' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
45
+ #require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /win32/
46
+
26
47
  module Colored
27
48
  extend self
28
49
 
@@ -127,7 +148,7 @@ module Colored
127
148
  end
128
149
 
129
150
  #
130
- # A class/instance method to colorize a string.
151
+ # Colorize a string (this method is called by #red, #blue, #red_on_green, etc.)
131
152
  #
132
153
  # Accepts options:
133
154
  # :foreground
@@ -137,6 +158,15 @@ module Colored
137
158
  # :extra
138
159
  # Extra styling, like 'bold', 'light', 'underline', 'reversed', or 'clear'.
139
160
  #
161
+ #
162
+ # With no options, it uses tagged colors:
163
+ #
164
+ # puts "<light_green><magenta>*</magenta> Hey mom! I am <light_blue>SO</light_blue> colored right now.</light_green>".colorize
165
+ #
166
+ # Or numeric ANSI tagged colors (from the BBS days):
167
+ # puts "<10><5>*</5> Hey mom! I am <9>SO</9> colored right now.</10>".colorize
168
+ #
169
+ #
140
170
  def colorize(string=nil, options = {})
141
171
  if string == nil
142
172
  return self.tagged_colors
@@ -227,14 +257,6 @@ module Colored
227
257
  #
228
258
  # Colorize a string that has "color tags".
229
259
  #
230
- # Examples:
231
- #
232
- # Colors as words:
233
- # puts "<light_green><magenta>*</magenta> Hey mom! I am <light_blue>SO</light_blue> colored right now.</light_green>".colorize
234
- #
235
- # Numeric ANSI colors (from the BBS days):
236
- # puts "<10><5>*</5> Hey mom! I am <9>SO</9> colored right now.</10>".colorize
237
- #
238
260
  def tagged_colors
239
261
  stack = []
240
262
 
@@ -0,0 +1,100 @@
1
+ require 'epitools'
2
+ require 'dbm'
3
+ require 'delegate'
4
+
5
+ class Ezdc < DelegateClass(Hash)
6
+
7
+ attr_reader :db, :path, :dirty
8
+
9
+ @@dirty = Set.new
10
+
11
+ def initialize(filename)
12
+ @path = Path[filename]
13
+
14
+ if @path.ext.nil?
15
+ @path.ext = "db"
16
+ else
17
+ @path.ext += ".db" if @path.ext != 'db'
18
+ end
19
+
20
+ @db = DBM::open(@path.with(:ext=>nil))
21
+
22
+ super
23
+ end
24
+
25
+ class Observed < BasicObject
26
+ MUTATORS = ::Set.new [
27
+ :<<, :push, :pop, :slice, :[]=
28
+ ]
29
+
30
+ def __send__(meth, *args)
31
+ if MUTATORS.include? meth
32
+ @@dirty.add self
33
+ end
34
+
35
+ end
36
+ end
37
+
38
+ def observed(obj)
39
+ obj.using(Observed)
40
+ end
41
+
42
+ def [](key)
43
+ observed(super[key])
44
+ end
45
+
46
+ def []=(key, val)
47
+ end
48
+
49
+ def keys
50
+ db.keys.map(&:unmarshal)
51
+ end
52
+
53
+ def delete!
54
+ @path.rm
55
+ end
56
+
57
+ def flush!
58
+ dirty.each do |key|
59
+ db[key.marshal] = super[key].marshal
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+
66
+ class Ezdb
67
+
68
+ attr_reader :db, :path
69
+
70
+ def initialize(filename)
71
+ @path = Path[filename]
72
+
73
+ if @path.ext.nil?
74
+ @path.ext = "db"
75
+ else
76
+ @path.ext += ".db" if @path.ext != 'db'
77
+ end
78
+
79
+ @db = DBM::open(@path.with(:ext=>nil))
80
+ end
81
+
82
+ def [](key)
83
+ val = db[key.marshal]
84
+ val = val.unmarshal if val
85
+ val
86
+ end
87
+
88
+ def []=(key, val)
89
+ db[key.marshal] = val.marshal
90
+ end
91
+
92
+ def keys
93
+ db.keys.map(&:unmarshal)
94
+ end
95
+
96
+ def delete!
97
+ @path.rm
98
+ end
99
+
100
+ end
@@ -1,5 +1,58 @@
1
+ #
2
+ # TODOs:
3
+ # Relative paths
4
+ # Rename bugs
5
+ # tmp bugs
6
+ #
7
+
1
8
  require 'epitools'
2
9
 
10
+ #
11
+ # Path: An object-oriented wrapper for files.
12
+ # (Combines useful methods from FileUtils, File, Dir, and more!)
13
+ #
14
+ # Each Path object has the following attributes:
15
+ #
16
+ # path => the entire path
17
+ # filename => just the name and extension
18
+ # basename => just the filename
19
+ # ext => just the extension
20
+ # dir => just the directory
21
+ # dirs => an array of directories
22
+ #
23
+ # Note: all of the above attributes can be modified to produce new paths!
24
+ # Here's a useful example:
25
+ #
26
+ # # Check if there's a '.git' directory in the current or parent directories.
27
+ # def inside_a_git_repository?
28
+ # path = Path.pwd # get the current directory
29
+ # while path.dirs.any?
30
+ # return true if (path/".git").exists?
31
+ # path.dirs.pop
32
+ # end
33
+ # false
34
+ # end
35
+ #
36
+ #
37
+ # Examples:
38
+ #
39
+ # Path["*.jpeg"].each { |path| path.rename(:ext=>"jpg") }
40
+ #
41
+ # Path["filename.txt"] << "Append data!"
42
+ #
43
+ # entries = Path["/etc"].ls
44
+ #
45
+ # Path
46
+ #
47
+ # Swap two files:
48
+ #
49
+ # a, b = Path["file_a", "file_b"]
50
+ # temp = a.with(:ext=>a.ext+".swapping")
51
+ # a.mv(temp)
52
+ # b.mv(a)
53
+ # temp.mv(b)
54
+ #
55
+ #
3
56
  class Path
4
57
 
5
58
  ## initializers
@@ -13,7 +66,6 @@ class Path
13
66
  end
14
67
 
15
68
  def self.[](path)
16
-
17
69
  case path
18
70
  when Path
19
71
  path
@@ -21,17 +73,22 @@ class Path
21
73
 
22
74
  if path =~ %r{^[a-z\-]+://}i # URL?
23
75
  Path::URL.new(path)
76
+ elsif path =~ /^javascript:/
77
+ Path::JS.new(path)
24
78
  else
25
- path = expand_path path
79
+
80
+ # todo: highlight backgrounds of codeblocks to show indent level & put boxes (or rules?) around (between?) double-spaced regions
81
+
82
+ path = Path.expand_path(path)
26
83
  if path =~ /(^|[^\\])[\?\*\{\}]/ # contains unescaped glob chars?
27
84
  glob(path)
28
85
  else
29
86
  new(path)
30
87
  end
88
+
31
89
  end
32
90
 
33
91
  end
34
-
35
92
  end
36
93
 
37
94
  ## setters
@@ -77,6 +134,8 @@ class Path
77
134
  @dirs = File.expand_path(newdir).split(File::SEPARATOR)[1..-1]
78
135
  end
79
136
 
137
+ # TODO: Figure out how to fix the 'path.with(:ext=>ext+".other")' problem (when 'ext == nil')...
138
+
80
139
  def ext=(newext)
81
140
  if newext.blank?
82
141
  @ext = nil
@@ -107,6 +166,28 @@ class Path
107
166
  end
108
167
  end
109
168
 
169
+ def relative_to(anchor=nil)
170
+ anchor ||= Path.pwd
171
+
172
+ # operations to transform anchor into self
173
+
174
+ # stage 1: go ".." until we find a common dir prefix
175
+ # (discard everything and go '/' if there's no common dir)
176
+ # stage 2: append the rest of the other path
177
+
178
+ # find common prefix
179
+ smaller, bigger = [ anchor.dirs, self.dirs ].sort_by(&:size)
180
+ common_prefix_end = bigger.zip(smaller).index { |a,b | a != b }
181
+ common_prefix = bigger[0...common_prefix_end]
182
+
183
+ if common_prefix.any?
184
+ dots = nil
185
+ end
186
+
187
+ self.dirs & anchor.dirs
188
+
189
+ end
190
+
110
191
  # The current directory (with a trailing /)
111
192
  def dir
112
193
  if dirs
@@ -168,6 +249,10 @@ class Path
168
249
  File.symlink? path
169
250
  end
170
251
 
252
+ def broken_symlink?
253
+ File.symlink?(path) and not File.exists?(path)
254
+ end
255
+
171
256
  def uri?
172
257
  false
173
258
  end
@@ -177,32 +262,23 @@ class Path
177
262
  end
178
263
 
179
264
 
180
- ## aliases
181
-
182
- alias_method :to_path, :path
183
- alias_method :to_str, :path
184
- alias_method :to_s, :path
185
-
186
- alias_method :pathname, :path
187
- alias_method :basename, :base
188
- alias_method :basename=, :base=
189
- alias_method :extname, :ext
190
- alias_method :extname=, :ext=
191
- alias_method :dirname, :dir
192
- alias_method :dirname=, :dir=
193
- alias_method :extension, :ext
194
- alias_method :extension=, :ext=
195
- alias_method :directory, :dir
196
- alias_method :directory=, :dir=
197
-
198
- alias_method :directory?, :dir?
199
-
200
265
  ## comparisons
201
266
 
202
267
  include Comparable
203
268
 
204
269
  def <=>(other)
205
- self.path <=> other.path
270
+ case other
271
+ when Path
272
+ self.path <=> other.path
273
+ when String
274
+ self.path == other
275
+ else
276
+ raise "Invalid comparison: Path to #{other.class}"
277
+ end
278
+ end
279
+
280
+ def ==(other)
281
+ self.path == other.to_s
206
282
  end
207
283
 
208
284
 
@@ -212,7 +288,10 @@ class Path
212
288
  # Path["/etc"]/"passwd" == Path["/etc/passwd"]
213
289
  #
214
290
  def /(other)
215
- Path.new( File.join(self, other) )
291
+ # / <- fixes jedit syntax highlighting bug.
292
+ # TODO: make it work for "/dir/dir"/"/dir/file"
293
+ #Path.new( File.join(self, other) )
294
+ Path[ File.join(self, other) ]
216
295
  end
217
296
 
218
297
  ## opening/reading files
@@ -230,11 +309,31 @@ class Path
230
309
  def read(length=nil, offset=nil)
231
310
  File.read(path, length, offset)
232
311
  end
312
+
313
+ #
314
+ # All the lines in this file, chomped.
315
+ #
316
+ def lines
317
+ io.lines.map(&:chomp)
318
+ end
319
+
320
+ def unmarshal
321
+ read.unmarshal
322
+ end
233
323
 
234
324
  def ls; Path[File.join(path, "*")]; end
235
325
 
236
326
  def ls_r; Path[File.join(path, "**/*")]; end
237
327
 
328
+
329
+ def siblings
330
+ ls - [self]
331
+ end
332
+
333
+ def touch
334
+ open("a") { }
335
+ self
336
+ end
238
337
 
239
338
  ## modifying files
240
339
 
@@ -248,7 +347,8 @@ class Path
248
347
  else
249
348
  yield f
250
349
  end
251
- end
350
+ end
351
+ self
252
352
  end
253
353
  alias_method :<<, :append
254
354
 
@@ -272,39 +372,68 @@ class Path
272
372
  # Path["Songy Song.aac"].rename(:dir=>"/music2")
273
373
  # Path["/music2/Songy Song.aac"].exists? #=> true
274
374
  #
375
+ def rename!(options)
376
+ raise "Broken!"
377
+
378
+ dest = rename(options)
379
+ self.path = dest.path # become dest
380
+ self
381
+ end
382
+
275
383
  def rename(options)
384
+ raise "Broken!"
385
+
276
386
  raise "Options must be a Hash" unless options.is_a? Hash
277
387
  dest = self.with(options)
278
388
 
279
389
  raise "Error: destination (#{dest.inspect}) already exists" if dest.exists?
280
390
  File.rename(path, dest)
281
391
 
282
- self.path = dest.path # become dest
392
+ dest
283
393
  end
284
394
 
285
395
  #
286
396
  # Renames the file the specified full path (like Dir.rename.)
287
397
  #
288
398
  def rename_to(path)
289
- rename :path=>path
399
+ raise "Broken!"
400
+
401
+ rename :path=>path.to_s
290
402
  end
291
403
  alias_method :mv, :rename_to
292
404
 
405
+ def rename_to!(path)
406
+ raise "Broken!"
407
+ rename! :path=>path.to_s
408
+ end
409
+ alias_method :mv!, :rename_to!
410
+
411
+ def reload!
412
+ self.path = to_s
413
+ end
414
+
415
+ #
416
+ # Generate two almost identical methods: mkdir and mkdir_p
417
+ #
293
418
  {
294
419
  :mkdir => "Dir.mkdir",
295
420
  :mkdir_p =>"FileUtils.mkdir_p"
296
- }.each do |method, expression|
421
+ }.each do |method, command|
297
422
  class_eval %{
298
423
  def #{method}
299
424
  if exists?
300
425
  if directory?
301
- false
426
+ Path[path]
302
427
  else
303
- raise "Error: Tried to make a directory over top of an existing file."
428
+ raise "Error: A file by this name already exists."
304
429
  end
305
430
  else
306
- #{expression}(path)
307
- true
431
+ #{command}(path)
432
+ #Path[path]
433
+ p [:path, path]
434
+ self.path = path # regenerate object
435
+ p [:path, path]
436
+ self
308
437
  end
309
438
  end
310
439
  }
@@ -314,6 +443,10 @@ class Path
314
443
  FileUtils.cp_r(path, dest) #if Path[dest].exists?
315
444
  end
316
445
 
446
+ def mv(dest)
447
+ FileUtils.mv(path, dest)
448
+ end
449
+
317
450
  def join(other)
318
451
  if uri?
319
452
  Path[URI.join(path, other).to_s]
@@ -322,11 +455,44 @@ class Path
322
455
  end
323
456
  end
324
457
 
458
+
325
459
  def ln_s(dest)
326
460
  dest = Path[dest]
327
461
  FileUtils.ln_s self, dest
328
462
  end
329
463
 
464
+ ## Owners and permissions
465
+
466
+ def chmod(mode)
467
+ FileUtils.chmod(mode, self)
468
+ self
469
+ end
470
+
471
+ def chown(usergroup)
472
+ user, group = usergroup.split(":")
473
+ FileUtils.chown(user, group, self)
474
+ self
475
+ end
476
+
477
+ def chmod_R(mode)
478
+ if directory?
479
+ FileUtils.chmod_R(mode, self)
480
+ self
481
+ else
482
+ raise "Not a directory."
483
+ end
484
+ end
485
+
486
+ def chown_R(usergroup)
487
+ user, group = usergroup.split(":")
488
+ if directory?
489
+ FileUtils.chown_R(user, group, self)
490
+ self
491
+ else
492
+ raise "Not a directory."
493
+ end
494
+ end
495
+
330
496
  ## Dangerous methods.
331
497
 
332
498
  def rm
@@ -341,7 +507,7 @@ class Path
341
507
  alias_method :"remove!", :rm
342
508
 
343
509
  def truncate(offset=0)
344
- File.truncate(self, offset)
510
+ File.truncate(self, offset) if exists?
345
511
  end
346
512
 
347
513
 
@@ -412,27 +578,25 @@ class Path
412
578
  alias_method :stream, :io
413
579
 
414
580
  def =~(pattern)
415
- path =~ pattern
581
+ to_s =~ pattern
582
+ end
583
+
584
+ def lstat
585
+ #@lstat ||= File.lstat self
586
+ File.lstat self
416
587
  end
417
588
 
418
- ## Class method versions of FileUtils-like things
589
+ def mode
590
+ lstat.mode
591
+ end
419
592
 
420
- %w[
421
- mkdir
422
- mkdir_p
423
- sha1
424
- sha2
425
- md5
426
- rm
427
- truncate
428
- ].each do |method|
429
- class_eval %{
430
- def self.#{method}(path)
431
- Path[path].#{method}
432
- end
433
- }
593
+ def parent
594
+ if file?
595
+ with(:filename=>nil)
596
+ else
597
+ with(:dirs=>dirs[0...-1])
598
+ end
434
599
  end
435
-
436
600
 
437
601
  # Mimetype finding and magic (requires 'mimemagic' gem)
438
602
 
@@ -458,6 +622,8 @@ class Path
458
622
  open { |io| MimeMagic.by_magic(io) }
459
623
  end
460
624
 
625
+ # TODO: rename type => magicext
626
+
461
627
  #
462
628
  # The filetype (as a standard file extension), verified with Magic.
463
629
  #
@@ -467,31 +633,93 @@ class Path
467
633
  # Note: Prefers long extensions (eg: jpeg over jpg)
468
634
  #
469
635
  def type
470
- @magictype ||= proc do
636
+ @cached_type ||= begin
471
637
 
472
- ext = self.ext
473
- magic = self.magic
638
+ if file? or symlink?
474
639
 
475
- if !ext and magic
476
- magic.ext
477
- elsif ext and !magic
478
- ext
479
- elsif !ext and !magic
480
- :unknown
481
- else # ext and magic
482
- if magic.extensions.include? ext
640
+ ext = self.ext
641
+ magic = self.magic
642
+
643
+ if ext and magic
644
+ if magic.extensions.include? ext
645
+ ext
646
+ else
647
+ magic.ext # in case the supplied extension is wrong...
648
+ end
649
+ elsif !ext and magic
650
+ magic.ext
651
+ elsif ext and !magic
483
652
  ext
484
- else
485
- magic.ext # in case the supplied extension is wrong...
653
+ else # !ext and !magic
654
+ :unknown
486
655
  end
656
+
657
+ elsif dir?
658
+ :directory
487
659
  end
488
660
 
489
- end.call
661
+ end
490
662
  end
491
663
 
664
+ ## aliases
665
+
666
+ alias_method :to_path, :path
667
+ alias_method :to_str, :path
668
+ alias_method :to_s, :path
669
+
670
+ alias_method :pathname, :path
671
+ alias_method :basename, :base
672
+ alias_method :basename=, :base=
673
+ alias_method :extname, :ext
674
+ alias_method :extname=, :ext=
675
+ alias_method :dirname, :dir
676
+ alias_method :dirname=, :dir=
677
+ alias_method :extension, :ext
678
+ alias_method :extension=, :ext=
679
+ alias_method :directory, :dir
680
+ alias_method :directory=, :dir=
681
+
682
+ alias_method :directory?, :dir?
683
+
684
+ alias_method :exist?, :exists?
685
+
492
686
  ############################################################################
493
687
  ## Class Methods
494
688
 
689
+ #
690
+ # FileUtils-like class-method versions of instance methods
691
+ # (eg: `Path.mv(src, dest)`)
692
+ #
693
+ # Note: Methods with cardinality 1 (`method/1`) are instance methods that take
694
+ # one parameter, and hence, class methods that take two parameters.
695
+ #
696
+ AUTOGENERATED_CLASS_METHODS = %w[
697
+ mkdir
698
+ mkdir_p
699
+ sha1
700
+ sha2
701
+ md5
702
+ rm
703
+ truncate
704
+ mv/1
705
+ move/1
706
+ chmod/1
707
+ chown/1
708
+ chown_R/1
709
+ chmod_R/1
710
+ ].each do |spec|
711
+ method, cardinality = spec.split("/")
712
+ cardinality = cardinality.to_i
713
+
714
+ class_eval %{
715
+ def self.#{method}(path#{", *args" if cardinality > 0})
716
+ Path[path].#{method}#{"(*args)" if cardinality > 0}
717
+ end
718
+ }
719
+ end
720
+
721
+
722
+
495
723
  #
496
724
  # Same as File.expand_path, except preserves the trailing '/'.
497
725
  #
@@ -510,6 +738,7 @@ class Path
510
738
  path
511
739
  end
512
740
  alias_class_method :tempfile, :tmpfile
741
+ alias_class_method :tmp, :tmpfile
513
742
 
514
743
  def self.home
515
744
  Path[ENV['HOME']]
@@ -548,13 +777,24 @@ class Path
548
777
  PATH_SEPARATOR = ":"
549
778
  BINARY_EXTENSION = ""
550
779
  end
551
-
552
- def self.which(bin)
553
- ENV["PATH"].split(PATH_SEPARATOR).find do |path|
554
- result = (Path[path] / (bin + BINARY_EXTENSION))
555
- return result if result.exists?
780
+
781
+ #
782
+ # A clone of `/usr/bin/which`: pass in the name of a binary, and it'll search the PATH
783
+ # returning the absolute location of the binary if it exists, or `nil` otherwise.
784
+ #
785
+ # (Note: If you pass more than one argument, it'll return an array of `Path`s instead of
786
+ # a single path.)
787
+ #
788
+ def self.which(bin, *extras)
789
+ if extras.empty?
790
+ ENV["PATH"].split(PATH_SEPARATOR).find do |path|
791
+ result = (Path[path] / (bin + BINARY_EXTENSION))
792
+ return result if result.exists?
793
+ end
794
+ nil
795
+ else
796
+ ([bin] + extras).map { |bin| which(bin) }
556
797
  end
557
- nil
558
798
  end
559
799
 
560
800
  end
@@ -566,6 +806,10 @@ end
566
806
  class Path::URL < Path
567
807
 
568
808
  attr_reader :uri
809
+
810
+ #
811
+ # TODO: only include certain methods from Path (delegate style)
812
+ # (eg: remove commands that write)
569
813
 
570
814
  def initialize(uri)
571
815
  @uri = URI.parse(uri)
@@ -575,11 +819,42 @@ class Path::URL < Path
575
819
  def uri?
576
820
  true
577
821
  end
822
+
823
+ #
824
+ # Example:
825
+ #
826
+ # When this is: http://host.com:port/path/filename.ext?param1=value1&param2=value2&...
827
+ #
828
+ def to_s
829
+ uri.to_s
830
+ end
831
+
832
+
833
+ #
834
+ # ...this is: 'http'
835
+ #
836
+ def scheme
837
+ uri.scheme
838
+ end
839
+ alias_method :protocol, :scheme
578
840
 
841
+ #
842
+ # ...and this is: 'host.com'
843
+ #
579
844
  def host
580
845
  uri.host
581
846
  end
582
847
 
848
+ #
849
+ # ...and this is: 80
850
+ #
851
+ def port
852
+ uri.host
853
+ end
854
+
855
+ #
856
+ # ...and this is: {param1: value1, param2: value2, ...etc... }
857
+ #
583
858
  def query
584
859
  if query = uri.query
585
860
  query.to_params
@@ -588,9 +863,7 @@ class Path::URL < Path
588
863
  end
589
864
  end
590
865
 
591
- def to_s
592
- uri.to_s
593
- end
866
+ # ...and `path` is /path/filename.ext
594
867
 
595
868
  end
596
869
 
@@ -602,10 +875,11 @@ def Path(*args)
602
875
  Path[*args]
603
876
  end
604
877
 
605
- if $0 == __FILE__
606
- require 'ruby-debug'
607
- #Path.pry
608
- #Path["http://google.com/"].pry
609
- debugger
610
- Path["?"]
878
+ class String
879
+ def to_Path
880
+ Path.new self
881
+ end
882
+
883
+ alias_method :to_P, :to_Path
611
884
  end
885
+