addressable 2.5.2 → 2.7.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: 609a17c9f46ce684482d241232f11a9b9fc81c81
4
- data.tar.gz: 5d2febfcf1ecfc3deafe09afb18f8d423a917597
3
+ metadata.gz: 73a7a5a0dfea976017780e3b434e97aa58216019
4
+ data.tar.gz: a708925a0882de04f840e9e54d279eb954775921
5
5
  SHA512:
6
- metadata.gz: c2643f4006865f0d7654ed88b925bd46a75fc3fc3eaf46dea2b7a57f72ffe7c2b90560bae9ce06ecca4d4a528db4fb8a28e24fdd882bc5d602177d2099ec1b6f
7
- data.tar.gz: 2dcaa8fad9dfe031485391dec12e42bd9a45e105a1486c8e1ba9faeaa6f3600f9742b43e557776bfec9424c16e0dd0a1e22bc219da0e8c80559bfcf04ac405d8
6
+ metadata.gz: c311a5594d7f1051df67287badef72551c52c6bc47598017099d5e8b5c2a9638144bcc6d635b418dfc1970e185cf31016ad54f681c7156d440223ef62f060bc5
7
+ data.tar.gz: 78527879654347fcf16be86684e1b37ca240c5a80d3baf2217cfdfb501cd6255de47a7cc8058c10e98e558b073981a27be06672f4dec8cf7d18e88a34609e6a1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ # Addressable 2.7.0
2
+ - added `:compacted` flag to `normalized_query`
3
+ - `heuristic_parse` handles `mailto:` more intuitively
4
+ - refactored validation to use a prepended module
5
+ - dropped explicit support for JRuby 9.0.5.0
6
+ - compatibility w/ public_suffix 4.x
7
+ - performance improvements
8
+
9
+ # Addressable 2.6.0
10
+ - added `tld=` method to allow assignment to the public suffix
11
+ - most `heuristic_parse` patterns are now case-insensitive
12
+ - `heuristic_parse` handles more `file://` URI variations
13
+ - fixes bug in `heuristic_parse` when uri starts with digit
14
+ - fixes bug in `request_uri=` with query strings
15
+ - fixes template issues with `nil` and `?` operator
16
+ - `frozen_string_literal` pragmas added
17
+ - minor performance improvements in regexps
18
+ - fixes to eliminate warnings
19
+
1
20
  # Addressable 2.5.2
2
21
  - better support for frozen string literals
3
22
  - fixed bug w/ uppercase characters in scheme
data/Gemfile CHANGED
@@ -3,8 +3,8 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  group :test do
6
- gem 'rspec', '~> 3.0'
7
- gem 'rspec-its', '~> 1.1'
6
+ gem 'rspec', '~> 3.8'
7
+ gem 'rspec-its', '~> 1.3'
8
8
  end
9
9
 
10
10
  group :development do
data/README.md CHANGED
@@ -9,21 +9,21 @@
9
9
 
10
10
  [![Gem Version](http://img.shields.io/gem/dt/addressable.svg)][gem]
11
11
  [![Build Status](https://secure.travis-ci.org/sporkmonger/addressable.svg?branch=master)][travis]
12
- [![Dependency Status](https://gemnasium.com/sporkmonger/addressable.svg?travis)][gemnasium]
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
14
 
16
15
  [gem]: https://rubygems.org/gems/addressable
17
16
  [travis]: http://travis-ci.org/sporkmonger/addressable
18
- [gemnasium]: https://gemnasium.com/sporkmonger/addressable
19
17
  [coveralls]: https://coveralls.io/r/sporkmonger/addressable
20
18
  [inch]: http://inch-ci.org/github/sporkmonger/addressable
21
19
 
22
20
  # Description
23
21
 
24
- Addressable is a replacement for the URI implementation that is part of
25
- Ruby's standard library. It more closely conforms to RFC 3986, RFC 3987, and
26
- RFC 6570 (level 4), providing support for IRIs and URI templates.
22
+ Addressable is an alternative implementation to the URI implementation
23
+ that is part of Ruby's standard library. It is flexible, offers heuristic
24
+ parsing, and additionally provides extensive support for IRIs and URI templates.
25
+
26
+ Addressable closely conforms to RFC 3986, RFC 3987, and RFC 6570 (level 4).
27
27
 
28
28
  # Reference
29
29
 
@@ -58,7 +58,7 @@ For more details, see [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570.txt).
58
58
 
59
59
  require "addressable/template"
60
60
 
61
- template = Addressable::Template.new("http://example.com/{?query*}/")
61
+ template = Addressable::Template.new("http://example.com/{?query*}")
62
62
  template.expand({
63
63
  "query" => {
64
64
  'foo' => 'bar',
@@ -105,7 +105,7 @@ $ gem install idn-ruby
105
105
 
106
106
  # Semantic Versioning
107
107
 
108
- This project uses sementic versioning. You can (and should) specify your
108
+ This project uses [Semantic Versioning](https://semver.org/). You can (and should) specify your
109
109
  dependency using a pessimistic version constraint covering the major and minor
110
110
  values:
111
111
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubygems'
2
4
  require 'rake'
3
5
 
@@ -12,9 +14,9 @@ RELEASE_NAME = "REL #{PKG_VERSION}"
12
14
 
13
15
  PKG_SUMMARY = "URI Implementation"
14
16
  PKG_DESCRIPTION = <<-TEXT
15
- Addressable is a replacement for the URI implementation that is part of
16
- Ruby's standard library. It more closely conforms to the relevant RFCs and
17
- adds support for IRIs and URI templates.
17
+ Addressable is an alternative implementation to the URI implementation that is
18
+ part of Ruby's standard library. It is flexible, offers heuristic parsing, and
19
+ additionally provides extensive support for IRIs and URI templates.
18
20
  TEXT
19
21
 
20
22
  PKG_FILES = FileList[
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
  # 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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # encoding:utf-8
2
4
  #--
3
5
  # Copyright (C) Bob Aman
@@ -133,7 +135,7 @@ module Addressable
133
135
  unpacked.map! { |codepoint| lookup_unicode_lowercase(codepoint) }
134
136
  return unpacked.pack("U*")
135
137
  end
136
- (class <<self; private :unicode_downcase; end)
138
+ private_class_method :unicode_downcase
137
139
 
138
140
  def self.unicode_compose(unpacked)
139
141
  unpacked_result = []
@@ -146,22 +148,19 @@ 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
162
161
  return unpacked_result
163
162
  end
164
- (class <<self; private :unicode_compose; end)
163
+ private_class_method :unicode_compose
165
164
 
166
165
  def self.unicode_compose_pair(ch_one, ch_two)
167
166
  if ch_one >= HANGUL_LBASE && ch_one < HANGUL_LBASE + HANGUL_LCOUNT &&
@@ -215,7 +214,7 @@ module Addressable
215
214
 
216
215
  return lookup_unicode_composition(p)
217
216
  end
218
- (class <<self; private :unicode_compose_pair; end)
217
+ private_class_method :unicode_compose_pair
219
218
 
220
219
  def self.unicode_sort_canonical(unpacked)
221
220
  unpacked = unpacked.dup
@@ -239,7 +238,7 @@ module Addressable
239
238
  end
240
239
  return unpacked
241
240
  end
242
- (class <<self; private :unicode_sort_canonical; end)
241
+ private_class_method :unicode_sort_canonical
243
242
 
244
243
  def self.unicode_decompose(unpacked)
245
244
  unpacked_result = []
@@ -260,7 +259,7 @@ module Addressable
260
259
  end
261
260
  return unpacked_result
262
261
  end
263
- (class <<self; private :unicode_decompose; end)
262
+ private_class_method :unicode_decompose
264
263
 
265
264
  def self.unicode_decompose_hangul(codepoint)
266
265
  sindex = codepoint - HANGUL_SBASE;
@@ -277,7 +276,7 @@ module Addressable
277
276
  end
278
277
  return l, v, t
279
278
  end
280
- (class <<self; private :unicode_decompose_hangul; end)
279
+ private_class_method :unicode_decompose_hangul
281
280
 
282
281
  def self.lookup_unicode_combining_class(codepoint)
283
282
  codepoint_data = UNICODE_DATA[codepoint]
@@ -285,14 +284,14 @@ module Addressable
285
284
  (codepoint_data[UNICODE_DATA_COMBINING_CLASS] || 0) :
286
285
  0)
287
286
  end
288
- (class <<self; private :lookup_unicode_combining_class; end)
287
+ private_class_method :lookup_unicode_combining_class
289
288
 
290
289
  def self.lookup_unicode_compatibility(codepoint)
291
290
  codepoint_data = UNICODE_DATA[codepoint]
292
291
  (codepoint_data ?
293
292
  codepoint_data[UNICODE_DATA_COMPATIBILITY] : nil)
294
293
  end
295
- (class <<self; private :lookup_unicode_compatibility; end)
294
+ private_class_method :lookup_unicode_compatibility
296
295
 
297
296
  def self.lookup_unicode_lowercase(codepoint)
298
297
  codepoint_data = UNICODE_DATA[codepoint]
@@ -300,12 +299,12 @@ module Addressable
300
299
  (codepoint_data[UNICODE_DATA_LOWERCASE] || codepoint) :
301
300
  codepoint)
302
301
  end
303
- (class <<self; private :lookup_unicode_lowercase; end)
302
+ private_class_method :lookup_unicode_lowercase
304
303
 
305
304
  def self.lookup_unicode_composition(unpacked)
306
305
  return COMPOSITION_TABLE[unpacked]
307
306
  end
308
- (class <<self; private :lookup_unicode_composition; end)
307
+ private_class_method :lookup_unicode_composition
309
308
 
310
309
  HANGUL_SBASE = 0xac00
311
310
  HANGUL_LBASE = 0x1100
@@ -342,7 +341,7 @@ module Addressable
342
341
  end
343
342
 
344
343
  COMPOSITION_TABLE = {}
345
- for codepoint, data in UNICODE_DATA
344
+ UNICODE_DATA.each do |codepoint, data|
346
345
  canonical = data[UNICODE_DATA_CANONICAL]
347
346
  exclusion = data[UNICODE_DATA_EXCLUSION]
348
347
 
@@ -501,7 +500,7 @@ module Addressable
501
500
 
502
501
  output[0..outlen].map { |x| x.chr }.join("").sub(/\0+\z/, "")
503
502
  end
504
- (class <<self; private :punycode_encode; end)
503
+ private_class_method :punycode_encode
505
504
 
506
505
  def self.punycode_decode(punycode)
507
506
  input = []
@@ -623,22 +622,22 @@ module Addressable
623
622
 
624
623
  output.pack("U*")
625
624
  end
626
- (class <<self; private :punycode_decode; end)
625
+ private_class_method :punycode_decode
627
626
 
628
627
  def self.punycode_basic?(codepoint)
629
628
  codepoint < 0x80
630
629
  end
631
- (class <<self; private :punycode_basic?; end)
630
+ private_class_method :punycode_basic?
632
631
 
633
632
  def self.punycode_delimiter?(codepoint)
634
633
  codepoint == PUNYCODE_DELIMITER
635
634
  end
636
- (class <<self; private :punycode_delimiter?; end)
635
+ private_class_method :punycode_delimiter?
637
636
 
638
637
  def self.punycode_encode_digit(d)
639
638
  d + 22 + 75 * ((d < 26) ? 1 : 0)
640
639
  end
641
- (class <<self; private :punycode_encode_digit; end)
640
+ private_class_method :punycode_encode_digit
642
641
 
643
642
  # Returns the numeric value of a basic codepoint
644
643
  # (for use in representing integers) in the range 0 to
@@ -654,7 +653,7 @@ module Addressable
654
653
  PUNYCODE_BASE
655
654
  end
656
655
  end
657
- (class <<self; private :punycode_decode_digit; end)
656
+ private_class_method :punycode_decode_digit
658
657
 
659
658
  # Bias adaptation method
660
659
  def self.punycode_adapt(delta, numpoints, firsttime)
@@ -671,7 +670,7 @@ module Addressable
671
670
 
672
671
  k + (difference + 1) * delta / (delta + PUNYCODE_SKEW)
673
672
  end
674
- (class <<self; private :punycode_adapt; end)
673
+ private_class_method :punycode_adapt
675
674
  end
676
675
  # :startdoc:
677
676
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # encoding:utf-8
2
4
  #--
3
5
  # Copyright (C) Bob Aman
@@ -728,54 +730,32 @@ module Addressable
728
730
  normalize_values = true)
729
731
  _, operator, varlist = *capture.match(EXPRESSION)
730
732
 
731
- vars = varlist.split(',')
733
+ vars = varlist.split(",")
732
734
 
733
- if '?' == operator
735
+ if operator == "?"
734
736
  # partial expansion of form style query variables sometimes requires a
735
737
  # slight reordering of the variables to produce a valid url.
736
738
  first_to_expand = vars.find { |varspec|
737
739
  _, name, _ = *varspec.match(VARSPEC)
738
- mapping.key? name
740
+ mapping.key?(name) && !mapping[name].nil?
739
741
  }
740
742
 
741
743
  vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand} if first_to_expand
742
744
  end
743
745
 
744
- vars
745
- .zip(operator_sequence(operator).take(vars.length))
746
- .reduce("".dup) do |acc, (varspec, op)|
746
+ vars.
747
+ inject("".dup) do |acc, varspec|
747
748
  _, name, _ = *varspec.match(VARSPEC)
748
-
749
- acc << if mapping.key? name
750
- transform_capture(mapping, "{#{op}#{varspec}}",
751
- processor, normalize_values)
752
- else
753
- "{#{op}#{varspec}}"
754
- end
755
- end
756
- end
757
-
758
- ##
759
- # Creates a lazy Enumerator of the operators that should be used to expand
760
- # variables in a varlist starting with `operator`. For example, an operator
761
- # `"?"` results in the sequence `"?","&","&"...`
762
- #
763
- # @param [String] operator from which to generate a sequence
764
- #
765
- # @return [Enumerator] sequence of operators
766
- def operator_sequence(operator)
767
- rest_operator = if "?" == operator
768
- "&"
769
- else
770
- operator
771
- end
772
- head_operator = operator
773
-
774
- Enumerator.new do |y|
775
- y << head_operator.to_s
776
- while true
777
- y << rest_operator.to_s
778
- 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
779
759
  end
780
760
  end
781
761
 
@@ -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,26 +184,33 @@ 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
200
208
  authority = fragments[3]
201
209
  if authority && authority.length > 0
202
- new_authority = authority.gsub(/\\/, '/').gsub(/ /, '%20')
210
+ new_authority = authority.tr("\\", "/").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)
@@ -209,10 +218,11 @@ module Addressable
209
218
  parsed = self.parse(hints[:scheme] + "://" + uri)
210
219
  end
211
220
  if parsed.path.include?(".")
212
- new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
213
- if new_host
221
+ if parsed.path[/\b@\b/]
222
+ parsed.scheme = "mailto" unless parsed.scheme
223
+ elsif new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
214
224
  parsed.defer_validation do
215
- new_path = parsed.path.gsub(
225
+ new_path = parsed.path.sub(
216
226
  Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR)
217
227
  parsed.host = new_host
218
228
  parsed.path = new_path
@@ -263,24 +273,24 @@ module Addressable
263
273
  # Otherwise, convert to a String
264
274
  path = path.to_str.strip
265
275
 
266
- path.gsub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
276
+ path.sub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
267
277
  path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/
268
278
  uri = self.parse(path)
269
279
 
270
280
  if uri.scheme == nil
271
281
  # Adjust windows-style uris
272
- uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
282
+ uri.path.sub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
273
283
  "/#{$1.downcase}:/"
274
284
  end
275
- uri.path.gsub!(/\\/, SLASH)
285
+ uri.path.tr!("\\", SLASH)
276
286
  if File.exist?(uri.path) &&
277
287
  File.stat(uri.path).directory?
278
- uri.path.gsub!(/\/$/, EMPTY_STR)
288
+ uri.path.chomp!(SLASH)
279
289
  uri.path = uri.path + '/'
280
290
  end
281
291
 
282
292
  # If the path is absolute, set the scheme and host.
283
- if uri.path =~ /^\//
293
+ if uri.path.start_with?(SLASH)
284
294
  uri.scheme = "file"
285
295
  uri.host = EMPTY_STR
286
296
  end
@@ -317,6 +327,21 @@ module Addressable
317
327
  return result
318
328
  end
319
329
 
330
+ ##
331
+ # Tables used to optimize encoding operations in `self.encode_component`
332
+ # and `self.normalize_component`
333
+ SEQUENCE_ENCODING_TABLE = Hash.new do |hash, sequence|
334
+ hash[sequence] = sequence.unpack("C*").map do |c|
335
+ format("%02x", c)
336
+ end.join
337
+ end
338
+
339
+ SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = Hash.new do |hash, sequence|
340
+ hash[sequence] = sequence.unpack("C*").map do |c|
341
+ format("%%%02X", c)
342
+ end.join
343
+ end
344
+
320
345
  ##
321
346
  # Percent encodes a URI component.
322
347
  #
@@ -383,12 +408,14 @@ module Addressable
383
408
  component.force_encoding(Encoding::ASCII_8BIT)
384
409
  # Avoiding gsub! because there are edge cases with frozen strings
385
410
  component = component.gsub(character_class) do |sequence|
386
- (sequence.unpack('C*').map { |c| "%" + ("%02x" % c).upcase }).join
411
+ SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[sequence]
387
412
  end
388
413
  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 }
414
+ upcase_encoded_chars = upcase_encoded.chars.map do |char|
415
+ SEQUENCE_ENCODING_TABLE[char]
416
+ end
417
+ component = component.gsub(/%(#{upcase_encoded_chars.join('|')})/,
418
+ &:upcase)
392
419
  end
393
420
  return component
394
421
  end
@@ -522,7 +549,7 @@ module Addressable
522
549
  character_class = "#{character_class}%" unless character_class.include?('%')
523
550
 
524
551
  "|%(?!#{leave_encoded.chars.map do |char|
525
- seq = char.unpack('C*').map { |c| '%02x' % c }.join
552
+ seq = SEQUENCE_ENCODING_TABLE[char]
526
553
  [seq.upcase, seq.downcase]
527
554
  end.flatten.join('|')})"
528
555
  end
@@ -1163,16 +1190,25 @@ module Addressable
1163
1190
  # Returns the top-level domain for this host.
1164
1191
  #
1165
1192
  # @example
1166
- # Addressable::URI.parse("www.example.co.uk").tld # => "co.uk"
1193
+ # Addressable::URI.parse("http://www.example.co.uk").tld # => "co.uk"
1167
1194
  def tld
1168
1195
  PublicSuffix.parse(self.host, ignore_private: true).tld
1169
1196
  end
1170
1197
 
1198
+ ##
1199
+ # Sets the top-level domain for this URI.
1200
+ #
1201
+ # @param [String, #to_str] new_tld The new top-level domain.
1202
+ def tld=(new_tld)
1203
+ replaced_tld = host.sub(/#{tld}\z/, new_tld)
1204
+ self.host = PublicSuffix::Domain.new(replaced_tld).to_s
1205
+ end
1206
+
1171
1207
  ##
1172
1208
  # Returns the public suffix domain for this host.
1173
1209
  #
1174
1210
  # @example
1175
- # Addressable::URI.parse("www.example.co.uk").domain # => "example.co.uk"
1211
+ # Addressable::URI.parse("http://www.example.co.uk").domain # => "example.co.uk"
1176
1212
  def domain
1177
1213
  PublicSuffix.domain(self.host, ignore_private: true)
1178
1214
  end
@@ -1235,9 +1271,9 @@ module Addressable
1235
1271
  new_user = new_userinfo.strip[/^([^:]*):?/, 1]
1236
1272
  new_password = new_userinfo.strip[/:(.*)$/, 1]
1237
1273
  end
1238
- new_host = new_authority.gsub(
1274
+ new_host = new_authority.sub(
1239
1275
  /^([^\[\]]*)@/, EMPTY_STR
1240
- ).gsub(
1276
+ ).sub(
1241
1277
  /:([^:@\[\]]*?)$/, EMPTY_STR
1242
1278
  )
1243
1279
  new_port =
@@ -1544,7 +1580,7 @@ module Addressable
1544
1580
  # @return [String] The path's basename.
1545
1581
  def basename
1546
1582
  # Path cannot be nil
1547
- return File.basename(self.path).gsub(/;[^\/]*$/, EMPTY_STR)
1583
+ return File.basename(self.path).sub(/;[^\/]*$/, EMPTY_STR)
1548
1584
  end
1549
1585
 
1550
1586
  ##
@@ -1577,6 +1613,7 @@ module Addressable
1577
1613
  # Make sure possible key-value pair delimiters are escaped.
1578
1614
  modified_query_class.sub!("\\&", "").sub!("\\;", "")
1579
1615
  pairs = (self.query || "").split("&", -1)
1616
+ pairs.delete_if(&:empty?) if flags.include?(:compacted)
1580
1617
  pairs.sort! if flags.include?(:sorted)
1581
1618
  component = pairs.map do |pair|
1582
1619
  Addressable::URI.normalize_component(pair, modified_query_class, "+")
@@ -1642,7 +1679,7 @@ module Addressable
1642
1679
  # Treating '+' as a space was just an unbelievably bad idea.
1643
1680
  # There was nothing wrong with '%20'!
1644
1681
  # If it ain't broke, don't fix it!
1645
- pair[1] = URI.unencode_component(pair[1].to_str.gsub(/\+/, " "))
1682
+ pair[1] = URI.unencode_component(pair[1].to_str.tr("+", " "))
1646
1683
  end
1647
1684
  if return_type == Hash
1648
1685
  accu[pair[0]] = pair[1]
@@ -1744,7 +1781,7 @@ module Addressable
1744
1781
  "Cannot set an HTTP request URI for a non-HTTP URI."
1745
1782
  end
1746
1783
  new_request_uri = new_request_uri.to_str
1747
- path_component = new_request_uri[/^([^\?]*)\?(?:.*)$/, 1]
1784
+ path_component = new_request_uri[/^([^\?]*)\??(?:.*)$/, 1]
1748
1785
  query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
1749
1786
  path_component = path_component.to_s
1750
1787
  path_component = (!path_component.empty? ? path_component : SLASH)
@@ -1899,8 +1936,8 @@ module Addressable
1899
1936
  # Section 5.2.3 of RFC 3986
1900
1937
  #
1901
1938
  # Removes the right-most path segment from the base path.
1902
- if base_path =~ /\//
1903
- base_path.gsub!(/\/[^\/]+$/, SLASH)
1939
+ if base_path.include?(SLASH)
1940
+ base_path.sub!(/\/[^\/]+$/, SLASH)
1904
1941
  else
1905
1942
  base_path = EMPTY_STR
1906
1943
  end
@@ -2349,10 +2386,10 @@ module Addressable
2349
2386
  #
2350
2387
  # @param [Proc] block
2351
2388
  # A set of operations to perform on a given URI.
2352
- def defer_validation(&block)
2353
- raise LocalJumpError, "No block given." unless block
2389
+ def defer_validation
2390
+ raise LocalJumpError, "No block given." unless block_given?
2354
2391
  @validation_deferred = true
2355
- block.call()
2392
+ yield
2356
2393
  @validation_deferred = false
2357
2394
  validate
2358
2395
  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 = 2
26
+ MINOR = 7
27
+ TINY = 0
26
28
 
27
29
  STRING = [MAJOR, MINOR, TINY].join('.')
28
30
  end
@@ -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
@@ -148,9 +150,9 @@ shared_examples_for "converting from unicode to ASCII" do
148
150
  end
149
151
 
150
152
  shared_examples_for "converting from ASCII to unicode" do
151
- LONG = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com'
152
- it "should convert '#{LONG}' correctly" do
153
- 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)
154
156
  end
155
157
 
156
158
  it "should return the identity conversion when punycode decode fails" do
@@ -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
 
@@ -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}/")
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # coding: utf-8
2
4
  # Copyright (C) Bob Aman
3
5
  #
@@ -1984,6 +1986,20 @@ describe Addressable::URI, "when parsed from " +
1984
1986
  expect(@uri.request_uri).to eq("/pub/WWW/TheProject.html")
1985
1987
  end
1986
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
+
1987
2003
  it "should have the correct request URI after assignment" do
1988
2004
  @uri.request_uri = "/some/where/else.html?query?string"
1989
2005
  expect(@uri.request_uri).to eq("/some/where/else.html?query?string")
@@ -3928,7 +3944,7 @@ describe Addressable::URI, "when parsed from " +
3928
3944
  it "should raise an error if assigning a bogus object to the hostname" do
3929
3945
  expect(lambda do
3930
3946
  @uri.hostname = Object.new
3931
- end).to raise_error
3947
+ end).to raise_error(TypeError)
3932
3948
  end
3933
3949
 
3934
3950
  it "should have the correct port after assignment" do
@@ -4287,6 +4303,26 @@ describe Addressable::URI, "when parsed from " +
4287
4303
  end
4288
4304
  end
4289
4305
 
4306
+ describe Addressable::URI, "when parsed from 'http://example/?b=1&a=2&c=3'" do
4307
+ before do
4308
+ @uri = Addressable::URI.parse("http://example/?b=1&a=2&c=3")
4309
+ end
4310
+
4311
+ it "should have a sorted normalized query of 'a=2&b=1&c=3'" do
4312
+ expect(@uri.normalized_query(:sorted)).to eq("a=2&b=1&c=3")
4313
+ end
4314
+ end
4315
+
4316
+ describe Addressable::URI, "when parsed from 'http://example/?&a&&c&'" do
4317
+ before do
4318
+ @uri = Addressable::URI.parse("http://example/?&a&&c&")
4319
+ end
4320
+
4321
+ it "should have a compacted normalized query of 'a&c'" do
4322
+ expect(@uri.normalized_query(:compacted)).to eq("a&c")
4323
+ end
4324
+ end
4325
+
4290
4326
  describe Addressable::URI, "when parsed from " +
4291
4327
  "'http://example.com/sound%2bvision'" do
4292
4328
  before do
@@ -4401,7 +4437,7 @@ describe Addressable::URI, "when parsed from " +
4401
4437
  expect(lambda do
4402
4438
  # This would create an invalid URI
4403
4439
  @uri.authority = nil
4404
- end).to raise_error
4440
+ end).to raise_error(Addressable::URI::InvalidURIError)
4405
4441
  end
4406
4442
  end
4407
4443
 
@@ -4804,7 +4840,7 @@ describe Addressable::URI, "when parsed from '?one=1&two=2&three=3'" do
4804
4840
 
4805
4841
  it "should raise an error for invalid return type values" do
4806
4842
  expect(lambda do
4807
- @uri.query_values(Fixnum)
4843
+ @uri.query_values(Integer)
4808
4844
  end).to raise_error(ArgumentError)
4809
4845
  end
4810
4846
 
@@ -5490,6 +5526,31 @@ describe Addressable::URI, "when given the path '/one/two/'" do
5490
5526
  end
5491
5527
  end
5492
5528
 
5529
+ describe Addressable::URI, "when given the tld " do
5530
+ it "'uk' should have a tld of 'uk'" do
5531
+ uri = Addressable::URI.parse("http://example.com")
5532
+ uri.tld = "uk"
5533
+
5534
+ expect(uri.tld).to eq("uk")
5535
+ end
5536
+
5537
+ context "which " do
5538
+ let (:uri) { Addressable::URI.parse("http://www.comrade.net/path/to/source/") }
5539
+
5540
+ it "contains a subdomain" do
5541
+ uri.tld = "co.uk"
5542
+
5543
+ expect(uri.to_s).to eq("http://www.comrade.co.uk/path/to/source/")
5544
+ end
5545
+
5546
+ it "is part of the domain" do
5547
+ uri.tld = "com"
5548
+
5549
+ expect(uri.to_s).to eq("http://www.comrade.com/path/to/source/")
5550
+ end
5551
+ end
5552
+ end
5553
+
5493
5554
  describe Addressable::URI, "when given the path " +
5494
5555
  "'c:\\windows\\My Documents 100%20\\foo.txt'" do
5495
5556
  before do
@@ -6227,6 +6288,18 @@ describe Addressable::URI, "when given the input " +
6227
6288
  end
6228
6289
  end
6229
6290
 
6291
+ describe Addressable::URI, "when given the input which "\
6292
+ "start with digits and has specified port" do
6293
+ before do
6294
+ @input = "7777.example.org:8089"
6295
+ end
6296
+
6297
+ it "should heuristically parse to 'http://7777.example.org:8089'" do
6298
+ uri = Addressable::URI.heuristic_parse(@input)
6299
+ expect(uri.to_s).to eq("http://7777.example.org:8089")
6300
+ end
6301
+ end
6302
+
6230
6303
  describe Addressable::URI, "when given the input " +
6231
6304
  "'feed:///example.com'" do
6232
6305
  before do
@@ -6239,6 +6312,18 @@ describe Addressable::URI, "when given the input " +
6239
6312
  end
6240
6313
  end
6241
6314
 
6315
+ describe Addressable::URI, "when given the input " +
6316
+ "'file://localhost/path/to/resource/'" do
6317
+ before do
6318
+ @input = "file://localhost/path/to/resource/"
6319
+ end
6320
+
6321
+ it "should heuristically parse to 'file:///path/to/resource/'" do
6322
+ @uri = Addressable::URI.heuristic_parse(@input)
6323
+ expect(@uri.to_s).to eq("file:///path/to/resource/")
6324
+ end
6325
+ end
6326
+
6242
6327
  describe Addressable::URI, "when given the input " +
6243
6328
  "'file://path/to/resource/'" do
6244
6329
  before do
@@ -6251,6 +6336,18 @@ describe Addressable::URI, "when given the input " +
6251
6336
  end
6252
6337
  end
6253
6338
 
6339
+ describe Addressable::URI, "when given the input " +
6340
+ "'file://///path/to/resource/'" do
6341
+ before do
6342
+ @input = "file:///////path/to/resource/"
6343
+ end
6344
+
6345
+ it "should heuristically parse to 'file:////path/to/resource/'" do
6346
+ @uri = Addressable::URI.heuristic_parse(@input)
6347
+ expect(@uri.to_s).to eq("file:////path/to/resource/")
6348
+ end
6349
+ end
6350
+
6254
6351
  describe Addressable::URI, "when given the input " +
6255
6352
  "'feed://http://example.com'" do
6256
6353
  before do
@@ -6275,6 +6372,44 @@ describe Addressable::URI, "when given the input " +
6275
6372
  end
6276
6373
  end
6277
6374
 
6375
+ describe Addressable::URI, "when given the input: 'user@domain.com'" do
6376
+ before do
6377
+ @input = "user@domain.com"
6378
+ end
6379
+
6380
+ context "for heuristic parse" do
6381
+ it "should remain 'mailto:user@domain.com'" do
6382
+ uri = Addressable::URI.heuristic_parse("mailto:#{@input}")
6383
+ expect(uri.to_s).to eq("mailto:user@domain.com")
6384
+ end
6385
+
6386
+ it "should have a scheme of 'mailto'" do
6387
+ uri = Addressable::URI.heuristic_parse(@input)
6388
+ expect(uri.to_s).to eq("mailto:user@domain.com")
6389
+ expect(uri.scheme).to eq("mailto")
6390
+ end
6391
+
6392
+ it "should remain 'acct:user@domain.com'" do
6393
+ uri = Addressable::URI.heuristic_parse("acct:#{@input}")
6394
+ expect(uri.to_s).to eq("acct:user@domain.com")
6395
+ end
6396
+
6397
+ context "HTTP" do
6398
+ before do
6399
+ @uri = Addressable::URI.heuristic_parse("http://#{@input}/")
6400
+ end
6401
+
6402
+ it "should remain 'http://user@domain.com/'" do
6403
+ expect(@uri.to_s).to eq("http://user@domain.com/")
6404
+ end
6405
+
6406
+ it "should have the username 'user' for HTTP basic authentication" do
6407
+ expect(@uri.user).to eq("user")
6408
+ end
6409
+ end
6410
+ end
6411
+ end
6412
+
6278
6413
  describe Addressable::URI, "when assigning query values" do
6279
6414
  before do
6280
6415
  @uri = Addressable::URI.new
data/spec/spec_helper.rb CHANGED
@@ -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
data/tasks/clobber.rake CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  desc "Remove all build products"
2
4
  task "clobber"
data/tasks/gem.rake CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rubygems/package_task"
2
4
 
3
5
  namespace :gem do
@@ -18,10 +20,10 @@ namespace :gem do
18
20
  exit(1)
19
21
  end
20
22
 
21
- s.required_ruby_version = '>= 2.0'
23
+ s.required_ruby_version = ">= 2.0"
22
24
 
23
- s.add_runtime_dependency 'public_suffix', '>= 2.0.2', '< 4.0'
24
- s.add_development_dependency 'bundler', '~> 1.0'
25
+ s.add_runtime_dependency "public_suffix", ">= 2.0.2", "< 5.0"
26
+ s.add_development_dependency "bundler", ">= 1.0", "< 3.0"
25
27
 
26
28
  s.require_path = "lib"
27
29
 
@@ -40,7 +42,7 @@ namespace :gem do
40
42
  desc "Generates .gemspec file"
41
43
  task :gemspec do
42
44
  spec_string = GEM_SPEC.to_ruby
43
- File.open("#{GEM_SPEC.name}.gemspec", 'w') do |file|
45
+ File.open("#{GEM_SPEC.name}.gemspec", "w") do |file|
44
46
  file.write spec_string
45
47
  end
46
48
  end
@@ -70,9 +72,9 @@ namespace :gem do
70
72
  desc "Reinstall the gem"
71
73
  task :reinstall => [:uninstall, :install]
72
74
 
73
- desc 'Package for release'
75
+ desc "Package for release"
74
76
  task :release => ["gem:package", "gem:gemspec"] do |t|
75
- v = ENV['VERSION'] or abort 'Must supply VERSION=x.y.z'
77
+ v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
76
78
  abort "Versions don't match #{v} vs #{PROJ.version}" if v != PKG_VERSION
77
79
  pkg = "pkg/#{GEM_SPEC.full_name}"
78
80
 
data/tasks/git.rake CHANGED
@@ -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"
data/tasks/metrics.rake CHANGED
@@ -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
data/tasks/rspec.rake CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rspec/core/rake_task"
2
4
 
3
5
  namespace :spec do
data/tasks/yard.rake CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rake"
2
4
 
3
5
  begin
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: addressable
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.2
4
+ version: 2.7.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: 2017-08-25 00:00:00.000000000 Z
11
+ date: 2019-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: public_suffix
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 2.0.2
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '4.0'
22
+ version: '5.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,25 +29,31 @@ dependencies:
29
29
  version: 2.0.2
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '4.0'
32
+ version: '5.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
- Addressable is a replacement for the URI implementation that is part of
49
- Ruby's standard library. It more closely conforms to the relevant RFCs and
50
- adds support for IRIs and URI templates.
54
+ Addressable is an alternative implementation to the URI implementation that is
55
+ part of Ruby's standard library. It is flexible, offers heuristic parsing, and
56
+ additionally provides extensive support for IRIs and URI templates.
51
57
  email: bob@sporkmonger.com
52
58
  executables: []
53
59
  extensions: []
@@ -102,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
108
  version: '0'
103
109
  requirements: []
104
110
  rubyforge_project:
105
- rubygems_version: 2.5.1
111
+ rubygems_version: 2.5.2.3
106
112
  signing_key:
107
113
  specification_version: 4
108
114
  summary: URI Implementation