Re-enable spec/library for full CI runs.
[rbx.git] / lib / uri / generic.rb
blob2557aade50e7ec69442aa99c70ff19176e73e1b8
2 # = uri/generic.rb
4 # Author:: Akira Yamada <akira@ruby-lang.org>
5 # License:: You can redistribute it and/or modify it under the same term as Ruby.
6 # Revision:: $Id: generic.rb 12860 2007-08-01 03:40:08Z nobu $
9 require 'uri/common'
11 module URI
12   
13   #
14   # Base class for all URI classes.
15   # Implements generic URI syntax as per RFC 2396.
16   #
17   class Generic
18     include URI
19     include REGEXP
21     DEFAULT_PORT = nil
23     #
24     # Returns default port
25     #
26     def self.default_port
27       self::DEFAULT_PORT
28     end
30     def default_port
31       self.class.default_port
32     end
34     COMPONENT = [
35       :scheme, 
36       :userinfo, :host, :port, :registry, 
37       :path, :opaque, 
38       :query, 
39       :fragment
40     ].freeze
42     #
43     # Components of the URI in the order.
44     #
45     def self.component
46       self::COMPONENT
47     end
49     USE_REGISTRY = false
51     #
52     # DOC: FIXME!
53     #
54     def self.use_registry
55       self::USE_REGISTRY
56     end
58     #
59     # == Synopsis
60     #
61     # See #new
62     #
63     # == Description
64     #
65     # At first, tries to create a new URI::Generic instance using
66     # URI::Generic::build. But, if exception URI::InvalidComponentError is raised, 
67     # then it URI::Escape.escape all URI components and tries again.
68     #
69     #
70     def self.build2(args)
71       begin
72         return self.build(args)
73       rescue InvalidComponentError
74         if args.kind_of?(Array)
75           return self.build(args.collect{|x| 
76             if x
77               URI.escape(x)
78             else
79               x
80             end
81           })
82         elsif args.kind_of?(Hash)
83           tmp = {}
84           args.each do |key, value|
85             tmp[key] = if value
86                 URI.escape(value)
87               else
88                 value
89               end
90           end
91           return self.build(tmp)
92         end
93       end
94     end
96     #
97     # == Synopsis
98     #
99     # See #new
100     #
101     # == Description
102     #
103     # Creates a new URI::Generic instance from components of URI::Generic
104     # with check.  Components are: scheme, userinfo, host, port, registry, path,
105     # opaque, query and fragment. You can provide arguments either by an Array or a Hash.
106     # See #new for hash keys to use or for order of array items.
107     #
108     def self.build(args)
109       if args.kind_of?(Array) &&
110           args.size == ::URI::Generic::COMPONENT.size
111         tmp = args
112       elsif args.kind_of?(Hash)
113         tmp = ::URI::Generic::COMPONENT.collect do |c|
114           if args.include?(c)
115             args[c]
116           else
117             nil
118           end
119         end
120       else
121         raise ArgumentError, 
122         "expected Array of or Hash of components of #{self.class} (#{self.class.component.join(', ')})"
123       end
125       tmp << true
126       return self.new(*tmp)
127     end
128     #
129     # == Args
130     #
131     # +scheme+::
132     #   Protocol scheme, i.e. 'http','ftp','mailto' and so on.
133     # +userinfo+::
134     #   User name and password, i.e. 'sdmitry:bla'
135     # +host+::
136     #   Server host name
137     # +port+::
138     #   Server port
139     # +registry+::
140     #   DOC: FIXME!
141     # +path+::
142     #   Path on server
143     # +opaque+::
144     #   DOC: FIXME!
145     # +query+::
146     #   Query data
147     # +fragment+::
148     #   A part of URI after '#' sign
149     # +arg_check+::
150     #   Check arguments [false by default]
151     #
152     # == Description
153     #
154     # Creates a new URI::Generic instance from ``generic'' components without check.
155     #
156     def initialize(scheme, 
157                    userinfo, host, port, registry, 
158                    path, opaque, 
159                    query, 
160                    fragment,
161                    arg_check = false)
162       @scheme = nil
163       @user = nil
164       @password = nil
165       @host = nil
166       @port = nil
167       @path = nil
168       @query = nil
169       @opaque = nil
170       @registry = nil
171       @fragment = nil
173       if arg_check
174         self.scheme = scheme
175         self.userinfo = userinfo
176         self.host = host
177         self.port = port
178         self.path = path
179         self.query = query
180         self.opaque = opaque
181         self.registry = registry
182         self.fragment = fragment
183       else
184         self.set_scheme(scheme)
185         self.set_userinfo(userinfo)
186         self.set_host(host)
187         self.set_port(port)
188         self.set_path(path)
189         self.set_query(query)
190         self.set_opaque(opaque)
191         self.set_registry(registry)
192         self.set_fragment(fragment)
193       end
194       if @registry && !self.class.use_registry
195         raise InvalidURIError, 
196           "the scheme #{@scheme} does not accept registry part: #{@registry} (or bad hostname?)"
197       end
198       
199       @scheme.freeze if @scheme
200       self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2)
201       self.set_port(self.default_port) if self.default_port && !@port
202     end
203     attr_reader :scheme
204     attr_reader :host
205     attr_reader :port
206     attr_reader :registry
207     attr_reader :path
208     attr_reader :query
209     attr_reader :opaque
210     attr_reader :fragment
212     # replace self by other URI object
213     def replace!(oth)
214       if self.class != oth.class
215         raise ArgumentError, "expected #{self.class} object"
216       end
218       component.each do |c|
219         self.__send__("#{c}=", oth.__send__(c))
220       end
221     end
222     private :replace!
224     def component
225       self.class.component
226     end
228     def check_scheme(v)
229       if v && SCHEME !~ v
230         raise InvalidComponentError,
231           "bad component(expected scheme component): #{v}"
232       end
234       return true
235     end
236     private :check_scheme
238     def set_scheme(v)
239       @scheme = v
240     end
241     protected :set_scheme
243     def scheme=(v)
244       check_scheme(v)
245       set_scheme(v)
246       v
247     end
249     def check_userinfo(user, password = nil)
250       if !password
251         user, password = split_userinfo(user)
252       end
253       check_user(user)
254       check_password(password, user)
256       return true
257     end
258     private :check_userinfo
260     def check_user(v)
261       if @registry || @opaque
262         raise InvalidURIError, 
263           "can not set user with registry or opaque"
264       end
266       return v unless v
268       if USERINFO !~ v
269         raise InvalidComponentError,
270           "bad component(expected userinfo component or user component): #{v}"
271       end
273       return true
274     end
275     private :check_user
277     def check_password(v, user = @user)
278       if @registry || @opaque
279         raise InvalidURIError, 
280           "can not set password with registry or opaque"
281       end
282       return v unless v
284       if !user
285         raise InvalidURIError,
286           "password component depends user component"
287       end
289       if USERINFO !~ v
290         raise InvalidComponentError,
291           "bad component(expected user component): #{v}"
292       end
294       return true
295     end
296     private :check_password
298     #
299     # Sets userinfo, argument is string like 'name:pass'
300     #
301     def userinfo=(userinfo)
302       if userinfo.nil?
303         return nil
304       end
305       check_userinfo(*userinfo)
306       set_userinfo(*userinfo)
307       # returns userinfo
308     end
310     def user=(user)
311       check_user(user)
312       set_user(user)
313       # returns user
314     end
315     
316     def password=(password)
317       check_password(password)
318       set_password(password)
319       # returns password
320     end
322     def set_userinfo(user, password = nil)
323       unless password 
324         user, password = split_userinfo(user)
325       end
326       @user     = user
327       @password = password if password
329       [@user, @password]
330     end
331     protected :set_userinfo
333     def set_user(v)
334       set_userinfo(v, @password)
335       v
336     end
337     protected :set_user
339     def set_password(v)
340       @password = v
341       # returns v
342     end
343     protected :set_password
345     def split_userinfo(ui)
346       return nil, nil unless ui
347       user, password = ui.split(/:/, 2)
349       return user, password
350     end
351     private :split_userinfo
353     def escape_userpass(v)
354       v = URI.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/
355     end
356     private :escape_userpass
358     def userinfo
359       if @user.nil?
360         nil
361       elsif @password.nil?
362         @user
363       else
364         @user + ':' + @password
365       end
366     end
368     def user
369       @user
370     end
372     def password
373       @password
374     end
376     def check_host(v)
377       return v unless v
379       if @registry || @opaque
380         raise InvalidURIError, 
381           "can not set host with registry or opaque"
382       elsif HOST !~ v
383         raise InvalidComponentError,
384           "bad component(expected host component): #{v}"
385       end
387       return true
388     end
389     private :check_host
391     def set_host(v)
392       @host = v
393     end
394     protected :set_host
396     def host=(v)
397       check_host(v)
398       set_host(v)
399       v
400     end
402     def check_port(v)
403       return v unless v
405       if @registry || @opaque
406         raise InvalidURIError, 
407           "can not set port with registry or opaque"
408       elsif !v.kind_of?(Fixnum) && PORT !~ v
409         raise InvalidComponentError,
410           "bad component(expected port component): #{v}"
411       end
413       return true
414     end
415     private :check_port
417     def set_port(v)
418       unless !v || v.kind_of?(Fixnum)
419         if v.empty?
420           v = nil
421         else
422           v = v.to_i
423         end
424       end
425       @port = v
426     end
427     protected :set_port
429     def port=(v)
430       check_port(v)
431       set_port(v)
432       port
433     end
435     def check_registry(v)
436       return v unless v
438       # raise if both server and registry are not nil, because:
439       # authority     = server | reg_name
440       # server        = [ [ userinfo "@" ] hostport ]
441       if @host || @port || @user # userinfo = @user + ':' + @password
442         raise InvalidURIError, 
443           "can not set registry with host, port, or userinfo"
444       elsif v && REGISTRY !~ v
445         raise InvalidComponentError,
446           "bad component(expected registry component): #{v}"
447       end
449       return true
450     end
451     private :check_registry
453     def set_registry(v)
454       @registry = v
455     end
456     protected :set_registry
458     def registry=(v)
459       check_registry(v)
460       set_registry(v)
461       v
462     end
464     def check_path(v)
465       # raise if both hier and opaque are not nil, because:
466       # absoluteURI   = scheme ":" ( hier_part | opaque_part )
467       # hier_part     = ( net_path | abs_path ) [ "?" query ]
468       if v && @opaque
469         raise InvalidURIError, 
470           "path conflicts with opaque"
471       end
473       if @scheme
474         if v && v != '' && ABS_PATH !~ v
475           raise InvalidComponentError, 
476             "bad component(expected absolute path component): #{v}"
477         end
478       else
479         if v && v != '' && ABS_PATH !~ v && REL_PATH !~ v
480           raise InvalidComponentError, 
481             "bad component(expected relative path component): #{v}"
482         end
483       end
485       return true
486     end
487     private :check_path
489     def set_path(v)
490       @path = v
491     end
492     protected :set_path
494     def path=(v)
495       check_path(v)
496       set_path(v)
497       v
498     end
500     def check_query(v)
501       return v unless v
503       # raise if both hier and opaque are not nil, because:
504       # absoluteURI   = scheme ":" ( hier_part | opaque_part )
505       # hier_part     = ( net_path | abs_path ) [ "?" query ]
506       if @opaque
507         raise InvalidURIError, 
508           "query conflicts with opaque"
509       end
511       if v && v != '' && QUERY !~ v
512           raise InvalidComponentError, 
513             "bad component(expected query component): #{v}"
514         end
516       return true
517     end
518     private :check_query
520     def set_query(v)
521       @query = v
522     end
523     protected :set_query
525     def query=(v)
526       check_query(v)
527       set_query(v)
528       v
529     end
531     def check_opaque(v)
532       return v unless v
534       # raise if both hier and opaque are not nil, because:
535       # absoluteURI   = scheme ":" ( hier_part | opaque_part )
536       # hier_part     = ( net_path | abs_path ) [ "?" query ]
537       if @host || @port || @user || @path  # userinfo = @user + ':' + @password
538         raise InvalidURIError, 
539           "can not set opaque with host, port, userinfo or path"
540       elsif v && OPAQUE !~ v
541         raise InvalidComponentError,
542           "bad component(expected opaque component): #{v}"
543       end
545       return true
546     end
547     private :check_opaque
549     def set_opaque(v)
550       @opaque = v
551     end
552     protected :set_opaque
554     def opaque=(v)
555       check_opaque(v)
556       set_opaque(v)
557       v
558     end
560     def check_fragment(v)
561       return v unless v
563       if v && v != '' && FRAGMENT !~ v
564         raise InvalidComponentError, 
565           "bad component(expected fragment component): #{v}"
566       end
568       return true
569     end
570     private :check_fragment
572     def set_fragment(v)
573       @fragment = v
574     end
575     protected :set_fragment
577     def fragment=(v)
578       check_fragment(v)
579       set_fragment(v)
580       v
581     end
583     #
584     # Checks if URI has a path
585     #
586     def hierarchical?
587       if @path
588         true
589       else
590         false
591       end
592     end
594     #
595     # Checks if URI is an absolute one
596     #
597     def absolute?
598       if @scheme
599         true
600       else
601         false
602       end
603     end
604     alias absolute absolute?
606     #
607     # Checks if URI is relative
608     #
609     def relative?
610       !absolute?
611     end
613     def split_path(path)
614       path.split(%r{/+}, -1)
615     end
616     private :split_path
618     def merge_path(base, rel)
620       # RFC2396, Section 5.2, 5)
621       # RFC2396, Section 5.2, 6)
622       base_path = split_path(base)
623       rel_path  = split_path(rel)
625       # RFC2396, Section 5.2, 6), a)
626       base_path << '' if base_path.last == '..'
627       while i = base_path.index('..')
628         base_path.slice!(i - 1, 2)
629       end
631       if (first = rel_path.first) and first.empty?
632         base_path.clear
633         rel_path.shift
634       end
636       # RFC2396, Section 5.2, 6), c)
637       # RFC2396, Section 5.2, 6), d)
638       rel_path.push('') if rel_path.last == '.' || rel_path.last == '..'
639       rel_path.delete('.')
641       # RFC2396, Section 5.2, 6), e)
642       tmp = []
643       rel_path.each do |x|
644         if x == '..' &&
645             !(tmp.empty? || tmp.last == '..')
646           tmp.pop
647         else
648           tmp << x
649         end
650       end
652       add_trailer_slash = !tmp.empty?
653       if base_path.empty?
654         base_path = [''] # keep '/' for root directory
655       elsif add_trailer_slash
656         base_path.pop
657       end
658       while x = tmp.shift
659         if x == '..'
660           # RFC2396, Section 4
661           # a .. or . in an absolute path has no special meaning
662           base_path.pop if base_path.size > 1
663         else
664           # if x == '..'
665           #   valid absolute (but abnormal) path "/../..."
666           # else
667           #   valid absolute path
668           # end
669           base_path << x
670           tmp.each {|t| base_path << t}
671           add_trailer_slash = false
672           break
673         end
674       end
675       base_path.push('') if add_trailer_slash
677       return base_path.join('/')
678     end
679     private :merge_path
681     #
682     # == Args
683     #
684     # +oth+::
685     #    URI or String
686     #
687     # == Description
688     #
689     # Destructive form of #merge
690     #
691     # == Usage
692     #
693     #   require 'uri'
694     #
695     #   uri = URI.parse("http://my.example.com")
696     #   uri.merge!("/main.rbx?page=1")
697     #   p uri
698     #   # =>  #<URI::HTTP:0x2021f3b0 URL:http://my.example.com/main.rbx?page=1>
699     #
700     def merge!(oth)
701       t = merge(oth)
702       if self == t
703         nil
704       else
705         replace!(t)
706         self
707       end
708     end
710     #
711     # == Args
712     #
713     # +oth+::
714     #    URI or String
715     #
716     # == Description
717     #
718     # Merges two URI's.
719     #
720     # == Usage
721     #
722     #   require 'uri'
723     #
724     #   uri = URI.parse("http://my.example.com")
725     #   p uri.merge("/main.rbx?page=1")
726     #   # =>  #<URI::HTTP:0x2021f3b0 URL:http://my.example.com/main.rbx?page=1>
727     #
728     def merge(oth)
729       begin
730         base, rel = merge0(oth)
731       rescue
732         raise $!.class, $!.message
733       end
735       if base == rel
736         return base
737       end
739       authority = rel.userinfo || rel.host || rel.port
741       # RFC2396, Section 5.2, 2)
742       if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query
743         base.set_fragment(rel.fragment) if rel.fragment
744         return base
745       end
747       base.set_query(nil)
748       base.set_fragment(nil)
750       # RFC2396, Section 5.2, 4)
751       if !authority
752         base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path
753       else
754         # RFC2396, Section 5.2, 4)
755         base.set_path(rel.path) if rel.path
756       end
758       # RFC2396, Section 5.2, 7)
759       base.set_userinfo(rel.userinfo) if rel.userinfo
760       base.set_host(rel.host)         if rel.host
761       base.set_port(rel.port)         if rel.port
762       base.set_query(rel.query)       if rel.query
763       base.set_fragment(rel.fragment) if rel.fragment
765       return base
766     end # merge
767     alias + merge
769     # return base and rel.
770     # you can modify `base', but can not `rel'.
771     def merge0(oth)
772       case oth
773       when Generic
774       when String
775         oth = URI.parse(oth)
776       else
777         raise ArgumentError,
778           "bad argument(expected URI object or URI string)"
779       end
781       if self.relative? && oth.relative?
782         raise BadURIError, 
783           "both URI are relative"
784       end
786       if self.absolute? && oth.absolute?
787         #raise BadURIError, 
788         #  "both URI are absolute"
789         # hmm... should return oth for usability?
790         return oth, oth
791       end
793       if self.absolute?
794         return self.dup, oth
795       else
796         return oth, oth
797       end
798     end
799     private :merge0
801     def route_from_path(src, dst)
802       # RFC2396, Section 4.2
803       return '' if src == dst
805       src_path = split_path(src)
806       dst_path = split_path(dst)
808       # hmm... dst has abnormal absolute path, 
809       # like "/./", "/../", "/x/../", ...
810       if dst_path.include?('..') ||
811           dst_path.include?('.')
812         return dst.dup
813       end
815       src_path.pop
817       # discard same parts
818       while dst_path.first == src_path.first
819         break if dst_path.empty?
821         src_path.shift
822         dst_path.shift
823       end
825       tmp = dst_path.join('/')
827       # calculate
828       if src_path.empty?
829         if tmp.empty?
830           return './'
831         elsif dst_path.first.include?(':') # (see RFC2396 Section 5)
832           return './' + tmp
833         else
834           return tmp
835         end
836       end
838       return '../' * src_path.size + tmp
839     end
840     private :route_from_path
842     def route_from0(oth)
843       case oth
844       when Generic
845       when String
846         oth = URI.parse(oth)
847       else
848         raise ArgumentError,
849           "bad argument(expected URI object or URI string)"
850       end
852       if self.relative?
853         raise BadURIError, 
854           "relative URI: #{self}"
855       end
856       if oth.relative?
857         raise BadURIError, 
858           "relative URI: #{oth}"
859       end
861       if self.scheme != oth.scheme
862         return self, self.dup
863       end
864       rel = URI::Generic.new(nil, # it is relative URI
865                              self.userinfo, self.host, self.port, 
866                              self.registry, self.path, self.opaque,
867                              self.query, self.fragment)
869       if rel.userinfo != oth.userinfo ||
870           rel.host.to_s.downcase != oth.host.to_s.downcase ||
871           rel.port != oth.port
872         if self.userinfo.nil? && self.host.nil?
873           return self, self.dup
874         end
875         rel.set_port(nil) if rel.port == oth.default_port
876         return rel, rel
877       end
878       rel.set_userinfo(nil)
879       rel.set_host(nil)
880       rel.set_port(nil)
882       if rel.path && rel.path == oth.path
883         rel.set_path('')
884         rel.set_query(nil) if rel.query == oth.query
885         return rel, rel
886       elsif rel.opaque && rel.opaque == oth.opaque
887         rel.set_opaque('')
888         rel.set_query(nil) if rel.query == oth.query
889         return rel, rel
890       end
892       # you can modify `rel', but can not `oth'.
893       return oth, rel
894     end
895     private :route_from0
896     #
897     # == Args
898     #
899     # +oth+::
900     #    URI or String
901     #
902     # == Description
903     #
904     # Calculates relative path from oth to self
905     #
906     # == Usage
907     #
908     #   require 'uri'
909     #
910     #   uri = URI.parse('http://my.example.com/main.rbx?page=1')
911     #   p uri.route_from('http://my.example.com')
912     #   #=> #<URI::Generic:0x20218858 URL:/main.rbx?page=1>
913     #
914     def route_from(oth)
915       # you can modify `rel', but can not `oth'.
916       begin
917         oth, rel = route_from0(oth)
918       rescue
919         raise $!.class, $!.message
920       end
921       if oth == rel
922         return rel
923       end
925       rel.set_path(route_from_path(oth.path, self.path))
926       if rel.path == './' && self.query
927         # "./?foo" -> "?foo"
928         rel.set_path('')
929       end
931       return rel
932     end
934     alias - route_from
936     #
937     # == Args
938     #
939     # +oth+::
940     #    URI or String
941     #
942     # == Description
943     #
944     # Calculates relative path to oth from self
945     #
946     # == Usage
947     #
948     #   require 'uri'
949     #
950     #   uri = URI.parse('http://my.example.com')
951     #   p uri.route_to('http://my.example.com/main.rbx?page=1')
952     #   #=> #<URI::Generic:0x2020c2f6 URL:/main.rbx?page=1>
953     #    
954     def route_to(oth)
955       case oth
956       when Generic
957       when String
958         oth = URI.parse(oth)
959       else
960         raise ArgumentError,
961           "bad argument(expected URI object or URI string)"
962       end
964       oth.route_from(self)
965     end
967     #
968     # Returns normalized URI
969     # 
970     def normalize
971       uri = dup
972       uri.normalize!
973       uri
974     end
976     #
977     # Destructive version of #normalize
978     #
979     def normalize!
980       if path && path == ''
981         set_path('/')
982       end
983       if host && host != host.downcase
984         set_host(self.host.downcase)
985       end        
986     end
988     def path_query
989       str = @path
990       if @query
991         str += '?' + @query
992       end
993       str
994     end
995     private :path_query
997     #
998     # Constructs String from URI
999     # 
1000     def to_s
1001       str = ''
1002       if @scheme
1003         str << @scheme
1004         str << ':'
1005       end
1007       if @opaque
1008         str << @opaque
1010       else
1011         if @registry
1012           str << @registry
1013         else
1014           if @host
1015             str << '//'
1016           end
1017           if self.userinfo
1018             str << self.userinfo
1019             str << '@'
1020           end
1021           if @host
1022             str << @host
1023           end
1024           if @port && @port != self.default_port
1025             str << ':'
1026             str << @port.to_s
1027           end
1028         end
1030         str << path_query
1031       end
1033       if @fragment
1034         str << '#'
1035         str << @fragment
1036       end
1038       str
1039     end
1041     #
1042     # Compares to URI's
1043     #
1044     def ==(oth)
1045       if self.class == oth.class
1046         self.normalize.component_ary == oth.normalize.component_ary
1047       else
1048         false
1049       end
1050     end
1052     def hash
1053       self.component_ary.hash
1054     end
1056     def eql?(oth)
1057       self.component_ary.eql?(oth.component_ary)
1058     end
1060 =begin
1062 --- URI::Generic#===(oth)
1064 =end
1065 #    def ===(oth)
1066 #      raise NotImplementedError
1067 #    end
1069 =begin
1070 =end
1071     def component_ary
1072       component.collect do |x|
1073         self.send(x)
1074       end
1075     end
1076     protected :component_ary
1078     # == Args
1079     #
1080     # +components+::
1081     #    Multiple Symbol arguments defined in URI::HTTP
1082     #
1083     # == Description
1084     #
1085     # Selects specified components from URI
1086     #
1087     # == Usage
1088     #
1089     #   require 'uri'
1090     #
1091     #   uri = URI.parse('http://myuser:mypass@my.example.com/test.rbx')
1092     #   p uri.select(:userinfo, :host, :path)
1093     #   # => ["myuser:mypass", "my.example.com", "/test.rbx"]
1094     #
1095     def select(*components)
1096       components.collect do |c|
1097         if component.include?(c)
1098           self.send(c)
1099         else
1100           raise ArgumentError, 
1101             "expected of components of #{self.class} (#{self.class.component.join(', ')})"
1102         end
1103       end
1104     end
1106     def inspect
1107       sprintf("#<%s:%#0x URL:%s>", self.class.to_s, self.object_id, self.to_s)
1108     end
1110     def coerce(oth)
1111       case oth
1112       when String
1113         oth = URI.parse(oth)
1114       else
1115         super
1116       end
1118       return oth, self
1119     end
1120   end