addressable 2.5.1 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 }