PageRenderTime 57ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/ruby/jruby/addressable-2.2.4/lib/addressable/uri.rb

https://github.com/iverson2329/FireApp
Ruby | 2291 lines | 1618 code | 109 blank | 564 comment | 224 complexity | 1c5b61f7719f8760a51a73e3e229840b MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.0, Apache-2.0, BSD-3-Clause, MIT, GPL-2.0, WTFPL

Large files files are truncated, but you can click here to view the full file

  1. # encoding:utf-8
  2. #--
  3. # Addressable, Copyright (c) 2006-2010 Bob Aman
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining
  6. # a copy of this software and associated documentation files (the
  7. # "Software"), to deal in the Software without restriction, including
  8. # without limitation the rights to use, copy, modify, merge, publish,
  9. # distribute, sublicense, and/or sell copies of the Software, and to
  10. # permit persons to whom the Software is furnished to do so, subject to
  11. # the following conditions:
  12. #
  13. # The above copyright notice and this permission notice shall be
  14. # included in all copies or substantial portions of the Software.
  15. #
  16. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. #++
  24. require "addressable/version"
  25. require "addressable/idna"
  26. module Addressable
  27. ##
  28. # This is an implementation of a URI parser based on
  29. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
  30. # <a href="http://www.ietf.org/rfc/rfc3987.txt">RFC 3987</a>.
  31. class URI
  32. ##
  33. # Raised if something other than a uri is supplied.
  34. class InvalidURIError < StandardError
  35. end
  36. ##
  37. # Container for the character classes specified in
  38. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
  39. module CharacterClasses
  40. ALPHA = "a-zA-Z"
  41. DIGIT = "0-9"
  42. GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
  43. SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
  44. RESERVED = GEN_DELIMS + SUB_DELIMS
  45. UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"
  46. PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
  47. SCHEME = ALPHA + DIGIT + "\\-\\+\\."
  48. AUTHORITY = PCHAR
  49. PATH = PCHAR + "\\/"
  50. QUERY = PCHAR + "\\/\\?"
  51. FRAGMENT = PCHAR + "\\/\\?"
  52. end
  53. ##
  54. # Returns a URI object based on the parsed string.
  55. #
  56. # @param [String, Addressable::URI, #to_str] uri
  57. # The URI string to parse.
  58. # No parsing is performed if the object is already an
  59. # <code>Addressable::URI</code>.
  60. #
  61. # @return [Addressable::URI] The parsed URI.
  62. def self.parse(uri)
  63. # If we were given nil, return nil.
  64. return nil unless uri
  65. # If a URI object is passed, just return itself.
  66. return uri if uri.kind_of?(self)
  67. # If a URI object of the Ruby standard library variety is passed,
  68. # convert it to a string, then parse the string.
  69. # We do the check this way because we don't want to accidentally
  70. # cause a missing constant exception to be thrown.
  71. if uri.class.name =~ /^URI\b/
  72. uri = uri.to_s
  73. end
  74. if !uri.respond_to?(:to_str)
  75. raise TypeError, "Can't convert #{uri.class} into String."
  76. end
  77. # Otherwise, convert to a String
  78. uri = uri.to_str
  79. # This Regexp supplied as an example in RFC 3986, and it works great.
  80. uri_regex =
  81. /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/
  82. scan = uri.scan(uri_regex)
  83. fragments = scan[0]
  84. scheme = fragments[1]
  85. authority = fragments[3]
  86. path = fragments[4]
  87. query = fragments[6]
  88. fragment = fragments[8]
  89. user = nil
  90. password = nil
  91. host = nil
  92. port = nil
  93. if authority != nil
  94. # The Regexp above doesn't split apart the authority.
  95. userinfo = authority[/^([^\[\]]*)@/, 1]
  96. if userinfo != nil
  97. user = userinfo.strip[/^([^:]*):?/, 1]
  98. password = userinfo.strip[/:(.*)$/, 1]
  99. end
  100. host = authority.gsub(/^([^\[\]]*)@/, "").gsub(/:([^:@\[\]]*?)$/, "")
  101. port = authority[/:([^:@\[\]]*?)$/, 1]
  102. end
  103. if port == ""
  104. port = nil
  105. end
  106. return Addressable::URI.new(
  107. :scheme => scheme,
  108. :user => user,
  109. :password => password,
  110. :host => host,
  111. :port => port,
  112. :path => path,
  113. :query => query,
  114. :fragment => fragment
  115. )
  116. end
  117. ##
  118. # Converts an input to a URI. The input does not have to be a valid
  119. # URI — the method will use heuristics to guess what URI was intended.
  120. # This is not standards-compliant, merely user-friendly.
  121. #
  122. # @param [String, Addressable::URI, #to_str] uri
  123. # The URI string to parse.
  124. # No parsing is performed if the object is already an
  125. # <code>Addressable::URI</code>.
  126. # @param [Hash] hints
  127. # A <code>Hash</code> of hints to the heuristic parser.
  128. # Defaults to <code>{:scheme => "http"}</code>.
  129. #
  130. # @return [Addressable::URI] The parsed URI.
  131. def self.heuristic_parse(uri, hints={})
  132. # If we were given nil, return nil.
  133. return nil unless uri
  134. # If a URI object is passed, just return itself.
  135. return uri if uri.kind_of?(self)
  136. if !uri.respond_to?(:to_str)
  137. raise TypeError, "Can't convert #{uri.class} into String."
  138. end
  139. # Otherwise, convert to a String
  140. uri = uri.to_str.dup
  141. hints = {
  142. :scheme => "http"
  143. }.merge(hints)
  144. case uri
  145. when /^http:\/+/
  146. uri.gsub!(/^http:\/+/, "http://")
  147. when /^feed:\/+http:\/+/
  148. uri.gsub!(/^feed:\/+http:\/+/, "feed:http://")
  149. when /^feed:\/+/
  150. uri.gsub!(/^feed:\/+/, "feed://")
  151. when /^file:\/+/
  152. uri.gsub!(/^file:\/+/, "file:///")
  153. end
  154. parsed = self.parse(uri)
  155. if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
  156. parsed = self.parse(hints[:scheme] + "://" + uri)
  157. end
  158. if parsed.path.include?(".")
  159. new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
  160. if new_host
  161. parsed.defer_validation do
  162. new_path = parsed.path.gsub(
  163. Regexp.new("^" + Regexp.escape(new_host)), "")
  164. parsed.host = new_host
  165. parsed.path = new_path
  166. parsed.scheme = hints[:scheme] unless parsed.scheme
  167. end
  168. end
  169. end
  170. return parsed
  171. end
  172. ##
  173. # Converts a path to a file scheme URI. If the path supplied is
  174. # relative, it will be returned as a relative URI. If the path supplied
  175. # is actually a non-file URI, it will parse the URI as if it had been
  176. # parsed with <code>Addressable::URI.parse</code>. Handles all of the
  177. # various Microsoft-specific formats for specifying paths.
  178. #
  179. # @param [String, Addressable::URI, #to_str] path
  180. # Typically a <code>String</code> path to a file or directory, but
  181. # will return a sensible return value if an absolute URI is supplied
  182. # instead.
  183. #
  184. # @return [Addressable::URI]
  185. # The parsed file scheme URI or the original URI if some other URI
  186. # scheme was provided.
  187. #
  188. # @example
  189. # base = Addressable::URI.convert_path("/absolute/path/")
  190. # uri = Addressable::URI.convert_path("relative/path")
  191. # (base + uri).to_s
  192. # #=> "file:///absolute/path/relative/path"
  193. #
  194. # Addressable::URI.convert_path(
  195. # "c:\\windows\\My Documents 100%20\\foo.txt"
  196. # ).to_s
  197. # #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt"
  198. #
  199. # Addressable::URI.convert_path("http://example.com/").to_s
  200. # #=> "http://example.com/"
  201. def self.convert_path(path)
  202. # If we were given nil, return nil.
  203. return nil unless path
  204. # If a URI object is passed, just return itself.
  205. return path if path.kind_of?(self)
  206. if !path.respond_to?(:to_str)
  207. raise TypeError, "Can't convert #{path.class} into String."
  208. end
  209. # Otherwise, convert to a String
  210. path = path.to_str.strip
  211. path.gsub!(/^file:\/?\/?/, "") if path =~ /^file:\/?\/?/
  212. path = "/" + path if path =~ /^([a-zA-Z])[\|:]/
  213. uri = self.parse(path)
  214. if uri.scheme == nil
  215. # Adjust windows-style uris
  216. uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
  217. "/#{$1.downcase}:/"
  218. end
  219. uri.path.gsub!(/\\/, "/")
  220. if File.exists?(uri.path) &&
  221. File.stat(uri.path).directory?
  222. uri.path.gsub!(/\/$/, "")
  223. uri.path = uri.path + '/'
  224. end
  225. # If the path is absolute, set the scheme and host.
  226. if uri.path =~ /^\//
  227. uri.scheme = "file"
  228. uri.host = ""
  229. end
  230. uri.normalize!
  231. end
  232. return uri
  233. end
  234. ##
  235. # Joins several URIs together.
  236. #
  237. # @param [String, Addressable::URI, #to_str] *uris
  238. # The URIs to join.
  239. #
  240. # @return [Addressable::URI] The joined URI.
  241. #
  242. # @example
  243. # base = "http://example.com/"
  244. # uri = Addressable::URI.parse("relative/path")
  245. # Addressable::URI.join(base, uri)
  246. # #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
  247. def self.join(*uris)
  248. uri_objects = uris.collect do |uri|
  249. if !uri.respond_to?(:to_str)
  250. raise TypeError, "Can't convert #{uri.class} into String."
  251. end
  252. uri.kind_of?(self) ? uri : self.parse(uri.to_str)
  253. end
  254. result = uri_objects.shift.dup
  255. for uri in uri_objects
  256. result.join!(uri)
  257. end
  258. return result
  259. end
  260. ##
  261. # Percent encodes a URI component.
  262. #
  263. # @param [String, #to_str] component The URI component to encode.
  264. #
  265. # @param [String, Regexp] character_class
  266. # The characters which are not percent encoded. If a <code>String</code>
  267. # is passed, the <code>String</code> must be formatted as a regular
  268. # expression character class. (Do not include the surrounding square
  269. # brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
  270. # everything but the letters 'b' through 'z' and the numbers '0' through
  271. # '9' to be percent encoded. If a <code>Regexp</code> is passed, the
  272. # value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A set of
  273. # useful <code>String</code> values may be found in the
  274. # <code>Addressable::URI::CharacterClasses</code> module. The default
  275. # value is the reserved plus unreserved character classes specified in
  276. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
  277. #
  278. # @return [String] The encoded component.
  279. #
  280. # @example
  281. # Addressable::URI.encode_component("simple/example", "b-zB-Z0-9")
  282. # => "simple%2Fex%61mple"
  283. # Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/)
  284. # => "simple%2Fex%61mple"
  285. # Addressable::URI.encode_component(
  286. # "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
  287. # )
  288. # => "simple%2Fexample"
  289. def self.encode_component(component, character_class=
  290. CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
  291. return nil if component.nil?
  292. if !component.respond_to?(:to_str)
  293. raise TypeError, "Can't convert #{component.class} into String."
  294. end
  295. component = component.to_str
  296. if ![String, Regexp].include?(character_class.class)
  297. raise TypeError,
  298. "Expected String or Regexp, got #{character_class.inspect}"
  299. end
  300. if character_class.kind_of?(String)
  301. character_class = /[^#{character_class}]/
  302. end
  303. if component.respond_to?(:force_encoding)
  304. # We can't perform regexps on invalid UTF sequences, but
  305. # here we need to, so switch to ASCII.
  306. component = component.dup
  307. component.force_encoding(Encoding::ASCII_8BIT)
  308. end
  309. return component.gsub(character_class) do |sequence|
  310. (sequence.unpack('C*').map { |c| "%" + ("%02x" % c).upcase }).join("")
  311. end
  312. end
  313. class << self
  314. alias_method :encode_component, :encode_component
  315. end
  316. ##
  317. # Unencodes any percent encoded characters within a URI component.
  318. # This method may be used for unencoding either components or full URIs,
  319. # however, it is recommended to use the <code>unencode_component</code>
  320. # alias when unencoding components.
  321. #
  322. # @param [String, Addressable::URI, #to_str] uri
  323. # The URI or component to unencode.
  324. #
  325. # @param [Class] returning
  326. # The type of object to return.
  327. # This value may only be set to <code>String</code> or
  328. # <code>Addressable::URI</code>. All other values are invalid. Defaults
  329. # to <code>String</code>.
  330. #
  331. # @return [String, Addressable::URI]
  332. # The unencoded component or URI.
  333. # The return type is determined by the <code>returning</code> parameter.
  334. def self.unencode(uri, returning=String)
  335. return nil if uri.nil?
  336. if !uri.respond_to?(:to_str)
  337. raise TypeError, "Can't convert #{uri.class} into String."
  338. end
  339. if ![String, ::Addressable::URI].include?(returning)
  340. raise TypeError,
  341. "Expected Class (String or Addressable::URI), " +
  342. "got #{returning.inspect}"
  343. end
  344. result = uri.to_str.gsub(/%[0-9a-f]{2}/i) do |sequence|
  345. sequence[1..3].to_i(16).chr
  346. end
  347. result.force_encoding("utf-8") if result.respond_to?(:force_encoding)
  348. if returning == String
  349. return result
  350. elsif returning == ::Addressable::URI
  351. return ::Addressable::URI.parse(result)
  352. end
  353. end
  354. class << self
  355. alias_method :unescape, :unencode
  356. alias_method :unencode_component, :unencode
  357. alias_method :unescape_component, :unencode
  358. end
  359. ##
  360. # Normalizes the encoding of a URI component.
  361. #
  362. # @param [String, #to_str] component The URI component to encode.
  363. #
  364. # @param [String, Regexp] character_class
  365. # The characters which are not percent encoded. If a <code>String</code>
  366. # is passed, the <code>String</code> must be formatted as a regular
  367. # expression character class. (Do not include the surrounding square
  368. # brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
  369. # everything but the letters 'b' through 'z' and the numbers '0' through
  370. # '9' to be percent encoded. If a <code>Regexp</code> is passed, the
  371. # value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A set of
  372. # useful <code>String</code> values may be found in the
  373. # <code>Addressable::URI::CharacterClasses</code> module. The default
  374. # value is the reserved plus unreserved character classes specified in
  375. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
  376. #
  377. # @return [String] The normalized component.
  378. #
  379. # @example
  380. # Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z")
  381. # => "simple%2Fex%61mple"
  382. # Addressable::URI.normalize_component(
  383. # "simpl%65/%65xampl%65", /[^b-zB-Z]/
  384. # )
  385. # => "simple%2Fex%61mple"
  386. # Addressable::URI.normalize_component(
  387. # "simpl%65/%65xampl%65",
  388. # Addressable::URI::CharacterClasses::UNRESERVED
  389. # )
  390. # => "simple%2Fexample"
  391. def self.normalize_component(component, character_class=
  392. CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
  393. return nil if component.nil?
  394. if !component.respond_to?(:to_str)
  395. raise TypeError, "Can't convert #{component.class} into String."
  396. end
  397. component = component.to_str
  398. if ![String, Regexp].include?(character_class.class)
  399. raise TypeError,
  400. "Expected String or Regexp, got #{character_class.inspect}"
  401. end
  402. if character_class.kind_of?(String)
  403. character_class = /[^#{character_class}]/
  404. end
  405. if component.respond_to?(:force_encoding)
  406. # We can't perform regexps on invalid UTF sequences, but
  407. # here we need to, so switch to ASCII.
  408. component = component.dup
  409. component.force_encoding(Encoding::ASCII_8BIT)
  410. end
  411. unencoded = self.unencode_component(component)
  412. begin
  413. encoded = self.encode_component(
  414. Addressable::IDNA.unicode_normalize_kc(unencoded),
  415. character_class
  416. )
  417. rescue ArgumentError
  418. encoded = self.encode_component(unencoded)
  419. end
  420. return encoded
  421. end
  422. ##
  423. # Percent encodes any special characters in the URI.
  424. #
  425. # @param [String, Addressable::URI, #to_str] uri
  426. # The URI to encode.
  427. #
  428. # @param [Class] returning
  429. # The type of object to return.
  430. # This value may only be set to <code>String</code> or
  431. # <code>Addressable::URI</code>. All other values are invalid. Defaults
  432. # to <code>String</code>.
  433. #
  434. # @return [String, Addressable::URI]
  435. # The encoded URI.
  436. # The return type is determined by the <code>returning</code> parameter.
  437. def self.encode(uri, returning=String)
  438. return nil if uri.nil?
  439. if !uri.respond_to?(:to_str)
  440. raise TypeError, "Can't convert #{uri.class} into String."
  441. end
  442. if ![String, ::Addressable::URI].include?(returning)
  443. raise TypeError,
  444. "Expected Class (String or Addressable::URI), " +
  445. "got #{returning.inspect}"
  446. end
  447. uri_object = uri.kind_of?(self) ? uri : self.parse(uri.to_str)
  448. encoded_uri = Addressable::URI.new(
  449. :scheme => self.encode_component(uri_object.scheme,
  450. Addressable::URI::CharacterClasses::SCHEME),
  451. :authority => self.encode_component(uri_object.authority,
  452. Addressable::URI::CharacterClasses::AUTHORITY),
  453. :path => self.encode_component(uri_object.path,
  454. Addressable::URI::CharacterClasses::PATH),
  455. :query => self.encode_component(uri_object.query,
  456. Addressable::URI::CharacterClasses::QUERY),
  457. :fragment => self.encode_component(uri_object.fragment,
  458. Addressable::URI::CharacterClasses::FRAGMENT)
  459. )
  460. if returning == String
  461. return encoded_uri.to_s
  462. elsif returning == ::Addressable::URI
  463. return encoded_uri
  464. end
  465. end
  466. class << self
  467. alias_method :escape, :encode
  468. end
  469. ##
  470. # Normalizes the encoding of a URI. Characters within a hostname are
  471. # not percent encoded to allow for internationalized domain names.
  472. #
  473. # @param [String, Addressable::URI, #to_str] uri
  474. # The URI to encode.
  475. #
  476. # @param [Class] returning
  477. # The type of object to return.
  478. # This value may only be set to <code>String</code> or
  479. # <code>Addressable::URI</code>. All other values are invalid. Defaults
  480. # to <code>String</code>.
  481. #
  482. # @return [String, Addressable::URI]
  483. # The encoded URI.
  484. # The return type is determined by the <code>returning</code> parameter.
  485. def self.normalized_encode(uri, returning=String)
  486. if !uri.respond_to?(:to_str)
  487. raise TypeError, "Can't convert #{uri.class} into String."
  488. end
  489. if ![String, ::Addressable::URI].include?(returning)
  490. raise TypeError,
  491. "Expected Class (String or Addressable::URI), " +
  492. "got #{returning.inspect}"
  493. end
  494. uri_object = uri.kind_of?(self) ? uri : self.parse(uri.to_str)
  495. components = {
  496. :scheme => self.unencode_component(uri_object.scheme),
  497. :user => self.unencode_component(uri_object.user),
  498. :password => self.unencode_component(uri_object.password),
  499. :host => self.unencode_component(uri_object.host),
  500. :port => uri_object.port,
  501. :path => self.unencode_component(uri_object.path),
  502. :query => self.unencode_component(uri_object.query),
  503. :fragment => self.unencode_component(uri_object.fragment)
  504. }
  505. components.each do |key, value|
  506. if value != nil
  507. begin
  508. components[key] =
  509. Addressable::IDNA.unicode_normalize_kc(value.to_str)
  510. rescue ArgumentError
  511. # Likely a malformed UTF-8 character, skip unicode normalization
  512. components[key] = value.to_str
  513. end
  514. end
  515. end
  516. encoded_uri = Addressable::URI.new(
  517. :scheme => self.encode_component(components[:scheme],
  518. Addressable::URI::CharacterClasses::SCHEME),
  519. :user => self.encode_component(components[:user],
  520. Addressable::URI::CharacterClasses::UNRESERVED),
  521. :password => self.encode_component(components[:password],
  522. Addressable::URI::CharacterClasses::UNRESERVED),
  523. :host => components[:host],
  524. :port => components[:port],
  525. :path => self.encode_component(components[:path],
  526. Addressable::URI::CharacterClasses::PATH),
  527. :query => self.encode_component(components[:query],
  528. Addressable::URI::CharacterClasses::QUERY),
  529. :fragment => self.encode_component(components[:fragment],
  530. Addressable::URI::CharacterClasses::FRAGMENT)
  531. )
  532. if returning == String
  533. return encoded_uri.to_s
  534. elsif returning == ::Addressable::URI
  535. return encoded_uri
  536. end
  537. end
  538. ##
  539. # Encodes a set of key/value pairs according to the rules for the
  540. # <code>application/x-www-form-urlencoded</code> MIME type.
  541. #
  542. # @param [#to_hash, #to_ary] form_values
  543. # The form values to encode.
  544. #
  545. # @param [TrueClass, FalseClass] sort
  546. # Sort the key/value pairs prior to encoding.
  547. # Defaults to <code>false</code>.
  548. #
  549. # @return [String]
  550. # The encoded value.
  551. def self.form_encode(form_values, sort=false)
  552. if form_values.respond_to?(:to_hash)
  553. form_values = form_values.to_hash.to_a
  554. elsif form_values.respond_to?(:to_ary)
  555. form_values = form_values.to_ary
  556. else
  557. raise TypeError, "Can't convert #{form_values.class} into Array."
  558. end
  559. form_values = form_values.map do |(key, value)|
  560. [key.to_s, value.to_s]
  561. end
  562. if sort
  563. # Useful for OAuth and optimizing caching systems
  564. form_values = form_values.sort
  565. end
  566. escaped_form_values = form_values.map do |(key, value)|
  567. # Line breaks are CRLF pairs
  568. [
  569. self.encode_component(
  570. key.gsub(/(\r\n|\n|\r)/, "\r\n"),
  571. CharacterClasses::UNRESERVED
  572. ).gsub("%20", "+"),
  573. self.encode_component(
  574. value.gsub(/(\r\n|\n|\r)/, "\r\n"),
  575. CharacterClasses::UNRESERVED
  576. ).gsub("%20", "+")
  577. ]
  578. end
  579. return (escaped_form_values.map do |(key, value)|
  580. "#{key}=#{value}"
  581. end).join("&")
  582. end
  583. ##
  584. # Decodes a <code>String</code> according to the rules for the
  585. # <code>application/x-www-form-urlencoded</code> MIME type.
  586. #
  587. # @param [String, #to_str] encoded_value
  588. # The form values to decode.
  589. #
  590. # @return [Array]
  591. # The decoded values.
  592. # This is not a <code>Hash</code> because of the possibility for
  593. # duplicate keys.
  594. def self.form_unencode(encoded_value)
  595. if !encoded_value.respond_to?(:to_str)
  596. raise TypeError, "Can't convert #{encoded_value.class} into String."
  597. end
  598. encoded_value = encoded_value.to_str
  599. split_values = encoded_value.split("&").map do |pair|
  600. pair.split("=", 2)
  601. end
  602. return split_values.map do |(key, value)|
  603. [
  604. key ? self.unencode_component(
  605. key.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n") : nil,
  606. value ? (self.unencode_component(
  607. value.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n")) : nil
  608. ]
  609. end
  610. end
  611. ##
  612. # Creates a new uri object from component parts.
  613. #
  614. # @option [String, #to_str] scheme The scheme component.
  615. # @option [String, #to_str] user The user component.
  616. # @option [String, #to_str] password The password component.
  617. # @option [String, #to_str] userinfo
  618. # The userinfo component. If this is supplied, the user and password
  619. # components must be omitted.
  620. # @option [String, #to_str] host The host component.
  621. # @option [String, #to_str] port The port component.
  622. # @option [String, #to_str] authority
  623. # The authority component. If this is supplied, the user, password,
  624. # userinfo, host, and port components must be omitted.
  625. # @option [String, #to_str] path The path component.
  626. # @option [String, #to_str] query The query component.
  627. # @option [String, #to_str] fragment The fragment component.
  628. #
  629. # @return [Addressable::URI] The constructed URI object.
  630. def initialize(options={})
  631. if options.has_key?(:authority)
  632. if (options.keys & [:userinfo, :user, :password, :host, :port]).any?
  633. raise ArgumentError,
  634. "Cannot specify both an authority and any of the components " +
  635. "within the authority."
  636. end
  637. end
  638. if options.has_key?(:userinfo)
  639. if (options.keys & [:user, :password]).any?
  640. raise ArgumentError,
  641. "Cannot specify both a userinfo and either the user or password."
  642. end
  643. end
  644. self.defer_validation do
  645. # Bunch of crazy logic required because of the composite components
  646. # like userinfo and authority.
  647. self.scheme = options[:scheme] if options[:scheme]
  648. self.user = options[:user] if options[:user]
  649. self.password = options[:password] if options[:password]
  650. self.userinfo = options[:userinfo] if options[:userinfo]
  651. self.host = options[:host] if options[:host]
  652. self.port = options[:port] if options[:port]
  653. self.authority = options[:authority] if options[:authority]
  654. self.path = options[:path] if options[:path]
  655. self.query = options[:query] if options[:query]
  656. self.fragment = options[:fragment] if options[:fragment]
  657. end
  658. end
  659. ##
  660. # The scheme component for this URI.
  661. #
  662. # @return [String] The scheme component.
  663. def scheme
  664. return @scheme ||= nil
  665. end
  666. ##
  667. # The scheme component for this URI, normalized.
  668. #
  669. # @return [String] The scheme component, normalized.
  670. def normalized_scheme
  671. @normalized_scheme ||= (begin
  672. if self.scheme != nil
  673. if self.scheme =~ /^\s*ssh\+svn\s*$/i
  674. "svn+ssh"
  675. else
  676. Addressable::URI.normalize_component(
  677. self.scheme.strip.downcase,
  678. Addressable::URI::CharacterClasses::SCHEME
  679. )
  680. end
  681. else
  682. nil
  683. end
  684. end)
  685. end
  686. ##
  687. # Sets the scheme component for this URI.
  688. #
  689. # @param [String, #to_str] new_scheme The new scheme component.
  690. def scheme=(new_scheme)
  691. # Check for frozenness
  692. raise TypeError, "Can't modify frozen URI." if self.frozen?
  693. if new_scheme && !new_scheme.respond_to?(:to_str)
  694. raise TypeError, "Can't convert #{new_scheme.class} into String."
  695. elsif new_scheme
  696. new_scheme = new_scheme.to_str
  697. end
  698. if new_scheme && new_scheme !~ /[a-z][a-z0-9\.\+\-]*/i
  699. raise InvalidURIError, "Invalid scheme format."
  700. end
  701. @scheme = new_scheme
  702. @scheme = nil if @scheme.to_s.strip == ""
  703. # Reset dependant values
  704. @normalized_scheme = nil
  705. @uri_string = nil
  706. @hash = nil
  707. # Ensure we haven't created an invalid URI
  708. validate()
  709. end
  710. ##
  711. # The user component for this URI.
  712. #
  713. # @return [String] The user component.
  714. def user
  715. return @user ||= nil
  716. end
  717. ##
  718. # The user component for this URI, normalized.
  719. #
  720. # @return [String] The user component, normalized.
  721. def normalized_user
  722. @normalized_user ||= (begin
  723. if self.user
  724. if normalized_scheme =~ /https?/ && self.user.strip == "" &&
  725. (!self.password || self.password.strip == "")
  726. nil
  727. else
  728. Addressable::URI.normalize_component(
  729. self.user.strip,
  730. Addressable::URI::CharacterClasses::UNRESERVED
  731. )
  732. end
  733. else
  734. nil
  735. end
  736. end)
  737. end
  738. ##
  739. # Sets the user component for this URI.
  740. #
  741. # @param [String, #to_str] new_user The new user component.
  742. def user=(new_user)
  743. # Check for frozenness
  744. raise TypeError, "Can't modify frozen URI." if self.frozen?
  745. if new_user && !new_user.respond_to?(:to_str)
  746. raise TypeError, "Can't convert #{new_user.class} into String."
  747. end
  748. @user = new_user ? new_user.to_str : nil
  749. # You can't have a nil user with a non-nil password
  750. @password ||= nil
  751. if @password != nil
  752. @user = "" if @user.nil?
  753. end
  754. # Reset dependant values
  755. @userinfo = nil
  756. @normalized_userinfo = nil
  757. @authority = nil
  758. @normalized_user = nil
  759. @uri_string = nil
  760. @hash = nil
  761. # Ensure we haven't created an invalid URI
  762. validate()
  763. end
  764. ##
  765. # The password component for this URI.
  766. #
  767. # @return [String] The password component.
  768. def password
  769. return @password ||= nil
  770. end
  771. ##
  772. # The password component for this URI, normalized.
  773. #
  774. # @return [String] The password component, normalized.
  775. def normalized_password
  776. @normalized_password ||= (begin
  777. if self.password
  778. if normalized_scheme =~ /https?/ && self.password.strip == "" &&
  779. (!self.user || self.user.strip == "")
  780. nil
  781. else
  782. Addressable::URI.normalize_component(
  783. self.password.strip,
  784. Addressable::URI::CharacterClasses::UNRESERVED
  785. )
  786. end
  787. else
  788. nil
  789. end
  790. end)
  791. end
  792. ##
  793. # Sets the password component for this URI.
  794. #
  795. # @param [String, #to_str] new_password The new password component.
  796. def password=(new_password)
  797. # Check for frozenness
  798. raise TypeError, "Can't modify frozen URI." if self.frozen?
  799. if new_password && !new_password.respond_to?(:to_str)
  800. raise TypeError, "Can't convert #{new_password.class} into String."
  801. end
  802. @password = new_password ? new_password.to_str : nil
  803. # You can't have a nil user with a non-nil password
  804. @password ||= nil
  805. @user ||= nil
  806. if @password != nil
  807. @user = "" if @user.nil?
  808. end
  809. # Reset dependant values
  810. @userinfo = nil
  811. @normalized_userinfo = nil
  812. @authority = nil
  813. @normalized_password = nil
  814. @uri_string = nil
  815. @hash = nil
  816. # Ensure we haven't created an invalid URI
  817. validate()
  818. end
  819. ##
  820. # The userinfo component for this URI.
  821. # Combines the user and password components.
  822. #
  823. # @return [String] The userinfo component.
  824. def userinfo
  825. @userinfo ||= (begin
  826. current_user = self.user
  827. current_password = self.password
  828. if !current_user && !current_password
  829. nil
  830. elsif current_user && current_password
  831. "#{current_user}:#{current_password}"
  832. elsif current_user && !current_password
  833. "#{current_user}"
  834. end
  835. end)
  836. end
  837. ##
  838. # The userinfo component for this URI, normalized.
  839. #
  840. # @return [String] The userinfo component, normalized.
  841. def normalized_userinfo
  842. @normalized_userinfo ||= (begin
  843. current_user = self.normalized_user
  844. current_password = self.normalized_password
  845. if !current_user && !current_password
  846. nil
  847. elsif current_user && current_password
  848. "#{current_user}:#{current_password}"
  849. elsif current_user && !current_password
  850. "#{current_user}"
  851. end
  852. end)
  853. end
  854. ##
  855. # Sets the userinfo component for this URI.
  856. #
  857. # @param [String, #to_str] new_userinfo The new userinfo component.
  858. def userinfo=(new_userinfo)
  859. # Check for frozenness
  860. raise TypeError, "Can't modify frozen URI." if self.frozen?
  861. if new_userinfo && !new_userinfo.respond_to?(:to_str)
  862. raise TypeError, "Can't convert #{new_userinfo.class} into String."
  863. end
  864. new_user, new_password = if new_userinfo
  865. [
  866. new_userinfo.to_str.strip[/^(.*):/, 1],
  867. new_userinfo.to_str.strip[/:(.*)$/, 1]
  868. ]
  869. else
  870. [nil, nil]
  871. end
  872. # Password assigned first to ensure validity in case of nil
  873. self.password = new_password
  874. self.user = new_user
  875. # Reset dependant values
  876. @authority = nil
  877. @uri_string = nil
  878. @hash = nil
  879. # Ensure we haven't created an invalid URI
  880. validate()
  881. end
  882. ##
  883. # The host component for this URI.
  884. #
  885. # @return [String] The host component.
  886. def host
  887. return @host ||= nil
  888. end
  889. ##
  890. # The host component for this URI, normalized.
  891. #
  892. # @return [String] The host component, normalized.
  893. def normalized_host
  894. @normalized_host ||= (begin
  895. if self.host != nil
  896. if self.host.strip != ""
  897. result = ::Addressable::IDNA.to_ascii(
  898. self.class.unencode_component(self.host.strip.downcase)
  899. )
  900. if result[-1..-1] == "."
  901. # Trailing dots are unnecessary
  902. result = result[0...-1]
  903. end
  904. result
  905. else
  906. ""
  907. end
  908. else
  909. nil
  910. end
  911. end)
  912. end
  913. ##
  914. # Sets the host component for this URI.
  915. #
  916. # @param [String, #to_str] new_host The new host component.
  917. def host=(new_host)
  918. # Check for frozenness
  919. raise TypeError, "Can't modify frozen URI." if self.frozen?
  920. if new_host && !new_host.respond_to?(:to_str)
  921. raise TypeError, "Can't convert #{new_host.class} into String."
  922. end
  923. @host = new_host ? new_host.to_str : nil
  924. # Reset dependant values
  925. @authority = nil
  926. @normalized_host = nil
  927. @uri_string = nil
  928. @hash = nil
  929. # Ensure we haven't created an invalid URI
  930. validate()
  931. end
  932. ##
  933. # The authority component for this URI.
  934. # Combines the user, password, host, and port components.
  935. #
  936. # @return [String] The authority component.
  937. def authority
  938. @authority ||= (begin
  939. if self.host.nil?
  940. nil
  941. else
  942. authority = ""
  943. if self.userinfo != nil
  944. authority << "#{self.userinfo}@"
  945. end
  946. authority << self.host
  947. if self.port != nil
  948. authority << ":#{self.port}"
  949. end
  950. authority
  951. end
  952. end)
  953. end
  954. ##
  955. # The authority component for this URI, normalized.
  956. #
  957. # @return [String] The authority component, normalized.
  958. def normalized_authority
  959. @normalized_authority ||= (begin
  960. if self.normalized_host.nil?
  961. nil
  962. else
  963. authority = ""
  964. if self.normalized_userinfo != nil
  965. authority << "#{self.normalized_userinfo}@"
  966. end
  967. authority << self.normalized_host
  968. if self.normalized_port != nil
  969. authority << ":#{self.normalized_port}"
  970. end
  971. authority
  972. end
  973. end)
  974. end
  975. ##
  976. # Sets the authority component for this URI.
  977. #
  978. # @param [String, #to_str] new_authority The new authority component.
  979. def authority=(new_authority)
  980. # Check for frozenness
  981. raise TypeError, "Can't modify frozen URI." if self.frozen?
  982. if new_authority
  983. if !new_authority.respond_to?(:to_str)
  984. raise TypeError, "Can't convert #{new_authority.class} into String."
  985. end
  986. new_authority = new_authority.to_str
  987. new_userinfo = new_authority[/^([^\[\]]*)@/, 1]
  988. if new_userinfo
  989. new_user = new_userinfo.strip[/^([^:]*):?/, 1]
  990. new_password = new_userinfo.strip[/:(.*)$/, 1]
  991. end
  992. new_host =
  993. new_authority.gsub(/^([^\[\]]*)@/, "").gsub(/:([^:@\[\]]*?)$/, "")
  994. new_port =
  995. new_authority[/:([^:@\[\]]*?)$/, 1]
  996. end
  997. # Password assigned first to ensure validity in case of nil
  998. self.password = defined?(new_password) ? new_password : nil
  999. self.user = defined?(new_user) ? new_user : nil
  1000. self.host = defined?(new_host) ? new_host : nil
  1001. self.port = defined?(new_port) ? new_port : nil
  1002. # Reset dependant values
  1003. @inferred_port = nil
  1004. @userinfo = nil
  1005. @normalized_userinfo = nil
  1006. @uri_string = nil
  1007. @hash = nil
  1008. # Ensure we haven't created an invalid URI
  1009. validate()
  1010. end
  1011. ##
  1012. # The origin for this URI, serialized to ASCII, as per
  1013. # draft-ietf-websec-origin-00, section 5.2.
  1014. #
  1015. # @return [String] The serialized origin.
  1016. def origin
  1017. return (if self.scheme && self.authority
  1018. if self.normalized_port
  1019. (
  1020. "#{self.normalized_scheme}://#{self.normalized_host}" +
  1021. ":#{self.normalized_port}"
  1022. )
  1023. else
  1024. "#{self.normalized_scheme}://#{self.normalized_host}"
  1025. end
  1026. else
  1027. "null"
  1028. end)
  1029. end
  1030. # Returns an array of known ip-based schemes. These schemes typically
  1031. # use a similar URI form:
  1032. # <code>//<user>:<password>@<host>:<port>/<url-path></code>
  1033. def self.ip_based_schemes
  1034. return self.port_mapping.keys
  1035. end
  1036. # Returns a hash of common IP-based schemes and their default port
  1037. # numbers. Adding new schemes to this hash, as necessary, will allow
  1038. # for better URI normalization.
  1039. def self.port_mapping
  1040. @port_mapping ||= {
  1041. "http" => 80,
  1042. "https" => 443,
  1043. "ftp" => 21,
  1044. "tftp" => 69,
  1045. "sftp" => 22,
  1046. "ssh" => 22,
  1047. "svn+ssh" => 22,
  1048. "telnet" => 23,
  1049. "nntp" => 119,
  1050. "gopher" => 70,
  1051. "wais" => 210,
  1052. "ldap" => 389,
  1053. "prospero" => 1525
  1054. }
  1055. end
  1056. ##
  1057. # The port component for this URI.
  1058. # This is the port number actually given in the URI. This does not
  1059. # infer port numbers from default values.
  1060. #
  1061. # @return [Integer] The port component.
  1062. def port
  1063. return @port ||= nil
  1064. end
  1065. ##
  1066. # The port component for this URI, normalized.
  1067. #
  1068. # @return [Integer] The port component, normalized.
  1069. def normalized_port
  1070. @normalized_port ||= (begin
  1071. if self.class.port_mapping[normalized_scheme] == self.port
  1072. nil
  1073. else
  1074. self.port
  1075. end
  1076. end)
  1077. end
  1078. ##
  1079. # Sets the port component for this URI.
  1080. #
  1081. # @param [String, Integer, #to_s] new_port The new port component.
  1082. def port=(new_port)
  1083. # Check for frozenness
  1084. raise TypeError, "Can't modify frozen URI." if self.frozen?
  1085. if new_port != nil && new_port.respond_to?(:to_str)
  1086. new_port = Addressable::URI.unencode_component(new_port.to_str)
  1087. end
  1088. if new_port != nil && !(new_port.to_s =~ /^\d+$/)
  1089. raise InvalidURIError,
  1090. "Invalid port number: #{new_port.inspect}"
  1091. end
  1092. @port = new_port.to_s.to_i
  1093. @port = nil if @port == 0
  1094. # Reset dependant values
  1095. @authority = nil
  1096. @inferred_port = nil
  1097. @normalized_port = nil
  1098. @uri_string = nil
  1099. @hash = nil
  1100. # Ensure we haven't created an invalid URI
  1101. validate()
  1102. end
  1103. ##
  1104. # The inferred port component for this URI.
  1105. # This method will normalize to the default port for the URI's scheme if
  1106. # the port isn't explicitly specified in the URI.
  1107. #
  1108. # @return [Integer] The inferred port component.
  1109. def inferred_port
  1110. @inferred_port ||= (begin
  1111. if port.to_i == 0
  1112. if scheme
  1113. self.class.port_mapping[scheme.strip.downcase]
  1114. else
  1115. nil
  1116. end
  1117. else
  1118. port.to_i
  1119. end
  1120. end)
  1121. end
  1122. ##
  1123. # The combination of components that represent a site.
  1124. # Combines the scheme, user, password, host, and port components.
  1125. # Primarily useful for HTTP and HTTPS.
  1126. #
  1127. # For example, <code>"http://example.com/path?query"</code> would have a
  1128. # <code>site</code> value of <code>"http://example.com"</code>.
  1129. #
  1130. # @return [String] The components that identify a site.
  1131. def site
  1132. @site ||= (begin
  1133. if self.scheme || self.authority
  1134. site_string = ""
  1135. site_string << "#{self.scheme}:" if self.scheme != nil
  1136. site_string << "//#{self.authority}" if self.authority != nil
  1137. site_string
  1138. else
  1139. nil
  1140. end
  1141. end)
  1142. end
  1143. ##
  1144. # The normalized combination of components that represent a site.
  1145. # Combines the scheme, user, password, host, and port components.
  1146. # Primarily useful for HTTP and HTTPS.
  1147. #
  1148. # For example, <code>"http://example.com/path?query"</code> would have a
  1149. # <code>site</code> value of <code>"http://example.com"</code>.
  1150. #
  1151. # @return [String] The normalized components that identify a site.
  1152. def normalized_site
  1153. @site ||= (begin
  1154. if self.normalized_scheme || self.normalized_authority
  1155. site_string = ""
  1156. if self.normalized_scheme != nil
  1157. site_string << "#{self.normalized_scheme}:"
  1158. end
  1159. if self.normalized_authority != nil
  1160. site_string << "//#{self.normalized_authority}"
  1161. end
  1162. site_string
  1163. else
  1164. nil
  1165. end
  1166. end)
  1167. end
  1168. ##
  1169. # Sets the site value for this URI.
  1170. #
  1171. # @param [String, #to_str] new_site The new site value.
  1172. def site=(new_site)
  1173. if new_site
  1174. if !new_site.respond_to?(:to_str)
  1175. raise TypeError, "Can't convert #{new_site.class} into String."
  1176. end
  1177. new_site = new_site.to_str
  1178. # These two regular expressions derived from the primary parsing
  1179. # expression
  1180. self.scheme = new_site[/^(?:([^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?$/, 1]
  1181. self.authority = new_site[
  1182. /^(?:(?:[^:\/?#]+):)?(?:\/\/([^\/?#]*))?$/, 1
  1183. ]
  1184. else
  1185. self.scheme = nil
  1186. self.authority = nil
  1187. end
  1188. end
  1189. ##
  1190. # The path component for this URI.
  1191. #
  1192. # @return [String] The path component.
  1193. def path
  1194. @path ||= ""
  1195. return @path
  1196. end
  1197. ##
  1198. # The path component for this URI, normalized.
  1199. #
  1200. # @return [String] The path component, normalized.
  1201. def normalized_path
  1202. @normalized_path ||= (begin
  1203. if self.scheme == nil && self.path != nil && self.path != "" &&
  1204. self.path =~ /^(?!\/)[^\/:]*:.*$/
  1205. # Relative paths with colons in the first segment are ambiguous.
  1206. self.path.sub!(":", "%2F")
  1207. end
  1208. # String#split(delimeter, -1) uses the more strict splitting behavior
  1209. # found by default in Python.
  1210. result = (self.path.strip.split("/", -1).map do |segment|
  1211. Addressable::URI.normalize_component(
  1212. segment,
  1213. Addressable::URI::CharacterClasses::PCHAR
  1214. )
  1215. end).join("/")
  1216. result = self.class.normalize_path(result)
  1217. if result == "" &&
  1218. ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
  1219. result = "/"
  1220. end
  1221. result
  1222. end)
  1223. end
  1224. ##
  1225. # Sets the path component for this URI.
  1226. #
  1227. # @param [String, #to_str] new_path The new path component.
  1228. def path=(new_path)
  1229. # Check for frozenness
  1230. raise TypeError, "Can't modify frozen URI." if self.frozen?
  1231. if new_path && !new_path.respond_to?(:to_str)
  1232. raise TypeError, "Can't convert #{new_path.class} into String."
  1233. end
  1234. @path = (new_path || "").to_str
  1235. if @path != "" && @path[0..0] != "/" && host != nil
  1236. @path = "/#{@path}"
  1237. end
  1238. # Reset dependant values
  1239. @normalized_path = nil
  1240. @uri_string = nil
  1241. @hash = nil
  1242. end
  1243. ##
  1244. # The basename, if any, of the file in the path component.
  1245. #
  1246. # @return [String] The path's basename.
  1247. def basename
  1248. # Path cannot be nil
  1249. return File.basename(self.path).gsub(/;[^\/]*$/, "")
  1250. end
  1251. ##
  1252. # The extname, if any, of the file in the path component.
  1253. # Empty string if there is no extension.
  1254. #
  1255. # @return [String] The path's extname.
  1256. def extname
  1257. return nil unless self.path
  1258. return File.extname(self.basename)
  1259. end
  1260. ##
  1261. # The query component for this URI.
  1262. #
  1263. # @return [String] The query component.
  1264. def query
  1265. return @query ||= nil
  1266. end
  1267. ##
  1268. # The query component for this URI, normalized.
  1269. #
  1270. # @return [String] The query component, normalized.
  1271. def normalized_query
  1272. @normalized_query ||= (begin
  1273. if self.query
  1274. Addressable::URI.normalize_component(
  1275. self.query.strip,
  1276. Addressable::URI::CharacterClasses::QUERY
  1277. )
  1278. else
  1279. nil
  1280. end
  1281. end)
  1282. end
  1283. ##
  1284. # Sets the query component for this URI.
  1285. #
  1286. # @param [String, #to_str] new_query The new query component.
  1287. def query=(new_query)
  1288. # Check for frozenness
  1289. raise TypeError, "Can't modify frozen URI." if self.frozen?
  1290. if new_query && !new_query.respond_to?(:to_str)
  1291. raise TypeError, "Can't convert #{new_query.class} into String."
  1292. end
  1293. @query = new_query ? new_query.to_str : nil
  1294. # Reset dependant values
  1295. @normalized_query = nil
  1296. @uri_string = nil
  1297. @hash = nil
  1298. end
  1299. ##
  1300. # Converts the query component to a Hash value.
  1301. #
  1302. # @option [Symbol] notation
  1303. # May be one of <code>:flat</code>, <code>:dot</code>, or
  1304. # <code>:subscript</code>. The <code>:dot</code> notation is not
  1305. # supported for assignment. Default value is <code>:subscript</code>.
  1306. #
  1307. # @return [Hash, Array] The query string parsed as a Hash or Array object.
  1308. #
  1309. # @example
  1310. # Addressable::URI.parse("?one=1&two=2&three=3").query_values
  1311. # #=> {"one" => "1", "two" => "2", "three" => "3"}
  1312. # Addressable::URI.parse("?one[two][three]=four").query_values
  1313. # #=> {"one" => {"two" => {"three" => "four"}}}
  1314. # Addressable::URI.parse("?one.two.three=four").query_values(
  1315. # :notation => :dot
  1316. # )
  1317. # #=> {"one" => {"two" => {"three" => "four"}}}
  1318. # Addressable::URI.parse("?one[two][three]=four").query_values(
  1319. # :notation => :flat
  1320. # )
  1321. # #=> {"one[two][three]" => "four"}
  1322. # Addressable::URI.parse("?one.two.three=four").query_values(
  1323. # :notation => :flat
  1324. # )
  1325. # #=> {"one.two.three" => "four"}
  1326. # Addressable::URI.parse(
  1327. # "?one[two][three][]=four&one[two][three][]=five"
  1328. # ).query_values
  1329. # #=> {"one" => {"two" => {"three" => ["four", "five"]}}}
  1330. # Addressable::URI.parse(
  1331. # "?one=two&one=three").query_values(:notation => :flat_array)
  1332. # #=> [['one', 'two'], ['one', 'three']]
  1333. def query_values(options={})
  1334. defaults = {:notation => :subscript}
  1335. options = defaults.merge(options)
  1336. if ![:flat, :dot, :subscript, :flat_array].include?(options[:notation])
  1337. raise ArgumentError,
  1338. "Invalid notation. Must be one of: " +
  1339. "[:flat, :dot, :subscript, :flat_array]."
  1340. end
  1341. dehash = lambda do |hash|
  1342. hash.each do |(key, value)|
  1343. if value.kind_of?(Hash)
  1344. hash[key] = dehash.call(value)
  1345. end
  1346. end
  1347. if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
  1348. hash.sort.inject([]) do |accu, (key, value)|
  1349. accu << value; accu
  1350. end
  1351. else
  1352. hash
  1353. end
  1354. end
  1355. return nil if self.query == nil
  1356. empty_accumulator = :flat_array == options[:notation] ? [] : {}
  1357. return ((self.query.split("&").map do |pair|
  1358. pair.split("=", 2) if pair && pair != ""
  1359. end).compact.inject(empty_accumulator.dup) do |accumulator, (key, value)|
  1360. value = true if value.nil?
  1361. key = self.class.unencode_component(key)
  1362. if value != true
  1363. value = self.class.unencode_component(value.gsub(/\+/, " "))
  1364. end
  1365. if options[:notation] == :flat
  1366. if accumulator[key]
  1367. raise ArgumentError, "Key was repeated: #{key.inspect}"
  1368. end
  1369. accumulator[key] = value
  1370. elsif options[:notation] == :flat_array
  1371. accumulator << [key, value]
  1372. else
  1373. if options[:notation] == :dot
  1374. array_value = false
  1375. subkeys = key.split(".")
  1376. elsif options[:notation] == :subscript
  1377. array_value = !!(key =~ /\[\]$/)
  1378. subkeys = key.split(/[\[\]]+/)
  1379. end
  1380. current_hash = accumulator
  1381. for i in 0...(subkeys.size - 1)
  1382. subkey = subkeys[i]
  1383. current_hash[subkey] = {} unless current_hash[subkey]
  1384. current_hash = current_hash[subkey]
  1385. end
  1386. if array_value
  1387. current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
  1388. current_hash[subkeys.last] << value
  1389. else
  1390. current_hash[subkeys.last] = value
  1391. end
  1392. end
  1393. accumulator
  1394. end).inject(empty_accumulator.dup) do |accumulator, (key, value)|
  1395. if options[:notation] == :flat_array
  1396. accumulator << [key, value]
  1397. else
  1398. accumulator[key] = value.kind_of?(Hash) ? dehash.call(value) : value
  1399. end
  1400. accumulator
  1401. end
  1402. end
  1403. ##
  1404. # Sets the query component for this URI from a Hash object.
  1405. # This method produces a query string using the :subscript notation.
  1406. # An empty Hash will result in a nil query.
  1407. #
  1408. # @param [Hash, #to_hash, Array] new_query_values The new query values.
  1409. def query_values=(new_query_values)
  1410. # Check for frozenness
  1411. raise TypeError, "Can't modify frozen URI." if self.frozen?
  1412. if new_query_values == nil
  1413. self.query = nil
  1414. return nil
  1415. end
  1416. if !new_query_values.is_a?(Array)
  1417. if !new_query_values.respond_to?(:to_hash)
  1418. raise TypeError,
  1419. "Can't convert #{new_query_values.class} into Hash."
  1420. end
  1421. new_query_values = new_query_values.to_hash

Large files files are truncated, but you can click here to view the full file