epitools 0.4.49 → 0.5.0

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.
@@ -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
+