addressable 2.2.4 → 2.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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}'"