PageRenderTime 56ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/uri/generic.rb

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