addressable 2.2.4 → 2.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +25 -19
- data/LICENSE +202 -20
- data/README.md +76 -0
- data/lib/addressable/idna.rb +24 -4870
- data/lib/addressable/idna/native.rb +43 -0
- data/lib/addressable/idna/pure.rb +4886 -0
- data/lib/addressable/template.rb +40 -42
- data/lib/addressable/uri.rb +157 -187
- data/lib/addressable/version.rb +13 -19
- data/spec/addressable/idna_spec.rb +43 -22
- data/spec/addressable/template_spec.rb +11 -20
- data/spec/addressable/uri_spec.rb +92 -30
- data/tasks/gem.rake +2 -2
- data/tasks/rdoc.rake +1 -1
- data/tasks/spec.rake +5 -3
- data/tasks/yard.rake +4 -4
- metadata +11 -9
- data/README +0 -60
data/lib/addressable/template.rb
CHANGED
@@ -1,27 +1,21 @@
|
|
1
1
|
# encoding:utf-8
|
2
2
|
#--
|
3
|
-
#
|
3
|
+
# Copyright (C) 2006-2011 Bob Aman
|
4
4
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
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
|
-
#
|
14
|
-
# included in all copies or substantial portions of the Software.
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
15
10
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
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)
|
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
|
-
#
|
159
|
-
#
|
160
|
-
# two parameters: [String] name and [String] value
|
161
|
-
# method should reverse any transformations that
|
162
|
-
# value to ensure a valid URI.
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
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
|
-
#
|
170
|
-
#
|
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
|
-
#
|
219
|
-
#
|
220
|
-
# two parameters: [String] name and [String] value
|
221
|
-
# method should reverse any transformations that
|
222
|
-
# value to ensure a valid URI.
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
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
|
-
#
|
230
|
-
#
|
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
|
data/lib/addressable/uri.rb
CHANGED
@@ -1,30 +1,26 @@
|
|
1
1
|
# encoding:utf-8
|
2
2
|
#--
|
3
|
-
#
|
3
|
+
# Copyright (C) 2006-2011 Bob Aman
|
4
4
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
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
|
-
#
|
14
|
-
# included in all copies or substantial portions of the Software.
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
15
10
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
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
|
-
|
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
|
-
|
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(/^([^\[\]]*)@/,
|
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:\/?\/?/,
|
223
|
-
path =
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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'
|
391
|
-
#
|
392
|
-
# value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
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 =
|
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 =
|
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
|
-
|
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(/^([^\[\]]*)@/,
|
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
|
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
|
-
|
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(
|
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
|
-
|
1301
|
-
|
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 ||
|
1321
|
-
if
|
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
|
-
|
1366
|
-
|
1367
|
-
|
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 =
|
1449
|
+
key = URI.unencode_component(key)
|
1457
1450
|
if value != true
|
1458
|
-
value =
|
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
|
-
|
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
|
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
|
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
|
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?(
|
1681
|
+
if !uri.kind_of?(URI)
|
1694
1682
|
# Otherwise, convert to a String, then parse.
|
1695
|
-
uri =
|
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 =
|
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 =
|
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 =
|
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 =
|
1741
|
-
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
|
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 =
|
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 =
|
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
|
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
|
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?(
|
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?(
|
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>",
|
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
|
-
|
2223
|
-
normalized_path.gsub!(
|
2224
|
-
|
2225
|
-
parent = normalized_path
|
2226
|
-
if parent !=
|
2227
|
-
|
2228
|
-
|
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
|
-
|
2237
|
-
normalized_path.gsub!(
|
2238
|
-
end until
|
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
|
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}'"
|