addressable 2.1.1 → 2.1.2
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.
- 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
|