PageRenderTime 57ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/mack-data_mapper/lib/gems/addressable-2.0.1/lib/addressable/uri.rb

https://github.com/giorgil2/mack-more
Ruby | 2510 lines | 1659 code | 135 blank | 716 comment | 249 complexity | 412aaa2ee5ab64e9710d4c37522b6a3f MD5 | raw file

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

  1. # coding:utf-8
  2. #--
  3. # Addressable, Copyright (c) 2006-2008 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. $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '/..')))
  25. $:.uniq!
  26. require "addressable/version"
  27. require "addressable/idna"
  28. module Addressable
  29. ##
  30. # This is an implementation of a URI parser based on
  31. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
  32. # <a href="http://www.ietf.org/rfc/rfc3987.txt">RFC 3987</a>.
  33. class URI
  34. ##
  35. # Raised if something other than a uri is supplied.
  36. class InvalidURIError < StandardError
  37. end
  38. ##
  39. # Raised if an invalid method option is supplied.
  40. class InvalidOptionError < StandardError
  41. end
  42. ##
  43. # Raised if an invalid template value is supplied.
  44. class InvalidTemplateValueError < StandardError
  45. end
  46. ##
  47. # Raised if an invalid template operator is used in a pattern.
  48. class InvalidTemplateOperatorError < StandardError
  49. end
  50. ##
  51. # Raised if an invalid template operator is used in a pattern.
  52. class TemplateOperatorAbortedError < StandardError
  53. end
  54. ##
  55. # Container for the character classes specified in
  56. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
  57. module CharacterClasses
  58. ALPHA = "a-zA-Z"
  59. DIGIT = "0-9"
  60. GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
  61. SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
  62. RESERVED = GEN_DELIMS + SUB_DELIMS
  63. UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"
  64. PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
  65. SCHEME = ALPHA + DIGIT + "\\-\\+\\."
  66. AUTHORITY = PCHAR
  67. PATH = PCHAR + "\\/"
  68. QUERY = PCHAR + "\\/\\?"
  69. FRAGMENT = PCHAR + "\\/\\?"
  70. end
  71. ##
  72. # Returns a URI object based on the parsed string.
  73. #
  74. # @param [String, Addressable::URI, #to_str] uri
  75. # The URI string to parse. No parsing is performed if the object is
  76. # already an <tt>Addressable::URI</tt>.
  77. #
  78. # @return [Addressable::URI] The parsed URI.
  79. def self.parse(uri)
  80. # If we were given nil, return nil.
  81. return nil unless uri
  82. # If a URI object is passed, just return itself.
  83. return uri if uri.kind_of?(self)
  84. if !uri.respond_to?(:to_str)
  85. raise TypeError, "Can't convert #{uri.class} into String."
  86. end
  87. # Otherwise, convert to a String
  88. uri = uri.to_str
  89. # If a URI object of the Ruby standard library variety is passed,
  90. # convert it to a string, then parse the string.
  91. # We do the check this way because we don't want to accidentally
  92. # cause a missing constant exception to be thrown.
  93. if uri.class.name =~ /^URI\b/
  94. uri = uri.to_s
  95. end
  96. # This Regexp supplied as an example in RFC 3986, and it works great.
  97. uri_regex =
  98. /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/
  99. scan = uri.scan(uri_regex)
  100. fragments = scan[0]
  101. return nil if fragments.nil?
  102. scheme = fragments[1]
  103. authority = fragments[3]
  104. path = fragments[4]
  105. query = fragments[6]
  106. fragment = fragments[8]
  107. userinfo = nil
  108. user = nil
  109. password = nil
  110. host = nil
  111. port = nil
  112. if authority != nil
  113. # The Regexp above doesn't split apart the authority.
  114. userinfo = authority[/^([^\[\]]*)@/, 1]
  115. if userinfo != nil
  116. user = userinfo.strip[/^([^:]*):?/, 1]
  117. password = userinfo.strip[/:(.*)$/, 1]
  118. end
  119. host = authority.gsub(/^([^\[\]]*)@/, "").gsub(/:([^:@\[\]]*?)$/, "")
  120. port = authority[/:([^:@\[\]]*?)$/, 1]
  121. end
  122. if port == ""
  123. port = nil
  124. end
  125. return Addressable::URI.new(
  126. :scheme => scheme,
  127. :user => user,
  128. :password => password,
  129. :host => host,
  130. :port => port,
  131. :path => path,
  132. :query => query,
  133. :fragment => fragment
  134. )
  135. end
  136. ##
  137. # Converts an input to a URI. The input does not have to be a valid
  138. # URI — the method will use heuristics to guess what URI was intended.
  139. # This is not standards-compliant, merely user-friendly.
  140. #
  141. # @param [String, Addressable::URI, #to_str] uri
  142. # The URI string to parse. No parsing is performed if the object is
  143. # already an <tt>Addressable::URI</tt>.
  144. # @param [Hash] hints
  145. # A <tt>Hash</tt> of hints to the heuristic parser. Defaults to
  146. # <tt>{:scheme => "http"}</tt>.
  147. #
  148. # @return [Addressable::URI] The parsed URI.
  149. def self.heuristic_parse(uri, hints={})
  150. # If we were given nil, return nil.
  151. return nil unless uri
  152. # If a URI object is passed, just return itself.
  153. return uri if uri.kind_of?(self)
  154. if !uri.respond_to?(:to_str)
  155. raise TypeError, "Can't convert #{uri.class} into String."
  156. end
  157. # Otherwise, convert to a String
  158. uri = uri.to_str.dup
  159. hints = {
  160. :scheme => "http"
  161. }.merge(hints)
  162. case uri
  163. when /^http:\/+/
  164. uri.gsub!(/^http:\/+/, "http://")
  165. when /^feed:\/+http:\/+/
  166. uri.gsub!(/^feed:\/+http:\/+/, "feed:http://")
  167. when /^feed:\/+/
  168. uri.gsub!(/^feed:\/+/, "feed://")
  169. when /^file:\/+/
  170. uri.gsub!(/^file:\/+/, "file:///")
  171. end
  172. parsed = self.parse(uri)
  173. if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
  174. parsed = self.parse(hints[:scheme] + "://" + uri)
  175. end
  176. if parsed.authority == nil
  177. if parsed.path =~ /^[^\/]+\./
  178. new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
  179. if new_host
  180. new_path = parsed.path.gsub(
  181. Regexp.new("^" + Regexp.escape(new_host)), "")
  182. parsed.host = new_host
  183. parsed.path = new_path
  184. parsed.scheme = hints[:scheme]
  185. end
  186. end
  187. end
  188. return parsed
  189. end
  190. ##
  191. # Converts a path to a file scheme URI. If the path supplied is
  192. # relative, it will be returned as a relative URI. If the path supplied
  193. # is actually a non-file URI, it will parse the URI as if it had been
  194. # parsed with <tt>Addressable::URI.parse</tt>. Handles all of the
  195. # various Microsoft-specific formats for specifying paths.
  196. #
  197. # @param [String, Addressable::URI, #to_str] path
  198. # Typically a <tt>String</tt> path to a file or directory, but
  199. # will return a sensible return value if an absolute URI is supplied
  200. # instead.
  201. #
  202. # @return [Addressable::URI]
  203. # The parsed file scheme URI or the original URI if some other URI
  204. # scheme was provided.
  205. #
  206. # @example
  207. # base = Addressable::URI.convert_path("/absolute/path/")
  208. # uri = Addressable::URI.convert_path("relative/path")
  209. # (base + uri).to_s
  210. # #=> "file:///absolute/path/relative/path"
  211. #
  212. # Addressable::URI.convert_path(
  213. # "c:\\windows\\My Documents 100%20\\foo.txt"
  214. # ).to_s
  215. # #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt"
  216. #
  217. # Addressable::URI.convert_path("http://example.com/").to_s
  218. # #=> "http://example.com/"
  219. def self.convert_path(path)
  220. # If we were given nil, return nil.
  221. return nil unless path
  222. # If a URI object is passed, just return itself.
  223. return path if path.kind_of?(self)
  224. if !path.respond_to?(:to_str)
  225. raise TypeError, "Can't convert #{path.class} into String."
  226. end
  227. # Otherwise, convert to a String
  228. path = path.to_str.strip
  229. path.gsub!(/^file:\/?\/?/, "") if path =~ /^file:\/?\/?/
  230. path = "/" + path if path =~ /^([a-zA-Z])(\||:)/
  231. uri = self.parse(path)
  232. if uri.scheme == nil
  233. # Adjust windows-style uris
  234. uri.path.gsub!(/^\/?([a-zA-Z])\|(\\|\/)/, "/\\1:/")
  235. uri.path.gsub!(/\\/, "/")
  236. if File.exists?(uri.path) &&
  237. File.stat(uri.path).directory?
  238. uri.path.gsub!(/\/$/, "")
  239. uri.path = uri.path + '/'
  240. end
  241. # If the path is absolute, set the scheme and host.
  242. if uri.path =~ /^\//
  243. uri.scheme = "file"
  244. uri.host = ""
  245. end
  246. uri.normalize!
  247. end
  248. return uri
  249. end
  250. ##
  251. # Expands a URI template into a full URI.
  252. #
  253. # @param [String, #to_str] pattern The URI template pattern.
  254. # @param [Hash] mapping The mapping that corresponds to the pattern.
  255. # @param [#validate, #transform] processor
  256. # An optional processor object may be supplied. The object should
  257. # respond to either the <tt>validate</tt> or <tt>transform</tt> messages
  258. # or both. Both the <tt>validate</tt> and <tt>transform</tt> methods
  259. # should take two parameters: <tt>name</tt> and <tt>value</tt>. The
  260. # <tt>validate</tt> method should return <tt>true</tt> or
  261. # <tt>false</tt>; <tt>true</tt> if the value of the variable is valid,
  262. # <tt>false</tt> otherwise. An <tt>InvalidTemplateValueError</tt>
  263. # exception will be raised if the value is invalid. The
  264. # <tt>transform</tt> method should return the transformed variable
  265. # value as a <tt>String</tt>.
  266. #
  267. # @return [Addressable::URI] The expanded URI template.
  268. #
  269. # @example
  270. # class ExampleProcessor
  271. # def self.validate(name, value)
  272. # return !!(value =~ /^[\w ]+$/) if name == "query"
  273. # return true
  274. # end
  275. #
  276. # def self.transform(name, value)
  277. # return value.gsub(/ /, "+") if name == "query"
  278. # return value
  279. # end
  280. # end
  281. #
  282. # Addressable::URI.expand_template(
  283. # "http://example.com/search/{query}/",
  284. # {"query" => "an example search query"},
  285. # ExampleProcessor
  286. # ).to_s
  287. # #=> "http://example.com/search/an+example+search+query/"
  288. #
  289. # Addressable::URI.expand_template(
  290. # "http://example.com/search/{-list|+|query}/",
  291. # {"query" => "an example search query".split(" ")}
  292. # ).to_s
  293. # #=> "http://example.com/search/an+example+search+query/"
  294. #
  295. # Addressable::URI.expand_template(
  296. # "http://example.com/search/{query}/",
  297. # {"query" => "bogus!"},
  298. # ExampleProcessor
  299. # ).to_s
  300. # #=> Addressable::URI::InvalidTemplateValueError
  301. def self.expand_template(pattern, mapping, processor=nil)
  302. # FIXME: MUST REFACTOR!!!
  303. result = pattern.dup
  304. reserved = Addressable::URI::CharacterClasses::RESERVED
  305. unreserved = Addressable::URI::CharacterClasses::UNRESERVED
  306. anything = reserved + unreserved
  307. operator_expansion =
  308. /\{-([a-zA-Z]+)\|([#{anything}]+)\|([#{anything}]+)\}/
  309. variable_expansion = /\{([#{anything}]+?)(=([#{anything}]+))?\}/
  310. transformed_mapping = mapping.inject({}) do |accu, pair|
  311. name, value = pair
  312. unless value.respond_to?(:to_ary) || value.respond_to?(:to_str)
  313. raise TypeError,
  314. "Can't convert #{value.class} into String or Array."
  315. end
  316. transformed_value =
  317. value.respond_to?(:to_ary) ? value.to_ary : value.to_str
  318. # Handle percent escaping, and unicode normalization
  319. if transformed_value.kind_of?(Array)
  320. transformed_value.map! do |value|
  321. self.encode_component(
  322. Addressable::IDNA.unicode_normalize_kc(value),
  323. Addressable::URI::CharacterClasses::UNRESERVED
  324. )
  325. end
  326. else
  327. transformed_value = self.encode_component(
  328. Addressable::IDNA.unicode_normalize_kc(transformed_value),
  329. Addressable::URI::CharacterClasses::UNRESERVED
  330. )
  331. end
  332. # Process, if we've got a processor
  333. if processor != nil
  334. if processor.respond_to?(:validate)
  335. if !processor.validate(name, value)
  336. display_value = value.kind_of?(Array) ? value.inspect : value
  337. raise InvalidTemplateValueError,
  338. "#{name}=#{display_value} is an invalid template value."
  339. end
  340. end
  341. if processor.respond_to?(:transform)
  342. transformed_value = processor.transform(name, value)
  343. end
  344. end
  345. accu[name] = transformed_value
  346. accu
  347. end
  348. result.gsub!(
  349. /#{operator_expansion}|#{variable_expansion}/
  350. ) do |capture|
  351. if capture =~ operator_expansion
  352. operator, argument, variables, default_mapping =
  353. parse_template_expansion(capture, transformed_mapping)
  354. expand_method = "expand_#{operator}_operator"
  355. if ([expand_method, expand_method.to_sym] & private_methods).empty?
  356. raise InvalidTemplateOperatorError,
  357. "Invalid template operator: #{operator}"
  358. else
  359. send(expand_method.to_sym, argument, variables, default_mapping)
  360. end
  361. else
  362. varname, _, vardefault = capture.scan(/^\{(.+?)(=(.*))?\}$/)[0]
  363. transformed_mapping[varname] || vardefault
  364. end
  365. end
  366. return Addressable::URI.parse(result)
  367. end
  368. ##
  369. # Expands a URI Template opt operator.
  370. #
  371. # @param [String] argument The argument to the operator.
  372. # @param [Array] variables The variables the operator is working on.
  373. # @param [Hash] mapping The mapping of variables to values.
  374. #
  375. # @return [String] The expanded result.
  376. def self.expand_opt_operator(argument, variables, mapping)
  377. if (variables.any? do |variable|
  378. mapping[variable] != [] &&
  379. mapping[variable]
  380. end)
  381. argument
  382. else
  383. ""
  384. end
  385. end
  386. class <<self; private :expand_opt_operator; end
  387. ##
  388. # Expands a URI Template neg operator.
  389. #
  390. # @param [String] argument The argument to the operator.
  391. # @param [Array] variables The variables the operator is working on.
  392. # @param [Hash] mapping The mapping of variables to values.
  393. #
  394. # @return [String] The expanded result.
  395. def self.expand_neg_operator(argument, variables, mapping)
  396. if (variables.any? do |variable|
  397. mapping[variable] != [] &&
  398. mapping[variable]
  399. end)
  400. ""
  401. else
  402. argument
  403. end
  404. end
  405. class <<self; private :expand_neg_operator; end
  406. ##
  407. # Expands a URI Template prefix operator.
  408. #
  409. # @param [String] argument The argument to the operator.
  410. # @param [Array] variables The variables the operator is working on.
  411. # @param [Hash] mapping The mapping of variables to values.
  412. #
  413. # @return [String] The expanded result.
  414. def self.expand_prefix_operator(argument, variables, mapping)
  415. if variables.size != 1
  416. raise InvalidTemplateOperatorError,
  417. "Template operator 'prefix' takes exactly one variable."
  418. end
  419. value = mapping[variables.first]
  420. if value.kind_of?(Array)
  421. (value.map { |list_value| argument + list_value }).join("")
  422. else
  423. argument + value.to_s
  424. end
  425. end
  426. class <<self; private :expand_prefix_operator; end
  427. ##
  428. # Expands a URI Template suffix operator.
  429. #
  430. # @param [String] argument The argument to the operator.
  431. # @param [Array] variables The variables the operator is working on.
  432. # @param [Hash] mapping The mapping of variables to values.
  433. #
  434. # @return [String] The expanded result.
  435. def self.expand_suffix_operator(argument, variables, mapping)
  436. if variables.size != 1
  437. raise InvalidTemplateOperatorError,
  438. "Template operator 'suffix' takes exactly one variable."
  439. end
  440. value = mapping[variables.first]
  441. if value.kind_of?(Array)
  442. (value.map { |list_value| list_value + argument }).join("")
  443. else
  444. value.to_s + argument
  445. end
  446. end
  447. class <<self; private :expand_suffix_operator; end
  448. ##
  449. # Expands a URI Template join operator.
  450. #
  451. # @param [String] argument The argument to the operator.
  452. # @param [Array] variables The variables the operator is working on.
  453. # @param [Hash] mapping The mapping of variables to values.
  454. #
  455. # @return [String] The expanded result.
  456. def self.expand_join_operator(argument, variables, mapping)
  457. variable_values = variables.inject([]) do |accu, variable|
  458. if !mapping[variable].kind_of?(Array)
  459. if mapping[variable]
  460. accu << variable + "=" + (mapping[variable])
  461. end
  462. else
  463. raise InvalidTemplateOperatorError,
  464. "Template operator 'join' does not accept Array values."
  465. end
  466. accu
  467. end
  468. variable_values.join(argument)
  469. end
  470. class <<self; private :expand_join_operator; end
  471. ##
  472. # Expands a URI Template list operator.
  473. #
  474. # @param [String] argument The argument to the operator.
  475. # @param [Array] variables The variables the operator is working on.
  476. # @param [Hash] mapping The mapping of variables to values.
  477. #
  478. # @return [String] The expanded result.
  479. def self.expand_list_operator(argument, variables, mapping)
  480. if variables.size != 1
  481. raise InvalidTemplateOperatorError,
  482. "Template operator 'list' takes exactly one variable."
  483. end
  484. mapping[variables.first].join(argument)
  485. end
  486. class <<self; private :expand_list_operator; end
  487. ##
  488. # Parses a URI template expansion <tt>String</tt>.
  489. #
  490. # @param [String] expansion The operator <tt>String</tt>.
  491. # @param [Hash] mapping The mapping to merge defaults into.
  492. #
  493. # @return [Array]
  494. # A tuple of the operator, argument, variables, and mapping.
  495. def self.parse_template_expansion(capture, mapping)
  496. operator, argument, variables = capture[1...-1].split("|")
  497. operator.gsub!(/^\-/, "")
  498. variables = variables.split(",")
  499. mapping = (variables.inject({}) do |accu, var|
  500. varname, _, vardefault = var.scan(/^(.+?)(=(.*))?$/)[0]
  501. accu[varname] = vardefault
  502. accu
  503. end).merge(mapping)
  504. variables = variables.map { |var| var.gsub(/=.*$/, "") }
  505. return operator, argument, variables, mapping
  506. end
  507. class <<self; private :parse_template_expansion; end
  508. ##
  509. # Extracts a mapping from the URI using a URI Template pattern.
  510. #
  511. # @param [String] pattern
  512. # A URI template pattern.
  513. # @param [#restore, #match] processor
  514. # A template processor object may optionally be supplied.
  515. # The object should respond to either the <tt>restore</tt> or
  516. # <tt>match</tt> messages or both. The <tt>restore</tt> method should
  517. # take two parameters: [String] name and [String] value. The
  518. # <tt>restore</tt> method should reverse any transformations that have
  519. # been performed on the value to ensure a valid URI. The
  520. # <tt>match</tt> method should take a single parameter: [String] name.
  521. # The <tt>match</tt> method should return a <tt>String</tt> containing
  522. # a regular expression capture group for matching on that particular
  523. # variable. The default value is ".*?". The <tt>match</tt> method has
  524. # no effect on multivariate operator expansions.
  525. # @return [Hash, NilClass]
  526. # The <tt>Hash</tt> mapping that was extracted from the URI, or
  527. # <tt>nil</tt> if the URI didn't match the template.
  528. #
  529. # @example
  530. # class ExampleProcessor
  531. # def self.restore(name, value)
  532. # return value.gsub(/\+/, " ") if name == "query"
  533. # return value
  534. # end
  535. #
  536. # def self.match(name)
  537. # return ".*?" if name == "first"
  538. # return ".*"
  539. # end
  540. # end
  541. #
  542. # uri = Addressable::URI.parse(
  543. # "http://example.com/search/an+example+search+query/"
  544. # )
  545. # uri.extract_mapping(
  546. # "http://example.com/search/{query}/",
  547. # ExampleProcessor
  548. # )
  549. # #=> {"query" => "an example search query"}
  550. #
  551. # uri = Addressable::URI.parse("http://example.com/a/b/c/")
  552. # uri.extract_mapping(
  553. # "http://example.com/{first}/{second}/",
  554. # ExampleProcessor
  555. # )
  556. # #=> {"first" => "a", "second" => "b/c"}
  557. #
  558. # uri = Addressable::URI.parse("http://example.com/a/b/c/")
  559. # uri.extract_mapping(
  560. # "http://example.com/{first}/{-list|/|second}/"
  561. # )
  562. # #=> {"first" => "a", "second" => ["b", "c"]}
  563. def extract_mapping(pattern, processor=nil)
  564. reserved = Addressable::URI::CharacterClasses::RESERVED
  565. unreserved = Addressable::URI::CharacterClasses::UNRESERVED
  566. anything = reserved + unreserved
  567. operator_expansion =
  568. /\{-([a-zA-Z]+)\|([#{anything}]+)\|([#{anything}]+)\}/
  569. variable_expansion = /\{([#{anything}]+?)(=([#{anything}]+))?\}/
  570. # First, we need to process the pattern, and extract the values.
  571. expansions, expansion_regexp =
  572. parse_template_pattern(pattern, processor)
  573. unparsed_values = self.to_s.scan(expansion_regexp).flatten
  574. mapping = {}
  575. if self.to_s == pattern
  576. return mapping
  577. elsif expansions.size > 0 && expansions.size == unparsed_values.size
  578. expansions.each_with_index do |expansion, index|
  579. unparsed_value = unparsed_values[index]
  580. if expansion =~ operator_expansion
  581. operator, argument, variables =
  582. parse_template_expansion(expansion)
  583. extract_method = "extract_#{operator}_operator"
  584. if ([extract_method, extract_method.to_sym] &
  585. private_methods).empty?
  586. raise InvalidTemplateOperatorError,
  587. "Invalid template operator: #{operator}"
  588. else
  589. begin
  590. send(
  591. extract_method.to_sym, unparsed_value, processor,
  592. argument, variables, mapping
  593. )
  594. rescue TemplateOperatorAbortedError
  595. return nil
  596. end
  597. end
  598. else
  599. name = expansion[variable_expansion, 1]
  600. value = unparsed_value
  601. if processor != nil && processor.respond_to?(:restore)
  602. value = processor.restore(name, value)
  603. end
  604. mapping[name] = value
  605. end
  606. end
  607. return mapping
  608. else
  609. return nil
  610. end
  611. end
  612. ##
  613. # Generates the <tt>Regexp</tt> that parses a template pattern.
  614. #
  615. # @param [String] pattern The URI template pattern.
  616. # @param [#match] processor The template processor to use.
  617. #
  618. # @return [Regexp]
  619. # A regular expression which may be used to parse a template pattern.
  620. def parse_template_pattern(pattern, processor)
  621. reserved = Addressable::URI::CharacterClasses::RESERVED
  622. unreserved = Addressable::URI::CharacterClasses::UNRESERVED
  623. anything = reserved + unreserved
  624. operator_expansion =
  625. /\{-[a-zA-Z]+\|[#{anything}]+\|[#{anything}]+\}/
  626. variable_expansion = /\{([#{anything}]+?)(=([#{anything}]+))?\}/
  627. # Escape the pattern. The two gsubs restore the escaped curly braces
  628. # back to their original form. Basically, escape everything that isn't
  629. # within an expansion.
  630. escaped_pattern = Regexp.escape(
  631. pattern
  632. ).gsub(/\\\{(.*?)\\\}/) do |escaped|
  633. escaped.gsub(/\\(.)/, "\\1")
  634. end
  635. expansions = []
  636. # Create a regular expression that captures the values of the
  637. # variables in the URI.
  638. regexp_string = escaped_pattern.gsub(
  639. /#{operator_expansion}|#{variable_expansion}/
  640. ) do |expansion|
  641. expansions << expansion
  642. if expansion =~ operator_expansion
  643. capture_group = "(.*)"
  644. if processor != nil && processor.respond_to?(:match)
  645. # We can only lookup the match values for single variable
  646. # operator expansions. Besides, ".*" is usually the only
  647. # reasonable value for multivariate operators anyways.
  648. operator, _, names, _ =
  649. parse_template_expansion(expansion)
  650. if ["prefix", "suffix", "list"].include?(operator)
  651. capture_group = "(#{processor.match(names.first)})"
  652. end
  653. end
  654. capture_group
  655. else
  656. capture_group = "(.*?)"
  657. if processor != nil && processor.respond_to?(:match)
  658. name = expansion[/\{([^\}=]+)(=[^\}]+)?\}/, 1]
  659. capture_group = "(#{processor.match(name)})"
  660. end
  661. capture_group
  662. end
  663. end
  664. # Ensure that the regular expression matches the whole URI.
  665. regexp_string = "^#{regexp_string}$"
  666. return expansions, Regexp.new(regexp_string)
  667. end
  668. private :parse_template_pattern
  669. ##
  670. # Parses a URI template expansion <tt>String</tt>.
  671. #
  672. # @param [String] expansion The operator <tt>String</tt>.
  673. #
  674. # @return [Array]
  675. # A tuple of the operator, argument, variables.
  676. def parse_template_expansion(capture)
  677. operator, argument, variables = capture[1...-1].split("|")
  678. operator.gsub!(/^\-/, "")
  679. variables = variables.split(",").map { |var| var.gsub(/=.*$/, "") }
  680. return operator, argument, variables
  681. end
  682. private :parse_template_expansion
  683. ##
  684. # Extracts a URI Template opt operator.
  685. #
  686. # @param [String] value The unparsed value to extract from.
  687. # @param [#restore] processor The processor object.
  688. # @param [String] argument The argument to the operator.
  689. # @param [Array] variables The variables the operator is working on.
  690. # @param [Hash] mapping The mapping of variables to values.
  691. #
  692. # @return [String] The extracted result.
  693. def extract_opt_operator(
  694. value, processor, argument, variables, mapping)
  695. if value != "" && value != argument
  696. raise TemplateOperatorAbortedError,
  697. "Value for template operator 'neg' was unexpected."
  698. end
  699. end
  700. private :extract_opt_operator
  701. ##
  702. # Extracts a URI Template neg operator.
  703. #
  704. # @param [String] value The unparsed value to extract from.
  705. # @param [#restore] processor The processor object.
  706. # @param [String] argument The argument to the operator.
  707. # @param [Array] variables The variables the operator is working on.
  708. # @param [Hash] mapping The mapping of variables to values.
  709. #
  710. # @return [String] The extracted result.
  711. def extract_neg_operator(
  712. value, processor, argument, variables, mapping)
  713. if value != "" && value != argument
  714. raise TemplateOperatorAbortedError,
  715. "Value for template operator 'neg' was unexpected."
  716. end
  717. end
  718. private :extract_neg_operator
  719. ##
  720. # Extracts a URI Template prefix operator.
  721. #
  722. # @param [String] value The unparsed value to extract from.
  723. # @param [#restore] processor The processor object.
  724. # @param [String] argument The argument to the operator.
  725. # @param [Array] variables The variables the operator is working on.
  726. # @param [Hash] mapping The mapping of variables to values.
  727. #
  728. # @return [String] The extracted result.
  729. def extract_prefix_operator(
  730. value, processor, argument, variables, mapping)
  731. if variables.size != 1
  732. raise InvalidTemplateOperatorError,
  733. "Template operator 'suffix' takes exactly one variable."
  734. end
  735. if value[0...argument.size] != argument
  736. raise TemplateOperatorAbortedError,
  737. "Value for template operator 'prefix' missing expected prefix."
  738. end
  739. values = value.split(argument)
  740. # Compensate for the crappy result from split.
  741. if value[-argument.size..-1] == argument
  742. values << ""
  743. end
  744. if values[0] == ""
  745. values.shift
  746. end
  747. if processor && processor.respond_to?(:restore)
  748. values.map! { |value| processor.restore(variables.first, value) }
  749. end
  750. mapping[variables.first] = values
  751. end
  752. private :extract_prefix_operator
  753. ##
  754. # Extracts a URI Template suffix operator.
  755. #
  756. # @param [String] value The unparsed value to extract from.
  757. # @param [#restore] processor The processor object.
  758. # @param [String] argument The argument to the operator.
  759. # @param [Array] variables The variables the operator is working on.
  760. # @param [Hash] mapping The mapping of variables to values.
  761. #
  762. # @return [String] The extracted result.
  763. def extract_suffix_operator(
  764. value, processor, argument, variables, mapping)
  765. if variables.size != 1
  766. raise InvalidTemplateOperatorError,
  767. "Template operator 'suffix' takes exactly one variable."
  768. end
  769. if value[-argument.size..-1] != argument
  770. raise TemplateOperatorAbortedError,
  771. "Value for template operator 'suffix' missing expected suffix."
  772. end
  773. values = value.split(argument)
  774. # Compensate for the crappy result from split.
  775. if value[-argument.size..-1] == argument
  776. values << ""
  777. end
  778. if values[-1] == ""
  779. values.pop
  780. end
  781. if processor && processor.respond_to?(:restore)
  782. values.map! { |value| processor.restore(variables.first, value) }
  783. end
  784. mapping[variables.first] = values
  785. end
  786. private :extract_suffix_operator
  787. ##
  788. # Extracts a URI Template join operator.
  789. #
  790. # @param [String] value The unparsed value to extract from.
  791. # @param [#restore] processor The processor object.
  792. # @param [String] argument The argument to the operator.
  793. # @param [Array] variables The variables the operator is working on.
  794. # @param [Hash] mapping The mapping of variables to values.
  795. #
  796. # @return [String] The extracted result.
  797. def extract_join_operator(value, processor, argument, variables, mapping)
  798. unparsed_values = value.split(argument)
  799. parsed_variables = []
  800. for unparsed_value in unparsed_values
  801. name = unparsed_value[/^(.+?)=(.+)$/, 1]
  802. parsed_variables << name
  803. parsed_value = unparsed_value[/^(.+?)=(.+)$/, 2]
  804. if processor && processor.respond_to?(:restore)
  805. parsed_value = processor.restore(name, parsed_value)
  806. end
  807. mapping[name] = parsed_value
  808. end
  809. if (parsed_variables & variables) != parsed_variables
  810. raise TemplateOperatorAbortedError,
  811. "Template operator 'join' variable mismatch: " +
  812. "#{parsed_variables.inspect}, #{variables.inspect}"
  813. end
  814. end
  815. private :extract_join_operator
  816. ##
  817. # Extracts a URI Template list operator.
  818. #
  819. # @param [String] value The unparsed value to extract from.
  820. # @param [#restore] processor The processor object.
  821. # @param [String] argument The argument to the operator.
  822. # @param [Array] variables The variables the operator is working on.
  823. # @param [Hash] mapping The mapping of variables to values.
  824. #
  825. # @return [String] The extracted result.
  826. def extract_list_operator(value, processor, argument, variables, mapping)
  827. if variables.size != 1
  828. raise InvalidTemplateOperatorError,
  829. "Template operator 'list' takes exactly one variable."
  830. end
  831. values = value.split(argument)
  832. if processor && processor.respond_to?(:restore)
  833. values.map! { |value| processor.restore(variables.first, value) }
  834. end
  835. mapping[variables.first] = values
  836. end
  837. private :extract_list_operator
  838. ##
  839. # Joins several URIs together.
  840. #
  841. # @param [String, Addressable::URI, #to_str] *uris
  842. # The URIs to join.
  843. #
  844. # @return [Addressable::URI] The joined URI.
  845. #
  846. # @example
  847. # base = "http://example.com/"
  848. # uri = Addressable::URI.parse("relative/path")
  849. # Addressable::URI.join(base, uri)
  850. # #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
  851. def self.join(*uris)
  852. uri_objects = uris.collect do |uri|
  853. if !uri.respond_to?(:to_str)
  854. raise TypeError, "Can't convert #{uri.class} into String."
  855. end
  856. uri.kind_of?(self) ? uri : self.parse(uri.to_str)
  857. end
  858. result = uri_objects.shift.dup
  859. for uri in uri_objects
  860. result.join!(uri)
  861. end
  862. return result
  863. end
  864. ##
  865. # Percent encodes a URI component.
  866. #
  867. # @param [String, #to_str] component The URI component to encode.
  868. #
  869. # @param [String, Regexp] character_class
  870. # The characters which are not percent encoded. If a <tt>String</tt>
  871. # is passed, the <tt>String</tt> must be formatted as a regular
  872. # expression character class. (Do not include the surrounding square
  873. # brackets.) For example, <tt>"b-zB-Z0-9"</tt> would cause everything
  874. # but the letters 'b' through 'z' and the numbers '0' through '9' to be
  875. # percent encoded. If a <tt>Regexp</tt> is passed, the value
  876. # <tt>/[^b-zB-Z0-9]/</tt> would have the same effect.
  877. # A set of useful <tt>String</tt> values may be found in the
  878. # <tt>Addressable::URI::CharacterClasses</tt> module. The default value
  879. # is the reserved plus unreserved character classes specified in
  880. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
  881. #
  882. # @return [String] The encoded component.
  883. #
  884. # @example
  885. # Addressable::URI.encode_component("simple/example", "b-zB-Z0-9")
  886. # => "simple%2Fex%61mple"
  887. # Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/)
  888. # => "simple%2Fex%61mple"
  889. # Addressable::URI.encode_component(
  890. # "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
  891. # )
  892. # => "simple%2Fexample"
  893. def self.encode_component(component, character_class=
  894. CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
  895. return nil if component.nil?
  896. if !component.respond_to?(:to_str)
  897. raise TypeError, "Can't convert #{component.class} into String."
  898. end
  899. component = component.to_str
  900. if ![String, Regexp].include?(character_class.class)
  901. raise TypeError,
  902. "Expected String or Regexp, got #{character_class.inspect}"
  903. end
  904. if character_class.kind_of?(String)
  905. character_class = /[^#{character_class}]/
  906. end
  907. return component.gsub(character_class) do |sequence|
  908. (sequence.unpack('C*').map { |c| "%#{c.to_s(16).upcase}" }).join("")
  909. end
  910. end
  911. class << self
  912. alias_method :encode_component, :encode_component
  913. end
  914. ##
  915. # Unencodes any percent encoded characters within a URI component.
  916. # This method may be used for unencoding either components or full URIs,
  917. # however, it is recommended to use the <tt>unencode_component</tt> alias
  918. # when unencoding components.
  919. #
  920. # @param [String, Addressable::URI, #to_str] uri
  921. # The URI or component to unencode.
  922. #
  923. # @param [Class] returning
  924. # The type of object to return. This value may only be set to
  925. # <tt>String</tt> or <tt>Addressable::URI</tt>. All other values
  926. # are invalid. Defaults to <tt>String</tt>.
  927. #
  928. # @return [String, Addressable::URI]
  929. # The unencoded component or URI. The return type is determined by
  930. # the <tt>returning</tt> parameter.
  931. def self.unencode(uri, returning=String)
  932. return nil if uri.nil?
  933. if !uri.respond_to?(:to_str)
  934. raise TypeError, "Can't convert #{uri.class} into String."
  935. end
  936. if ![String, ::Addressable::URI].include?(returning)
  937. raise TypeError,
  938. "Expected Class (String or Addressable::URI), " +
  939. "got #{returning.inspect}"
  940. end
  941. result = uri.to_str.gsub(/%[0-9a-f]{2}/i) do |sequence|
  942. sequence[1..3].to_i(16).chr
  943. end
  944. result.force_encoding("utf-8") if result.respond_to?(:force_encoding)
  945. if returning == String
  946. return result
  947. elsif returning == ::Addressable::URI
  948. return ::Addressable::URI.parse(result)
  949. end
  950. end
  951. class << self
  952. alias_method :unescape, :unencode
  953. alias_method :unencode_component, :unencode
  954. alias_method :unescape_component, :unencode
  955. end
  956. ##
  957. # Percent encodes any special characters in the URI.
  958. #
  959. # @param [String, Addressable::URI, #to_str] uri
  960. # The URI to encode.
  961. #
  962. # @param [Class] returning
  963. # The type of object to return. This value may only be set to
  964. # <tt>String</tt> or <tt>Addressable::URI</tt>. All other values
  965. # are invalid. Defaults to <tt>String</tt>.
  966. #
  967. # @return [String, Addressable::URI]
  968. # The encoded URI. The return type is determined by
  969. # the <tt>returning</tt> parameter.
  970. def self.encode(uri, returning=String)
  971. return nil if uri.nil?
  972. if !uri.respond_to?(:to_str)
  973. raise TypeError, "Can't convert #{uri.class} into String."
  974. end
  975. if ![String, ::Addressable::URI].include?(returning)
  976. raise TypeError,
  977. "Expected Class (String or Addressable::URI), " +
  978. "got #{returning.inspect}"
  979. end
  980. uri_object = uri.kind_of?(self) ? uri : self.parse(uri.to_str)
  981. encoded_uri = Addressable::URI.new(
  982. :scheme => self.encode_component(uri_object.scheme,
  983. Addressable::URI::CharacterClasses::SCHEME),
  984. :authority => self.encode_component(uri_object.authority,
  985. Addressable::URI::CharacterClasses::AUTHORITY),
  986. :path => self.encode_component(uri_object.path,
  987. Addressable::URI::CharacterClasses::PATH),
  988. :query => self.encode_component(uri_object.query,
  989. Addressable::URI::CharacterClasses::QUERY),
  990. :fragment => self.encode_component(uri_object.fragment,
  991. Addressable::URI::CharacterClasses::FRAGMENT)
  992. )
  993. if returning == String
  994. return encoded_uri.to_s
  995. elsif returning == ::Addressable::URI
  996. return encoded_uri
  997. end
  998. end
  999. class << self
  1000. alias_method :escape, :encode
  1001. end
  1002. ##
  1003. # Normalizes the encoding of a URI. Characters within a hostname are
  1004. # not percent encoded to allow for internationalized domain names.
  1005. #
  1006. # @param [String, Addressable::URI, #to_str] uri
  1007. # The URI to encode.
  1008. #
  1009. # @param [Class] returning
  1010. # The type of object to return. This value may only be set to
  1011. # <tt>String</tt> or <tt>Addressable::URI</tt>. All other values
  1012. # are invalid. Defaults to <tt>String</tt>.
  1013. #
  1014. # @return [String, Addressable::URI]
  1015. # The encoded URI. The return type is determined by
  1016. # the <tt>returning</tt> parameter.
  1017. def self.normalized_encode(uri, returning=String)
  1018. if !uri.respond_to?(:to_str)
  1019. raise TypeError, "Can't convert #{uri.class} into String."
  1020. end
  1021. if ![String, ::Addressable::URI].include?(returning)
  1022. raise TypeError,
  1023. "Expected Class (String or Addressable::URI), " +
  1024. "got #{returning.inspect}"
  1025. end
  1026. uri_object = uri.kind_of?(self) ? uri : self.parse(uri.to_str)
  1027. components = {
  1028. :scheme => self.unencode_component(uri_object.scheme),
  1029. :user => self.unencode_component(uri_object.user),
  1030. :password => self.unencode_component(uri_object.password),
  1031. :host => self.unencode_component(uri_object.host),
  1032. :port => uri_object.port,
  1033. :path => self.unencode_component(uri_object.path),
  1034. :query => self.unencode_component(uri_object.query),
  1035. :fragment => self.unencode_component(uri_object.fragment)
  1036. }
  1037. components.each do |key, value|
  1038. if value != nil
  1039. components[key] = Addressable::IDNA.unicode_normalize_kc(value.to_s)
  1040. end
  1041. end
  1042. encoded_uri = Addressable::URI.new(
  1043. :scheme => self.encode_component(components[:scheme],
  1044. Addressable::URI::CharacterClasses::SCHEME),
  1045. :user => self.encode_component(components[:user],
  1046. Addressable::URI::CharacterClasses::UNRESERVED),
  1047. :password => self.encode_component(components[:password],
  1048. Addressable::URI::CharacterClasses::UNRESERVED),
  1049. :host => components[:host],
  1050. :port => components[:port],
  1051. :path => self.encode_component(components[:path],
  1052. Addressable::URI::CharacterClasses::PATH),
  1053. :query => self.encode_component(components[:query],
  1054. Addressable::URI::CharacterClasses::QUERY),
  1055. :fragment => self.encode_component(components[:fragment],
  1056. Addressable::URI::CharacterClasses::FRAGMENT)
  1057. )
  1058. if returning == String
  1059. return encoded_uri.to_s
  1060. elsif returning == ::Addressable::URI
  1061. return encoded_uri
  1062. end
  1063. end
  1064. ##
  1065. # Extracts uris from an arbitrary body of text.
  1066. #
  1067. # @param [String, #to_str] text
  1068. # The body of text to extract URIs from.
  1069. #
  1070. # @option [String, Addressable::URI, #to_str] base
  1071. # Causes any relative URIs to be resolved against the base URI.
  1072. #
  1073. # @option [TrueClass, FalseClass] parse
  1074. # If parse is true, all extracted URIs will be parsed. If parse is
  1075. # false, the return value with be an <tt>Array</tt> of <tt>Strings</aa>.
  1076. # Defaults to false.
  1077. #
  1078. # @return [Array] The extracted URIs.
  1079. def self.extract(text, options={})
  1080. defaults = {:base => nil, :parse => false}
  1081. options = defaults.merge(options)
  1082. raise InvalidOptionError unless (options.keys - defaults.keys).empty?
  1083. # This regular expression needs to be less forgiving or else it would
  1084. # match virtually all text. Which isn't exactly what we're going for.
  1085. extract_regex = /((([a-z\+]+):)[^ \n\<\>\"\\]+[\w\/])/
  1086. extracted_uris =
  1087. text.scan(extract_regex).collect { |match| match[0] }
  1088. sgml_extract_regex = /<[^>]+href=\"([^\"]+?)\"[^>]*>/
  1089. sgml_extracted_uris =
  1090. text.scan(sgml_extract_regex).collect { |match| match[0] }
  1091. extracted_uris.concat(sgml_extracted_uris - extracted_uris)
  1092. textile_extract_regex = /\".+?\":([^ ]+\/[^ ]+)[ \,\.\;\:\?\!\<\>\"]/i
  1093. textile_extracted_uris =
  1094. text.scan(textile_extract_regex).collect { |match| match[0] }
  1095. extracted_uris.concat(textile_extracted_uris - extracted_uris)
  1096. parsed_uris = []
  1097. base_uri = nil
  1098. if options[:base] != nil
  1099. base_uri = options[:base] if options[:base].kind_of?(self)
  1100. base_uri = self.parse(options[:base].to_s) if base_uri == nil
  1101. end
  1102. for uri_string in extracted_uris
  1103. begin
  1104. if base_uri == nil
  1105. parsed_uris << self.parse(uri_string)
  1106. else
  1107. parsed_uris << (base_uri + self.parse(uri_string))
  1108. end
  1109. rescue Exception
  1110. nil
  1111. end
  1112. end
  1113. parsed_uris = parsed_uris.select do |uri|
  1114. (self.ip_based_schemes | [
  1115. "file", "git", "svn", "mailto", "tel"
  1116. ]).include?(uri.normalized_scheme)
  1117. end
  1118. if options[:parse]
  1119. return parsed_uris
  1120. else
  1121. return parsed_uris.collect { |uri| uri.to_s }
  1122. end
  1123. end
  1124. ##
  1125. # Creates a new uri object from component parts.
  1126. #
  1127. # @option [String, #to_str] scheme The scheme component.
  1128. # @option [String, #to_str] user The user component.
  1129. # @option [String, #to_str] password The password component.
  1130. # @option [String, #to_str] userinfo
  1131. # The userinfo component. If this is supplied, the user and password
  1132. # components must be omitted.
  1133. # @option [String, #to_str] host The host component.
  1134. # @option [String, #to_str] port The port component.
  1135. # @option [String, #to_str] authority
  1136. # The authority component. If this is supplied, the user, password,
  1137. # userinfo, host, and port components must be omitted.
  1138. # @option [String, #to_str] path The path component.
  1139. # @option [String, #to_str] query The query component.
  1140. # @option [String, #to_str] fragment The fragment component.
  1141. #
  1142. # @return [Addressable::URI] The constructed URI object.
  1143. def initialize(options={})
  1144. if options.has_key?(:authority)
  1145. if (options.keys & [:userinfo, :user, :password, :host, :port]).any?
  1146. raise ArgumentError,
  1147. "Cannot specify both an authority and any of the components " +
  1148. "within the authority."
  1149. end
  1150. end
  1151. if options.has_key?(:userinfo)
  1152. if (options.keys & [:user, :password]).any?
  1153. raise ArgumentError,
  1154. "Cannot specify both a userinfo and either the user or password."
  1155. end
  1156. end
  1157. self.validation_deferred = true
  1158. self.scheme = options[:scheme] if options[:scheme]
  1159. self.user = options[:user] if options[:user]
  1160. self.password = options[:password] if options[:password]
  1161. self.userinfo = options[:userinfo] if options[:userinfo]
  1162. self.host = options[:host] if options[:host]
  1163. self.port = options[:port] if options[:port]
  1164. self.authority = options[:authority] if options[:authority]
  1165. self.path = options[:path] if options[:path]
  1166. self.query = options[:query] if options[:query]
  1167. self.fragment = options[:fragment] if options[:fragment]
  1168. self.validation_deferred = false
  1169. end
  1170. ##
  1171. # The scheme component for this URI.
  1172. #
  1173. # @return [String] The scheme component.
  1174. def scheme
  1175. return @scheme
  1176. end
  1177. ##
  1178. # The scheme component for this URI, normalized.
  1179. #
  1180. # @return [String] The scheme component, normalized.
  1181. def normalized_scheme
  1182. @normalized_scheme ||= (begin
  1183. if self.scheme != nil
  1184. if self.scheme =~ /^\s*ssh\+svn\s*$/i
  1185. "svn+ssh"
  1186. else
  1187. Addressable::URI.encode_component(
  1188. Addressable::IDNA.unicode_normalize_kc(
  1189. Addressable::URI.unencode_component(
  1190. self.scheme.strip.downcase)),
  1191. Addressable::URI::CharacterClasses::SCHEME
  1192. )
  1193. end
  1194. else
  1195. nil
  1196. end
  1197. end)
  1198. end
  1199. ##
  1200. # Sets the scheme component for this URI.
  1201. #
  1202. # @param [String, #to_str] new_scheme The new scheme component.
  1203. def scheme=(new_scheme)
  1204. @scheme = new_scheme ? new_scheme.to_str : nil
  1205. @scheme = nil if @scheme.to_s.strip == ""
  1206. # Reset dependant values
  1207. @normalized_scheme = nil
  1208. end
  1209. ##
  1210. # The user component for this URI.
  1211. #
  1212. # @return [String] The user component.
  1213. def user
  1214. return @user
  1215. end
  1216. ##
  1217. # The user component for this URI, normalized.
  1218. #
  1219. # @return [String] The user component, normalized.
  1220. def normalized_user
  1221. @normalized_user ||= (begin
  1222. if self.user
  1223. if normalized_scheme =~ /https?/ && self.user.strip == "" &&
  1224. (!self.password || self.password.strip == "")
  1225. nil
  1226. else
  1227. Addressable::URI.encode_component(
  1228. Addressable::IDNA.unicode_normalize_kc(
  1229. Addressable::URI.unencode_component(self.user.strip)),
  1230. Addressable::URI::CharacterClasses::UNRESERVED
  1231. )
  1232. end
  1233. else
  1234. nil
  1235. end
  1236. end)
  1237. end
  1238. ##
  1239. # Sets the user component for this URI.
  1240. #
  1241. # @param [String, #to_str] new_user The new user component.
  1242. def user=(new_user)
  1243. @user = new_user ? new_user.to_str : nil
  1244. # You can't have a nil user with a non-nil password
  1245. if @password != nil
  1246. @user = "" if @user.nil?
  1247. end
  1248. # Reset dependant values
  1249. @userinfo = nil
  1250. @normalized_userinfo = nil
  1251. @authority = nil
  1252. @normalized_user = nil
  1253. # Ensure we haven't created an invalid URI
  1254. validate()
  1255. end
  1256. ##
  1257. # The password component for this URI.
  1258. #
  1259. # @return [String] The password component.
  1260. def password
  1261. return @password
  1262. end
  1263. ##
  1264. # The password component for this URI, normalized.
  1265. #
  1266. # @return [String] The password component, normalized.
  1267. def normalized_password
  1268. @normalized_password ||= (begin
  1269. if self.password
  1270. if normalized_scheme =~ /https?/ && self.password.strip == "" &&
  1271. (!self.user || self.user.strip == "")
  1272. nil
  1273. else
  1274. Addressable::URI.encode_component(
  1275. Addressable::IDNA.unicode_normalize_kc(
  1276. Addressable::URI.unencode_component(self.password.strip)),
  1277. Addressable::URI::CharacterClasses::UNRESERVED
  1278. )
  1279. end
  1280. else
  1281. nil
  1282. end
  1283. end)
  1284. end
  1285. ##
  1286. # Sets the password component for this URI.
  1287. #
  1288. # @param [String, #to_str] new_password The new password component.
  1289. def password=(new_password)
  1290. @password = new_password ? new_password.to_str : nil
  1291. # You can't have a nil user with a non-nil password
  1292. if @password != nil
  1293. @user = "" if @user.nil?
  1294. end
  1295. # Reset dependant values
  1296. @userinfo = nil
  1297. @normalized_userinfo = nil
  1298. @authority = nil
  1299. @normalized_password = nil
  1300. # Ensure we haven't created an invalid URI
  1301. validate()
  1302. end
  1303. ##
  1304. # The userinfo component for this URI.
  1305. # Combines the user and password components.
  1306. #
  1307. # @return [String] The userinfo component.
  1308. def userinfo
  1309. @userinfo ||= (begin
  1310. current_user = self.user

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