| Class | Addressable::URI |
| In: |
lib/addressable/uri.rb
|
| Parent: | Object |
This is an implementation of a URI parser based on <a href="RFC">www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>, <a href="RFC">www.ietf.org/rfc/rfc3987.txt">RFC 3987</a>.
| encode_component | -> | encode_component |
| unencode | -> | unescape |
| unencode | -> | unencode_component |
| unencode | -> | unescape_component |
| encode | -> | escape |
Converts a path to a file scheme URI. If the path supplied is relative, it will be returned as a relative URI. If the path supplied is actually a non-file URI, it will parse the URI as if it had been parsed with Addressable::URI.parse. Handles all of the various Microsoft-specific formats for specifying paths.
@param [String, Addressable::URI, to_str] path
Typically a String path to a file or directory, but will return a sensible return value if an absolute URI is supplied instead.
@return [Addressable::URI]
The parsed file scheme URI or the original URI if some other URI scheme was provided.
@example
base = Addressable::URI.convert_path("/absolute/path/")
uri = Addressable::URI.convert_path("relative/path")
(base + uri).to_s
#=> "file:///absolute/path/relative/path"
Addressable::URI.convert_path(
"c:\\windows\\My Documents 100%20\\foo.txt"
).to_s
#=> "file:///c:/windows/My%20Documents%20100%20/foo.txt"
Addressable::URI.convert_path("http://example.com/").to_s
#=> "http://example.com/"
# File lib/addressable/uri.rb, line 209
209: def self.convert_path(path)
210: # If we were given nil, return nil.
211: return nil unless path
212: # If a URI object is passed, just return itself.
213: return path if path.kind_of?(self)
214: if !path.respond_to?(:to_str)
215: raise TypeError, "Can't convert #{path.class} into String."
216: end
217: # Otherwise, convert to a String
218: path = path.to_str.strip
219:
220: path.gsub!(/^file:\/?\/?/, "") if path =~ /^file:\/?\/?/
221: path = "/" + path if path =~ /^([a-zA-Z])[\|:]/
222: uri = self.parse(path)
223:
224: if uri.scheme == nil
225: # Adjust windows-style uris
226: uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
227: "/#{$1.downcase}:/"
228: end
229: uri.path.gsub!(/\\/, "/")
230: if File.exists?(uri.path) &&
231: File.stat(uri.path).directory?
232: uri.path.gsub!(/\/$/, "")
233: uri.path = uri.path + '/'
234: end
235:
236: # If the path is absolute, set the scheme and host.
237: if uri.path =~ /^\//
238: uri.scheme = "file"
239: uri.host = ""
240: end
241: uri.normalize!
242: end
243:
244: return uri
245: end
Percent encodes any special characters in the URI.
@param [String, Addressable::URI, to_str] uri
The URI to encode.
@param [Class] returning
The type of object to return.
This value may only be set to String or Addressable::URI. All other values are invalid. Defaults to String.
@return [String, Addressable::URI]
The encoded URI.
The return type is determined by the returning parameter.
# File lib/addressable/uri.rb, line 457
457: def self.encode(uri, returning=String)
458: return nil if uri.nil?
459: if !uri.respond_to?(:to_str)
460: raise TypeError, "Can't convert #{uri.class} into String."
461: end
462: if ![String, ::Addressable::URI].include?(returning)
463: raise TypeError,
464: "Expected Class (String or Addressable::URI), " +
465: "got #{returning.inspect}"
466: end
467: uri_object = uri.kind_of?(self) ? uri : self.parse(uri.to_str)
468: encoded_uri = Addressable::URI.new(
469: :scheme => self.encode_component(uri_object.scheme,
470: Addressable::URI::CharacterClasses::SCHEME),
471: :authority => self.encode_component(uri_object.authority,
472: Addressable::URI::CharacterClasses::AUTHORITY),
473: :path => self.encode_component(uri_object.path,
474: Addressable::URI::CharacterClasses::PATH),
475: :query => self.encode_component(uri_object.query,
476: Addressable::URI::CharacterClasses::QUERY),
477: :fragment => self.encode_component(uri_object.fragment,
478: Addressable::URI::CharacterClasses::FRAGMENT)
479: )
480: if returning == String
481: return encoded_uri.to_s
482: elsif returning == ::Addressable::URI
483: return encoded_uri
484: end
485: end
Percent encodes a URI component.
@param [String, to_str] component The URI component to encode.
@param [String, Regexp] character_class
The characters which are not percent encoded. If a String is passed, the String must be formatted as a regular expression character class. (Do not include the surrounding square brackets.) For example, "b-zB-Z0-9" would cause everything but the letters ‘b’ through ‘z’ and the numbers ‘0’ through ‘9’ to be percent encoded. If a Regexp is passed, the value /[^b-zB-Z0-9]/ would have the same effect. A set of useful String values may be found in the Addressable::URI::CharacterClasses module. The default value is the reserved plus unreserved character classes specified in <a href="RFC">www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
@return [String] The encoded component.
@example
Addressable::URI.encode_component("simple/example", "b-zB-Z0-9")
=> "simple%2Fex%61mple"
Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/)
=> "simple%2Fex%61mple"
Addressable::URI.encode_component(
"simple/example", Addressable::URI::CharacterClasses::UNRESERVED
)
=> "simple%2Fexample"
# File lib/addressable/uri.rb, line 303
303: def self.encode_component(component, character_class=
304: CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
305: return nil if component.nil?
306: if !component.respond_to?(:to_str)
307: raise TypeError, "Can't convert #{component.class} into String."
308: end
309: component = component.to_str
310: if ![String, Regexp].include?(character_class.class)
311: raise TypeError,
312: "Expected String or Regexp, got #{character_class.inspect}"
313: end
314: if character_class.kind_of?(String)
315: character_class = /[^#{character_class}]/
316: end
317: if component.respond_to?(:force_encoding)
318: # We can't perform regexps on invalid UTF sequences, but
319: # here we need to, so switch to ASCII.
320: component = component.dup
321: component.force_encoding(Encoding::ASCII_8BIT)
322: end
323: return component.gsub(character_class) do |sequence|
324: (sequence.unpack('C*').map { |c| "%" + ("%02x" % c).upcase }).join("")
325: end
326: end
Converts an input to a URI. The input does not have to be a valid URI — the method will use heuristics to guess what URI was intended. This is not standards-compliant, merely user-friendly.
@param [String, Addressable::URI, to_str] uri
The URI string to parse.
No parsing is performed if the object is already an Addressable::URI. @param [Hash] hints
A <tt>Hash</tt> of hints to the heuristic parser.
Defaults to {:scheme => "http"}.
@return [Addressable::URI] The parsed URI.
# File lib/addressable/uri.rb, line 140
140: def self.heuristic_parse(uri, hints={})
141: # If we were given nil, return nil.
142: return nil unless uri
143: # If a URI object is passed, just return itself.
144: return uri if uri.kind_of?(self)
145: if !uri.respond_to?(:to_str)
146: raise TypeError, "Can't convert #{uri.class} into String."
147: end
148: # Otherwise, convert to a String
149: uri = uri.to_str.dup
150: hints = {
151: :scheme => "http"
152: }.merge(hints)
153: case uri
154: when /^http:\/+/
155: uri.gsub!(/^http:\/+/, "http://")
156: when /^feed:\/+http:\/+/
157: uri.gsub!(/^feed:\/+http:\/+/, "feed:http://")
158: when /^feed:\/+/
159: uri.gsub!(/^feed:\/+/, "feed://")
160: when /^file:\/+/
161: uri.gsub!(/^file:\/+/, "file:///")
162: end
163: parsed = self.parse(uri)
164: if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
165: parsed = self.parse(hints[:scheme] + "://" + uri)
166: end
167: if parsed.path.include?(".")
168: new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
169: if new_host
170: new_path = parsed.path.gsub(
171: Regexp.new("^" + Regexp.escape(new_host)), "")
172: parsed.host = new_host
173: parsed.path = new_path
174: parsed.scheme = hints[:scheme] unless parsed.scheme
175: end
176: end
177: return parsed
178: end
Joins several URIs together.
@param [String, Addressable::URI, to_str] *uris
The URIs to join.
@return [Addressable::URI] The joined URI.
@example
base = "http://example.com/"
uri = Addressable::URI.parse("relative/path")
Addressable::URI.join(base, uri)
#=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
# File lib/addressable/uri.rb, line 260
260: def self.join(*uris)
261: uri_objects = uris.collect do |uri|
262: if !uri.respond_to?(:to_str)
263: raise TypeError, "Can't convert #{uri.class} into String."
264: end
265: uri.kind_of?(self) ? uri : self.parse(uri.to_str)
266: end
267: result = uri_objects.shift.dup
268: for uri in uri_objects
269: result.join!(uri)
270: end
271: return result
272: end
Creates a new uri object from component parts.
@option [String, to_str] scheme The scheme component. @option [String, to_str] user The user component. @option [String, to_str] password The password component. @option [String, to_str] userinfo
The userinfo component. If this is supplied, the user and password components must be omitted.
@option [String, to_str] host The host component. @option [String, to_str] port The port component. @option [String, to_str] authority
The authority component. If this is supplied, the user, password, userinfo, host, and port components must be omitted.
@option [String, to_str] path The path component. @option [String, to_str] query The query component. @option [String, to_str] fragment The fragment component.
@return [Addressable::URI] The constructed URI object.
# File lib/addressable/uri.rb, line 580
580: def initialize(options={})
581: if options.has_key?(:authority)
582: if (options.keys & [:userinfo, :user, :password, :host, :port]).any?
583: raise ArgumentError,
584: "Cannot specify both an authority and any of the components " +
585: "within the authority."
586: end
587: end
588: if options.has_key?(:userinfo)
589: if (options.keys & [:user, :password]).any?
590: raise ArgumentError,
591: "Cannot specify both a userinfo and either the user or password."
592: end
593: end
594:
595: self.defer_validation do
596: # Bunch of crazy logic required because of the composite components
597: # like userinfo and authority.
598: self.scheme = options[:scheme] if options[:scheme]
599: self.user = options[:user] if options[:user]
600: self.password = options[:password] if options[:password]
601: self.userinfo = options[:userinfo] if options[:userinfo]
602: self.host = options[:host] if options[:host]
603: self.port = options[:port] if options[:port]
604: self.authority = options[:authority] if options[:authority]
605: self.path = options[:path] if options[:path]
606: self.query = options[:query] if options[:query]
607: self.fragment = options[:fragment] if options[:fragment]
608: end
609: end
Normalizes the encoding of a URI component.
@param [String, to_str] component The URI component to encode.
@param [String, Regexp] character_class
The characters which are not percent encoded. If a String is passed, the String must be formatted as a regular expression character class. (Do not include the surrounding square brackets.) For example, "b-zB-Z0-9" would cause everything but the letters ‘b’ through ‘z’ and the numbers ‘0’ through ‘9’ to be percent encoded. If a Regexp is passed, the value /[^b-zB-Z0-9]/ would have the same effect. A set of useful String values may be found in the Addressable::URI::CharacterClasses module. The default value is the reserved plus unreserved character classes specified in <a href="RFC">www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
@return [String] The normalized component.
@example
Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z")
=> "simple%2Fex%61mple"
Addressable::URI.normalize_component(
"simpl%65/%65xampl%65", /[^b-zB-Z]/
)
=> "simple%2Fex%61mple"
Addressable::URI.normalize_component(
"simpl%65/%65xampl%65",
Addressable::URI::CharacterClasses::UNRESERVED
)
=> "simple%2Fexample"
# File lib/addressable/uri.rb, line 410
410: def self.normalize_component(component, character_class=
411: CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
412: return nil if component.nil?
413: if !component.respond_to?(:to_str)
414: raise TypeError, "Can't convert #{component.class} into String."
415: end
416: component = component.to_str
417: if ![String, Regexp].include?(character_class.class)
418: raise TypeError,
419: "Expected String or Regexp, got #{character_class.inspect}"
420: end
421: if character_class.kind_of?(String)
422: character_class = /[^#{character_class}]/
423: end
424: if component.respond_to?(:force_encoding)
425: # We can't perform regexps on invalid UTF sequences, but
426: # here we need to, so switch to ASCII.
427: component = component.dup
428: component.force_encoding(Encoding::ASCII_8BIT)
429: end
430: unencoded = self.unencode_component(component)
431: begin
432: encoded = self.encode_component(
433: Addressable::IDNA.unicode_normalize_kc(unencoded),
434: character_class
435: )
436: rescue ArgumentError
437: encoded = self.encode_component(unencoded)
438: end
439: return encoded
440: end
Normalizes the encoding of a URI. Characters within a hostname are not percent encoded to allow for internationalized domain names.
@param [String, Addressable::URI, to_str] uri
The URI to encode.
@param [Class] returning
The type of object to return.
This value may only be set to String or Addressable::URI. All other values are invalid. Defaults to String.
@return [String, Addressable::URI]
The encoded URI.
The return type is determined by the returning parameter.
# File lib/addressable/uri.rb, line 507
507: def self.normalized_encode(uri, returning=String)
508: if !uri.respond_to?(:to_str)
509: raise TypeError, "Can't convert #{uri.class} into String."
510: end
511: if ![String, ::Addressable::URI].include?(returning)
512: raise TypeError,
513: "Expected Class (String or Addressable::URI), " +
514: "got #{returning.inspect}"
515: end
516: uri_object = uri.kind_of?(self) ? uri : self.parse(uri.to_str)
517: components = {
518: :scheme => self.unencode_component(uri_object.scheme),
519: :user => self.unencode_component(uri_object.user),
520: :password => self.unencode_component(uri_object.password),
521: :host => self.unencode_component(uri_object.host),
522: :port => uri_object.port,
523: :path => self.unencode_component(uri_object.path),
524: :query => self.unencode_component(uri_object.query),
525: :fragment => self.unencode_component(uri_object.fragment)
526: }
527: components.each do |key, value|
528: if value != nil
529: begin
530: components[key] =
531: Addressable::IDNA.unicode_normalize_kc(value.to_str)
532: rescue ArgumentError
533: # Likely a malformed UTF-8 character, skip unicode normalization
534: components[key] = value.to_str
535: end
536: end
537: end
538: encoded_uri = Addressable::URI.new(
539: :scheme => self.encode_component(components[:scheme],
540: Addressable::URI::CharacterClasses::SCHEME),
541: :user => self.encode_component(components[:user],
542: Addressable::URI::CharacterClasses::UNRESERVED),
543: :password => self.encode_component(components[:password],
544: Addressable::URI::CharacterClasses::UNRESERVED),
545: :host => components[:host],
546: :port => components[:port],
547: :path => self.encode_component(components[:path],
548: Addressable::URI::CharacterClasses::PATH),
549: :query => self.encode_component(components[:query],
550: Addressable::URI::CharacterClasses::QUERY),
551: :fragment => self.encode_component(components[:fragment],
552: Addressable::URI::CharacterClasses::FRAGMENT)
553: )
554: if returning == String
555: return encoded_uri.to_s
556: elsif returning == ::Addressable::URI
557: return encoded_uri
558: end
559: end
Returns a URI object based on the parsed string.
@param [String, Addressable::URI, to_str] uri
The URI string to parse.
No parsing is performed if the object is already an Addressable::URI.
@return [Addressable::URI] The parsed URI.
# File lib/addressable/uri.rb, line 66
66: def self.parse(uri)
67: # If we were given nil, return nil.
68: return nil unless uri
69: # If a URI object is passed, just return itself.
70: return uri if uri.kind_of?(self)
71:
72: # If a URI object of the Ruby standard library variety is passed,
73: # convert it to a string, then parse the string.
74: # We do the check this way because we don't want to accidentally
75: # cause a missing constant exception to be thrown.
76: if uri.class.name =~ /^URI\b/
77: uri = uri.to_s
78: end
79:
80: if !uri.respond_to?(:to_str)
81: raise TypeError, "Can't convert #{uri.class} into String."
82: end
83: # Otherwise, convert to a String
84: uri = uri.to_str
85:
86: # This Regexp supplied as an example in RFC 3986, and it works great.
87: uri_regex =
88: /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/
89: scan = uri.scan(uri_regex)
90: fragments = scan[0]
91: scheme = fragments[1]
92: authority = fragments[3]
93: path = fragments[4]
94: query = fragments[6]
95: fragment = fragments[8]
96: user = nil
97: password = nil
98: host = nil
99: port = nil
100: if authority != nil
101: # The Regexp above doesn't split apart the authority.
102: userinfo = authority[/^([^\[\]]*)@/, 1]
103: if userinfo != nil
104: user = userinfo.strip[/^([^:]*):?/, 1]
105: password = userinfo.strip[/:(.*)$/, 1]
106: end
107: host = authority.gsub(/^([^\[\]]*)@/, "").gsub(/:([^:@\[\]]*?)$/, "")
108: port = authority[/:([^:@\[\]]*?)$/, 1]
109: end
110: if port == ""
111: port = nil
112: end
113:
114: return Addressable::URI.new(
115: :scheme => scheme,
116: :user => user,
117: :password => password,
118: :host => host,
119: :port => port,
120: :path => path,
121: :query => query,
122: :fragment => fragment
123: )
124: end
Returns a hash of common IP-based schemes and their default port numbers. Adding new schemes to this hash, as necessary, will allow for better URI normalization.
# File lib/addressable/uri.rb, line 1002
1002: def self.port_mapping
1003: @port_mapping ||= {
1004: "http" => 80,
1005: "https" => 443,
1006: "ftp" => 21,
1007: "tftp" => 69,
1008: "sftp" => 22,
1009: "ssh" => 22,
1010: "svn+ssh" => 22,
1011: "telnet" => 23,
1012: "nntp" => 119,
1013: "gopher" => 70,
1014: "wais" => 210,
1015: "ldap" => 389,
1016: "prospero" => 1525
1017: }
1018: end
Unencodes any percent encoded characters within a URI component. This method may be used for unencoding either components or full URIs, however, it is recommended to use the unencode_component alias when unencoding components.
@param [String, Addressable::URI, to_str] uri
The URI or component to unencode.
@param [Class] returning
The type of object to return.
This value may only be set to String or Addressable::URI. All other values are invalid. Defaults to String.
@return [String, Addressable::URI]
The unencoded component or URI.
The return type is determined by the returning parameter.
# File lib/addressable/uri.rb, line 350
350: def self.unencode(uri, returning=String)
351: return nil if uri.nil?
352: if !uri.respond_to?(:to_str)
353: raise TypeError, "Can't convert #{uri.class} into String."
354: end
355: if ![String, ::Addressable::URI].include?(returning)
356: raise TypeError,
357: "Expected Class (String or Addressable::URI), " +
358: "got #{returning.inspect}"
359: end
360: result = uri.to_str.gsub(/%[0-9a-f]{2}/i) do |sequence|
361: sequence[1..3].to_i(16).chr
362: end
363: result.force_encoding("utf-8") if result.respond_to?(:force_encoding)
364: if returning == String
365: return result
366: elsif returning == ::Addressable::URI
367: return ::Addressable::URI.parse(result)
368: end
369: end
Returns true if the URI objects are equal. This method normalizes both URIs before doing the comparison.
@param [Object] uri The URI to compare.
@return [TrueClass, FalseClass]
<tt>true</tt> if the URIs are equivalent, <tt>false</tt> otherwise.
# File lib/addressable/uri.rb, line 1816
1816: def ==(uri)
1817: return false unless uri.kind_of?(self.class)
1818: return self.normalize.to_s == uri.normalize.to_s
1819: end
Returns true if the URI objects are equal. This method normalizes both URIs before doing the comparison, and allows comparison against Strings.
@param [Object] uri The URI to compare.
@return [TrueClass, FalseClass]
<tt>true</tt> if the URIs are equivalent, <tt>false</tt> otherwise.
# File lib/addressable/uri.rb, line 1795
1795: def ===(uri)
1796: if uri.respond_to?(:normalize)
1797: uri_string = uri.normalize.to_s
1798: else
1799: begin
1800: uri_string = ::Addressable::URI.parse(uri).normalize.to_s
1801: rescue InvalidURIError, TypeError
1802: return false
1803: end
1804: end
1805: return self.normalize.to_s == uri_string
1806: end
The authority component for this URI. Combines the user, password, host, and port components.
@return [String] The authority component.
# File lib/addressable/uri.rb, line 912
912: def authority
913: @authority ||= (begin
914: if self.host.nil?
915: nil
916: else
917: authority = ""
918: if self.userinfo != nil
919: authority << "#{self.userinfo}@"
920: end
921: authority << self.host
922: if self.port != nil
923: authority << ":#{self.port}"
924: end
925: authority
926: end
927: end)
928: end
Sets the authority component for this URI.
@param [String, to_str] new_authority The new authority component.
# File lib/addressable/uri.rb, line 956
956: def authority=(new_authority)
957: # Check for frozenness
958: raise TypeError, "Can't modify frozen URI." if self.frozen?
959:
960: if new_authority
961: if !new_authority.respond_to?(:to_str)
962: raise TypeError, "Can't convert #{new_authority.class} into String."
963: end
964: new_authority = new_authority.to_str
965: new_userinfo = new_authority[/^([^\[\]]*)@/, 1]
966: if new_userinfo
967: new_user = new_userinfo.strip[/^([^:]*):?/, 1]
968: new_password = new_userinfo.strip[/:(.*)$/, 1]
969: end
970: new_host =
971: new_authority.gsub(/^([^\[\]]*)@/, "").gsub(/:([^:@\[\]]*?)$/, "")
972: new_port =
973: new_authority[/:([^:@\[\]]*?)$/, 1]
974: end
975:
976: # Password assigned first to ensure validity in case of nil
977: self.password = defined?(new_password) ? new_password : nil
978: self.user = defined?(new_user) ? new_user : nil
979: self.host = defined?(new_host) ? new_host : nil
980: self.port = defined?(new_port) ? new_port : nil
981:
982: # Reset dependant values
983: @inferred_port = nil
984: @userinfo = nil
985: @normalized_userinfo = nil
986: @uri_string = nil
987:
988: # Ensure we haven't created an invalid URI
989: validate()
990: end
This method allows you to make several changes to a URI simultaneously, which separately would cause validation errors, but in conjunction, are valid. The URI will be revalidated as soon as the entire block has been executed.
@param [Proc] block
A set of operations to perform on a given URI.
# File lib/addressable/uri.rb, line 1982
1982: def defer_validation(&block)
1983: raise LocalJumpError, "No block given." unless block
1984: @validation_deferred = true
1985: block.call()
1986: @validation_deferred = false
1987: validate
1988: return nil
1989: end
Creates a URI suitable for display to users. If semantic attacks are likely, the application should try to detect these and warn the user. See <a href="RFC">www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>, section 7.6 for more information.
@return [Addressable::URI] A URI suitable for display purposes.
# File lib/addressable/uri.rb, line 1780
1780: def display_uri
1781: display_uri = self.normalize
1782: display_uri.host = ::Addressable::IDNA.to_unicode(display_uri.host)
1783: return display_uri
1784: end
Clones the URI object.
@return [Addressable::URI] The cloned URI.
# File lib/addressable/uri.rb, line 1847
1847: def dup
1848: duplicated_uri = Addressable::URI.new(
1849: :scheme => self.scheme ? self.scheme.dup : nil,
1850: :user => self.user ? self.user.dup : nil,
1851: :password => self.password ? self.password.dup : nil,
1852: :host => self.host ? self.host.dup : nil,
1853: :port => self.port,
1854: :path => self.path ? self.path.dup : nil,
1855: :query => self.query ? self.query.dup : nil,
1856: :fragment => self.fragment ? self.fragment.dup : nil
1857: )
1858: return duplicated_uri
1859: end
Returns true if the URI objects are equal. This method does NOT normalize either URI before doing the comparison.
@param [Object] uri The URI to compare.
@return [TrueClass, FalseClass]
<tt>true</tt> if the URIs are equivalent, <tt>false</tt> otherwise.
# File lib/addressable/uri.rb, line 1829
1829: def eql?(uri)
1830: return false unless uri.kind_of?(self.class)
1831: return self.to_s == uri.to_s
1832: end
Sets the fragment component for this URI.
@param [String, to_str] new_fragment The new fragment component.
# File lib/addressable/uri.rb, line 1420
1420: def fragment=(new_fragment)
1421: # Check for frozenness
1422: raise TypeError, "Can't modify frozen URI." if self.frozen?
1423:
1424: if new_fragment && !new_fragment.respond_to?(:to_str)
1425: raise TypeError, "Can't convert #{new_fragment.class} into String."
1426: end
1427: @fragment = new_fragment ? new_fragment.to_str : nil
1428:
1429: # Reset dependant values
1430: @normalized_fragment = nil
1431: @uri_string = nil
1432:
1433: # Ensure we haven't created an invalid URI
1434: validate()
1435: end
Freezes the URI object.
@return [Addressable::URI] The frozen URI.
# File lib/addressable/uri.rb, line 1865
1865: def freeze
1866: # Unfortunately, because of the memoized implementation of many of the
1867: # URI methods, the default freeze method will cause unexpected errors.
1868: # As an alternative, we freeze the string representation of the URI
1869: # instead. This should generally produce the desired effect.
1870: self.to_s.freeze
1871: return self
1872: end
Sets the host component for this URI.
@param [String, to_str] new_host The new host component.
# File lib/addressable/uri.rb, line 889
889: def host=(new_host)
890: # Check for frozenness
891: raise TypeError, "Can't modify frozen URI." if self.frozen?
892:
893: if new_host && !new_host.respond_to?(:to_str)
894: raise TypeError, "Can't convert #{new_host.class} into String."
895: end
896: @host = new_host ? new_host.to_str : nil
897:
898: # Reset dependant values
899: @authority = nil
900: @normalized_host = nil
901: @uri_string = nil
902:
903: # Ensure we haven't created an invalid URI
904: validate()
905: end
The inferred port component for this URI. This method will normalize to the default port for the URI‘s scheme if the port isn‘t explicitly specified in the URI.
@return [Integer] The inferred port component.
# File lib/addressable/uri.rb, line 1079
1079: def inferred_port
1080: @inferred_port ||= (begin
1081: if port.to_i == 0
1082: if scheme
1083: self.class.port_mapping[scheme.strip.downcase]
1084: else
1085: nil
1086: end
1087: else
1088: port.to_i
1089: end
1090: end)
1091: end
Determines if the scheme indicates an IP-based protocol.
@return [TrueClass, FalseClass] true if the scheme indicates an IP-based protocol. false otherwise.
# File lib/addressable/uri.rb, line 1443
1443: def ip_based?
1444: if self.scheme
1445: return self.class.ip_based_schemes.include?(
1446: self.scheme.strip.downcase)
1447: end
1448: return false
1449: end
Joins two URIs together.
@param [String, Addressable::URI, to_str] The URI to join with.
@return [Addressable::URI] The joined URI.
# File lib/addressable/uri.rb, line 1475
1475: def join(uri)
1476: if !uri.respond_to?(:to_str)
1477: raise TypeError, "Can't convert #{uri.class} into String."
1478: end
1479: if !uri.kind_of?(self.class)
1480: # Otherwise, convert to a String, then parse.
1481: uri = self.class.parse(uri.to_str)
1482: end
1483: if uri.to_s == ""
1484: return self.dup
1485: end
1486:
1487: joined_scheme = nil
1488: joined_user = nil
1489: joined_password = nil
1490: joined_host = nil
1491: joined_port = nil
1492: joined_path = nil
1493: joined_query = nil
1494: joined_fragment = nil
1495:
1496: # Section 5.2.2 of RFC 3986
1497: if uri.scheme != nil
1498: joined_scheme = uri.scheme
1499: joined_user = uri.user
1500: joined_password = uri.password
1501: joined_host = uri.host
1502: joined_port = uri.port
1503: joined_path = self.class.normalize_path(uri.path)
1504: joined_query = uri.query
1505: else
1506: if uri.authority != nil
1507: joined_user = uri.user
1508: joined_password = uri.password
1509: joined_host = uri.host
1510: joined_port = uri.port
1511: joined_path = self.class.normalize_path(uri.path)
1512: joined_query = uri.query
1513: else
1514: if uri.path == nil || uri.path == ""
1515: joined_path = self.path
1516: if uri.query != nil
1517: joined_query = uri.query
1518: else
1519: joined_query = self.query
1520: end
1521: else
1522: if uri.path[0..0] == "/"
1523: joined_path = self.class.normalize_path(uri.path)
1524: else
1525: base_path = self.path.dup
1526: base_path = "" if base_path == nil
1527: base_path = self.class.normalize_path(base_path)
1528:
1529: # Section 5.2.3 of RFC 3986
1530: #
1531: # Removes the right-most path segment from the base path.
1532: if base_path =~ /\//
1533: base_path.gsub!(/\/[^\/]+$/, "/")
1534: else
1535: base_path = ""
1536: end
1537:
1538: # If the base path is empty and an authority segment has been
1539: # defined, use a base path of "/"
1540: if base_path == "" && self.authority != nil
1541: base_path = "/"
1542: end
1543:
1544: joined_path = self.class.normalize_path(base_path + uri.path)
1545: end
1546: joined_query = uri.query
1547: end
1548: joined_user = self.user
1549: joined_password = self.password
1550: joined_host = self.host
1551: joined_port = self.port
1552: end
1553: joined_scheme = self.scheme
1554: end
1555: joined_fragment = uri.fragment
1556:
1557: return Addressable::URI.new(
1558: :scheme => joined_scheme,
1559: :user => joined_user,
1560: :password => joined_password,
1561: :host => joined_host,
1562: :port => joined_port,
1563: :path => joined_path,
1564: :query => joined_query,
1565: :fragment => joined_fragment
1566: )
1567: end
Destructive form of join.
@param [String, Addressable::URI, to_str] The URI to join with.
@return [Addressable::URI] The joined URI.
# File lib/addressable/uri.rb, line 1578
1578: def join!(uri)
1579: replace_self(self.join(uri))
1580: end
Merges a URI with a Hash of components. This method has different behavior from join. Any components present in the hash parameter will override the original components. The path component is not treated specially.
@param [Hash, Addressable::URI, to_hash] The components to merge with.
@return [Addressable::URI] The merged URI.
@see Hash#merge
# File lib/addressable/uri.rb, line 1593
1593: def merge(hash)
1594: if !hash.respond_to?(:to_hash)
1595: raise TypeError, "Can't convert #{hash.class} into Hash."
1596: end
1597: hash = hash.to_hash
1598:
1599: if hash.has_key?(:authority)
1600: if (hash.keys & [:userinfo, :user, :password, :host, :port]).any?
1601: raise ArgumentError,
1602: "Cannot specify both an authority and any of the components " +
1603: "within the authority."
1604: end
1605: end
1606: if hash.has_key?(:userinfo)
1607: if (hash.keys & [:user, :password]).any?
1608: raise ArgumentError,
1609: "Cannot specify both a userinfo and either the user or password."
1610: end
1611: end
1612:
1613: uri = Addressable::URI.new
1614: uri.defer_validation do
1615: # Bunch of crazy logic required because of the composite components
1616: # like userinfo and authority.
1617: uri.scheme =
1618: hash.has_key?(:scheme) ? hash[:scheme] : self.scheme
1619: if hash.has_key?(:authority)
1620: uri.authority =
1621: hash.has_key?(:authority) ? hash[:authority] : self.authority
1622: end
1623: if hash.has_key?(:userinfo)
1624: uri.userinfo =
1625: hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo
1626: end
1627: if !hash.has_key?(:userinfo) && !hash.has_key?(:authority)
1628: uri.user =
1629: hash.has_key?(:user) ? hash[:user] : self.user
1630: uri.password =
1631: hash.has_key?(:password) ? hash[:password] : self.password
1632: end
1633: if !hash.has_key?(:authority)
1634: uri.host =
1635: hash.has_key?(:host) ? hash[:host] : self.host
1636: uri.port =
1637: hash.has_key?(:port) ? hash[:port] : self.port
1638: end
1639: uri.path =
1640: hash.has_key?(:path) ? hash[:path] : self.path
1641: uri.query =
1642: hash.has_key?(:query) ? hash[:query] : self.query
1643: uri.fragment =
1644: hash.has_key?(:fragment) ? hash[:fragment] : self.fragment
1645: end
1646:
1647: return uri
1648: end
Destructive form of merge.
@param [Hash, Addressable::URI, to_hash] The components to merge with.
@return [Addressable::URI] The merged URI.
# File lib/addressable/uri.rb, line 1658
1658: def merge!(uri)
1659: replace_self(self.merge(uri))
1660: end
Returns a normalized URI object.
NOTE: This method does not attempt to fully conform to specifications. It exists largely to correct other people‘s failures to read the specifications, and also to deal with caching issues since several different URIs may represent the same resource and should not be cached multiple times.
@return [Addressable::URI] The normalized URI.
# File lib/addressable/uri.rb, line 1743
1743: def normalize
1744: # This is a special exception for the frequently misused feed
1745: # URI scheme.
1746: if normalized_scheme == "feed"
1747: if self.to_s =~ /^feed:\/*http:\/*/
1748: return self.class.parse(
1749: self.to_s[/^feed:\/*(http:\/*.*)/, 1]
1750: ).normalize
1751: end
1752: end
1753:
1754: return Addressable::URI.new(
1755: :scheme => normalized_scheme,
1756: :authority => normalized_authority,
1757: :path => normalized_path,
1758: :query => normalized_query,
1759: :fragment => normalized_fragment
1760: )
1761: end
Destructively normalizes this URI object.
@return [Addressable::URI] The normalized URI.
@see Addressable::URI#normalize
# File lib/addressable/uri.rb, line 1769
1769: def normalize!
1770: replace_self(self.normalize)
1771: end
The authority component for this URI, normalized.
@return [String] The authority component, normalized.
# File lib/addressable/uri.rb, line 934
934: def normalized_authority
935: @normalized_authority ||= (begin
936: if self.normalized_host.nil?
937: nil
938: else
939: authority = ""
940: if self.normalized_userinfo != nil
941: authority << "#{self.normalized_userinfo}@"
942: end
943: authority << self.normalized_host
944: if self.normalized_port != nil
945: authority << ":#{self.normalized_port}"
946: end
947: authority
948: end
949: end)
950: end
The fragment component for this URI, normalized.
@return [String] The fragment component, normalized.
# File lib/addressable/uri.rb, line 1403
1403: def normalized_fragment
1404: @normalized_fragment ||= (begin
1405: if self.fragment
1406: Addressable::URI.normalize_component(
1407: self.fragment.strip,
1408: Addressable::URI::CharacterClasses::FRAGMENT
1409: )
1410: else
1411: nil
1412: end
1413: end)
1414: end
The host component for this URI, normalized.
@return [String] The host component, normalized.
# File lib/addressable/uri.rb, line 864
864: def normalized_host
865: @normalized_host ||= (begin
866: if self.host != nil
867: if self.host.strip != ""
868: result = ::Addressable::IDNA.to_ascii(
869: self.class.unencode_component(self.host.strip.downcase)
870: )
871: if result[-1..-1] == "."
872: # Trailing dots are unnecessary
873: result = result[0...-1]
874: end
875: result
876: else
877: ""
878: end
879: else
880: nil
881: end
882: end)
883: end
The password component for this URI, normalized.
@return [String] The password component, normalized.
# File lib/addressable/uri.rb, line 734
734: def normalized_password
735: @normalized_password ||= (begin
736: if self.password
737: if normalized_scheme =~ /https?/ && self.password.strip == "" &&
738: (!self.user || self.user.strip == "")
739: nil
740: else
741: Addressable::URI.normalize_component(
742: self.password.strip,
743: Addressable::URI::CharacterClasses::UNRESERVED
744: )
745: end
746: else
747: nil
748: end
749: end)
750: end
The path component for this URI, normalized.
@return [String] The path component, normalized.
# File lib/addressable/uri.rb, line 1106
1106: def normalized_path
1107: @normalized_path ||= (begin
1108: # String#split(delimeter, -1) uses the more strict splitting behavior
1109: # found in Python.
1110: result = (self.path.strip.split("/", -1).map do |segment|
1111: Addressable::URI.normalize_component(
1112: segment,
1113: Addressable::URI::CharacterClasses::PCHAR
1114: )
1115: end).join("/")
1116: result = self.class.normalize_path(result)
1117: if result == "" &&
1118: ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
1119: result = "/"
1120: end
1121: result
1122: end)
1123: end
The port component for this URI, normalized.
@return [Integer] The port component, normalized.
# File lib/addressable/uri.rb, line 1034
1034: def normalized_port
1035: @normalized_port ||= (begin
1036: if self.class.port_mapping[normalized_scheme] == self.port
1037: nil
1038: else
1039: self.port
1040: end
1041: end)
1042: end
The query component for this URI, normalized.
@return [String] The query component, normalized.
# File lib/addressable/uri.rb, line 1177
1177: def normalized_query
1178: @normalized_query ||= (begin
1179: if self.query
1180: Addressable::URI.normalize_component(
1181: self.query.strip,
1182: Addressable::URI::CharacterClasses::QUERY
1183: )
1184: else
1185: nil
1186: end
1187: end)
1188: end
The scheme component for this URI, normalized.
@return [String] The scheme component, normalized.
# File lib/addressable/uri.rb, line 623
623: def normalized_scheme
624: @normalized_scheme ||= (begin
625: if self.scheme != nil
626: if self.scheme =~ /^\s*ssh\+svn\s*$/i
627: "svn+ssh"
628: else
629: Addressable::URI.normalize_component(
630: self.scheme.strip.downcase,
631: Addressable::URI::CharacterClasses::SCHEME
632: )
633: end
634: else
635: nil
636: end
637: end)
638: end
The user component for this URI, normalized.
@return [String] The user component, normalized.
# File lib/addressable/uri.rb, line 674
674: def normalized_user
675: @normalized_user ||= (begin
676: if self.user
677: if normalized_scheme =~ /https?/ && self.user.strip == "" &&
678: (!self.password || self.password.strip == "")
679: nil
680: else
681: Addressable::URI.normalize_component(
682: self.user.strip,
683: Addressable::URI::CharacterClasses::UNRESERVED
684: )
685: end
686: else
687: nil
688: end
689: end)
690: end
The userinfo component for this URI, normalized.
@return [String] The userinfo component, normalized.
# File lib/addressable/uri.rb, line 806
806: def normalized_userinfo
807: @normalized_userinfo ||= (begin
808: current_user = self.normalized_user
809: current_password = self.normalized_password
810: if !current_user && !current_password
811: nil
812: elsif current_user && current_password
813: "#{current_user}:#{current_password}"
814: elsif current_user && !current_password
815: "#{current_user}"
816: end
817: end)
818: end
Omits components from a URI.
@param [Symbol] *components The components to be omitted.
@return [Addressable::URI] The URI with components omitted.
@example
uri = Addressable::URI.parse("http://example.com/path?query")
#=> #<Addressable::URI:0xcc5e7a URI:http://example.com/path?query>
uri.omit(:scheme, :authority)
#=> #<Addressable::URI:0xcc4d86 URI:/path?query>
# File lib/addressable/uri.rb, line 1895
1895: def omit(*components)
1896: invalid_components = components - [
1897: :scheme, :user, :password, :userinfo, :host, :port, :authority,
1898: :path, :query, :fragment
1899: ]
1900: unless invalid_components.empty?
1901: raise ArgumentError,
1902: "Invalid component names: #{invalid_components.inspect}."
1903: end
1904: duplicated_uri = self.dup
1905: duplicated_uri.defer_validation do
1906: components.each do |component|
1907: duplicated_uri.send((component.to_s + "=").to_sym, nil)
1908: end
1909: duplicated_uri.user = duplicated_uri.normalized_user
1910: end
1911: duplicated_uri
1912: end
Destructive form of omit.
@param [Symbol] *components The components to be omitted.
@return [Addressable::URI] The URI with components omitted.
# File lib/addressable/uri.rb, line 1922
1922: def omit!(*components)
1923: replace_self(self.omit(*components))
1924: end
Sets the password component for this URI.
@param [String, to_str] new_password The new password component.
# File lib/addressable/uri.rb, line 756
756: def password=(new_password)
757: # Check for frozenness
758: raise TypeError, "Can't modify frozen URI." if self.frozen?
759:
760: if new_password && !new_password.respond_to?(:to_str)
761: raise TypeError, "Can't convert #{new_password.class} into String."
762: end
763: @password = new_password ? new_password.to_str : nil
764:
765: # You can't have a nil user with a non-nil password
766: @password ||= nil
767: @user ||= nil
768: if @password != nil
769: @user = "" if @user.nil?
770: end
771:
772: # Reset dependant values
773: @userinfo = nil
774: @normalized_userinfo = nil
775: @authority = nil
776: @normalized_password = nil
777: @uri_string = nil
778:
779: # Ensure we haven't created an invalid URI
780: validate()
781: end
Sets the path component for this URI.
@param [String, to_str] new_path The new path component.
# File lib/addressable/uri.rb, line 1129
1129: def path=(new_path)
1130: # Check for frozenness
1131: raise TypeError, "Can't modify frozen URI." if self.frozen?
1132:
1133: if new_path && !new_path.respond_to?(:to_str)
1134: raise TypeError, "Can't convert #{new_path.class} into String."
1135: end
1136: @path = (new_path || "").to_str
1137: if @path != "" && @path[0..0] != "/" && host != nil
1138: @path = "/#{@path}"
1139: end
1140:
1141: # Reset dependant values
1142: @normalized_path = nil
1143: @uri_string = nil
1144: end
Sets the port component for this URI.
@param [String, Integer, to_s] new_port The new port component.
# File lib/addressable/uri.rb, line 1048
1048: def port=(new_port)
1049: # Check for frozenness
1050: raise TypeError, "Can't modify frozen URI." if self.frozen?
1051:
1052: if new_port != nil && new_port.respond_to?(:to_str)
1053: new_port = Addressable::URI.unencode_component(new_port.to_str)
1054: end
1055: if new_port != nil && !(new_port.to_s =~ /^\d+$/)
1056: raise InvalidURIError,
1057: "Invalid port number: #{new_port.inspect}"
1058: end
1059:
1060: @port = new_port.to_s.to_i
1061: @port = nil if @port == 0
1062:
1063: # Reset dependant values
1064: @authority = nil
1065: @inferred_port = nil
1066: @normalized_port = nil
1067: @uri_string = nil
1068:
1069: # Ensure we haven't created an invalid URI
1070: validate()
1071: end
Sets the query component for this URI.
@param [String, to_str] new_query The new query component.
# File lib/addressable/uri.rb, line 1194
1194: def query=(new_query)
1195: # Check for frozenness
1196: raise TypeError, "Can't modify frozen URI." if self.frozen?
1197:
1198: if new_query && !new_query.respond_to?(:to_str)
1199: raise TypeError, "Can't convert #{new_query.class} into String."
1200: end
1201: @query = new_query ? new_query.to_str : nil
1202:
1203: # Reset dependant values
1204: @normalized_query = nil
1205: @uri_string = nil
1206: end
Converts the query component to a Hash value.
@option [Symbol] notation
May be one of <tt>:flat</tt>, <tt>:dot</tt>, or <tt>:subscript</tt>.
The :dot notation is not supported for assignment. Default value is :subscript.
@return [Hash] The query string parsed as a Hash object.
@example
Addressable::URI.parse("?one=1&two=2&three=3").query_values
#=> {"one" => "1", "two" => "2", "three" => "3"}
Addressable::URI.parse("?one[two][three]=four").query_values
#=> {"one" => {"two" => {"three" => "four"}}}
Addressable::URI.parse("?one.two.three=four").query_values(
:notation => :dot
)
#=> {"one" => {"two" => {"three" => "four"}}}
Addressable::URI.parse("?one[two][three]=four").query_values(
:notation => :flat
)
#=> {"one[two][three]" => "four"}
Addressable::URI.parse("?one.two.three=four").query_values(
:notation => :flat
)
#=> {"one.two.three" => "four"}
Addressable::URI.parse(
"?one[two][three][]=four&one[two][three][]=five"
).query_values
#=> {"one" => {"two" => {"three" => ["four", "five"]}}}
# File lib/addressable/uri.rb, line 1239
1239: def query_values(options={})
1240: defaults = {:notation => :subscript}
1241: options = defaults.merge(options)
1242: if ![:flat, :dot, :subscript].include?(options[:notation])
1243: raise ArgumentError,
1244: "Invalid notation. Must be one of: [:flat, :dot, :subscript]."
1245: end
1246: dehash = lambda do |hash|
1247: hash.each do |(key, value)|
1248: if value.kind_of?(Hash)
1249: hash[key] = dehash.call(value)
1250: end
1251: end
1252: if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
1253: hash.sort.inject([]) do |accu, (key, value)|
1254: accu << value; accu
1255: end
1256: else
1257: hash
1258: end
1259: end
1260: return nil if self.query == nil
1261: return ((self.query.split("&").map do |pair|
1262: pair.split("=")
1263: end).inject({}) do |accumulator, (key, value)|
1264: value = true if value.nil?
1265: key = self.class.unencode_component(key)
1266: if value != true
1267: value = self.class.unencode_component(value).gsub(/\+/, " ")
1268: end
1269: if options[:notation] == :flat
1270: if accumulator[key]
1271: raise ArgumentError, "Key was repeated: #{key.inspect}"
1272: end
1273: accumulator[key] = value
1274: else
1275: if options[:notation] == :dot
1276: array_value = false
1277: subkeys = key.split(".")
1278: elsif options[:notation] == :subscript
1279: array_value = !!(key =~ /\[\]$/)
1280: subkeys = key.split(/[\[\]]+/)
1281: end
1282: current_hash = accumulator
1283: for i in 0...(subkeys.size - 1)
1284: subkey = subkeys[i]
1285: current_hash[subkey] = {} unless current_hash[subkey]
1286: current_hash = current_hash[subkey]
1287: end
1288: if array_value
1289: current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
1290: current_hash[subkeys.last] << value
1291: else
1292: current_hash[subkeys.last] = value
1293: end
1294: end
1295: accumulator
1296: end).inject({}) do |accumulator, (key, value)|
1297: accumulator[key] = value.kind_of?(Hash) ? dehash.call(value) : value
1298: accumulator
1299: end
1300: end
Sets the query component for this URI from a Hash object. This method produces a query string using the :subscript notation.
@param [Hash, to_hash] new_query_values The new query values.
# File lib/addressable/uri.rb, line 1307
1307: def query_values=(new_query_values)
1308: # Check for frozenness
1309: raise TypeError, "Can't modify frozen URI." if self.frozen?
1310: if !new_query_values.respond_to?(:to_hash)
1311: raise TypeError, "Can't convert #{new_query_values.class} into Hash."
1312: end
1313: new_query_values = new_query_values.to_hash
1314:
1315: # Algorithm shamelessly stolen from Julien Genestoux, slightly modified
1316: buffer = ""
1317: stack = []
1318: e = lambda do |component|
1319: component = component.to_s if component.kind_of?(Symbol)
1320: self.class.encode_component(component, CharacterClasses::UNRESERVED)
1321: end
1322: new_query_values.each do |key, value|
1323: if value.kind_of?(Hash)
1324: stack << [key, value]
1325: elsif value.kind_of?(Array)
1326: stack << [
1327: key,
1328: value.inject({}) { |accu, x| accu[accu.size.to_s] = x; accu }
1329: ]
1330: elsif value == true
1331: buffer << "#{e.call(key)}&"
1332: else
1333: buffer << "#{e.call(key)}=#{e.call(value)}&"
1334: end
1335: end
1336: stack.each do |(parent, hash)|
1337: (hash.sort_by { |key| key.to_s }).each do |(key, value)|
1338: if value.kind_of?(Hash)
1339: stack << ["#{parent}[#{key}]", value]
1340: elsif value == true
1341: buffer << "#{parent}[#{e.call(key)}]&"
1342: else
1343: buffer << "#{parent}[#{e.call(key)}]=#{e.call(value)}&"
1344: end
1345: end
1346: end
1347: @query = buffer.chop
1348:
1349: # Reset dependant values
1350: @normalized_query = nil
1351: @uri_string = nil
1352: end
The HTTP request URI for this URI. This is the path and the query string.
@return [String] The request URI required for an HTTP request.
# File lib/addressable/uri.rb, line 1359
1359: def request_uri
1360: return nil if self.absolute? && self.scheme !~ /^https?$/
1361: return (
1362: (self.path != "" ? self.path : "/") +
1363: (self.query ? "?#{self.query}" : "")
1364: )
1365: end
Sets the HTTP request URI for this URI.
@param [String, to_str] new_request_uri The new HTTP request URI.
# File lib/addressable/uri.rb, line 1371
1371: def request_uri=(new_request_uri)
1372: if !new_request_uri.respond_to?(:to_str)
1373: raise TypeError, "Can't convert #{new_request_uri.class} into String."
1374: end
1375: if self.absolute? && self.scheme !~ /^https?$/
1376: raise InvalidURIError,
1377: "Cannot set an HTTP request URI for a non-HTTP URI."
1378: end
1379: new_request_uri = new_request_uri.to_str
1380: path_component = new_request_uri[/^([^\?]*)\?(?:.*)$/, 1]
1381: query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
1382: path_component = path_component.to_s
1383: path_component = (path_component != "" ? path_component : "/")
1384: self.path = path_component
1385: self.query = query_component
1386:
1387: # Reset dependant values
1388: @uri_string = nil
1389: end
Returns the shortest normalized relative form of this URI that uses the supplied URI as a base for resolution. Returns an absolute URI if necessary. This is effectively the opposite of route_to.
@param [String, Addressable::URI, to_str] uri The URI to route from.
@return [Addressable::URI]
The normalized relative URI that is equivalent to the original URI.
# File lib/addressable/uri.rb, line 1671
1671: def route_from(uri)
1672: uri = self.class.parse(uri).normalize
1673: normalized_self = self.normalize
1674: if normalized_self.relative?
1675: raise ArgumentError, "Expected absolute URI, got: #{self.to_s}"
1676: end
1677: if uri.relative?
1678: raise ArgumentError, "Expected absolute URI, got: #{uri.to_s}"
1679: end
1680: if normalized_self == uri
1681: return Addressable::URI.parse("##{normalized_self.fragment}")
1682: end
1683: components = normalized_self.to_hash
1684: if normalized_self.scheme == uri.scheme
1685: components[:scheme] = nil
1686: if normalized_self.authority == uri.authority
1687: components[:user] = nil
1688: components[:password] = nil
1689: components[:host] = nil
1690: components[:port] = nil
1691: if normalized_self.path == uri.path
1692: components[:path] = nil
1693: if normalized_self.query == uri.query
1694: components[:query] = nil
1695: end
1696: else
1697: if uri.path != "/"
1698: components[:path].gsub!(
1699: Regexp.new("^" + Regexp.escape(uri.path)), "")
1700: end
1701: end
1702: end
1703: end
1704: # Avoid network-path references.
1705: if components[:host] != nil
1706: components[:scheme] = normalized_self.scheme
1707: end
1708: return Addressable::URI.new(
1709: :scheme => components[:scheme],
1710: :user => components[:user],
1711: :password => components[:password],
1712: :host => components[:host],
1713: :port => components[:port],
1714: :path => components[:path],
1715: :query => components[:query],
1716: :fragment => components[:fragment]
1717: )
1718: end
Returns the shortest normalized relative form of the supplied URI that uses this URI as a base for resolution. Returns an absolute URI if necessary. This is effectively the opposite of route_from.
@param [String, Addressable::URI, to_str] uri The URI to route to.
@return [Addressable::URI]
The normalized relative URI that is equivalent to the supplied URI.
# File lib/addressable/uri.rb, line 1729
1729: def route_to(uri)
1730: return self.class.parse(uri).route_from(self)
1731: end
Sets the scheme component for this URI.
@param [String, to_str] new_scheme The new scheme component.
# File lib/addressable/uri.rb, line 644
644: def scheme=(new_scheme)
645: # Check for frozenness
646: raise TypeError, "Can't modify frozen URI." if self.frozen?
647:
648: if new_scheme && !new_scheme.respond_to?(:to_str)
649: raise TypeError, "Can't convert #{new_scheme.class} into String."
650: end
651: @scheme = new_scheme ? new_scheme.to_str : nil
652: @scheme = nil if @scheme.to_s.strip == ""
653:
654: # Reset dependant values
655: @normalized_scheme = nil
656: @uri_string = nil
657:
658: # Ensure we haven't created an invalid URI
659: validate()
660: end
Returns a Hash of the URI components.
@return [Hash] The URI as a Hash of components.
# File lib/addressable/uri.rb, line 1953
1953: def to_hash
1954: return {
1955: :scheme => self.scheme,
1956: :user => self.user,
1957: :password => self.password,
1958: :host => self.host,
1959: :port => self.port,
1960: :path => self.path,
1961: :query => self.query,
1962: :fragment => self.fragment
1963: }
1964: end
Converts the URI to a String.
@return [String] The URI‘s String representation.
# File lib/addressable/uri.rb, line 1930
1930: def to_s
1931: @uri_string ||= (begin
1932: uri_string = ""
1933: uri_string << "#{self.scheme}:" if self.scheme != nil
1934: uri_string << "//#{self.authority}" if self.authority != nil
1935: uri_string << self.path.to_s
1936: uri_string << "?#{self.query}" if self.query != nil
1937: uri_string << "##{self.fragment}" if self.fragment != nil
1938: if uri_string.respond_to?(:force_encoding)
1939: uri_string.force_encoding(Encoding::UTF_8)
1940: end
1941: uri_string
1942: end)
1943: end
Sets the user component for this URI.
@param [String, to_str] new_user The new user component.
# File lib/addressable/uri.rb, line 696
696: def user=(new_user)
697: # Check for frozenness
698: raise TypeError, "Can't modify frozen URI." if self.frozen?
699:
700: if new_user && !new_user.respond_to?(:to_str)
701: raise TypeError, "Can't convert #{new_user.class} into String."
702: end
703: @user = new_user ? new_user.to_str : nil
704:
705: # You can't have a nil user with a non-nil password
706: @password ||= nil
707: if @password != nil
708: @user = "" if @user.nil?
709: end
710:
711: # Reset dependant values
712: @userinfo = nil
713: @normalized_userinfo = nil
714: @authority = nil
715: @normalized_user = nil
716: @uri_string = nil
717:
718: # Ensure we haven't created an invalid URI
719: validate()
720: end
The userinfo component for this URI. Combines the user and password components.
@return [String] The userinfo component.
# File lib/addressable/uri.rb, line 788
788: def userinfo
789: @userinfo ||= (begin
790: current_user = self.user
791: current_password = self.password
792: if !current_user && !current_password
793: nil
794: elsif current_user && current_password
795: "#{current_user}:#{current_password}"
796: elsif current_user && !current_password
797: "#{current_user}"
798: end
799: end)
800: end
Sets the userinfo component for this URI.
@param [String, to_str] new_userinfo The new userinfo component.
# File lib/addressable/uri.rb, line 824
824: def userinfo=(new_userinfo)
825: # Check for frozenness
826: raise TypeError, "Can't modify frozen URI." if self.frozen?
827:
828: if new_userinfo && !new_userinfo.respond_to?(:to_str)
829: raise TypeError, "Can't convert #{new_userinfo.class} into String."
830: end
831: new_user, new_password = if new_userinfo
832: [
833: new_userinfo.to_str.strip[/^(.*):/, 1],
834: new_userinfo.to_str.strip[/:(.*)$/, 1]
835: ]
836: else
837: [nil, nil]
838: end
839:
840: # Password assigned first to ensure validity in case of nil
841: self.password = new_password
842: self.user = new_user
843:
844: # Reset dependant values
845: @authority = nil
846: @uri_string = nil
847:
848: # Ensure we haven't created an invalid URI
849: validate()
850: end