addressable 2.2.4 → 2.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,27 +1,21 @@
1
1
  # encoding:utf-8
2
2
  #--
3
- # Addressable, Copyright (c) 2006-2010 Bob Aman
3
+ # Copyright (C) 2006-2011 Bob Aman
4
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:
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
12
8
  #
13
- # The above copyright notice and this permission notice shall be
14
- # included in all copies or substantial portions of the Software.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
15
10
  #
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.
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
23
16
  #++
24
17
 
18
+
25
19
  require "addressable/version"
26
20
  require "addressable/uri"
27
21
 
@@ -63,7 +57,7 @@ module Addressable
63
57
  #
64
58
  # @param [Addressable::URI] uri
65
59
  # The URI that the template was matched against.
66
- def initialize(uri, template, mapping) # :nodoc:
60
+ def initialize(uri, template, mapping)
67
61
  @uri = uri.dup.freeze
68
62
  @template = template
69
63
  @mapping = mapping.dup.freeze
@@ -154,20 +148,22 @@ module Addressable
154
148
  #
155
149
  # @param [#restore, #match] processor
156
150
  # A template processor object may optionally be supplied.
157
- #
158
- # The object should respond to either the <tt>restore</tt> or
159
- # <tt>match</tt> messages or both. The <tt>restore</tt> method should take
160
- # two parameters: [String] name and [String] value. The <tt>restore</tt>
161
- # method should reverse any transformations that have been performed on the
162
- # value to ensure a valid URI. The <tt>match</tt> method should take a
163
- # single parameter: [String] name. The <tt>match</tt> method should return
164
- # a <tt>String</tt> containing a regular expression capture group for
165
- # matching on that particular variable. The default value is ".*?". The
166
- # <tt>match</tt> method has no effect on multivariate operator expansions.
151
+ #
152
+ # The object should respond to either the <tt>restore</tt> or
153
+ # <tt>match</tt> messages or both. The <tt>restore</tt> method should
154
+ # take two parameters: `[String] name` and `[String] value`.
155
+ # The <tt>restore</tt> method should reverse any transformations that
156
+ # have been performed on the value to ensure a valid URI.
157
+ # The <tt>match</tt> method should take a single
158
+ # parameter: `[String] name`. The <tt>match</tt> method should return
159
+ # a <tt>String</tt> containing a regular expression capture group for
160
+ # matching on that particular variable. The default value is `".*?"`.
161
+ # The <tt>match</tt> method has no effect on multivariate operator
162
+ # expansions.
167
163
  #
168
164
  # @return [Hash, NilClass]
169
- # The <tt>Hash</tt> mapping that was extracted from the URI, or
170
- # <tt>nil</tt> if the URI didn't match the template.
165
+ # The <tt>Hash</tt> mapping that was extracted from the URI, or
166
+ # <tt>nil</tt> if the URI didn't match the template.
171
167
  #
172
168
  # @example
173
169
  # class ExampleProcessor
@@ -214,20 +210,22 @@ module Addressable
214
210
  #
215
211
  # @param [#restore, #match] processor
216
212
  # A template processor object may optionally be supplied.
217
- #
218
- # The object should respond to either the <tt>restore</tt> or
219
- # <tt>match</tt> messages or both. The <tt>restore</tt> method should take
220
- # two parameters: [String] name and [String] value. The <tt>restore</tt>
221
- # method should reverse any transformations that have been performed on the
222
- # value to ensure a valid URI. The <tt>match</tt> method should take a
223
- # single parameter: [String] name. The <tt>match</tt> method should return
224
- # a <tt>String</tt> containing a regular expression capture group for
225
- # matching on that particular variable. The default value is ".*?". The
226
- # <tt>match</tt> method has no effect on multivariate operator expansions.
213
+ #
214
+ # The object should respond to either the <tt>restore</tt> or
215
+ # <tt>match</tt> messages or both. The <tt>restore</tt> method should
216
+ # take two parameters: `[String] name` and `[String] value`.
217
+ # The <tt>restore</tt> method should reverse any transformations that
218
+ # have been performed on the value to ensure a valid URI.
219
+ # The <tt>match</tt> method should take a single
220
+ # parameter: `[String] name`. The <tt>match</tt> method should return
221
+ # a <tt>String</tt> containing a regular expression capture group for
222
+ # matching on that particular variable. The default value is `".*?"`.
223
+ # The <tt>match</tt> method has no effect on multivariate operator
224
+ # expansions.
227
225
  #
228
226
  # @return [Hash, NilClass]
229
- # The <tt>Hash</tt> mapping that was extracted from the URI, or
230
- # <tt>nil</tt> if the URI didn't match the template.
227
+ # The <tt>Hash</tt> mapping that was extracted from the URI, or
228
+ # <tt>nil</tt> if the URI didn't match the template.
231
229
  #
232
230
  # @example
233
231
  # class ExampleProcessor
@@ -1,30 +1,26 @@
1
1
  # encoding:utf-8
2
2
  #--
3
- # Addressable, Copyright (c) 2006-2010 Bob Aman
3
+ # Copyright (C) 2006-2011 Bob Aman
4
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:
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
12
8
  #
13
- # The above copyright notice and this permission notice shall be
14
- # included in all copies or substantial portions of the Software.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
15
10
  #
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.
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
23
16
  #++
24
17
 
18
+
25
19
  require "addressable/version"
26
20
  require "addressable/idna"
27
21
 
22
+ ##
23
+ # Addressable is a library for processing links and URIs.
28
24
  module Addressable
29
25
  ##
30
26
  # This is an implementation of a URI parser based on
@@ -54,6 +50,11 @@ module Addressable
54
50
  FRAGMENT = PCHAR + "\\/\\?"
55
51
  end
56
52
 
53
+ SLASH = '/'
54
+ EMPTYSTR = ''
55
+
56
+ URIREGEX = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/
57
+
57
58
  ##
58
59
  # Returns a URI object based on the parsed string.
59
60
  #
@@ -67,7 +68,7 @@ module Addressable
67
68
  # If we were given nil, return nil.
68
69
  return nil unless uri
69
70
  # If a URI object is passed, just return itself.
70
- return uri if uri.kind_of?(self)
71
+ return uri.dup if uri.kind_of?(self)
71
72
 
72
73
  # If a URI object of the Ruby standard library variety is passed,
73
74
  # convert it to a string, then parse the string.
@@ -77,16 +78,15 @@ module Addressable
77
78
  uri = uri.to_s
78
79
  end
79
80
 
80
- if !uri.respond_to?(:to_str)
81
- raise TypeError, "Can't convert #{uri.class} into String."
82
- end
83
81
  # Otherwise, convert to a String
84
- uri = uri.to_str
82
+ begin
83
+ uri = uri.to_str
84
+ rescue TypeError, NoMethodError
85
+ raise TypeError, "Can't convert #{uri.class} into String."
86
+ end if not uri.is_a? String
85
87
 
86
88
  # This Regexp supplied as an example in RFC 3986, and it works great.
87
- uri_regex =
88
- /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/
89
- scan = uri.scan(uri_regex)
89
+ scan = uri.scan(URIREGEX)
90
90
  fragments = scan[0]
91
91
  scheme = fragments[1]
92
92
  authority = fragments[3]
@@ -104,10 +104,10 @@ module Addressable
104
104
  user = userinfo.strip[/^([^:]*):?/, 1]
105
105
  password = userinfo.strip[/:(.*)$/, 1]
106
106
  end
107
- host = authority.gsub(/^([^\[\]]*)@/, "").gsub(/:([^:@\[\]]*?)$/, "")
107
+ host = authority.gsub(/^([^\[\]]*)@/, EMPTYSTR).gsub(/:([^:@\[\]]*?)$/, EMPTYSTR)
108
108
  port = authority[/:([^:@\[\]]*?)$/, 1]
109
109
  end
110
- if port == ""
110
+ if port == EMPTYSTR
111
111
  port = nil
112
112
  end
113
113
 
@@ -141,7 +141,7 @@ module Addressable
141
141
  # If we were given nil, return nil.
142
142
  return nil unless uri
143
143
  # If a URI object is passed, just return itself.
144
- return uri if uri.kind_of?(self)
144
+ return uri.dup if uri.kind_of?(self)
145
145
  if !uri.respond_to?(:to_str)
146
146
  raise TypeError, "Can't convert #{uri.class} into String."
147
147
  end
@@ -169,7 +169,7 @@ module Addressable
169
169
  if new_host
170
170
  parsed.defer_validation do
171
171
  new_path = parsed.path.gsub(
172
- Regexp.new("^" + Regexp.escape(new_host)), "")
172
+ Regexp.new("^" + Regexp.escape(new_host)), EMPTYSTR)
173
173
  parsed.host = new_host
174
174
  parsed.path = new_path
175
175
  parsed.scheme = hints[:scheme] unless parsed.scheme
@@ -219,8 +219,8 @@ module Addressable
219
219
  # Otherwise, convert to a String
220
220
  path = path.to_str.strip
221
221
 
222
- path.gsub!(/^file:\/?\/?/, "") if path =~ /^file:\/?\/?/
223
- path = "/" + path if path =~ /^([a-zA-Z])[\|:]/
222
+ path.gsub!(/^file:\/?\/?/, EMPTYSTR) if path =~ /^file:\/?\/?/
223
+ path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/
224
224
  uri = self.parse(path)
225
225
 
226
226
  if uri.scheme == nil
@@ -228,17 +228,17 @@ module Addressable
228
228
  uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
229
229
  "/#{$1.downcase}:/"
230
230
  end
231
- uri.path.gsub!(/\\/, "/")
231
+ uri.path.gsub!(/\\/, SLASH)
232
232
  if File.exists?(uri.path) &&
233
233
  File.stat(uri.path).directory?
234
- uri.path.gsub!(/\/$/, "")
234
+ uri.path.gsub!(/\/$/, EMPTYSTR)
235
235
  uri.path = uri.path + '/'
236
236
  end
237
237
 
238
238
  # If the path is absolute, set the scheme and host.
239
239
  if uri.path =~ /^\//
240
240
  uri.scheme = "file"
241
- uri.host = ""
241
+ uri.host = EMPTYSTR
242
242
  end
243
243
  uri.normalize!
244
244
  end
@@ -305,10 +305,13 @@ module Addressable
305
305
  def self.encode_component(component, character_class=
306
306
  CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
307
307
  return nil if component.nil?
308
- if !component.respond_to?(:to_str)
308
+
309
+ begin
310
+ component = component.to_str
311
+ rescue TypeError, NoMethodError
309
312
  raise TypeError, "Can't convert #{component.class} into String."
310
- end
311
- component = component.to_str
313
+ end if !component.is_a? String
314
+
312
315
  if ![String, Regexp].include?(character_class.class)
313
316
  raise TypeError,
314
317
  "Expected String or Regexp, got #{character_class.inspect}"
@@ -323,7 +326,7 @@ module Addressable
323
326
  component.force_encoding(Encoding::ASCII_8BIT)
324
327
  end
325
328
  return component.gsub(character_class) do |sequence|
326
- (sequence.unpack('C*').map { |c| "%" + ("%02x" % c).upcase }).join("")
329
+ (sequence.unpack('C*').map { |c| "%" + ("%02x" % c).upcase }).join
327
330
  end
328
331
  end
329
332
 
@@ -351,15 +354,18 @@ module Addressable
351
354
  # The return type is determined by the <code>returning</code> parameter.
352
355
  def self.unencode(uri, returning=String)
353
356
  return nil if uri.nil?
354
- if !uri.respond_to?(:to_str)
357
+
358
+ begin
359
+ uri = uri.to_str
360
+ rescue NoMethodError, TypeError
355
361
  raise TypeError, "Can't convert #{uri.class} into String."
356
- end
362
+ end if !uri.is_a? String
357
363
  if ![String, ::Addressable::URI].include?(returning)
358
364
  raise TypeError,
359
365
  "Expected Class (String or Addressable::URI), " +
360
366
  "got #{returning.inspect}"
361
367
  end
362
- result = uri.to_str.gsub(/%[0-9a-f]{2}/i) do |sequence|
368
+ result = uri.gsub(/%[0-9a-f]{2}/i) do |sequence|
363
369
  sequence[1..3].to_i(16).chr
364
370
  end
365
371
  result.force_encoding("utf-8") if result.respond_to?(:force_encoding)
@@ -387,10 +393,10 @@ module Addressable
387
393
  # is passed, the <code>String</code> must be formatted as a regular
388
394
  # expression character class. (Do not include the surrounding square
389
395
  # brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
390
- # everything but the letters 'b' through 'z' and the numbers '0' through
391
- # '9' to be percent encoded. If a <code>Regexp</code> is passed, the
392
- # value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A set of
393
- # useful <code>String</code> values may be found in the
396
+ # everything but the letters 'b' through 'z' and the numbers '0'
397
+ # through '9' to be percent encoded. If a <code>Regexp</code> is passed,
398
+ # the value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A
399
+ # set of useful <code>String</code> values may be found in the
394
400
  # <code>Addressable::URI::CharacterClasses</code> module. The default
395
401
  # value is the reserved plus unreserved character classes specified in
396
402
  # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
@@ -412,10 +418,13 @@ module Addressable
412
418
  def self.normalize_component(component, character_class=
413
419
  CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
414
420
  return nil if component.nil?
415
- if !component.respond_to?(:to_str)
421
+
422
+ begin
423
+ component = component.to_str
424
+ rescue NoMethodError, TypeError
416
425
  raise TypeError, "Can't convert #{component.class} into String."
417
- end
418
- component = component.to_str
426
+ end if !component.is_a? String
427
+
419
428
  if ![String, Regexp].include?(character_class.class)
420
429
  raise TypeError,
421
430
  "Expected String or Regexp, got #{character_class.inspect}"
@@ -458,15 +467,19 @@ module Addressable
458
467
  # The return type is determined by the <code>returning</code> parameter.
459
468
  def self.encode(uri, returning=String)
460
469
  return nil if uri.nil?
461
- if !uri.respond_to?(:to_str)
470
+
471
+ begin
472
+ uri = uri.to_str
473
+ rescue NoMethodError, TypeError
462
474
  raise TypeError, "Can't convert #{uri.class} into String."
463
- end
475
+ end if !uri.is_a? String
476
+
464
477
  if ![String, ::Addressable::URI].include?(returning)
465
478
  raise TypeError,
466
479
  "Expected Class (String or Addressable::URI), " +
467
480
  "got #{returning.inspect}"
468
481
  end
469
- uri_object = uri.kind_of?(self) ? uri : self.parse(uri.to_str)
482
+ uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
470
483
  encoded_uri = Addressable::URI.new(
471
484
  :scheme => self.encode_component(uri_object.scheme,
472
485
  Addressable::URI::CharacterClasses::SCHEME),
@@ -507,15 +520,18 @@ module Addressable
507
520
  # The encoded URI.
508
521
  # The return type is determined by the <code>returning</code> parameter.
509
522
  def self.normalized_encode(uri, returning=String)
510
- if !uri.respond_to?(:to_str)
523
+ begin
524
+ uri = uri.to_str
525
+ rescue NoMethodError, TypeError
511
526
  raise TypeError, "Can't convert #{uri.class} into String."
512
- end
527
+ end if !uri.is_a? String
528
+
513
529
  if ![String, ::Addressable::URI].include?(returning)
514
530
  raise TypeError,
515
531
  "Expected Class (String or Addressable::URI), " +
516
532
  "got #{returning.inspect}"
517
533
  end
518
- uri_object = uri.kind_of?(self) ? uri : self.parse(uri.to_str)
534
+ uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
519
535
  components = {
520
536
  :scheme => self.unencode_component(uri_object.scheme),
521
537
  :user => self.unencode_component(uri_object.user),
@@ -719,9 +735,6 @@ module Addressable
719
735
  #
720
736
  # @param [String, #to_str] new_scheme The new scheme component.
721
737
  def scheme=(new_scheme)
722
- # Check for frozenness
723
- raise TypeError, "Can't modify frozen URI." if self.frozen?
724
-
725
738
  if new_scheme && !new_scheme.respond_to?(:to_str)
726
739
  raise TypeError, "Can't convert #{new_scheme.class} into String."
727
740
  elsif new_scheme
@@ -731,7 +744,7 @@ module Addressable
731
744
  raise InvalidURIError, "Invalid scheme format."
732
745
  end
733
746
  @scheme = new_scheme
734
- @scheme = nil if @scheme.to_s.strip == ""
747
+ @scheme = nil if @scheme.to_s.strip.empty?
735
748
 
736
749
  # Reset dependant values
737
750
  @normalized_scheme = nil
@@ -757,8 +770,8 @@ module Addressable
757
770
  def normalized_user
758
771
  @normalized_user ||= (begin
759
772
  if self.user
760
- if normalized_scheme =~ /https?/ && self.user.strip == "" &&
761
- (!self.password || self.password.strip == "")
773
+ if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
774
+ (!self.password || self.password.strip.empty?)
762
775
  nil
763
776
  else
764
777
  Addressable::URI.normalize_component(
@@ -777,9 +790,6 @@ module Addressable
777
790
  #
778
791
  # @param [String, #to_str] new_user The new user component.
779
792
  def user=(new_user)
780
- # Check for frozenness
781
- raise TypeError, "Can't modify frozen URI." if self.frozen?
782
-
783
793
  if new_user && !new_user.respond_to?(:to_str)
784
794
  raise TypeError, "Can't convert #{new_user.class} into String."
785
795
  end
@@ -788,7 +798,7 @@ module Addressable
788
798
  # You can't have a nil user with a non-nil password
789
799
  @password ||= nil
790
800
  if @password != nil
791
- @user = "" if @user.nil?
801
+ @user = EMPTYSTR if @user.nil?
792
802
  end
793
803
 
794
804
  # Reset dependant values
@@ -818,8 +828,8 @@ module Addressable
818
828
  def normalized_password
819
829
  @normalized_password ||= (begin
820
830
  if self.password
821
- if normalized_scheme =~ /https?/ && self.password.strip == "" &&
822
- (!self.user || self.user.strip == "")
831
+ if normalized_scheme =~ /https?/ && self.password.strip.empty? &&
832
+ (!self.user || self.user.strip.empty?)
823
833
  nil
824
834
  else
825
835
  Addressable::URI.normalize_component(
@@ -838,9 +848,6 @@ module Addressable
838
848
  #
839
849
  # @param [String, #to_str] new_password The new password component.
840
850
  def password=(new_password)
841
- # Check for frozenness
842
- raise TypeError, "Can't modify frozen URI." if self.frozen?
843
-
844
851
  if new_password && !new_password.respond_to?(:to_str)
845
852
  raise TypeError, "Can't convert #{new_password.class} into String."
846
853
  end
@@ -850,7 +857,7 @@ module Addressable
850
857
  @password ||= nil
851
858
  @user ||= nil
852
859
  if @password != nil
853
- @user = "" if @user.nil?
860
+ @user = EMPTYSTR if @user.nil?
854
861
  end
855
862
 
856
863
  # Reset dependant values
@@ -907,9 +914,6 @@ module Addressable
907
914
  #
908
915
  # @param [String, #to_str] new_userinfo The new userinfo component.
909
916
  def userinfo=(new_userinfo)
910
- # Check for frozenness
911
- raise TypeError, "Can't modify frozen URI." if self.frozen?
912
-
913
917
  if new_userinfo && !new_userinfo.respond_to?(:to_str)
914
918
  raise TypeError, "Can't convert #{new_userinfo.class} into String."
915
919
  end
@@ -950,9 +954,9 @@ module Addressable
950
954
  def normalized_host
951
955
  @normalized_host ||= (begin
952
956
  if self.host != nil
953
- if self.host.strip != ""
957
+ if !self.host.strip.empty?
954
958
  result = ::Addressable::IDNA.to_ascii(
955
- self.class.unencode_component(self.host.strip.downcase)
959
+ URI.unencode_component(self.host.strip.downcase)
956
960
  )
957
961
  if result[-1..-1] == "."
958
962
  # Trailing dots are unnecessary
@@ -960,7 +964,7 @@ module Addressable
960
964
  end
961
965
  result
962
966
  else
963
- ""
967
+ EMPTYSTR
964
968
  end
965
969
  else
966
970
  nil
@@ -973,9 +977,6 @@ module Addressable
973
977
  #
974
978
  # @param [String, #to_str] new_host The new host component.
975
979
  def host=(new_host)
976
- # Check for frozenness
977
- raise TypeError, "Can't modify frozen URI." if self.frozen?
978
-
979
980
  if new_host && !new_host.respond_to?(:to_str)
980
981
  raise TypeError, "Can't convert #{new_host.class} into String."
981
982
  end
@@ -1041,9 +1042,6 @@ module Addressable
1041
1042
  #
1042
1043
  # @param [String, #to_str] new_authority The new authority component.
1043
1044
  def authority=(new_authority)
1044
- # Check for frozenness
1045
- raise TypeError, "Can't modify frozen URI." if self.frozen?
1046
-
1047
1045
  if new_authority
1048
1046
  if !new_authority.respond_to?(:to_str)
1049
1047
  raise TypeError, "Can't convert #{new_authority.class} into String."
@@ -1055,7 +1053,7 @@ module Addressable
1055
1053
  new_password = new_userinfo.strip[/:(.*)$/, 1]
1056
1054
  end
1057
1055
  new_host =
1058
- new_authority.gsub(/^([^\[\]]*)@/, "").gsub(/:([^:@\[\]]*?)$/, "")
1056
+ new_authority.gsub(/^([^\[\]]*)@/, EMPTYSTR).gsub(/:([^:@\[\]]*?)$/, EMPTYSTR)
1059
1057
  new_port =
1060
1058
  new_authority[/:([^:@\[\]]*?)$/, 1]
1061
1059
  end
@@ -1141,7 +1139,7 @@ module Addressable
1141
1139
  # @return [Integer] The port component, normalized.
1142
1140
  def normalized_port
1143
1141
  @normalized_port ||= (begin
1144
- if self.class.port_mapping[normalized_scheme] == self.port
1142
+ if URI.port_mapping[normalized_scheme] == self.port
1145
1143
  nil
1146
1144
  else
1147
1145
  self.port
@@ -1154,9 +1152,6 @@ module Addressable
1154
1152
  #
1155
1153
  # @param [String, Integer, #to_s] new_port The new port component.
1156
1154
  def port=(new_port)
1157
- # Check for frozenness
1158
- raise TypeError, "Can't modify frozen URI." if self.frozen?
1159
-
1160
1155
  if new_port != nil && new_port.respond_to?(:to_str)
1161
1156
  new_port = Addressable::URI.unencode_component(new_port.to_str)
1162
1157
  end
@@ -1189,7 +1184,7 @@ module Addressable
1189
1184
  @inferred_port ||= (begin
1190
1185
  if port.to_i == 0
1191
1186
  if scheme
1192
- self.class.port_mapping[scheme.strip.downcase]
1187
+ URI.port_mapping[scheme.strip.downcase]
1193
1188
  else
1194
1189
  nil
1195
1190
  end
@@ -1274,33 +1269,35 @@ module Addressable
1274
1269
  #
1275
1270
  # @return [String] The path component.
1276
1271
  def path
1277
- @path ||= ""
1272
+ @path ||= EMPTYSTR
1278
1273
  return @path
1279
1274
  end
1280
1275
 
1276
+ NORMPATH = /^(?!\/)[^\/:]*:.*$/
1281
1277
  ##
1282
1278
  # The path component for this URI, normalized.
1283
1279
  #
1284
1280
  # @return [String] The path component, normalized.
1285
1281
  def normalized_path
1286
1282
  @normalized_path ||= (begin
1287
- if self.scheme == nil && self.path != nil && self.path != "" &&
1288
- self.path =~ /^(?!\/)[^\/:]*:.*$/
1283
+ if self.scheme == nil && self.path != nil && !self.path.empty? &&
1284
+ self.path =~ NORMPATH
1289
1285
  # Relative paths with colons in the first segment are ambiguous.
1290
1286
  self.path.sub!(":", "%2F")
1291
1287
  end
1292
1288
  # String#split(delimeter, -1) uses the more strict splitting behavior
1293
1289
  # found by default in Python.
1294
- result = (self.path.strip.split("/", -1).map do |segment|
1290
+ result = (self.path.strip.split(SLASH, -1).map do |segment|
1295
1291
  Addressable::URI.normalize_component(
1296
1292
  segment,
1297
1293
  Addressable::URI::CharacterClasses::PCHAR
1298
1294
  )
1299
- end).join("/")
1300
- result = self.class.normalize_path(result)
1301
- if result == "" &&
1295
+ end).join(SLASH)
1296
+
1297
+ result = URI.normalize_path(result)
1298
+ if result.empty? &&
1302
1299
  ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
1303
- result = "/"
1300
+ result = SLASH
1304
1301
  end
1305
1302
  result
1306
1303
  end)
@@ -1311,14 +1308,11 @@ module Addressable
1311
1308
  #
1312
1309
  # @param [String, #to_str] new_path The new path component.
1313
1310
  def path=(new_path)
1314
- # Check for frozenness
1315
- raise TypeError, "Can't modify frozen URI." if self.frozen?
1316
-
1317
1311
  if new_path && !new_path.respond_to?(:to_str)
1318
1312
  raise TypeError, "Can't convert #{new_path.class} into String."
1319
1313
  end
1320
- @path = (new_path || "").to_str
1321
- if @path != "" && @path[0..0] != "/" && host != nil
1314
+ @path = (new_path || EMPTYSTR).to_str
1315
+ if !@path.empty? && @path[0..0] != SLASH && host != nil
1322
1316
  @path = "/#{@path}"
1323
1317
  end
1324
1318
 
@@ -1334,7 +1328,7 @@ module Addressable
1334
1328
  # @return [String] The path's basename.
1335
1329
  def basename
1336
1330
  # Path cannot be nil
1337
- return File.basename(self.path).gsub(/;[^\/]*$/, "")
1331
+ return File.basename(self.path).gsub(/;[^\/]*$/, EMPTYSTR)
1338
1332
  end
1339
1333
 
1340
1334
  ##
@@ -1362,10 +1356,12 @@ module Addressable
1362
1356
  def normalized_query
1363
1357
  @normalized_query ||= (begin
1364
1358
  if self.query
1365
- Addressable::URI.normalize_component(
1366
- self.query.strip,
1367
- Addressable::URI::CharacterClasses::QUERY
1368
- )
1359
+ (self.query.split("&", -1).map do |pair|
1360
+ Addressable::URI.normalize_component(
1361
+ pair,
1362
+ Addressable::URI::CharacterClasses::QUERY.sub("\\&", "")
1363
+ )
1364
+ end).join("&")
1369
1365
  else
1370
1366
  nil
1371
1367
  end
@@ -1377,9 +1373,6 @@ module Addressable
1377
1373
  #
1378
1374
  # @param [String, #to_str] new_query The new query component.
1379
1375
  def query=(new_query)
1380
- # Check for frozenness
1381
- raise TypeError, "Can't modify frozen URI." if self.frozen?
1382
-
1383
1376
  if new_query && !new_query.respond_to?(:to_str)
1384
1377
  raise TypeError, "Can't convert #{new_query.class} into String."
1385
1378
  end
@@ -1450,12 +1443,12 @@ module Addressable
1450
1443
  return nil if self.query == nil
1451
1444
  empty_accumulator = :flat_array == options[:notation] ? [] : {}
1452
1445
  return ((self.query.split("&").map do |pair|
1453
- pair.split("=", 2) if pair && pair != ""
1446
+ pair.split("=", 2) if pair && !pair.empty?
1454
1447
  end).compact.inject(empty_accumulator.dup) do |accumulator, (key, value)|
1455
1448
  value = true if value.nil?
1456
- key = self.class.unencode_component(key)
1449
+ key = URI.unencode_component(key)
1457
1450
  if value != true
1458
- value = self.class.unencode_component(value.gsub(/\+/, " "))
1451
+ value = URI.unencode_component(value.gsub(/\+/, " "))
1459
1452
  end
1460
1453
  if options[:notation] == :flat
1461
1454
  if accumulator[key]
@@ -1503,8 +1496,6 @@ module Addressable
1503
1496
  #
1504
1497
  # @param [Hash, #to_hash, Array] new_query_values The new query values.
1505
1498
  def query_values=(new_query_values)
1506
- # Check for frozenness
1507
- raise TypeError, "Can't modify frozen URI." if self.frozen?
1508
1499
  if new_query_values == nil
1509
1500
  self.query = nil
1510
1501
  return nil
@@ -1531,7 +1522,7 @@ module Addressable
1531
1522
  stack = []
1532
1523
  e = lambda do |component|
1533
1524
  component = component.to_s if component.kind_of?(Symbol)
1534
- self.class.encode_component(component, CharacterClasses::UNRESERVED)
1525
+ URI.encode_component(component, CharacterClasses::UNRESERVED)
1535
1526
  end
1536
1527
  new_query_values.each do |key, value|
1537
1528
  if value.kind_of?(Hash)
@@ -1569,8 +1560,8 @@ module Addressable
1569
1560
  def request_uri
1570
1561
  return nil if self.absolute? && self.scheme !~ /^https?$/
1571
1562
  return (
1572
- (self.path != "" ? self.path : "/") +
1573
- (self.query ? "?#{self.query}" : "")
1563
+ (!self.path.empty? ? self.path : SLASH) +
1564
+ (self.query ? "?#{self.query}" : EMPTYSTR)
1574
1565
  )
1575
1566
  end
1576
1567
 
@@ -1590,7 +1581,7 @@ module Addressable
1590
1581
  path_component = new_request_uri[/^([^\?]*)\?(?:.*)$/, 1]
1591
1582
  query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
1592
1583
  path_component = path_component.to_s
1593
- path_component = (path_component != "" ? path_component : "/")
1584
+ path_component = (!path_component.empty? ? path_component : SLASH)
1594
1585
  self.path = path_component
1595
1586
  self.query = query_component
1596
1587
 
@@ -1629,9 +1620,6 @@ module Addressable
1629
1620
  #
1630
1621
  # @param [String, #to_str] new_fragment The new fragment component.
1631
1622
  def fragment=(new_fragment)
1632
- # Check for frozenness
1633
- raise TypeError, "Can't modify frozen URI." if self.frozen?
1634
-
1635
1623
  if new_fragment && !new_fragment.respond_to?(:to_str)
1636
1624
  raise TypeError, "Can't convert #{new_fragment.class} into String."
1637
1625
  end
@@ -1654,7 +1642,7 @@ module Addressable
1654
1642
  # <code>false</code> otherwise.
1655
1643
  def ip_based?
1656
1644
  if self.scheme
1657
- return self.class.ip_based_schemes.include?(
1645
+ return URI.ip_based_schemes.include?(
1658
1646
  self.scheme.strip.downcase)
1659
1647
  end
1660
1648
  return false
@@ -1690,11 +1678,11 @@ module Addressable
1690
1678
  if !uri.respond_to?(:to_str)
1691
1679
  raise TypeError, "Can't convert #{uri.class} into String."
1692
1680
  end
1693
- if !uri.kind_of?(self.class)
1681
+ if !uri.kind_of?(URI)
1694
1682
  # Otherwise, convert to a String, then parse.
1695
- uri = self.class.parse(uri.to_str)
1683
+ uri = URI.parse(uri.to_str)
1696
1684
  end
1697
- if uri.to_s == ""
1685
+ if uri.to_s.empty?
1698
1686
  return self.dup
1699
1687
  end
1700
1688
 
@@ -1714,7 +1702,7 @@ module Addressable
1714
1702
  joined_password = uri.password
1715
1703
  joined_host = uri.host
1716
1704
  joined_port = uri.port
1717
- joined_path = self.class.normalize_path(uri.path)
1705
+ joined_path = URI.normalize_path(uri.path)
1718
1706
  joined_query = uri.query
1719
1707
  else
1720
1708
  if uri.authority != nil
@@ -1722,10 +1710,10 @@ module Addressable
1722
1710
  joined_password = uri.password
1723
1711
  joined_host = uri.host
1724
1712
  joined_port = uri.port
1725
- joined_path = self.class.normalize_path(uri.path)
1713
+ joined_path = URI.normalize_path(uri.path)
1726
1714
  joined_query = uri.query
1727
1715
  else
1728
- if uri.path == nil || uri.path == ""
1716
+ if uri.path == nil || uri.path.empty?
1729
1717
  joined_path = self.path
1730
1718
  if uri.query != nil
1731
1719
  joined_query = uri.query
@@ -1733,29 +1721,29 @@ module Addressable
1733
1721
  joined_query = self.query
1734
1722
  end
1735
1723
  else
1736
- if uri.path[0..0] == "/"
1737
- joined_path = self.class.normalize_path(uri.path)
1724
+ if uri.path[0..0] == SLASH
1725
+ joined_path = URI.normalize_path(uri.path)
1738
1726
  else
1739
1727
  base_path = self.path.dup
1740
- base_path = "" if base_path == nil
1741
- base_path = self.class.normalize_path(base_path)
1728
+ base_path = EMPTYSTR if base_path == nil
1729
+ base_path = URI.normalize_path(base_path)
1742
1730
 
1743
1731
  # Section 5.2.3 of RFC 3986
1744
1732
  #
1745
1733
  # Removes the right-most path segment from the base path.
1746
1734
  if base_path =~ /\//
1747
- base_path.gsub!(/\/[^\/]+$/, "/")
1735
+ base_path.gsub!(/\/[^\/]+$/, SLASH)
1748
1736
  else
1749
- base_path = ""
1737
+ base_path = EMPTYSTR
1750
1738
  end
1751
1739
 
1752
1740
  # If the base path is empty and an authority segment has been
1753
- # defined, use a base path of "/"
1754
- if base_path == "" && self.authority != nil
1755
- base_path = "/"
1741
+ # defined, use a base path of SLASH
1742
+ if base_path.empty? && self.authority != nil
1743
+ base_path = SLASH
1756
1744
  end
1757
1745
 
1758
- joined_path = self.class.normalize_path(base_path + uri.path)
1746
+ joined_path = URI.normalize_path(base_path + uri.path)
1759
1747
  end
1760
1748
  joined_query = uri.query
1761
1749
  end
@@ -1883,7 +1871,7 @@ module Addressable
1883
1871
  # @return [Addressable::URI]
1884
1872
  # The normalized relative URI that is equivalent to the original URI.
1885
1873
  def route_from(uri)
1886
- uri = self.class.parse(uri).normalize
1874
+ uri = URI.parse(uri).normalize
1887
1875
  normalized_self = self.normalize
1888
1876
  if normalized_self.relative?
1889
1877
  raise ArgumentError, "Expected absolute URI, got: #{self.to_s}"
@@ -1908,9 +1896,9 @@ module Addressable
1908
1896
  components[:query] = nil
1909
1897
  end
1910
1898
  else
1911
- if uri.path != "/"
1899
+ if uri.path != SLASH
1912
1900
  components[:path].gsub!(
1913
- Regexp.new("^" + Regexp.escape(uri.path)), "")
1901
+ Regexp.new("^" + Regexp.escape(uri.path)), EMPTYSTR)
1914
1902
  end
1915
1903
  end
1916
1904
  end
@@ -1941,7 +1929,7 @@ module Addressable
1941
1929
  # @return [Addressable::URI]
1942
1930
  # The normalized relative URI that is equivalent to the supplied URI.
1943
1931
  def route_to(uri)
1944
- return self.class.parse(uri).route_from(self)
1932
+ return URI.parse(uri).route_from(self)
1945
1933
  end
1946
1934
 
1947
1935
  ##
@@ -1959,7 +1947,7 @@ module Addressable
1959
1947
  # URI scheme.
1960
1948
  if normalized_scheme == "feed"
1961
1949
  if self.to_s =~ /^feed:\/*http:\/*/
1962
- return self.class.parse(
1950
+ return URI.parse(
1963
1951
  self.to_s[/^feed:\/*(http:\/*.*)/, 1]
1964
1952
  ).normalize
1965
1953
  end
@@ -2030,7 +2018,7 @@ module Addressable
2030
2018
  # <code>true</code> if the URIs are equivalent, <code>false</code>
2031
2019
  # otherwise.
2032
2020
  def ==(uri)
2033
- return false unless uri.kind_of?(self.class)
2021
+ return false unless uri.kind_of?(URI)
2034
2022
  return self.normalize.to_s == uri.normalize.to_s
2035
2023
  end
2036
2024
 
@@ -2044,7 +2032,7 @@ module Addressable
2044
2032
  # <code>true</code> if the URIs are equivalent, <code>false</code>
2045
2033
  # otherwise.
2046
2034
  def eql?(uri)
2047
- return false unless uri.kind_of?(self.class)
2035
+ return false unless uri.kind_of?(URI)
2048
2036
  return self.to_s == uri.to_s
2049
2037
  end
2050
2038
 
@@ -2075,28 +2063,6 @@ module Addressable
2075
2063
  return duplicated_uri
2076
2064
  end
2077
2065
 
2078
- ##
2079
- # Freezes the URI object.
2080
- #
2081
- # @return [Addressable::URI] The frozen URI.
2082
- def freeze
2083
- # Unfortunately, because of the memoized implementation of many of the
2084
- # URI methods, the default freeze method will cause unexpected errors.
2085
- # As an alternative, we freeze the string representation of the URI
2086
- # instead. This should generally produce the desired effect.
2087
- self.to_s.freeze
2088
- return self
2089
- end
2090
-
2091
- ##
2092
- # Determines if the URI is frozen.
2093
- #
2094
- # @return [TrueClass, FalseClass]
2095
- # <code>true</code> if the URI is frozen, <code>false</code> otherwise.
2096
- def frozen?
2097
- self.to_s.frozen?
2098
- end
2099
-
2100
2066
  ##
2101
2067
  # Omits components from a URI.
2102
2068
  #
@@ -2185,7 +2151,7 @@ module Addressable
2185
2151
  #
2186
2152
  # @return [String] The URI object's state, as a <code>String</code>.
2187
2153
  def inspect
2188
- sprintf("#<%s:%#0x URI:%s>", self.class.to_s, self.object_id, self.to_s)
2154
+ sprintf("#<%s:%#0x URI:%s>", URI.to_s, self.object_id, self.to_s)
2189
2155
  end
2190
2156
 
2191
2157
  ##
@@ -2212,30 +2178,34 @@ module Addressable
2212
2178
  # @param [String] path The path to normalize.
2213
2179
  #
2214
2180
  # @return [String] The normalized path.
2181
+
2182
+ PARENT1 = '.'
2183
+ PARENT2 = '..'
2184
+
2185
+ NPATH1 = /\/\.\/|\/\.$/
2186
+ NPATH2 = /\/([^\/]+)\/\.\.\/|\/([^\/]+)\/\.\.$/
2187
+ NPATH3 = /^\.\.?\/?/
2188
+ NPATH4 = /^\/\.\.?\/|^(\/\.\.?)+\/?$/
2189
+
2215
2190
  def self.normalize_path(path)
2216
2191
  # Section 5.2.4 of RFC 3986
2217
2192
 
2218
2193
  return nil if path.nil?
2219
2194
  normalized_path = path.dup
2220
- previous_state = normalized_path.dup
2221
2195
  begin
2222
- previous_state = normalized_path.dup
2223
- normalized_path.gsub!(/\/\.\//, "/")
2224
- normalized_path.gsub!(/\/\.$/, "/")
2225
- parent = normalized_path[/\/([^\/]+)\/\.\.\//, 1]
2226
- if parent != "." && parent != ".."
2227
- normalized_path.gsub!(/\/#{parent}\/\.\.\//, "/")
2228
- end
2229
- parent = normalized_path[/\/([^\/]+)\/\.\.$/, 1]
2230
- if parent != "." && parent != ".."
2231
- normalized_path.gsub!(/\/#{parent}\/\.\.$/, "/")
2196
+ mod = nil
2197
+ mod ||= normalized_path.gsub!(NPATH1, SLASH)
2198
+
2199
+ parent = normalized_path.match(NPATH2)
2200
+ if parent && ((parent[1] != PARENT1 && parent[1] != PARENT2) \
2201
+ || (parent[2] != PARENT1 && parent[2] != PARENT2))
2202
+ mod ||= normalized_path.gsub!(/\/#{parent[1]}\/\.\.\/|(\/#{parent[2]}\/\.\.$)/, SLASH)
2232
2203
  end
2233
- normalized_path.gsub!(/^\.\.?\/?/, "")
2234
- normalized_path.gsub!(/^\/\.\.?\//, "/")
2235
2204
 
2236
- # Non-standard
2237
- normalized_path.gsub!(/^(\/\.\.?)+\/?$/, "/")
2238
- end until previous_state == normalized_path
2205
+ mod ||= normalized_path.gsub!(NPATH3, EMPTYSTR)
2206
+ mod ||= normalized_path.gsub!(NPATH4, SLASH)
2207
+ end until mod.nil?
2208
+
2239
2209
  return normalized_path
2240
2210
  end
2241
2211
 
@@ -2244,8 +2214,8 @@ module Addressable
2244
2214
  def validate
2245
2215
  return if !!@validation_deferred
2246
2216
  if self.scheme != nil &&
2247
- (self.host == nil || self.host == "") &&
2248
- (self.path == nil || self.path == "")
2217
+ (self.host == nil || self.host.empty?) &&
2218
+ (self.path == nil || self.path.empty?)
2249
2219
  raise InvalidURIError,
2250
2220
  "Absolute URI missing hierarchical segment: '#{self.to_s}'"
2251
2221
  end
@@ -2256,7 +2226,7 @@ module Addressable
2256
2226
  raise InvalidURIError, "Hostname not supplied: '#{self.to_s}'"
2257
2227
  end
2258
2228
  end
2259
- if self.path != nil && self.path != "" && self.path[0..0] != "/" &&
2229
+ if self.path != nil && !self.path.empty? && self.path[0..0] != SLASH &&
2260
2230
  self.authority != nil
2261
2231
  raise InvalidURIError,
2262
2232
  "Cannot have a relative path with an authority set: '#{self.to_s}'"