addressable 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of addressable might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7327067f0dbb5a74d2da6ddff4227408af2f91aa
4
- data.tar.gz: a2d86db5e9be6a83ba2e17c825fe4d30bfbac28f
3
+ metadata.gz: afa411655b3715e655e0cdca551d20df31b83491
4
+ data.tar.gz: 0cd2871c45abb19f6b6238ad202856e7afb8619d
5
5
  SHA512:
6
- metadata.gz: afd48eec1e6c38e64f34834cbac4af4f59464f66c9616a95ec74fc0f2c005d2c1c27046d6eccbdafb81d4e0bd974d9cd9fc40beb534911f2943f56c9bcc4da7b
7
- data.tar.gz: 6a4de13abead387b0f31861e16887181a8fd686dcdb37f82f04a2fe69be91bdde5b34436269677b47477061d66d98192fbb31c67c1114b10e205c8f23b542d2b
6
+ metadata.gz: 16274bd51d38c58b3712dbbdad1368417051937dbe33d74fa73951a39b71d824092c0ecd50005d467db9c914cc074fd0344282df2e23e1182c764b1a1da9750a
7
+ data.tar.gz: c53ff1d0e09a5e3d997a13a464d50c45fd7d548326f94d3ad40dc97753ce3a47aad4804a62faea48b89775394538982e70ac22d7488fab8ee6594b6816d8ff28
@@ -1,3 +1,24 @@
1
+ # Addressable 2.6.0
2
+ - added `tld=` method to allow assignment to the public suffix
3
+ - most `heuristic_parse` patterns are now case-insensitive
4
+ - `heuristic_parse` handles more `file://` URI variations
5
+ - fixes bug in `heuristic_parse` when uri starts with digit
6
+ - fixes bug in `request_uri=` with query strings
7
+ - fixes template issues with `nil` and `?` operator
8
+ - `frozen_string_literal` pragmas added
9
+ - minor performance improvements in regexps
10
+ - fixes to eliminate warnings
11
+
12
+ # Addressable 2.5.2
13
+ - better support for frozen string literals
14
+ - fixed bug w/ uppercase characters in scheme
15
+ - IDNA errors w/ emoji URLs
16
+ - compatibility w/ public_suffix 3.x
17
+
18
+ # Addressable 2.5.1
19
+ - allow unicode normalization to be disabled for URI Template expansion
20
+ - removed duplicate test
21
+
1
22
  # Addressable 2.5.0
2
23
  - dropping support for Ruby 1.9
3
24
  - adding support for Ruby 2.4 preview
data/Gemfile CHANGED
@@ -3,7 +3,7 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  group :test do
6
- gem 'rspec', '~> 3.0'
6
+ gem 'rspec', '~> 3.5'
7
7
  gem 'rspec-its', '~> 1.1'
8
8
  end
9
9
 
@@ -29,4 +29,4 @@ group :test, :development do
29
29
  end
30
30
  end
31
31
 
32
- gem 'idn-ruby', :platform => [:mri_20, :mri_21, :mri_22]
32
+ gem 'idn-ruby', :platform => [:mri_20, :mri_21, :mri_22, :mri_23, :mri_24]
data/README.md CHANGED
@@ -8,15 +8,12 @@
8
8
  </dl>
9
9
 
10
10
  [![Gem Version](http://img.shields.io/gem/dt/addressable.svg)][gem]
11
- [![Build Status](https://secure.travis-ci.org/sporkmonger/addressable.png?branch=master)][travis]
12
- [![Dependency Status](https://gemnasium.com/sporkmonger/addressable.png?travis)][gemnasium]
11
+ [![Build Status](https://secure.travis-ci.org/sporkmonger/addressable.svg?branch=master)][travis]
13
12
  [![Test Coverage Status](https://img.shields.io/coveralls/sporkmonger/addressable.svg)][coveralls]
14
13
  [![Documentation Coverage Status](http://inch-ci.org/github/sporkmonger/addressable.svg?branch=master)][inch]
15
- [![Gittip Donate](http://img.shields.io/gittip/sporkmonger.png)](https://www.gittip.com/sporkmonger/ "Support Open Source Development w/ Gittip")
16
14
 
17
15
  [gem]: https://rubygems.org/gems/addressable
18
16
  [travis]: http://travis-ci.org/sporkmonger/addressable
19
- [gemnasium]: https://gemnasium.com/sporkmonger/addressable
20
17
  [coveralls]: https://coveralls.io/r/sporkmonger/addressable
21
18
  [inch]: http://inch-ci.org/github/sporkmonger/addressable
22
19
 
@@ -59,7 +56,7 @@ For more details, see [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570.txt).
59
56
 
60
57
  require "addressable/template"
61
58
 
62
- template = Addressable::Template.new("http://example.com/{?query*}/")
59
+ template = Addressable::Template.new("http://example.com/{?query*}")
63
60
  template.expand({
64
61
  "query" => {
65
62
  'foo' => 'bar',
@@ -106,7 +103,7 @@ $ gem install idn-ruby
106
103
 
107
104
  # Semantic Versioning
108
105
 
109
- This project uses sementic versioning. You can (and should) specify your
106
+ This project uses [Semantic Versioning](https://semver.org/). You can (and should) specify your
110
107
  dependency using a pessimistic version constraint covering the major and minor
111
108
  values:
112
109
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubygems'
2
4
  require 'rake'
3
5
 
@@ -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
  # encoding:utf-8
2
4
  #--
3
5
  # Copyright (C) Bob Aman
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # encoding:utf-8
2
4
  #--
3
5
  # Copyright (C) Bob Aman
@@ -35,7 +37,7 @@ module Addressable
35
37
  def self.to_ascii(value)
36
38
  value.to_s.split('.', -1).map do |segment|
37
39
  if segment.size > 0 && segment.size < 64
38
- IDN::Idna.toASCII(segment)
40
+ IDN::Idna.toASCII(segment, IDN::Idna::ALLOW_UNASSIGNED)
39
41
  elsif segment.size >= 64
40
42
  segment
41
43
  else
@@ -47,7 +49,7 @@ module Addressable
47
49
  def self.to_unicode(value)
48
50
  value.to_s.split('.', -1).map do |segment|
49
51
  if segment.size > 0 && segment.size < 64
50
- IDN::Idna.toUnicode(segment)
52
+ IDN::Idna.toUnicode(segment, IDN::Idna::ALLOW_UNASSIGNED)
51
53
  elsif segment.size >= 64
52
54
  segment
53
55
  else
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # encoding:utf-8
2
4
  #--
3
5
  # Copyright (C) Bob Aman
@@ -146,16 +148,13 @@ module Addressable
146
148
  starter_cc = 256 if starter_cc != 0
147
149
  for i in 1...length
148
150
  ch = unpacked[i]
149
- cc = lookup_unicode_combining_class(ch)
150
151
 
151
152
  if (starter_cc == 0 &&
152
153
  (composite = unicode_compose_pair(starter, ch)) != nil)
153
154
  starter = composite
154
- startercc = lookup_unicode_combining_class(composite)
155
155
  else
156
156
  unpacked_result << starter
157
157
  starter = ch
158
- startercc = cc
159
158
  end
160
159
  end
161
160
  unpacked_result << starter
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # encoding:utf-8
2
4
  #--
3
5
  # Copyright (C) Bob Aman
@@ -488,6 +490,8 @@ module Addressable
488
490
  # @param [Hash] mapping The mapping that corresponds to the pattern.
489
491
  # @param [#validate, #transform] processor
490
492
  # An optional processor object may be supplied.
493
+ # @param [Boolean] normalize_values
494
+ # Optional flag to enable/disable unicode normalization. Default: true
491
495
  #
492
496
  # The object should respond to either the <tt>validate</tt> or
493
497
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -518,11 +522,11 @@ module Addressable
518
522
  # "http://example.com/{?one,two,three}/"
519
523
  # ).partial_expand({"one" => "1", "three" => 3}).pattern
520
524
  # #=> "http://example.com/?one=1{&two}&three=3"
521
- def partial_expand(mapping, processor=nil)
525
+ def partial_expand(mapping, processor=nil, normalize_values=true)
522
526
  result = self.pattern.dup
523
527
  mapping = normalize_keys(mapping)
524
528
  result.gsub!( EXPRESSION ) do |capture|
525
- transform_partial_capture(mapping, capture, processor)
529
+ transform_partial_capture(mapping, capture, processor, normalize_values)
526
530
  end
527
531
  return Addressable::Template.new(result)
528
532
  end
@@ -533,6 +537,8 @@ module Addressable
533
537
  # @param [Hash] mapping The mapping that corresponds to the pattern.
534
538
  # @param [#validate, #transform] processor
535
539
  # An optional processor object may be supplied.
540
+ # @param [Boolean] normalize_values
541
+ # Optional flag to enable/disable unicode normalization. Default: true
536
542
  #
537
543
  # The object should respond to either the <tt>validate</tt> or
538
544
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -583,11 +589,11 @@ module Addressable
583
589
  # ExampleProcessor
584
590
  # ).to_str
585
591
  # #=> Addressable::Template::InvalidTemplateValueError
586
- def expand(mapping, processor=nil)
592
+ def expand(mapping, processor=nil, normalize_values=true)
587
593
  result = self.pattern.dup
588
594
  mapping = normalize_keys(mapping)
589
595
  result.gsub!( EXPRESSION ) do |capture|
590
- transform_capture(mapping, capture, processor)
596
+ transform_capture(mapping, capture, processor, normalize_values)
591
597
  end
592
598
  return Addressable::URI.parse(result)
593
599
  end
@@ -704,6 +710,8 @@ module Addressable
704
710
  # The expression to expand
705
711
  # @param [#validate, #transform] processor
706
712
  # An optional processor object may be supplied.
713
+ # @param [Boolean] normalize_values
714
+ # Optional flag to enable/disable unicode normalization. Default: true
707
715
  #
708
716
  # The object should respond to either the <tt>validate</tt> or
709
717
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -718,56 +726,36 @@ module Addressable
718
726
  # after sending the value to the transform method.
719
727
  #
720
728
  # @return [String] The expanded expression
721
- def transform_partial_capture(mapping, capture, processor = nil)
729
+ def transform_partial_capture(mapping, capture, processor = nil,
730
+ normalize_values = true)
722
731
  _, operator, varlist = *capture.match(EXPRESSION)
723
732
 
724
- vars = varlist.split(',')
733
+ vars = varlist.split(",")
725
734
 
726
- if '?' == operator
735
+ if operator == "?"
727
736
  # partial expansion of form style query variables sometimes requires a
728
737
  # slight reordering of the variables to produce a valid url.
729
738
  first_to_expand = vars.find { |varspec|
730
739
  _, name, _ = *varspec.match(VARSPEC)
731
- mapping.key? name
740
+ mapping.key?(name) && !mapping[name].nil?
732
741
  }
733
742
 
734
743
  vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand} if first_to_expand
735
744
  end
736
745
 
737
- vars
738
- .zip(operator_sequence(operator).take(vars.length))
739
- .reduce("") do |acc, (varspec, op)|
746
+ vars.
747
+ inject("".dup) do |acc, varspec|
740
748
  _, name, _ = *varspec.match(VARSPEC)
741
-
742
- acc << if mapping.key? name
743
- transform_capture(mapping, "{#{op}#{varspec}}", processor)
744
- else
745
- "{#{op}#{varspec}}"
746
- end
747
- end
748
- end
749
-
750
- ##
751
- # Creates a lazy Enumerator of the operators that should be used to expand
752
- # variables in a varlist starting with `operator`. For example, an operator
753
- # `"?"` results in the sequence `"?","&","&"...`
754
- #
755
- # @param [String] operator from which to generate a sequence
756
- #
757
- # @return [Enumerator] sequence of operators
758
- def operator_sequence(operator)
759
- rest_operator = if "?" == operator
760
- "&"
761
- else
762
- operator
763
- end
764
- head_operator = operator
765
-
766
- Enumerator.new do |y|
767
- y << head_operator.to_s
768
- while true
769
- y << rest_operator.to_s
770
- end
749
+ next_val = if mapping.key? name
750
+ transform_capture(mapping, "{#{operator}#{varspec}}",
751
+ processor, normalize_values)
752
+ else
753
+ "{#{operator}#{varspec}}"
754
+ end
755
+ # If we've already expanded at least one '?' operator with non-empty
756
+ # value, change to '&'
757
+ operator = "&" if (operator == "?") && (next_val != "")
758
+ acc << next_val
771
759
  end
772
760
  end
773
761
 
@@ -780,6 +768,9 @@ module Addressable
780
768
  # The expression to replace
781
769
  # @param [#validate, #transform] processor
782
770
  # An optional processor object may be supplied.
771
+ # @param [Boolean] normalize_values
772
+ # Optional flag to enable/disable unicode normalization. Default: true
773
+ #
783
774
  #
784
775
  # The object should respond to either the <tt>validate</tt> or
785
776
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -794,7 +785,8 @@ module Addressable
794
785
  # after sending the value to the transform method.
795
786
  #
796
787
  # @return [String] The expanded expression
797
- def transform_capture(mapping, capture, processor=nil)
788
+ def transform_capture(mapping, capture, processor=nil,
789
+ normalize_values=true)
798
790
  _, operator, varlist = *capture.match(EXPRESSION)
799
791
  return_value = varlist.split(',').inject([]) do |acc, varspec|
800
792
  _, name, modifier = *varspec.match(VARSPEC)
@@ -814,7 +806,7 @@ module Addressable
814
806
  "Can't convert #{value.class} into String or Array."
815
807
  end
816
808
 
817
- value = normalize_value(value)
809
+ value = normalize_value(value) if normalize_values
818
810
 
819
811
  if processor == nil || !processor.respond_to?(:transform)
820
812
  # Handle percent escaping
@@ -877,7 +869,9 @@ module Addressable
877
869
  end
878
870
  if processor.respond_to?(:transform)
879
871
  transformed_value = processor.transform(name, value)
880
- transformed_value = normalize_value(transformed_value)
872
+ if normalize_values
873
+ transformed_value = normalize_value(transformed_value)
874
+ end
881
875
  end
882
876
  end
883
877
  acc << [name, transformed_value]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # encoding:utf-8
2
4
  #--
3
5
  # Copyright (C) Bob Aman
@@ -122,9 +124,9 @@ module Addressable
122
124
  user = userinfo.strip[/^([^:]*):?/, 1]
123
125
  password = userinfo.strip[/:(.*)$/, 1]
124
126
  end
125
- host = authority.gsub(
127
+ host = authority.sub(
126
128
  /^([^\[\]]*)@/, EMPTY_STR
127
- ).gsub(
129
+ ).sub(
128
130
  /:([^:@\[\]]*?)$/, EMPTY_STR
129
131
  )
130
132
  port = authority[/:([^:@\[\]]*?)$/, 1]
@@ -182,18 +184,24 @@ module Addressable
182
184
  :scheme => "http"
183
185
  }.merge(hints)
184
186
  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:///")
187
+ when /^http:\//i
188
+ uri.sub!(/^http:\/+/i, "http://")
189
+ when /^https:\//i
190
+ uri.sub!(/^https:\/+/i, "https://")
191
+ when /^feed:\/+http:\//i
192
+ uri.sub!(/^feed:\/+http:\/+/i, "feed:http://")
193
+ when /^feed:\//i
194
+ uri.sub!(/^feed:\/+/i, "feed://")
195
+ when %r[^file:/{4}]i
196
+ uri.sub!(%r[^file:/+]i, "file:////")
197
+ when %r[^file://localhost/]i
198
+ uri.sub!(%r[^file://localhost/+]i, "file:///")
199
+ when %r[^file:/+]i
200
+ uri.sub!(%r[^file:/+]i, "file:///")
195
201
  when /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
196
- uri.gsub!(/^/, hints[:scheme] + "://")
202
+ uri.sub!(/^/, hints[:scheme] + "://")
203
+ when /\A\d+\..*:\d+\z/
204
+ uri = "#{hints[:scheme]}://#{uri}"
197
205
  end
198
206
  match = uri.match(URIREGEX)
199
207
  fragments = match.captures
@@ -202,6 +210,7 @@ module Addressable
202
210
  new_authority = authority.gsub(/\\/, '/').gsub(/ /, '%20')
203
211
  # NOTE: We want offset 4, not 3!
204
212
  offset = match.offset(4)
213
+ uri = uri.dup
205
214
  uri[offset[0]...offset[1]] = new_authority
206
215
  end
207
216
  parsed = self.parse(uri)
@@ -212,7 +221,7 @@ module Addressable
212
221
  new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
213
222
  if new_host
214
223
  parsed.defer_validation do
215
- new_path = parsed.path.gsub(
224
+ new_path = parsed.path.sub(
216
225
  Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR)
217
226
  parsed.host = new_host
218
227
  parsed.path = new_path
@@ -263,19 +272,19 @@ module Addressable
263
272
  # Otherwise, convert to a String
264
273
  path = path.to_str.strip
265
274
 
266
- path.gsub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
275
+ path.sub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
267
276
  path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/
268
277
  uri = self.parse(path)
269
278
 
270
279
  if uri.scheme == nil
271
280
  # Adjust windows-style uris
272
- uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
281
+ uri.path.sub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
273
282
  "/#{$1.downcase}:/"
274
283
  end
275
284
  uri.path.gsub!(/\\/, SLASH)
276
285
  if File.exist?(uri.path) &&
277
286
  File.stat(uri.path).directory?
278
- uri.path.gsub!(/\/$/, EMPTY_STR)
287
+ uri.path.sub!(/\/$/, EMPTY_STR)
279
288
  uri.path = uri.path + '/'
280
289
  end
281
290
 
@@ -436,7 +445,7 @@ module Addressable
436
445
  uri = uri.dup
437
446
  # Seriously, only use UTF-8. I'm really not kidding!
438
447
  uri.force_encoding("utf-8")
439
- leave_encoded.force_encoding("utf-8")
448
+ leave_encoded = leave_encoded.dup.force_encoding("utf-8")
440
449
  result = uri.gsub(/%[0-9a-f]{2}/iu) do |sequence|
441
450
  c = sequence[1..3].to_i(16).chr
442
451
  c.force_encoding("utf-8")
@@ -847,7 +856,7 @@ module Addressable
847
856
  return nil unless self.scheme
848
857
  @normalized_scheme ||= begin
849
858
  if self.scheme =~ /^\s*ssh\+svn\s*$/i
850
- "svn+ssh"
859
+ "svn+ssh".dup
851
860
  else
852
861
  Addressable::URI.normalize_component(
853
862
  self.scheme.strip.downcase,
@@ -1032,9 +1041,9 @@ module Addressable
1032
1041
  if !current_user && !current_password
1033
1042
  nil
1034
1043
  elsif current_user && current_password
1035
- "#{current_user}:#{current_password}"
1044
+ "#{current_user}:#{current_password}".dup
1036
1045
  elsif current_user && !current_password
1037
- "#{current_user}"
1046
+ "#{current_user}".dup
1038
1047
  end
1039
1048
  end
1040
1049
  # All normalized values should be UTF-8
@@ -1101,7 +1110,7 @@ module Addressable
1101
1110
  CharacterClasses::HOST)
1102
1111
  result
1103
1112
  else
1104
- EMPTY_STR
1113
+ EMPTY_STR.dup
1105
1114
  end
1106
1115
  end
1107
1116
  # All normalized values should be UTF-8
@@ -1168,6 +1177,15 @@ module Addressable
1168
1177
  PublicSuffix.parse(self.host, ignore_private: true).tld
1169
1178
  end
1170
1179
 
1180
+ ##
1181
+ # Sets the top-level domain for this URI.
1182
+ #
1183
+ # @param [String, #to_str] new_tld The new top-level domain.
1184
+ def tld=(new_tld)
1185
+ replaced_tld = domain.sub(/#{tld}\z/, new_tld)
1186
+ self.host = PublicSuffix::Domain.new(replaced_tld).to_s
1187
+ end
1188
+
1171
1189
  ##
1172
1190
  # Returns the public suffix domain for this host.
1173
1191
  #
@@ -1235,9 +1253,9 @@ module Addressable
1235
1253
  new_user = new_userinfo.strip[/^([^:]*):?/, 1]
1236
1254
  new_password = new_userinfo.strip[/:(.*)$/, 1]
1237
1255
  end
1238
- new_host = new_authority.gsub(
1256
+ new_host = new_authority.sub(
1239
1257
  /^([^\[\]]*)@/, EMPTY_STR
1240
- ).gsub(
1258
+ ).sub(
1241
1259
  /:([^:@\[\]]*?)$/, EMPTY_STR
1242
1260
  )
1243
1261
  new_port =
@@ -1421,7 +1439,7 @@ module Addressable
1421
1439
  # @return [String] The components that identify a site.
1422
1440
  def site
1423
1441
  (self.scheme || self.authority) && @site ||= begin
1424
- site_string = ""
1442
+ site_string = "".dup
1425
1443
  site_string << "#{self.scheme}:" if self.scheme != nil
1426
1444
  site_string << "//#{self.authority}" if self.authority != nil
1427
1445
  site_string
@@ -1440,7 +1458,7 @@ module Addressable
1440
1458
  def normalized_site
1441
1459
  return nil unless self.site
1442
1460
  @normalized_site ||= begin
1443
- site_string = ""
1461
+ site_string = "".dup
1444
1462
  if self.normalized_scheme != nil
1445
1463
  site_string << "#{self.normalized_scheme}:"
1446
1464
  end
@@ -1508,7 +1526,7 @@ module Addressable
1508
1526
  result = URI.normalize_path(result)
1509
1527
  if result.empty? &&
1510
1528
  ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
1511
- result = SLASH
1529
+ result = SLASH.dup
1512
1530
  end
1513
1531
  result
1514
1532
  end
@@ -1544,7 +1562,7 @@ module Addressable
1544
1562
  # @return [String] The path's basename.
1545
1563
  def basename
1546
1564
  # Path cannot be nil
1547
- return File.basename(self.path).gsub(/;[^\/]*$/, EMPTY_STR)
1565
+ return File.basename(self.path).sub(/;[^\/]*$/, EMPTY_STR)
1548
1566
  end
1549
1567
 
1550
1568
  ##
@@ -1694,7 +1712,7 @@ module Addressable
1694
1712
  end
1695
1713
 
1696
1714
  # new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
1697
- buffer = ""
1715
+ buffer = "".dup
1698
1716
  new_query_values.each do |key, value|
1699
1717
  encoded_key = URI.encode_component(
1700
1718
  key, CharacterClasses::UNRESERVED
@@ -1724,7 +1742,7 @@ module Addressable
1724
1742
  #
1725
1743
  # @return [String] The request URI required for an HTTP request.
1726
1744
  def request_uri
1727
- return nil if self.absolute? && self.scheme !~ /^https?$/
1745
+ return nil if self.absolute? && self.scheme !~ /^https?$/i
1728
1746
  return (
1729
1747
  (!self.path.empty? ? self.path : SLASH) +
1730
1748
  (self.query ? "?#{self.query}" : EMPTY_STR)
@@ -1739,12 +1757,12 @@ module Addressable
1739
1757
  if !new_request_uri.respond_to?(:to_str)
1740
1758
  raise TypeError, "Can't convert #{new_request_uri.class} into String."
1741
1759
  end
1742
- if self.absolute? && self.scheme !~ /^https?$/
1760
+ if self.absolute? && self.scheme !~ /^https?$/i
1743
1761
  raise InvalidURIError,
1744
1762
  "Cannot set an HTTP request URI for a non-HTTP URI."
1745
1763
  end
1746
1764
  new_request_uri = new_request_uri.to_str
1747
- path_component = new_request_uri[/^([^\?]*)\?(?:.*)$/, 1]
1765
+ path_component = new_request_uri[/^([^\?]*)\??(?:.*)$/, 1]
1748
1766
  query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
1749
1767
  path_component = path_component.to_s
1750
1768
  path_component = (!path_component.empty? ? path_component : SLASH)
@@ -1900,7 +1918,7 @@ module Addressable
1900
1918
  #
1901
1919
  # Removes the right-most path segment from the base path.
1902
1920
  if base_path =~ /\//
1903
- base_path.gsub!(/\/[^\/]+$/, SLASH)
1921
+ base_path.sub!(/\/[^\/]+$/, SLASH)
1904
1922
  else
1905
1923
  base_path = EMPTY_STR
1906
1924
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # encoding:utf-8
2
4
  #--
3
5
  # Copyright (C) Bob Aman
@@ -21,7 +23,7 @@ if !defined?(Addressable::VERSION)
21
23
  module Addressable
22
24
  module VERSION
23
25
  MAJOR = 2
24
- MINOR = 5
26
+ MINOR = 6
25
27
  TINY = 0
26
28
 
27
29
  STRING = [MAJOR, MINOR, TINY].join('.')
@@ -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"
@@ -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
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # coding: utf-8
2
4
  # Copyright (C) Bob Aman
3
5
  #
@@ -82,7 +84,7 @@ describe "Type conversion" do
82
84
  :hello => 1234,
83
85
  :nothing => nil,
84
86
  :sym => :symbolic,
85
- :decimal => BigDecimal.new('1')
87
+ :decimal => BigDecimal('1')
86
88
  }
87
89
  }
88
90
 
@@ -91,7 +93,7 @@ describe "Type conversion" do
91
93
  '{hello}' => '1234',
92
94
  '{nothing}' => '',
93
95
  '{sym}' => 'symbolic',
94
- '{decimal}' => '0.1E1'
96
+ '{decimal}' => RUBY_VERSION < '2.4.0' ? '0.1E1' : '0.1e1'
95
97
  }
96
98
  end
97
99
 
@@ -949,6 +951,36 @@ describe Addressable::Template do
949
951
  )
950
952
  end
951
953
  end
954
+ context "issue #307 - partial_expand form query with nil params" do
955
+ subject do
956
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
957
+ end
958
+ it "builds a new pattern with two=nil" do
959
+ expect(subject.partial_expand(two: nil).pattern).to eq(
960
+ "http://example.com/{?one}{&three}/"
961
+ )
962
+ end
963
+ it "builds a new pattern with one=nil and two=nil" do
964
+ expect(subject.partial_expand(one: nil, two: nil).pattern).to eq(
965
+ "http://example.com/{?three}/"
966
+ )
967
+ end
968
+ it "builds a new pattern with one=1 and two=nil" do
969
+ expect(subject.partial_expand(one: 1, two: nil).pattern).to eq(
970
+ "http://example.com/?one=1{&three}/"
971
+ )
972
+ end
973
+ it "builds a new pattern with one=nil and two=2" do
974
+ expect(subject.partial_expand(one: nil, two: 2).pattern).to eq(
975
+ "http://example.com/?two=2{&three}/"
976
+ )
977
+ end
978
+ it "builds a new pattern with one=nil" do
979
+ expect(subject.partial_expand(one: nil).pattern).to eq(
980
+ "http://example.com/{?two}{&three}/"
981
+ )
982
+ end
983
+ end
952
984
  context "partial_expand with query string" do
953
985
  subject {
954
986
  Addressable::Template.new("http://example.com/{?two,one}/")
@@ -969,6 +1001,24 @@ describe Addressable::Template do
969
1001
  )
970
1002
  end
971
1003
  end
1004
+ context "partial expand with unicode values" do
1005
+ subject do
1006
+ Addressable::Template.new("http://example.com/{resource}/{query}/")
1007
+ end
1008
+ it "normalizes unicode by default" do
1009
+ template = subject.partial_expand("query" => "Cafe\u0301")
1010
+ expect(template.pattern).to eq(
1011
+ "http://example.com/{resource}/Caf%C3%A9/"
1012
+ )
1013
+ end
1014
+
1015
+ it "does not normalize unicode when byte semantics requested" do
1016
+ template = subject.partial_expand({"query" => "Cafe\u0301"}, nil, false)
1017
+ expect(template.pattern).to eq(
1018
+ "http://example.com/{resource}/Cafe%CC%81/"
1019
+ )
1020
+ end
1021
+ end
972
1022
  end
973
1023
  describe "Partial expand with strings" do
974
1024
  context "partial_expand with two simple values" do
@@ -1013,6 +1063,20 @@ describe Addressable::Template do
1013
1063
  end
1014
1064
  end
1015
1065
  describe "Expand" do
1066
+ context "expand with unicode values" do
1067
+ subject do
1068
+ Addressable::Template.new("http://example.com/search/{query}/")
1069
+ end
1070
+ it "normalizes unicode by default" do
1071
+ uri = subject.expand("query" => "Cafe\u0301").to_str
1072
+ expect(uri).to eq("http://example.com/search/Caf%C3%A9/")
1073
+ end
1074
+
1075
+ it "does not normalize unicode when byte semantics requested" do
1076
+ uri = subject.expand({ "query" => "Cafe\u0301" }, nil, false).to_str
1077
+ expect(uri).to eq("http://example.com/search/Cafe%CC%81/")
1078
+ end
1079
+ end
1016
1080
  context "expand with a processor" do
1017
1081
  subject {
1018
1082
  Addressable::Template.new("http://example.com/search/{query}/")
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # coding: utf-8
2
4
  # Copyright (C) Bob Aman
3
5
  #
@@ -125,14 +127,6 @@ describe Addressable::URI, "when created with a non-string authority" do
125
127
  end
126
128
  end
127
129
 
128
- describe Addressable::URI, "when created with a non-string authority" do
129
- it "should raise an error" do
130
- expect(lambda do
131
- Addressable::URI.new(:authority => :bogus)
132
- end).to raise_error(TypeError)
133
- end
134
- end
135
-
136
130
  describe Addressable::URI, "when created with a non-string path" do
137
131
  it "should raise an error" do
138
132
  expect(lambda do
@@ -963,6 +957,10 @@ describe Addressable::URI, "when frozen" do
963
957
  expect(@uri.normalize.query).to eq("a=1")
964
958
  end
965
959
 
960
+ it "returns '/%70a%74%68?a=%31' for #request_uri" do
961
+ expect(@uri.request_uri).to eq("/%70a%74%68?a=%31")
962
+ end
963
+
966
964
  it "returns '1%323' for #fragment" do
967
965
  expect(@uri.fragment).to eq("1%323")
968
966
  end
@@ -1979,15 +1977,29 @@ end
1979
1977
 
1980
1978
  # Section 5.1.2 of RFC 2616
1981
1979
  describe Addressable::URI, "when parsed from " +
1982
- "'http://www.w3.org/pub/WWW/TheProject.html'" do
1980
+ "'HTTP://www.w3.org/pub/WWW/TheProject.html'" do
1983
1981
  before do
1984
- @uri = Addressable::URI.parse("http://www.w3.org/pub/WWW/TheProject.html")
1982
+ @uri = Addressable::URI.parse("HTTP://www.w3.org/pub/WWW/TheProject.html")
1985
1983
  end
1986
1984
 
1987
1985
  it "should have the correct request URI" do
1988
1986
  expect(@uri.request_uri).to eq("/pub/WWW/TheProject.html")
1989
1987
  end
1990
1988
 
1989
+ it "should have the correct request URI after assignment" do
1990
+ @uri.request_uri = "/pub/WWW/TheProject.html?"
1991
+ expect(@uri.request_uri).to eq("/pub/WWW/TheProject.html?")
1992
+ expect(@uri.path).to eq("/pub/WWW/TheProject.html")
1993
+ expect(@uri.query).to eq("")
1994
+ end
1995
+
1996
+ it "should have the correct request URI after assignment" do
1997
+ @uri.request_uri = "/some/where/else.html"
1998
+ expect(@uri.request_uri).to eq("/some/where/else.html")
1999
+ expect(@uri.path).to eq("/some/where/else.html")
2000
+ expect(@uri.query).to eq(nil)
2001
+ end
2002
+
1991
2003
  it "should have the correct request URI after assignment" do
1992
2004
  @uri.request_uri = "/some/where/else.html?query?string"
1993
2005
  expect(@uri.request_uri).to eq("/some/where/else.html?query?string")
@@ -2016,7 +2028,7 @@ describe Addressable::URI, "when parsed from " +
2016
2028
 
2017
2029
  it "should correctly convert to a hash" do
2018
2030
  expect(@uri.to_hash).to eq({
2019
- :scheme => "http",
2031
+ :scheme => "HTTP",
2020
2032
  :user => nil,
2021
2033
  :password => nil,
2022
2034
  :host => "www.w3.org",
@@ -5494,6 +5506,31 @@ describe Addressable::URI, "when given the path '/one/two/'" do
5494
5506
  end
5495
5507
  end
5496
5508
 
5509
+ describe Addressable::URI, "when given the tld " do
5510
+ it "'uk' should have a tld of 'uk'" do
5511
+ uri = Addressable::URI.parse("http://example.com")
5512
+ uri.tld = "uk"
5513
+
5514
+ expect(uri.tld).to eq("uk")
5515
+ end
5516
+
5517
+ context "which " do
5518
+ let (:uri) { Addressable::URI.parse("http://comrade.net/path/to/source/") }
5519
+
5520
+ it "contains a subdomain" do
5521
+ uri.tld = "co.uk"
5522
+
5523
+ expect(uri.to_s).to eq("http://comrade.co.uk/path/to/source/")
5524
+ end
5525
+
5526
+ it "is part of the domain" do
5527
+ uri.tld = "com"
5528
+
5529
+ expect(uri.to_s).to eq("http://comrade.com/path/to/source/")
5530
+ end
5531
+ end
5532
+ end
5533
+
5497
5534
  describe Addressable::URI, "when given the path " +
5498
5535
  "'c:\\windows\\My Documents 100%20\\foo.txt'" do
5499
5536
  before do
@@ -6231,6 +6268,18 @@ describe Addressable::URI, "when given the input " +
6231
6268
  end
6232
6269
  end
6233
6270
 
6271
+ describe Addressable::URI, "when given the input which "\
6272
+ "start with digits and has specified port" do
6273
+ before do
6274
+ @input = "7777.example.org:8089"
6275
+ end
6276
+
6277
+ it "should heuristically parse to 'http://7777.example.org:8089'" do
6278
+ uri = Addressable::URI.heuristic_parse(@input)
6279
+ expect(uri.to_s).to eq("http://7777.example.org:8089")
6280
+ end
6281
+ end
6282
+
6234
6283
  describe Addressable::URI, "when given the input " +
6235
6284
  "'feed:///example.com'" do
6236
6285
  before do
@@ -6243,6 +6292,18 @@ describe Addressable::URI, "when given the input " +
6243
6292
  end
6244
6293
  end
6245
6294
 
6295
+ describe Addressable::URI, "when given the input " +
6296
+ "'file://localhost/path/to/resource/'" do
6297
+ before do
6298
+ @input = "file://localhost/path/to/resource/"
6299
+ end
6300
+
6301
+ it "should heuristically parse to 'file:///path/to/resource/'" do
6302
+ @uri = Addressable::URI.heuristic_parse(@input)
6303
+ expect(@uri.to_s).to eq("file:///path/to/resource/")
6304
+ end
6305
+ end
6306
+
6246
6307
  describe Addressable::URI, "when given the input " +
6247
6308
  "'file://path/to/resource/'" do
6248
6309
  before do
@@ -6255,6 +6316,18 @@ describe Addressable::URI, "when given the input " +
6255
6316
  end
6256
6317
  end
6257
6318
 
6319
+ describe Addressable::URI, "when given the input " +
6320
+ "'file://///path/to/resource/'" do
6321
+ before do
6322
+ @input = "file:///////path/to/resource/"
6323
+ end
6324
+
6325
+ it "should heuristically parse to 'file:////path/to/resource/'" do
6326
+ @uri = Addressable::URI.heuristic_parse(@input)
6327
+ expect(@uri.to_s).to eq("file:////path/to/resource/")
6328
+ end
6329
+ end
6330
+
6258
6331
  describe Addressable::URI, "when given the input " +
6259
6332
  "'feed://http://example.com'" do
6260
6333
  before do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/setup'
2
4
  require 'rspec/its'
3
5
 
@@ -18,4 +20,5 @@ end
18
20
 
19
21
  RSpec.configure do |config|
20
22
  config.warnings = true
23
+ config.filter_run_when_matching :focus
21
24
  end
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  desc "Remove all build products"
2
4
  task "clobber"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rubygems/package_task"
2
4
 
3
5
  namespace :gem do
@@ -20,8 +22,8 @@ namespace :gem do
20
22
 
21
23
  s.required_ruby_version = '>= 2.0'
22
24
 
23
- s.add_runtime_dependency 'public_suffix', '~> 2.0', '>= 2.0.2'
24
- s.add_development_dependency 'bundler', '~> 1.0'
25
+ s.add_runtime_dependency 'public_suffix', '>= 2.0.2', '< 4.0'
26
+ s.add_development_dependency 'bundler', '>= 1.0', '< 3.0'
25
27
 
26
28
  s.require_path = "lib"
27
29
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :git do
2
4
  namespace :tag do
3
5
  desc "List tags from the Git repository"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :metrics do
2
4
  task :lines do
3
5
  lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rspec/core/rake_task"
2
4
 
3
5
  namespace :spec do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rake"
2
4
 
3
5
  begin
metadata CHANGED
@@ -1,49 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: addressable
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Aman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-04 00:00:00.000000000 Z
11
+ date: 2019-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: public_suffix
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '2.0'
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
19
  version: 2.0.2
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '4.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '2.0'
30
27
  - - ">="
31
28
  - !ruby/object:Gem::Version
32
29
  version: 2.0.2
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '4.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: bundler
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - "~>"
37
+ - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.0'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '3.0'
40
43
  type: :development
41
44
  prerelease: false
42
45
  version_requirements: !ruby/object:Gem::Requirement
43
46
  requirements:
44
- - - "~>"
47
+ - - ">="
45
48
  - !ruby/object:Gem::Version
46
49
  version: '1.0'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '3.0'
47
53
  description: |
48
54
  Addressable is a replacement for the URI implementation that is part of
49
55
  Ruby's standard library. It more closely conforms to the relevant RFCs and