addressable 2.1.1 → 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +13 -1
- data/lib/addressable/template.rb +6 -6
- data/lib/addressable/uri.rb +216 -134
- data/lib/addressable/version.rb +1 -1
- data/spec/addressable/template_spec.rb +9 -0
- data/spec/addressable/uri_spec.rb +255 -18
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -1,6 +1,18 @@
|
|
1
|
+
=== Addressable 2.1.2
|
2
|
+
* added HTTP request URI methods
|
3
|
+
* better handling of Windows file paths
|
4
|
+
* validation_deferred boolean replaced with defer_validation block
|
5
|
+
* normalization of percent-encoded paths should now be correct
|
6
|
+
* fixed issue with constructing URIs with relative paths
|
7
|
+
* fixed warnings
|
8
|
+
|
1
9
|
=== Addressable 2.1.1
|
2
|
-
*
|
10
|
+
* more type checking changes
|
3
11
|
* fixed issue with unicode normalization
|
12
|
+
* added method to find template defaults
|
13
|
+
* symbolic keys are now allowed in template mappings
|
14
|
+
* numeric values and symbolic values are now allowed in template mappings
|
15
|
+
|
4
16
|
=== Addressable 2.1.0
|
5
17
|
* refactored URI template support out into its own class
|
6
18
|
* removed extract method due to being useless and unreliable
|
data/lib/addressable/template.rb
CHANGED
@@ -495,13 +495,13 @@ module Addressable
|
|
495
495
|
#
|
496
496
|
# @return [Hash] Mapping of template variables to their defaults
|
497
497
|
def variable_defaults
|
498
|
-
@variable_defaults ||=
|
498
|
+
@variable_defaults ||=
|
499
|
+
Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten]
|
499
500
|
end
|
500
501
|
|
501
502
|
private
|
502
|
-
|
503
503
|
def ordered_variable_defaults
|
504
|
-
@ordered_variable_defaults ||= begin
|
504
|
+
@ordered_variable_defaults ||= (begin
|
505
505
|
expansions, expansion_regexp = parse_template_pattern(pattern)
|
506
506
|
|
507
507
|
expansions.inject([]) do |result, expansion|
|
@@ -514,7 +514,7 @@ module Addressable
|
|
514
514
|
end
|
515
515
|
result
|
516
516
|
end
|
517
|
-
end
|
517
|
+
end)
|
518
518
|
end
|
519
519
|
|
520
520
|
##
|
@@ -800,9 +800,9 @@ module Addressable
|
|
800
800
|
# @return [Array]
|
801
801
|
# A tuple of the operator, argument, variables, and mapping.
|
802
802
|
def parse_template_expansion(capture, mapping={})
|
803
|
-
operator, argument, variables = capture[1...-1].split("|")
|
803
|
+
operator, argument, variables = capture[1...-1].split("|", -1)
|
804
804
|
operator.gsub!(/^\-/, "")
|
805
|
-
variables = variables.split(",")
|
805
|
+
variables = variables.split(",", -1)
|
806
806
|
mapping = (variables.inject({}) do |accu, var|
|
807
807
|
varname, _, vardefault = var.scan(/^(.+?)(=(.*))?$/)[0]
|
808
808
|
accu[varname] = vardefault
|
data/lib/addressable/uri.rb
CHANGED
@@ -36,11 +36,6 @@ module Addressable
|
|
36
36
|
class InvalidURIError < StandardError
|
37
37
|
end
|
38
38
|
|
39
|
-
##
|
40
|
-
# Raised if an invalid method option is supplied.
|
41
|
-
class InvalidOptionError < StandardError
|
42
|
-
end
|
43
|
-
|
44
39
|
##
|
45
40
|
# Container for the character classes specified in
|
46
41
|
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
|
@@ -172,11 +167,13 @@ module Addressable
|
|
172
167
|
if parsed.path.include?(".")
|
173
168
|
new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
|
174
169
|
if new_host
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
170
|
+
parsed.defer_validation do
|
171
|
+
new_path = parsed.path.gsub(
|
172
|
+
Regexp.new("^" + Regexp.escape(new_host)), "")
|
173
|
+
parsed.host = new_host
|
174
|
+
parsed.path = new_path
|
175
|
+
parsed.scheme = hints[:scheme] unless parsed.scheme
|
176
|
+
end
|
180
177
|
end
|
181
178
|
end
|
182
179
|
return parsed
|
@@ -223,12 +220,14 @@ module Addressable
|
|
223
220
|
path = path.to_str.strip
|
224
221
|
|
225
222
|
path.gsub!(/^file:\/?\/?/, "") if path =~ /^file:\/?\/?/
|
226
|
-
path = "/" + path if path =~ /^([a-zA-Z])
|
223
|
+
path = "/" + path if path =~ /^([a-zA-Z])[\|:]/
|
227
224
|
uri = self.parse(path)
|
228
225
|
|
229
226
|
if uri.scheme == nil
|
230
227
|
# Adjust windows-style uris
|
231
|
-
uri.path.gsub!(/^\/?([a-zA-Z])
|
228
|
+
uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
|
229
|
+
"/#{$1.downcase}:/"
|
230
|
+
end
|
232
231
|
uri.path.gsub!(/\\/, "/")
|
233
232
|
if File.exists?(uri.path) &&
|
234
233
|
File.stat(uri.path).directory?
|
@@ -377,6 +376,71 @@ module Addressable
|
|
377
376
|
alias_method :unescape_component, :unencode
|
378
377
|
end
|
379
378
|
|
379
|
+
|
380
|
+
##
|
381
|
+
# Normalizes the encoding of a URI component.
|
382
|
+
#
|
383
|
+
# @param [String, #to_str] component The URI component to encode.
|
384
|
+
#
|
385
|
+
# @param [String, Regexp] character_class
|
386
|
+
#
|
387
|
+
# The characters which are not percent encoded. If a <tt>String</tt> is
|
388
|
+
# passed, the <tt>String</tt> must be formatted as a regular expression
|
389
|
+
# character class. (Do not include the surrounding square brackets.) For
|
390
|
+
# example, <tt>"b-zB-Z0-9"</tt> would cause everything but the letters 'b'
|
391
|
+
# through 'z' and the numbers '0' through '9' to be percent encoded. If a
|
392
|
+
# <tt>Regexp</tt> is passed, the value <tt>/[^b-zB-Z0-9]/</tt> would have
|
393
|
+
# the same effect. A set of useful <tt>String</tt> values may be found in
|
394
|
+
# the <tt>Addressable::URI::CharacterClasses</tt> module. The default
|
395
|
+
# value is the reserved plus unreserved character classes specified in <a
|
396
|
+
# href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
|
397
|
+
#
|
398
|
+
# @return [String] The normalized component.
|
399
|
+
#
|
400
|
+
# @example
|
401
|
+
# Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z")
|
402
|
+
# => "simple%2Fex%61mple"
|
403
|
+
# Addressable::URI.normalize_component(
|
404
|
+
# "simpl%65/%65xampl%65", /[^b-zB-Z]/
|
405
|
+
# )
|
406
|
+
# => "simple%2Fex%61mple"
|
407
|
+
# Addressable::URI.normalize_component(
|
408
|
+
# "simpl%65/%65xampl%65",
|
409
|
+
# Addressable::URI::CharacterClasses::UNRESERVED
|
410
|
+
# )
|
411
|
+
# => "simple%2Fexample"
|
412
|
+
def self.normalize_component(component, character_class=
|
413
|
+
CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
|
414
|
+
return nil if component.nil?
|
415
|
+
if !component.respond_to?(:to_str)
|
416
|
+
raise TypeError, "Can't convert #{component.class} into String."
|
417
|
+
end
|
418
|
+
component = component.to_str
|
419
|
+
if ![String, Regexp].include?(character_class.class)
|
420
|
+
raise TypeError,
|
421
|
+
"Expected String or Regexp, got #{character_class.inspect}"
|
422
|
+
end
|
423
|
+
if character_class.kind_of?(String)
|
424
|
+
character_class = /[^#{character_class}]/
|
425
|
+
end
|
426
|
+
if component.respond_to?(:force_encoding)
|
427
|
+
# We can't perform regexps on invalid UTF sequences, but
|
428
|
+
# here we need to, so switch to ASCII.
|
429
|
+
component = component.dup
|
430
|
+
component.force_encoding(Encoding::ASCII_8BIT)
|
431
|
+
end
|
432
|
+
unencoded = self.unencode_component(component)
|
433
|
+
begin
|
434
|
+
encoded = self.encode_component(
|
435
|
+
Addressable::IDNA.unicode_normalize_kc(unencoded),
|
436
|
+
character_class
|
437
|
+
)
|
438
|
+
rescue ArgumentError
|
439
|
+
encoded = self.encode_component(unencoded)
|
440
|
+
end
|
441
|
+
return encoded
|
442
|
+
end
|
443
|
+
|
380
444
|
##
|
381
445
|
# Percent encodes any special characters in the URI.
|
382
446
|
#
|
@@ -530,18 +594,20 @@ module Addressable
|
|
530
594
|
end
|
531
595
|
end
|
532
596
|
|
533
|
-
self.
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
597
|
+
self.defer_validation do
|
598
|
+
# Bunch of crazy logic required because of the composite components
|
599
|
+
# like userinfo and authority.
|
600
|
+
self.scheme = options[:scheme] if options[:scheme]
|
601
|
+
self.user = options[:user] if options[:user]
|
602
|
+
self.password = options[:password] if options[:password]
|
603
|
+
self.userinfo = options[:userinfo] if options[:userinfo]
|
604
|
+
self.host = options[:host] if options[:host]
|
605
|
+
self.port = options[:port] if options[:port]
|
606
|
+
self.authority = options[:authority] if options[:authority]
|
607
|
+
self.path = options[:path] if options[:path]
|
608
|
+
self.query = options[:query] if options[:query]
|
609
|
+
self.fragment = options[:fragment] if options[:fragment]
|
610
|
+
end
|
545
611
|
end
|
546
612
|
|
547
613
|
##
|
@@ -549,7 +615,7 @@ module Addressable
|
|
549
615
|
#
|
550
616
|
# @return [String] The scheme component.
|
551
617
|
def scheme
|
552
|
-
return @scheme
|
618
|
+
return @scheme ||= nil
|
553
619
|
end
|
554
620
|
|
555
621
|
##
|
@@ -562,10 +628,8 @@ module Addressable
|
|
562
628
|
if self.scheme =~ /^\s*ssh\+svn\s*$/i
|
563
629
|
"svn+ssh"
|
564
630
|
else
|
565
|
-
Addressable::URI.
|
566
|
-
|
567
|
-
Addressable::URI.unencode_component(
|
568
|
-
self.scheme.strip.downcase)),
|
631
|
+
Addressable::URI.normalize_component(
|
632
|
+
self.scheme.strip.downcase,
|
569
633
|
Addressable::URI::CharacterClasses::SCHEME
|
570
634
|
)
|
571
635
|
end
|
@@ -602,7 +666,7 @@ module Addressable
|
|
602
666
|
#
|
603
667
|
# @return [String] The user component.
|
604
668
|
def user
|
605
|
-
return @user
|
669
|
+
return @user ||= nil
|
606
670
|
end
|
607
671
|
|
608
672
|
##
|
@@ -616,9 +680,8 @@ module Addressable
|
|
616
680
|
(!self.password || self.password.strip == "")
|
617
681
|
nil
|
618
682
|
else
|
619
|
-
Addressable::URI.
|
620
|
-
|
621
|
-
Addressable::URI.unencode_component(self.user.strip)),
|
683
|
+
Addressable::URI.normalize_component(
|
684
|
+
self.user.strip,
|
622
685
|
Addressable::URI::CharacterClasses::UNRESERVED
|
623
686
|
)
|
624
687
|
end
|
@@ -663,7 +726,7 @@ module Addressable
|
|
663
726
|
#
|
664
727
|
# @return [String] The password component.
|
665
728
|
def password
|
666
|
-
return @password
|
729
|
+
return @password ||= nil
|
667
730
|
end
|
668
731
|
|
669
732
|
##
|
@@ -677,9 +740,8 @@ module Addressable
|
|
677
740
|
(!self.user || self.user.strip == "")
|
678
741
|
nil
|
679
742
|
else
|
680
|
-
Addressable::URI.
|
681
|
-
|
682
|
-
Addressable::URI.unencode_component(self.password.strip)),
|
743
|
+
Addressable::URI.normalize_component(
|
744
|
+
self.password.strip,
|
683
745
|
Addressable::URI::CharacterClasses::UNRESERVED
|
684
746
|
)
|
685
747
|
end
|
@@ -794,7 +856,7 @@ module Addressable
|
|
794
856
|
#
|
795
857
|
# @return [String] The host component.
|
796
858
|
def host
|
797
|
-
return @host
|
859
|
+
return @host ||= nil
|
798
860
|
end
|
799
861
|
|
800
862
|
##
|
@@ -1035,7 +1097,8 @@ module Addressable
|
|
1035
1097
|
#
|
1036
1098
|
# @return [String] The path component.
|
1037
1099
|
def path
|
1038
|
-
|
1100
|
+
@path ||= ""
|
1101
|
+
return @path
|
1039
1102
|
end
|
1040
1103
|
|
1041
1104
|
##
|
@@ -1044,19 +1107,19 @@ module Addressable
|
|
1044
1107
|
# @return [String] The path component, normalized.
|
1045
1108
|
def normalized_path
|
1046
1109
|
@normalized_path ||= (begin
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
Addressable::URI::CharacterClasses::PATH
|
1052
|
-
)
|
1053
|
-
rescue ArgumentError
|
1054
|
-
# Likely a malformed UTF-8 character, skip unicode normalization
|
1055
|
-
result = Addressable::URI.encode_component(
|
1056
|
-
Addressable::URI.unencode_component(self.path.strip),
|
1057
|
-
Addressable::URI::CharacterClasses::PATH
|
1058
|
-
)
|
1110
|
+
if self.scheme == nil && self.path != nil && self.path != "" &&
|
1111
|
+
self.path =~ /^(?!\/)[^\/:]*:.*$/
|
1112
|
+
# Relative paths with colons in the first segment are ambiguous.
|
1113
|
+
self.path.sub!(":", "%2F")
|
1059
1114
|
end
|
1115
|
+
# String#split(delimeter, -1) uses the more strict splitting behavior
|
1116
|
+
# found by default in Python.
|
1117
|
+
result = (self.path.strip.split("/", -1).map do |segment|
|
1118
|
+
Addressable::URI.normalize_component(
|
1119
|
+
segment,
|
1120
|
+
Addressable::URI::CharacterClasses::PCHAR
|
1121
|
+
)
|
1122
|
+
end).join("/")
|
1060
1123
|
result = self.class.normalize_path(result)
|
1061
1124
|
if result == "" &&
|
1062
1125
|
["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
|
@@ -1111,7 +1174,7 @@ module Addressable
|
|
1111
1174
|
#
|
1112
1175
|
# @return [String] The query component.
|
1113
1176
|
def query
|
1114
|
-
return @query
|
1177
|
+
return @query ||= nil
|
1115
1178
|
end
|
1116
1179
|
|
1117
1180
|
##
|
@@ -1121,19 +1184,10 @@ module Addressable
|
|
1121
1184
|
def normalized_query
|
1122
1185
|
@normalized_query ||= (begin
|
1123
1186
|
if self.query
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
Addressable::URI::CharacterClasses::QUERY
|
1129
|
-
)
|
1130
|
-
rescue ArgumentError
|
1131
|
-
# Likely a malformed UTF-8 character, skip unicode normalization
|
1132
|
-
Addressable::URI.encode_component(
|
1133
|
-
Addressable::URI.unencode_component(self.query.strip),
|
1134
|
-
Addressable::URI::CharacterClasses::QUERY
|
1135
|
-
)
|
1136
|
-
end
|
1187
|
+
Addressable::URI.normalize_component(
|
1188
|
+
self.query.strip,
|
1189
|
+
Addressable::URI::CharacterClasses::QUERY
|
1190
|
+
)
|
1137
1191
|
else
|
1138
1192
|
nil
|
1139
1193
|
end
|
@@ -1304,12 +1358,49 @@ module Addressable
|
|
1304
1358
|
@uri_string = nil
|
1305
1359
|
end
|
1306
1360
|
|
1361
|
+
##
|
1362
|
+
# The HTTP request URI for this URI. This is the path and the
|
1363
|
+
# query string.
|
1364
|
+
#
|
1365
|
+
# @return [String] The request URI required for an HTTP request.
|
1366
|
+
def request_uri
|
1367
|
+
return nil if self.absolute? && self.scheme !~ /^https?$/
|
1368
|
+
return (
|
1369
|
+
(self.path != "" ? self.path : "/") +
|
1370
|
+
(self.query ? "?#{self.query}" : "")
|
1371
|
+
)
|
1372
|
+
end
|
1373
|
+
|
1374
|
+
##
|
1375
|
+
# Sets the HTTP request URI for this URI.
|
1376
|
+
#
|
1377
|
+
# @param [String, #to_str] new_request_uri The new HTTP request URI.
|
1378
|
+
def request_uri=(new_request_uri)
|
1379
|
+
if !new_request_uri.respond_to?(:to_str)
|
1380
|
+
raise TypeError, "Can't convert #{new_request_uri.class} into String."
|
1381
|
+
end
|
1382
|
+
if self.absolute? && self.scheme !~ /^https?$/
|
1383
|
+
raise InvalidURIError,
|
1384
|
+
"Cannot set an HTTP request URI for a non-HTTP URI."
|
1385
|
+
end
|
1386
|
+
new_request_uri = new_request_uri.to_str
|
1387
|
+
path_component = new_request_uri[/^([^\?]*)\?(?:.*)$/, 1]
|
1388
|
+
query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
|
1389
|
+
path_component = path_component.to_s
|
1390
|
+
path_component = (path_component != "" ? path_component : "/")
|
1391
|
+
self.path = path_component
|
1392
|
+
self.query = query_component
|
1393
|
+
|
1394
|
+
# Reset dependant values
|
1395
|
+
@uri_string = nil
|
1396
|
+
end
|
1397
|
+
|
1307
1398
|
##
|
1308
1399
|
# The fragment component for this URI.
|
1309
1400
|
#
|
1310
1401
|
# @return [String] The fragment component.
|
1311
1402
|
def fragment
|
1312
|
-
return @fragment
|
1403
|
+
return @fragment ||= nil
|
1313
1404
|
end
|
1314
1405
|
|
1315
1406
|
##
|
@@ -1319,19 +1410,10 @@ module Addressable
|
|
1319
1410
|
def normalized_fragment
|
1320
1411
|
@normalized_fragment ||= (begin
|
1321
1412
|
if self.fragment
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
Addressable::URI::CharacterClasses::FRAGMENT
|
1327
|
-
)
|
1328
|
-
rescue ArgumentError
|
1329
|
-
# Likely a malformed UTF-8 character, skip unicode normalization
|
1330
|
-
Addressable::URI.encode_component(
|
1331
|
-
Addressable::URI.unencode_component(self.fragment.strip),
|
1332
|
-
Addressable::URI::CharacterClasses::FRAGMENT
|
1333
|
-
)
|
1334
|
-
end
|
1413
|
+
Addressable::URI.normalize_component(
|
1414
|
+
self.fragment.strip,
|
1415
|
+
Addressable::URI::CharacterClasses::FRAGMENT
|
1416
|
+
)
|
1335
1417
|
else
|
1336
1418
|
nil
|
1337
1419
|
end
|
@@ -1536,36 +1618,38 @@ module Addressable
|
|
1536
1618
|
end
|
1537
1619
|
|
1538
1620
|
uri = Addressable::URI.new
|
1539
|
-
uri.
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1621
|
+
uri.defer_validation do
|
1622
|
+
# Bunch of crazy logic required because of the composite components
|
1623
|
+
# like userinfo and authority.
|
1624
|
+
uri.scheme =
|
1625
|
+
hash.has_key?(:scheme) ? hash[:scheme] : self.scheme
|
1626
|
+
if hash.has_key?(:authority)
|
1627
|
+
uri.authority =
|
1628
|
+
hash.has_key?(:authority) ? hash[:authority] : self.authority
|
1629
|
+
end
|
1630
|
+
if hash.has_key?(:userinfo)
|
1631
|
+
uri.userinfo =
|
1632
|
+
hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo
|
1633
|
+
end
|
1634
|
+
if !hash.has_key?(:userinfo) && !hash.has_key?(:authority)
|
1635
|
+
uri.user =
|
1636
|
+
hash.has_key?(:user) ? hash[:user] : self.user
|
1637
|
+
uri.password =
|
1638
|
+
hash.has_key?(:password) ? hash[:password] : self.password
|
1639
|
+
end
|
1640
|
+
if !hash.has_key?(:authority)
|
1641
|
+
uri.host =
|
1642
|
+
hash.has_key?(:host) ? hash[:host] : self.host
|
1643
|
+
uri.port =
|
1644
|
+
hash.has_key?(:port) ? hash[:port] : self.port
|
1645
|
+
end
|
1646
|
+
uri.path =
|
1647
|
+
hash.has_key?(:path) ? hash[:path] : self.path
|
1648
|
+
uri.query =
|
1649
|
+
hash.has_key?(:query) ? hash[:query] : self.query
|
1650
|
+
uri.fragment =
|
1651
|
+
hash.has_key?(:fragment) ? hash[:fragment] : self.fragment
|
1545
1652
|
end
|
1546
|
-
if hash.has_key?(:userinfo)
|
1547
|
-
uri.userinfo =
|
1548
|
-
hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo
|
1549
|
-
end
|
1550
|
-
if !hash.has_key?(:userinfo) && !hash.has_key?(:authority)
|
1551
|
-
uri.user =
|
1552
|
-
hash.has_key?(:user) ? hash[:user] : self.user
|
1553
|
-
uri.password =
|
1554
|
-
hash.has_key?(:password) ? hash[:password] : self.password
|
1555
|
-
end
|
1556
|
-
if !hash.has_key?(:authority)
|
1557
|
-
uri.host =
|
1558
|
-
hash.has_key?(:host) ? hash[:host] : self.host
|
1559
|
-
uri.port =
|
1560
|
-
hash.has_key?(:port) ? hash[:port] : self.port
|
1561
|
-
end
|
1562
|
-
uri.path =
|
1563
|
-
hash.has_key?(:path) ? hash[:path] : self.path
|
1564
|
-
uri.query =
|
1565
|
-
hash.has_key?(:query) ? hash[:query] : self.query
|
1566
|
-
uri.fragment =
|
1567
|
-
hash.has_key?(:fragment) ? hash[:fragment] : self.fragment
|
1568
|
-
uri.validation_deferred = false
|
1569
1653
|
|
1570
1654
|
return uri
|
1571
1655
|
end
|
@@ -1825,12 +1909,12 @@ module Addressable
|
|
1825
1909
|
"Invalid component names: #{invalid_components.inspect}."
|
1826
1910
|
end
|
1827
1911
|
duplicated_uri = self.dup
|
1828
|
-
duplicated_uri.
|
1829
|
-
|
1830
|
-
|
1912
|
+
duplicated_uri.defer_validation do
|
1913
|
+
components.each do |component|
|
1914
|
+
duplicated_uri.send((component.to_s + "=").to_sym, nil)
|
1915
|
+
end
|
1916
|
+
duplicated_uri.user = duplicated_uri.normalized_user
|
1831
1917
|
end
|
1832
|
-
duplicated_uri.user = duplicated_uri.normalized_user
|
1833
|
-
duplicated_uri.validation_deferred = false
|
1834
1918
|
duplicated_uri
|
1835
1919
|
end
|
1836
1920
|
|
@@ -1895,27 +1979,20 @@ module Addressable
|
|
1895
1979
|
end
|
1896
1980
|
|
1897
1981
|
##
|
1898
|
-
#
|
1899
|
-
#
|
1900
|
-
#
|
1901
|
-
#
|
1902
|
-
# <tt>false</tt> otherwise.
|
1903
|
-
def validation_deferred
|
1904
|
-
!!@validation_deferred
|
1905
|
-
end
|
1906
|
-
|
1907
|
-
##
|
1908
|
-
# If URI validation needs to be disabled, this can be set to true.
|
1982
|
+
# This method allows you to make several changes to a URI simultaneously,
|
1983
|
+
# which separately would cause validation errors, but in conjunction,
|
1984
|
+
# are valid. The URI will be revalidated as soon as the entire block has
|
1985
|
+
# been executed.
|
1909
1986
|
#
|
1910
|
-
# @param [
|
1911
|
-
#
|
1912
|
-
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1916
|
-
|
1917
|
-
|
1918
|
-
|
1987
|
+
# @param [Proc] block
|
1988
|
+
# A set of operations to perform on a given URI.
|
1989
|
+
def defer_validation(&block)
|
1990
|
+
raise LocalJumpError, "No block given." unless block
|
1991
|
+
@validation_deferred = true
|
1992
|
+
block.call()
|
1993
|
+
@validation_deferred = false
|
1994
|
+
validate
|
1995
|
+
return nil
|
1919
1996
|
end
|
1920
1997
|
|
1921
1998
|
private
|
@@ -1952,7 +2029,7 @@ module Addressable
|
|
1952
2029
|
##
|
1953
2030
|
# Ensures that the URI is valid.
|
1954
2031
|
def validate
|
1955
|
-
return if
|
2032
|
+
return if !!@validation_deferred
|
1956
2033
|
if self.scheme != nil &&
|
1957
2034
|
(self.host == nil || self.host == "") &&
|
1958
2035
|
(self.path == nil || self.path == "")
|
@@ -1966,6 +2043,11 @@ module Addressable
|
|
1966
2043
|
raise InvalidURIError, "Hostname not supplied: '#{self.to_s}'"
|
1967
2044
|
end
|
1968
2045
|
end
|
2046
|
+
if self.path != nil && self.path != "" && self.path[0..0] != "/" &&
|
2047
|
+
self.authority != nil
|
2048
|
+
raise InvalidURIError,
|
2049
|
+
"Cannot have a relative path with an authority set: '#{self.to_s}'"
|
2050
|
+
end
|
1969
2051
|
return nil
|
1970
2052
|
end
|
1971
2053
|
|
data/lib/addressable/version.rb
CHANGED
@@ -1475,6 +1475,15 @@ describe Addressable::Template, "with a partially expanded template" do
|
|
1475
1475
|
@initial_template.expand({"one" => "1", "two" => "2"}).should ==
|
1476
1476
|
@partial_template.expand({"two" => "2"})
|
1477
1477
|
end
|
1478
|
+
|
1479
|
+
it "should raise an error if the template is expanded with bogus values" do
|
1480
|
+
(lambda do
|
1481
|
+
@initial_template.expand({"one" => Object.new, "two" => Object.new})
|
1482
|
+
end).should raise_error(TypeError)
|
1483
|
+
(lambda do
|
1484
|
+
@partial_template.expand({"two" => Object.new})
|
1485
|
+
end).should raise_error(TypeError)
|
1486
|
+
end
|
1478
1487
|
end
|
1479
1488
|
|
1480
1489
|
describe Addressable::Template, "with a partially expanded template" do
|
@@ -323,6 +323,10 @@ describe Addressable::URI, "when parsed from ''" do
|
|
323
323
|
@uri.path.should == ""
|
324
324
|
end
|
325
325
|
|
326
|
+
it "should have a request URI of '/'" do
|
327
|
+
@uri.request_uri.should == "/"
|
328
|
+
end
|
329
|
+
|
326
330
|
it "should be considered relative" do
|
327
331
|
@uri.should be_relative
|
328
332
|
end
|
@@ -355,6 +359,10 @@ describe Addressable::URI, "when parsed from " +
|
|
355
359
|
@uri.path.should == "/rfc/rfc1808.txt"
|
356
360
|
end
|
357
361
|
|
362
|
+
it "should not have a request URI" do
|
363
|
+
@uri.request_uri.should == nil
|
364
|
+
end
|
365
|
+
|
358
366
|
it "should be considered to be in normal form" do
|
359
367
|
@uri.normalize.should be_eql(@uri)
|
360
368
|
end
|
@@ -383,6 +391,10 @@ describe Addressable::URI, "when parsed from " +
|
|
383
391
|
@uri.path.should == "/rfc/rfc2396.txt"
|
384
392
|
end
|
385
393
|
|
394
|
+
it "should have a request URI of '/rfc/rfc2396.txt'" do
|
395
|
+
@uri.request_uri.should == "/rfc/rfc2396.txt"
|
396
|
+
end
|
397
|
+
|
386
398
|
it "should be considered to be in normal form" do
|
387
399
|
@uri.normalize.should be_eql(@uri)
|
388
400
|
end
|
@@ -421,6 +433,16 @@ describe Addressable::URI, "when parsed from " +
|
|
421
433
|
@uri.path.should == "/c=GB"
|
422
434
|
end
|
423
435
|
|
436
|
+
it "should not have a request URI" do
|
437
|
+
@uri.request_uri.should == nil
|
438
|
+
end
|
439
|
+
|
440
|
+
it "should not allow request URI assignment" do
|
441
|
+
(lambda do
|
442
|
+
@uri.request_uri = "/"
|
443
|
+
end).should raise_error(Addressable::URI::InvalidURIError)
|
444
|
+
end
|
445
|
+
|
424
446
|
it "should have a query of 'objectClass?one'" do
|
425
447
|
@uri.query.should == "objectClass?one"
|
426
448
|
end
|
@@ -465,6 +487,10 @@ describe Addressable::URI, "when parsed from " +
|
|
465
487
|
@uri.path.should == "John.Doe@example.com"
|
466
488
|
end
|
467
489
|
|
490
|
+
it "should not have a request URI" do
|
491
|
+
@uri.request_uri.should == nil
|
492
|
+
end
|
493
|
+
|
468
494
|
it "should be considered to be in normal form" do
|
469
495
|
@uri.normalize.should be_eql(@uri)
|
470
496
|
end
|
@@ -489,6 +515,10 @@ describe Addressable::URI, "when parsed from " +
|
|
489
515
|
@uri.path.should == "comp.infosystems.www.servers.unix"
|
490
516
|
end
|
491
517
|
|
518
|
+
it "should not have a request URI" do
|
519
|
+
@uri.request_uri.should == nil
|
520
|
+
end
|
521
|
+
|
492
522
|
it "should be considered to be in normal form" do
|
493
523
|
@uri.normalize.should be_eql(@uri)
|
494
524
|
end
|
@@ -513,6 +543,10 @@ describe Addressable::URI, "when parsed from " +
|
|
513
543
|
@uri.path.should == "+1-816-555-1212"
|
514
544
|
end
|
515
545
|
|
546
|
+
it "should not have a request URI" do
|
547
|
+
@uri.request_uri.should == nil
|
548
|
+
end
|
549
|
+
|
516
550
|
it "should be considered to be in normal form" do
|
517
551
|
@uri.normalize.should be_eql(@uri)
|
518
552
|
end
|
@@ -545,6 +579,10 @@ describe Addressable::URI, "when parsed from " +
|
|
545
579
|
@uri.path.should == "/"
|
546
580
|
end
|
547
581
|
|
582
|
+
it "should not have a request URI" do
|
583
|
+
@uri.request_uri.should == nil
|
584
|
+
end
|
585
|
+
|
548
586
|
it "should be considered to be in normal form" do
|
549
587
|
@uri.normalize.should be_eql(@uri)
|
550
588
|
end
|
@@ -571,6 +609,10 @@ describe Addressable::URI, "when parsed from " +
|
|
571
609
|
@uri.path.should == "oasis:names:specification:docbook:dtd:xml:4.1.2"
|
572
610
|
end
|
573
611
|
|
612
|
+
it "should not have a request URI" do
|
613
|
+
@uri.request_uri.should == nil
|
614
|
+
end
|
615
|
+
|
574
616
|
it "should be considered to be in normal form" do
|
575
617
|
@uri.normalize.should be_eql(@uri)
|
576
618
|
end
|
@@ -639,6 +681,10 @@ describe Addressable::URI, "when parsed from " +
|
|
639
681
|
@uri.query_values.should == nil
|
640
682
|
end
|
641
683
|
|
684
|
+
it "should have a request URI of '/'" do
|
685
|
+
@uri.request_uri.should == "/"
|
686
|
+
end
|
687
|
+
|
642
688
|
it "should have no fragment" do
|
643
689
|
@uri.fragment.should == nil
|
644
690
|
end
|
@@ -826,6 +872,51 @@ describe Addressable::URI, "when parsed from " +
|
|
826
872
|
end
|
827
873
|
end
|
828
874
|
|
875
|
+
# Section 5.1.2 of RFC 2616
|
876
|
+
describe Addressable::URI, "when parsed from " +
|
877
|
+
"'http://www.w3.org/pub/WWW/TheProject.html'" do
|
878
|
+
before do
|
879
|
+
@uri = Addressable::URI.parse("http://www.w3.org/pub/WWW/TheProject.html")
|
880
|
+
end
|
881
|
+
|
882
|
+
it "should have the correct request URI" do
|
883
|
+
@uri.request_uri.should == "/pub/WWW/TheProject.html"
|
884
|
+
end
|
885
|
+
|
886
|
+
it "should have the correct request URI after assignment" do
|
887
|
+
@uri.request_uri = "/some/where/else.html?query?string"
|
888
|
+
@uri.request_uri.should == "/some/where/else.html?query?string"
|
889
|
+
@uri.path.should == "/some/where/else.html"
|
890
|
+
@uri.query.should == "query?string"
|
891
|
+
end
|
892
|
+
|
893
|
+
it "should have the correct request URI after assignment" do
|
894
|
+
@uri.request_uri = "?x=y"
|
895
|
+
@uri.request_uri.should == "/?x=y"
|
896
|
+
@uri.path.should == "/"
|
897
|
+
@uri.query.should == "x=y"
|
898
|
+
end
|
899
|
+
|
900
|
+
it "should raise an error if the request URI is set to something bogus" do
|
901
|
+
(lambda do
|
902
|
+
@uri.request_uri = 42
|
903
|
+
end).should raise_error(TypeError)
|
904
|
+
end
|
905
|
+
|
906
|
+
it "should correctly convert to a hash" do
|
907
|
+
@uri.to_hash.should == {
|
908
|
+
:scheme => "http",
|
909
|
+
:user => nil,
|
910
|
+
:password => nil,
|
911
|
+
:host => "www.w3.org",
|
912
|
+
:port => nil,
|
913
|
+
:path => "/pub/WWW/TheProject.html",
|
914
|
+
:query => nil,
|
915
|
+
:fragment => nil
|
916
|
+
}
|
917
|
+
end
|
918
|
+
end
|
919
|
+
|
829
920
|
describe Addressable::URI, "when parsed from " +
|
830
921
|
"'http://example.com/'" do
|
831
922
|
before do
|
@@ -871,6 +962,10 @@ describe Addressable::URI, "when parsed from " +
|
|
871
962
|
@uri.to_s.should == "http://example.com/"
|
872
963
|
end
|
873
964
|
|
965
|
+
it "should have a request URI of '/'" do
|
966
|
+
@uri.request_uri.should == "/"
|
967
|
+
end
|
968
|
+
|
874
969
|
it "should correctly convert to a hash" do
|
875
970
|
@uri.to_hash.should == {
|
876
971
|
:scheme => "http",
|
@@ -1021,6 +1116,34 @@ describe Addressable::URI, "when parsed from " +
|
|
1021
1116
|
end
|
1022
1117
|
end
|
1023
1118
|
|
1119
|
+
describe Addressable::URI, "when parsed from " +
|
1120
|
+
"'http://example.com/path%2Fsegment/'" do
|
1121
|
+
before do
|
1122
|
+
@uri = Addressable::URI.parse("http://example.com/path%2Fsegment/")
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
it "should be considered to be in normal form" do
|
1126
|
+
@uri.normalize.should be_eql(@uri)
|
1127
|
+
end
|
1128
|
+
|
1129
|
+
it "should be equal to 'http://example.com/path%2Fsegment/'" do
|
1130
|
+
@uri.normalize.should be_eql(
|
1131
|
+
Addressable::URI.parse("http://example.com/path%2Fsegment/")
|
1132
|
+
)
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
it "should not be equal to 'http://example.com/path/segment/'" do
|
1136
|
+
@uri.should_not ==
|
1137
|
+
Addressable::URI.parse("http://example.com/path/segment/")
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
it "should not be equal to 'http://example.com/path/segment/'" do
|
1141
|
+
@uri.normalize.should_not be_eql(
|
1142
|
+
Addressable::URI.parse("http://example.com/path/segment/")
|
1143
|
+
)
|
1144
|
+
end
|
1145
|
+
end
|
1146
|
+
|
1024
1147
|
describe Addressable::URI, "when parsed from " +
|
1025
1148
|
"'http://example.com/?%F6'" do
|
1026
1149
|
before do
|
@@ -2985,7 +3108,8 @@ describe Addressable::URI, "with a base uri of 'http://a/b/c/d;p?q'" do
|
|
2985
3108
|
# Section 5.4.1 of RFC 3986
|
2986
3109
|
it "when joined with '#s' should resolve to http://a/b/c/d;p?q#s" do
|
2987
3110
|
(@uri + "#s").to_s.should == "http://a/b/c/d;p?q#s"
|
2988
|
-
Addressable::URI.join(@uri.to_s, "#s").to_s.should ==
|
3111
|
+
Addressable::URI.join(@uri.to_s, "#s").to_s.should ==
|
3112
|
+
"http://a/b/c/d;p?q#s"
|
2989
3113
|
end
|
2990
3114
|
|
2991
3115
|
# Section 5.4.1 of RFC 3986
|
@@ -3082,7 +3206,8 @@ describe Addressable::URI, "with a base uri of 'http://a/b/c/d;p?q'" do
|
|
3082
3206
|
|
3083
3207
|
it "when joined with '../.././../g' should resolve to http://a/g" do
|
3084
3208
|
(@uri + "../.././../g").to_s.should == "http://a/g"
|
3085
|
-
Addressable::URI.join(@uri.to_s, "../.././../g").to_s.should ==
|
3209
|
+
Addressable::URI.join(@uri.to_s, "../.././../g").to_s.should ==
|
3210
|
+
"http://a/g"
|
3086
3211
|
end
|
3087
3212
|
|
3088
3213
|
# Section 5.4.2 of RFC 3986
|
@@ -3248,25 +3373,25 @@ describe Addressable::URI, "when converting a bogus path" do
|
|
3248
3373
|
end
|
3249
3374
|
end
|
3250
3375
|
|
3251
|
-
describe Addressable::URI, "when given
|
3376
|
+
describe Addressable::URI, "when given a UNIX root directory" do
|
3252
3377
|
before do
|
3253
|
-
|
3254
|
-
@path = "C:\\"
|
3255
|
-
else
|
3256
|
-
@path = "/"
|
3257
|
-
end
|
3378
|
+
@path = "/"
|
3258
3379
|
end
|
3259
3380
|
|
3260
|
-
|
3261
|
-
|
3262
|
-
|
3263
|
-
|
3264
|
-
|
3265
|
-
|
3266
|
-
|
3267
|
-
|
3268
|
-
|
3269
|
-
|
3381
|
+
it "should convert to \'file:///\'" do
|
3382
|
+
@uri = Addressable::URI.convert_path(@path)
|
3383
|
+
@uri.to_str.should == "file:///"
|
3384
|
+
end
|
3385
|
+
end
|
3386
|
+
|
3387
|
+
describe Addressable::URI, "when given a Windows root directory" do
|
3388
|
+
before do
|
3389
|
+
@path = "C:\\"
|
3390
|
+
end
|
3391
|
+
|
3392
|
+
it "should convert to \'file:///c:/\'" do
|
3393
|
+
@uri = Addressable::URI.convert_path(@path)
|
3394
|
+
@uri.to_str.should == "file:///c:/"
|
3270
3395
|
end
|
3271
3396
|
end
|
3272
3397
|
|
@@ -3390,6 +3515,71 @@ describe Addressable::URI, "when parsing a non-String object" do
|
|
3390
3515
|
end
|
3391
3516
|
end
|
3392
3517
|
|
3518
|
+
describe Addressable::URI, "when normalizing a non-String object" do
|
3519
|
+
it "should correctly parse anything with a 'to_str' method" do
|
3520
|
+
Addressable::URI.normalize_component(SuperString.new(42))
|
3521
|
+
end
|
3522
|
+
|
3523
|
+
it "should raise a TypeError for objects than cannot be converted" do
|
3524
|
+
(lambda do
|
3525
|
+
Addressable::URI.normalize_component(42)
|
3526
|
+
end).should raise_error(TypeError, "Can't convert Fixnum into String.")
|
3527
|
+
end
|
3528
|
+
|
3529
|
+
it "should raise a TypeError for objects than cannot be converted" do
|
3530
|
+
(lambda do
|
3531
|
+
Addressable::URI.normalize_component("component", 42)
|
3532
|
+
end).should raise_error(TypeError)
|
3533
|
+
end
|
3534
|
+
end
|
3535
|
+
|
3536
|
+
describe Addressable::URI, "when normalizing a path with an encoded slash" do
|
3537
|
+
it "should result in correct percent encoded sequence" do
|
3538
|
+
Addressable::URI.parse("/path%2Fsegment/").normalize.path.should ==
|
3539
|
+
"/path%2Fsegment/"
|
3540
|
+
end
|
3541
|
+
end
|
3542
|
+
|
3543
|
+
describe Addressable::URI, "when normalizing a partially encoded string" do
|
3544
|
+
it "should result in correct percent encoded sequence" do
|
3545
|
+
Addressable::URI.normalize_component(
|
3546
|
+
"partially % encoded%21"
|
3547
|
+
).should == "partially%20%25%20encoded!"
|
3548
|
+
end
|
3549
|
+
|
3550
|
+
it "should result in correct percent encoded sequence" do
|
3551
|
+
Addressable::URI.normalize_component(
|
3552
|
+
"partially %25 encoded!"
|
3553
|
+
).should == "partially%20%25%20encoded!"
|
3554
|
+
end
|
3555
|
+
end
|
3556
|
+
|
3557
|
+
describe Addressable::URI, "when normalizing a unicode sequence" do
|
3558
|
+
it "should result in correct percent encoded sequence" do
|
3559
|
+
Addressable::URI.normalize_component(
|
3560
|
+
"/C%CC%A7"
|
3561
|
+
).should == "/%C3%87"
|
3562
|
+
end
|
3563
|
+
|
3564
|
+
it "should result in correct percent encoded sequence" do
|
3565
|
+
Addressable::URI.normalize_component(
|
3566
|
+
"/%C3%87"
|
3567
|
+
).should == "/%C3%87"
|
3568
|
+
end
|
3569
|
+
end
|
3570
|
+
|
3571
|
+
describe Addressable::URI, "when normalizing a multibyte string" do
|
3572
|
+
it "should result in correct percent encoded sequence" do
|
3573
|
+
Addressable::URI.normalize_component("günther").should ==
|
3574
|
+
"g%C3%BCnther"
|
3575
|
+
end
|
3576
|
+
|
3577
|
+
it "should result in correct percent encoded sequence" do
|
3578
|
+
Addressable::URI.normalize_component("g%C3%BCnther").should ==
|
3579
|
+
"g%C3%BCnther"
|
3580
|
+
end
|
3581
|
+
end
|
3582
|
+
|
3393
3583
|
describe Addressable::URI, "when encoding a multibyte string" do
|
3394
3584
|
it "should result in correct percent encoded sequence" do
|
3395
3585
|
Addressable::URI.encode_component("günther").should == "g%C3%BCnther"
|
@@ -3675,3 +3865,50 @@ describe Addressable::URI, "when assigning query values" do
|
|
3675
3865
|
@uri.query.should == "a=a&b[c]&b[d]=d"
|
3676
3866
|
end
|
3677
3867
|
end
|
3868
|
+
|
3869
|
+
describe Addressable::URI, "when assigning path values" do
|
3870
|
+
before do
|
3871
|
+
@uri = Addressable::URI.new
|
3872
|
+
end
|
3873
|
+
|
3874
|
+
it "should correctly assign paths containing colons" do
|
3875
|
+
@uri.path = "acct:bob@sporkmonger.com"
|
3876
|
+
Addressable::URI.parse(@uri.normalize.to_str).path.should == @uri.path
|
3877
|
+
@uri.normalize.to_str.should == "acct%2Fbob@sporkmonger.com"
|
3878
|
+
end
|
3879
|
+
|
3880
|
+
it "should correctly assign paths containing colons" do
|
3881
|
+
@uri.path = "/acct:bob@sporkmonger.com"
|
3882
|
+
@uri.authority = "example.com"
|
3883
|
+
@uri.normalize.to_str.should == "//example.com/acct:bob@sporkmonger.com"
|
3884
|
+
end
|
3885
|
+
|
3886
|
+
it "should correctly assign paths containing colons" do
|
3887
|
+
@uri.path = "acct:bob@sporkmonger.com"
|
3888
|
+
@uri.scheme = "something"
|
3889
|
+
@uri.normalize.to_str.should == "something:acct:bob@sporkmonger.com"
|
3890
|
+
end
|
3891
|
+
|
3892
|
+
it "should not allow relative paths to be assigned on absolute URIs" do
|
3893
|
+
(lambda do
|
3894
|
+
@uri.scheme = "http"
|
3895
|
+
@uri.host = "example.com"
|
3896
|
+
@uri.path = "acct:bob@sporkmonger.com"
|
3897
|
+
end).should raise_error(Addressable::URI::InvalidURIError)
|
3898
|
+
end
|
3899
|
+
|
3900
|
+
it "should not allow relative paths to be assigned on absolute URIs" do
|
3901
|
+
(lambda do
|
3902
|
+
@uri.path = "acct:bob@sporkmonger.com"
|
3903
|
+
@uri.scheme = "http"
|
3904
|
+
@uri.host = "example.com"
|
3905
|
+
end).should raise_error(Addressable::URI::InvalidURIError)
|
3906
|
+
end
|
3907
|
+
|
3908
|
+
it "should not allow relative paths to be assigned on absolute URIs" do
|
3909
|
+
(lambda do
|
3910
|
+
@uri.path = "uuid:0b3ecf60-3f93-11df-a9c3-001f5bfffe12"
|
3911
|
+
@uri.scheme = "urn"
|
3912
|
+
end).should_not raise_error(Addressable::URI::InvalidURIError)
|
3913
|
+
end
|
3914
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: addressable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Aman
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-04-23 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|