addressable 2.5.1 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # encoding:utf-8
2
4
  #--
3
5
  # Copyright (C) Bob Aman
@@ -46,12 +48,21 @@ module Addressable
46
48
  PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
47
49
  SCHEME = ALPHA + DIGIT + "\\-\\+\\."
48
50
  HOST = UNRESERVED + SUB_DELIMS + "\\[\\:\\]"
49
- AUTHORITY = PCHAR
51
+ AUTHORITY = PCHAR + "\\[\\:\\]"
50
52
  PATH = PCHAR + "\\/"
51
53
  QUERY = PCHAR + "\\/\\?"
52
54
  FRAGMENT = PCHAR + "\\/\\?"
53
55
  end
54
56
 
57
+ module NormalizeCharacterClasses
58
+ HOST = /[^#{CharacterClasses::HOST}]/
59
+ UNRESERVED = /[^#{CharacterClasses::UNRESERVED}]/
60
+ PCHAR = /[^#{CharacterClasses::PCHAR}]/
61
+ SCHEME = /[^#{CharacterClasses::SCHEME}]/
62
+ FRAGMENT = /[^#{CharacterClasses::FRAGMENT}]/
63
+ QUERY = %r{[^a-zA-Z0-9\-\.\_\~\!\$\'\(\)\*\+\,\=\:\@\/\?%]|%(?!2B|2b)}
64
+ end
65
+
55
66
  SLASH = '/'
56
67
  EMPTY_STR = ''
57
68
 
@@ -71,7 +82,7 @@ module Addressable
71
82
  "wais" => 210,
72
83
  "ldap" => 389,
73
84
  "prospero" => 1525
74
- }
85
+ }.freeze
75
86
 
76
87
  ##
77
88
  # Returns a URI object based on the parsed string.
@@ -122,9 +133,9 @@ module Addressable
122
133
  user = userinfo.strip[/^([^:]*):?/, 1]
123
134
  password = userinfo.strip[/:(.*)$/, 1]
124
135
  end
125
- host = authority.gsub(
136
+ host = authority.sub(
126
137
  /^([^\[\]]*)@/, EMPTY_STR
127
- ).gsub(
138
+ ).sub(
128
139
  /:([^:@\[\]]*?)$/, EMPTY_STR
129
140
  )
130
141
  port = authority[/:([^:@\[\]]*?)$/, 1]
@@ -182,26 +193,33 @@ module Addressable
182
193
  :scheme => "http"
183
194
  }.merge(hints)
184
195
  case uri
185
- when /^http:\/+/
186
- uri.gsub!(/^http:\/+/, "http://")
187
- when /^https:\/+/
188
- uri.gsub!(/^https:\/+/, "https://")
189
- when /^feed:\/+http:\/+/
190
- uri.gsub!(/^feed:\/+http:\/+/, "feed:http://")
191
- when /^feed:\/+/
192
- uri.gsub!(/^feed:\/+/, "feed://")
193
- when /^file:\/+/
194
- uri.gsub!(/^file:\/+/, "file:///")
196
+ when /^http:\//i
197
+ uri.sub!(/^http:\/+/i, "http://")
198
+ when /^https:\//i
199
+ uri.sub!(/^https:\/+/i, "https://")
200
+ when /^feed:\/+http:\//i
201
+ uri.sub!(/^feed:\/+http:\/+/i, "feed:http://")
202
+ when /^feed:\//i
203
+ uri.sub!(/^feed:\/+/i, "feed://")
204
+ when %r[^file:/{4}]i
205
+ uri.sub!(%r[^file:/+]i, "file:////")
206
+ when %r[^file://localhost/]i
207
+ uri.sub!(%r[^file://localhost/+]i, "file:///")
208
+ when %r[^file:/+]i
209
+ uri.sub!(%r[^file:/+]i, "file:///")
195
210
  when /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
196
- uri.gsub!(/^/, hints[:scheme] + "://")
211
+ uri.sub!(/^/, hints[:scheme] + "://")
212
+ when /\A\d+\..*:\d+\z/
213
+ uri = "#{hints[:scheme]}://#{uri}"
197
214
  end
198
215
  match = uri.match(URIREGEX)
199
216
  fragments = match.captures
200
217
  authority = fragments[3]
201
218
  if authority && authority.length > 0
202
- new_authority = authority.gsub(/\\/, '/').gsub(/ /, '%20')
219
+ new_authority = authority.tr("\\", "/").gsub(" ", "%20")
203
220
  # NOTE: We want offset 4, not 3!
204
221
  offset = match.offset(4)
222
+ uri = uri.dup
205
223
  uri[offset[0]...offset[1]] = new_authority
206
224
  end
207
225
  parsed = self.parse(uri)
@@ -209,10 +227,11 @@ module Addressable
209
227
  parsed = self.parse(hints[:scheme] + "://" + uri)
210
228
  end
211
229
  if parsed.path.include?(".")
212
- new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
213
- if new_host
230
+ if parsed.path[/\b@\b/]
231
+ parsed.scheme = "mailto" unless parsed.scheme
232
+ elsif new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
214
233
  parsed.defer_validation do
215
- new_path = parsed.path.gsub(
234
+ new_path = parsed.path.sub(
216
235
  Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR)
217
236
  parsed.host = new_host
218
237
  parsed.path = new_path
@@ -263,24 +282,24 @@ module Addressable
263
282
  # Otherwise, convert to a String
264
283
  path = path.to_str.strip
265
284
 
266
- path.gsub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
285
+ path.sub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
267
286
  path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/
268
287
  uri = self.parse(path)
269
288
 
270
289
  if uri.scheme == nil
271
290
  # Adjust windows-style uris
272
- uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
291
+ uri.path.sub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
273
292
  "/#{$1.downcase}:/"
274
293
  end
275
- uri.path.gsub!(/\\/, SLASH)
294
+ uri.path.tr!("\\", SLASH)
276
295
  if File.exist?(uri.path) &&
277
296
  File.stat(uri.path).directory?
278
- uri.path.gsub!(/\/$/, EMPTY_STR)
297
+ uri.path.chomp!(SLASH)
279
298
  uri.path = uri.path + '/'
280
299
  end
281
300
 
282
301
  # If the path is absolute, set the scheme and host.
283
- if uri.path =~ /^\//
302
+ if uri.path.start_with?(SLASH)
284
303
  uri.scheme = "file"
285
304
  uri.host = EMPTY_STR
286
305
  end
@@ -317,6 +336,21 @@ module Addressable
317
336
  return result
318
337
  end
319
338
 
339
+ ##
340
+ # Tables used to optimize encoding operations in `self.encode_component`
341
+ # and `self.normalize_component`
342
+ SEQUENCE_ENCODING_TABLE = Hash.new do |hash, sequence|
343
+ hash[sequence] = sequence.unpack("C*").map do |c|
344
+ format("%02x", c)
345
+ end.join
346
+ end
347
+
348
+ SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = Hash.new do |hash, sequence|
349
+ hash[sequence] = sequence.unpack("C*").map do |c|
350
+ format("%%%02X", c)
351
+ end.join
352
+ end
353
+
320
354
  ##
321
355
  # Percent encodes a URI component.
322
356
  #
@@ -383,18 +417,20 @@ module Addressable
383
417
  component.force_encoding(Encoding::ASCII_8BIT)
384
418
  # Avoiding gsub! because there are edge cases with frozen strings
385
419
  component = component.gsub(character_class) do |sequence|
386
- (sequence.unpack('C*').map { |c| "%" + ("%02x" % c).upcase }).join
420
+ SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[sequence]
387
421
  end
388
422
  if upcase_encoded.length > 0
389
- component = component.gsub(/%(#{upcase_encoded.chars.map do |char|
390
- char.unpack('C*').map { |c| '%02x' % c }.join
391
- end.join('|')})/i) { |s| s.upcase }
423
+ upcase_encoded_chars = upcase_encoded.chars.map do |char|
424
+ SEQUENCE_ENCODING_TABLE[char]
425
+ end
426
+ component = component.gsub(/%(#{upcase_encoded_chars.join('|')})/,
427
+ &:upcase)
392
428
  end
393
429
  return component
394
430
  end
395
431
 
396
432
  class << self
397
- alias_method :encode_component, :encode_component
433
+ alias_method :escape_component, :encode_component
398
434
  end
399
435
 
400
436
  ##
@@ -436,7 +472,11 @@ module Addressable
436
472
  uri = uri.dup
437
473
  # Seriously, only use UTF-8. I'm really not kidding!
438
474
  uri.force_encoding("utf-8")
439
- leave_encoded.force_encoding("utf-8")
475
+
476
+ unless leave_encoded.empty?
477
+ leave_encoded = leave_encoded.dup.force_encoding("utf-8")
478
+ end
479
+
440
480
  result = uri.gsub(/%[0-9a-f]{2}/iu) do |sequence|
441
481
  c = sequence[1..3].to_i(16).chr
442
482
  c.force_encoding("utf-8")
@@ -522,12 +562,16 @@ module Addressable
522
562
  character_class = "#{character_class}%" unless character_class.include?('%')
523
563
 
524
564
  "|%(?!#{leave_encoded.chars.map do |char|
525
- seq = char.unpack('C*').map { |c| '%02x' % c }.join
565
+ seq = SEQUENCE_ENCODING_TABLE[char]
526
566
  [seq.upcase, seq.downcase]
527
567
  end.flatten.join('|')})"
528
568
  end
529
569
 
530
- character_class = /[^#{character_class}]#{leave_re}/
570
+ character_class = if leave_re
571
+ /[^#{character_class}]#{leave_re}/
572
+ else
573
+ /[^#{character_class}]/
574
+ end
531
575
  end
532
576
  # We can't perform regexps on invalid UTF sequences, but
533
577
  # here we need to, so switch to ASCII.
@@ -847,11 +891,11 @@ module Addressable
847
891
  return nil unless self.scheme
848
892
  @normalized_scheme ||= begin
849
893
  if self.scheme =~ /^\s*ssh\+svn\s*$/i
850
- "svn+ssh"
894
+ "svn+ssh".dup
851
895
  else
852
896
  Addressable::URI.normalize_component(
853
897
  self.scheme.strip.downcase,
854
- Addressable::URI::CharacterClasses::SCHEME
898
+ Addressable::URI::NormalizeCharacterClasses::SCHEME
855
899
  )
856
900
  end
857
901
  end
@@ -871,7 +915,7 @@ module Addressable
871
915
  new_scheme = new_scheme.to_str
872
916
  end
873
917
  if new_scheme && new_scheme !~ /\A[a-z][a-z0-9\.\+\-]*\z/i
874
- raise InvalidURIError, "Invalid scheme format: #{new_scheme}"
918
+ raise InvalidURIError, "Invalid scheme format: '#{new_scheme}'"
875
919
  end
876
920
  @scheme = new_scheme
877
921
  @scheme = nil if @scheme.to_s.strip.empty?
@@ -906,7 +950,7 @@ module Addressable
906
950
  else
907
951
  Addressable::URI.normalize_component(
908
952
  self.user.strip,
909
- Addressable::URI::CharacterClasses::UNRESERVED
953
+ Addressable::URI::NormalizeCharacterClasses::UNRESERVED
910
954
  )
911
955
  end
912
956
  end
@@ -963,7 +1007,7 @@ module Addressable
963
1007
  else
964
1008
  Addressable::URI.normalize_component(
965
1009
  self.password.strip,
966
- Addressable::URI::CharacterClasses::UNRESERVED
1010
+ Addressable::URI::NormalizeCharacterClasses::UNRESERVED
967
1011
  )
968
1012
  end
969
1013
  end
@@ -1032,9 +1076,9 @@ module Addressable
1032
1076
  if !current_user && !current_password
1033
1077
  nil
1034
1078
  elsif current_user && current_password
1035
- "#{current_user}:#{current_password}"
1079
+ "#{current_user}:#{current_password}".dup
1036
1080
  elsif current_user && !current_password
1037
- "#{current_user}"
1081
+ "#{current_user}".dup
1038
1082
  end
1039
1083
  end
1040
1084
  # All normalized values should be UTF-8
@@ -1087,6 +1131,7 @@ module Addressable
1087
1131
  # @return [String] The host component, normalized.
1088
1132
  def normalized_host
1089
1133
  return nil unless self.host
1134
+
1090
1135
  @normalized_host ||= begin
1091
1136
  if !self.host.strip.empty?
1092
1137
  result = ::Addressable::IDNA.to_ascii(
@@ -1098,14 +1143,17 @@ module Addressable
1098
1143
  end
1099
1144
  result = Addressable::URI.normalize_component(
1100
1145
  result,
1101
- CharacterClasses::HOST)
1146
+ NormalizeCharacterClasses::HOST
1147
+ )
1102
1148
  result
1103
1149
  else
1104
- EMPTY_STR
1150
+ EMPTY_STR.dup
1105
1151
  end
1106
1152
  end
1107
1153
  # All normalized values should be UTF-8
1108
- @normalized_host.force_encoding(Encoding::UTF_8) if @normalized_host
1154
+ if @normalized_host && !@normalized_host.empty?
1155
+ @normalized_host.force_encoding(Encoding::UTF_8)
1156
+ end
1109
1157
  @normalized_host
1110
1158
  end
1111
1159
 
@@ -1163,16 +1211,25 @@ module Addressable
1163
1211
  # Returns the top-level domain for this host.
1164
1212
  #
1165
1213
  # @example
1166
- # Addressable::URI.parse("www.example.co.uk").tld # => "co.uk"
1214
+ # Addressable::URI.parse("http://www.example.co.uk").tld # => "co.uk"
1167
1215
  def tld
1168
1216
  PublicSuffix.parse(self.host, ignore_private: true).tld
1169
1217
  end
1170
1218
 
1219
+ ##
1220
+ # Sets the top-level domain for this URI.
1221
+ #
1222
+ # @param [String, #to_str] new_tld The new top-level domain.
1223
+ def tld=(new_tld)
1224
+ replaced_tld = host.sub(/#{tld}\z/, new_tld)
1225
+ self.host = PublicSuffix::Domain.new(replaced_tld).to_s
1226
+ end
1227
+
1171
1228
  ##
1172
1229
  # Returns the public suffix domain for this host.
1173
1230
  #
1174
1231
  # @example
1175
- # Addressable::URI.parse("www.example.co.uk").domain # => "example.co.uk"
1232
+ # Addressable::URI.parse("http://www.example.co.uk").domain # => "example.co.uk"
1176
1233
  def domain
1177
1234
  PublicSuffix.domain(self.host, ignore_private: true)
1178
1235
  end
@@ -1235,9 +1292,9 @@ module Addressable
1235
1292
  new_user = new_userinfo.strip[/^([^:]*):?/, 1]
1236
1293
  new_password = new_userinfo.strip[/:(.*)$/, 1]
1237
1294
  end
1238
- new_host = new_authority.gsub(
1295
+ new_host = new_authority.sub(
1239
1296
  /^([^\[\]]*)@/, EMPTY_STR
1240
- ).gsub(
1297
+ ).sub(
1241
1298
  /:([^:@\[\]]*?)$/, EMPTY_STR
1242
1299
  )
1243
1300
  new_port =
@@ -1421,7 +1478,7 @@ module Addressable
1421
1478
  # @return [String] The components that identify a site.
1422
1479
  def site
1423
1480
  (self.scheme || self.authority) && @site ||= begin
1424
- site_string = ""
1481
+ site_string = "".dup
1425
1482
  site_string << "#{self.scheme}:" if self.scheme != nil
1426
1483
  site_string << "//#{self.authority}" if self.authority != nil
1427
1484
  site_string
@@ -1440,7 +1497,7 @@ module Addressable
1440
1497
  def normalized_site
1441
1498
  return nil unless self.site
1442
1499
  @normalized_site ||= begin
1443
- site_string = ""
1500
+ site_string = "".dup
1444
1501
  if self.normalized_scheme != nil
1445
1502
  site_string << "#{self.normalized_scheme}:"
1446
1503
  end
@@ -1501,14 +1558,14 @@ module Addressable
1501
1558
  result = path.strip.split(SLASH, -1).map do |segment|
1502
1559
  Addressable::URI.normalize_component(
1503
1560
  segment,
1504
- Addressable::URI::CharacterClasses::PCHAR
1561
+ Addressable::URI::NormalizeCharacterClasses::PCHAR
1505
1562
  )
1506
1563
  end.join(SLASH)
1507
1564
 
1508
1565
  result = URI.normalize_path(result)
1509
1566
  if result.empty? &&
1510
1567
  ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
1511
- result = SLASH
1568
+ result = SLASH.dup
1512
1569
  end
1513
1570
  result
1514
1571
  end
@@ -1544,7 +1601,7 @@ module Addressable
1544
1601
  # @return [String] The path's basename.
1545
1602
  def basename
1546
1603
  # Path cannot be nil
1547
- return File.basename(self.path).gsub(/;[^\/]*$/, EMPTY_STR)
1604
+ return File.basename(self.path).sub(/;[^\/]*$/, EMPTY_STR)
1548
1605
  end
1549
1606
 
1550
1607
  ##
@@ -1576,10 +1633,15 @@ module Addressable
1576
1633
  modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
1577
1634
  # Make sure possible key-value pair delimiters are escaped.
1578
1635
  modified_query_class.sub!("\\&", "").sub!("\\;", "")
1579
- pairs = (self.query || "").split("&", -1)
1636
+ pairs = (query || "").split("&", -1)
1637
+ pairs.delete_if(&:empty?).uniq! if flags.include?(:compacted)
1580
1638
  pairs.sort! if flags.include?(:sorted)
1581
1639
  component = pairs.map do |pair|
1582
- Addressable::URI.normalize_component(pair, modified_query_class, "+")
1640
+ Addressable::URI.normalize_component(
1641
+ pair,
1642
+ Addressable::URI::NormalizeCharacterClasses::QUERY,
1643
+ "+"
1644
+ )
1583
1645
  end.join("&")
1584
1646
  component == "" ? nil : component
1585
1647
  end
@@ -1638,11 +1700,13 @@ module Addressable
1638
1700
  # so it's best to make all changes in-place.
1639
1701
  pair[0] = URI.unencode_component(pair[0])
1640
1702
  if pair[1].respond_to?(:to_str)
1703
+ value = pair[1].to_str
1641
1704
  # I loathe the fact that I have to do this. Stupid HTML 4.01.
1642
1705
  # Treating '+' as a space was just an unbelievably bad idea.
1643
1706
  # There was nothing wrong with '%20'!
1644
1707
  # If it ain't broke, don't fix it!
1645
- pair[1] = URI.unencode_component(pair[1].to_str.gsub(/\+/, " "))
1708
+ value = value.tr("+", " ") if ["http", "https", nil].include?(scheme)
1709
+ pair[1] = URI.unencode_component(value)
1646
1710
  end
1647
1711
  if return_type == Hash
1648
1712
  accu[pair[0]] = pair[1]
@@ -1694,7 +1758,7 @@ module Addressable
1694
1758
  end
1695
1759
 
1696
1760
  # new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
1697
- buffer = ""
1761
+ buffer = "".dup
1698
1762
  new_query_values.each do |key, value|
1699
1763
  encoded_key = URI.encode_component(
1700
1764
  key, CharacterClasses::UNRESERVED
@@ -1724,7 +1788,7 @@ module Addressable
1724
1788
  #
1725
1789
  # @return [String] The request URI required for an HTTP request.
1726
1790
  def request_uri
1727
- return nil if self.absolute? && self.scheme !~ /^https?$/
1791
+ return nil if self.absolute? && self.scheme !~ /^https?$/i
1728
1792
  return (
1729
1793
  (!self.path.empty? ? self.path : SLASH) +
1730
1794
  (self.query ? "?#{self.query}" : EMPTY_STR)
@@ -1739,12 +1803,12 @@ module Addressable
1739
1803
  if !new_request_uri.respond_to?(:to_str)
1740
1804
  raise TypeError, "Can't convert #{new_request_uri.class} into String."
1741
1805
  end
1742
- if self.absolute? && self.scheme !~ /^https?$/
1806
+ if self.absolute? && self.scheme !~ /^https?$/i
1743
1807
  raise InvalidURIError,
1744
1808
  "Cannot set an HTTP request URI for a non-HTTP URI."
1745
1809
  end
1746
1810
  new_request_uri = new_request_uri.to_str
1747
- path_component = new_request_uri[/^([^\?]*)\?(?:.*)$/, 1]
1811
+ path_component = new_request_uri[/^([^\?]*)\??(?:.*)$/, 1]
1748
1812
  query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
1749
1813
  path_component = path_component.to_s
1750
1814
  path_component = (!path_component.empty? ? path_component : SLASH)
@@ -1773,7 +1837,7 @@ module Addressable
1773
1837
  @normalized_fragment ||= begin
1774
1838
  component = Addressable::URI.normalize_component(
1775
1839
  self.fragment,
1776
- Addressable::URI::CharacterClasses::FRAGMENT
1840
+ Addressable::URI::NormalizeCharacterClasses::FRAGMENT
1777
1841
  )
1778
1842
  component == "" ? nil : component
1779
1843
  end
@@ -1899,8 +1963,8 @@ module Addressable
1899
1963
  # Section 5.2.3 of RFC 3986
1900
1964
  #
1901
1965
  # Removes the right-most path segment from the base path.
1902
- if base_path =~ /\//
1903
- base_path.gsub!(/\/[^\/]+$/, SLASH)
1966
+ if base_path.include?(SLASH)
1967
+ base_path.sub!(/\/[^\/]+$/, SLASH)
1904
1968
  else
1905
1969
  base_path = EMPTY_STR
1906
1970
  end
@@ -2349,10 +2413,10 @@ module Addressable
2349
2413
  #
2350
2414
  # @param [Proc] block
2351
2415
  # A set of operations to perform on a given URI.
2352
- def defer_validation(&block)
2353
- raise LocalJumpError, "No block given." unless block
2416
+ def defer_validation
2417
+ raise LocalJumpError, "No block given." unless block_given?
2354
2418
  @validation_deferred = true
2355
- block.call()
2419
+ yield
2356
2420
  @validation_deferred = false
2357
2421
  validate
2358
2422
  return nil
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # encoding:utf-8
2
4
  #--
3
5
  # Copyright (C) Bob Aman
@@ -21,8 +23,8 @@ if !defined?(Addressable::VERSION)
21
23
  module Addressable
22
24
  module VERSION
23
25
  MAJOR = 2
24
- MINOR = 5
25
- TINY = 1
26
+ MINOR = 8
27
+ TINY = 0
26
28
 
27
29
  STRING = [MAJOR, MINOR, TINY].join('.')
28
30
  end
data/lib/addressable.rb CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'addressable/uri'
2
4
  require 'addressable/template'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # coding: utf-8
2
4
  # Copyright (C) Bob Aman
3
5
  #
@@ -26,9 +28,9 @@ shared_examples_for "converting from unicode to ASCII" do
26
28
  expect(Addressable::IDNA.to_ascii("www.google.com")).to eq("www.google.com")
27
29
  end
28
30
 
29
- LONG = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com'
30
- it "should convert '#{LONG}' correctly" do
31
- expect(Addressable::IDNA.to_ascii(LONG)).to eq(LONG)
31
+ long = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com'
32
+ it "should convert '#{long}' correctly" do
33
+ expect(Addressable::IDNA.to_ascii(long)).to eq(long)
32
34
  end
33
35
 
34
36
  it "should convert 'www.詹姆斯.com' correctly" do
@@ -134,6 +136,12 @@ shared_examples_for "converting from unicode to ASCII" do
134
136
  )).to eq("xn--4ud")
135
137
  end
136
138
 
139
+ it "should convert '🌹🌹🌹.ws' correctly" do
140
+ expect(Addressable::IDNA.to_ascii(
141
+ "\360\237\214\271\360\237\214\271\360\237\214\271.ws"
142
+ )).to eq("xn--2h8haa.ws")
143
+ end
144
+
137
145
  it "should handle two adjacent '.'s correctly" do
138
146
  expect(Addressable::IDNA.to_ascii(
139
147
  "example..host"
@@ -142,9 +150,9 @@ shared_examples_for "converting from unicode to ASCII" do
142
150
  end
143
151
 
144
152
  shared_examples_for "converting from ASCII to unicode" do
145
- LONG = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com'
146
- it "should convert '#{LONG}' correctly" do
147
- expect(Addressable::IDNA.to_unicode(LONG)).to eq(LONG)
153
+ long = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com'
154
+ it "should convert '#{long}' correctly" do
155
+ expect(Addressable::IDNA.to_unicode(long)).to eq(long)
148
156
  end
149
157
 
150
158
  it "should return the identity conversion when punycode decode fails" do
@@ -231,6 +239,12 @@ shared_examples_for "converting from ASCII to unicode" do
231
239
  )).to eq("\341\206\265")
232
240
  end
233
241
 
242
+ it "should convert '🌹🌹🌹.ws' correctly" do
243
+ expect(Addressable::IDNA.to_unicode(
244
+ "xn--2h8haa.ws"
245
+ )).to eq("\360\237\214\271\360\237\214\271\360\237\214\271.ws")
246
+ end
247
+
234
248
  it "should handle two adjacent '.'s correctly" do
235
249
  expect(Addressable::IDNA.to_unicode(
236
250
  "example..host"
@@ -280,7 +294,9 @@ begin
280
294
  it_should_behave_like "converting from unicode to ASCII"
281
295
  it_should_behave_like "converting from ASCII to unicode"
282
296
  end
283
- rescue LoadError
297
+ rescue LoadError => error
298
+ raise error if ENV["CI"] && TestHelper.native_supported?
299
+
284
300
  # Cannot test the native implementation without libidn support.
285
301
  warn('Could not load native IDN implementation.')
286
302
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # coding: utf-8
2
4
  # Copyright (C) Bob Aman
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # coding: utf-8
2
4
  # Copyright (C) Bob Aman
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # coding: utf-8
2
4
  # Copyright (C) Bob Aman
3
5
  #
@@ -17,6 +19,7 @@
17
19
  require "spec_helper"
18
20
 
19
21
  require "bigdecimal"
22
+ require "timeout"
20
23
  require "addressable/template"
21
24
 
22
25
  shared_examples_for 'expands' do |tests|
@@ -82,7 +85,7 @@ describe "Type conversion" do
82
85
  :hello => 1234,
83
86
  :nothing => nil,
84
87
  :sym => :symbolic,
85
- :decimal => BigDecimal.new('1')
88
+ :decimal => BigDecimal('1')
86
89
  }
87
90
  }
88
91
 
@@ -949,6 +952,36 @@ describe Addressable::Template do
949
952
  )
950
953
  end
951
954
  end
955
+ context "issue #307 - partial_expand form query with nil params" do
956
+ subject do
957
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
958
+ end
959
+ it "builds a new pattern with two=nil" do
960
+ expect(subject.partial_expand(two: nil).pattern).to eq(
961
+ "http://example.com/{?one}{&three}/"
962
+ )
963
+ end
964
+ it "builds a new pattern with one=nil and two=nil" do
965
+ expect(subject.partial_expand(one: nil, two: nil).pattern).to eq(
966
+ "http://example.com/{?three}/"
967
+ )
968
+ end
969
+ it "builds a new pattern with one=1 and two=nil" do
970
+ expect(subject.partial_expand(one: 1, two: nil).pattern).to eq(
971
+ "http://example.com/?one=1{&three}/"
972
+ )
973
+ end
974
+ it "builds a new pattern with one=nil and two=2" do
975
+ expect(subject.partial_expand(one: nil, two: 2).pattern).to eq(
976
+ "http://example.com/?two=2{&three}/"
977
+ )
978
+ end
979
+ it "builds a new pattern with one=nil" do
980
+ expect(subject.partial_expand(one: nil).pattern).to eq(
981
+ "http://example.com/{?two}{&three}/"
982
+ )
983
+ end
984
+ end
952
985
  context "partial_expand with query string" do
953
986
  subject {
954
987
  Addressable::Template.new("http://example.com/{?two,one}/")
@@ -1308,6 +1341,14 @@ describe Addressable::Template do
1308
1341
  expect(subject).not_to match("foo_bar*")
1309
1342
  expect(subject).not_to match("foo_bar:20")
1310
1343
  end
1344
+
1345
+ it 'should parse in a reasonable time' do
1346
+ expect do
1347
+ Timeout.timeout(0.1) do
1348
+ expect(subject).not_to match("0"*25 + "!")
1349
+ end
1350
+ end.not_to raise_error
1351
+ end
1311
1352
  end
1312
1353
  context "VARIABLE_LIST" do
1313
1354
  subject { Addressable::Template::VARIABLE_LIST }