addressable 2.8.1 → 2.8.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ddda72232f6aef9f6f4311c2855f1b3fea9acc80f51c4e5e90bd23820b0d74e
4
- data.tar.gz: 88f208cb2d73dec64663e6e3c1710dc08b188402937038fefce6a2d47debc97d
3
+ metadata.gz: b18375911dede706411b9ec0d6c8b104e9f49a2ebf53ea18d89aec89a349d8f2
4
+ data.tar.gz: 4701c220482e3e92a10ebe9605ecb03fcf5d295bbef20346aeb49fdfbab4bbb3
5
5
  SHA512:
6
- metadata.gz: 4e1b0c83fc2f2e54cba6804f3840c8bb29f0d73259979d5bf678ebd5d32257b91585d9b71a780321427835d10f98b9c07969267878f0b032c53d031c30202a4c
7
- data.tar.gz: 35abc3652c8ff92032411e4daad7133b7c4649aff4a1a25fe622cf1c9b661d56f096a1084392e053788baea79f551a9d4a448d16c9e61e23bf4d9692a6728bf3
6
+ metadata.gz: 8e6c9605ceec0aa65ceb3b4a1152d9c89830f1748e20c6667746634a9952a13fcff404ec1783f904e9240d642b11e2907914d43581fd8c451971c48106e70710
7
+ data.tar.gz: 7865e8accde73f97022353c51e9556f7f1f1786ed2e004789a9c4e5ffa82486ae2abe17166fa724f2bf17b8e853e40f185abf0ee16a4108aca712c202540b3e2
data/CHANGELOG.md CHANGED
@@ -1,13 +1,22 @@
1
+ # Addressable 2.8.2
2
+ - Improve cache hits and JIT friendliness ([#486](https://github.com/sporkmonger/addressable/pull/486))
3
+ - Improve code style and test coverage ([#482](https://github.com/sporkmonger/addressable/pull/482))
4
+ - Ensure reset of deferred validation ([#481](https://github.com/sporkmonger/addressable/pull/481))
5
+ - Resolve normalization differences between `IDNA::Native` and `IDNA::Pure` ([#408](https://github.com/sporkmonger/addressable/issues/408), [#492])
6
+ - Remove redundant colon in `Addressable::URI::CharacterClasses::AUTHORITY` regex ([#438](https://github.com/sporkmonger/addressable/pull/438)) (accidentally reverted by [#449] merge but [added back](https://github.com/sporkmonger/addressable/pull/492#discussion_r1105125280) in [#492])
7
+
8
+ [#492]: https://github.com/sporkmonger/addressable/pull/492
9
+
1
10
  # Addressable 2.8.1
2
11
  - refactor `Addressable::URI.normalize_path` to address linter offenses ([#430](https://github.com/sporkmonger/addressable/pull/430))
3
- - remove redundant colon in `Addressable::URI::CharacterClasses::AUTHORITY` regex ([#438](https://github.com/sporkmonger/addressable/pull/438))
4
12
  - update gemspec to reflect supported Ruby versions ([#466], [#464], [#463])
5
13
  - compatibility w/ public_suffix 5.x ([#466], [#465], [#460])
6
14
  - fixes "invalid byte sequence in UTF-8" exception when unencoding URLs containing non UTF-8 characters ([#459](https://github.com/sporkmonger/addressable/pull/459))
7
- - `Ractor` compatibility ([#449](https://github.com/sporkmonger/addressable/pull/449))
15
+ - `Ractor` compatibility ([#449])
8
16
  - use the whole string instead of a single line for template match ([#431](https://github.com/sporkmonger/addressable/pull/431))
9
17
  - force UTF-8 encoding only if needed ([#341](https://github.com/sporkmonger/addressable/pull/341))
10
18
 
19
+ [#449]: https://github.com/sporkmonger/addressable/pull/449
11
20
  [#460]: https://github.com/sporkmonger/addressable/pull/460
12
21
  [#463]: https://github.com/sporkmonger/addressable/pull/463
13
22
  [#464]: https://github.com/sporkmonger/addressable/pull/464
data/Rakefile CHANGED
@@ -24,7 +24,8 @@ PKG_FILES = FileList[
24
24
  "tasks/**/*",
25
25
  "[A-Z]*", "Rakefile"
26
26
  ].exclude(/pkg/).exclude(/database\.yml/).
27
- exclude(/Gemfile\.lock/).exclude(/[_\.]git$/)
27
+ exclude(/Gemfile\.lock/).exclude(/[_\.]git$/).
28
+ exclude(/coverage/)
28
29
 
29
30
  task :default => "spec"
30
31
 
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # stub: addressable 2.8.2 ruby lib
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "addressable".freeze
6
+ s.version = "2.8.2"
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
+ s.metadata = { "changelog_uri" => "https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md" } if s.respond_to? :metadata=
10
+ s.require_paths = ["lib".freeze]
11
+ s.authors = ["Bob Aman".freeze]
12
+ s.date = "2023-04-01"
13
+ s.description = "Addressable is an alternative implementation to the URI implementation that is\npart of Ruby's standard library. It is flexible, offers heuristic parsing, and\nadditionally provides extensive support for IRIs and URI templates.\n".freeze
14
+ s.email = "bob@sporkmonger.com".freeze
15
+ s.extra_rdoc_files = ["README.md".freeze]
16
+ s.files = ["CHANGELOG.md".freeze, "Gemfile".freeze, "LICENSE.txt".freeze, "README.md".freeze, "Rakefile".freeze, "addressable.gemspec".freeze, "data/unicode.data".freeze, "lib/addressable.rb".freeze, "lib/addressable/idna.rb".freeze, "lib/addressable/idna/native.rb".freeze, "lib/addressable/idna/pure.rb".freeze, "lib/addressable/template.rb".freeze, "lib/addressable/uri.rb".freeze, "lib/addressable/version.rb".freeze, "spec/addressable/idna_spec.rb".freeze, "spec/addressable/net_http_compat_spec.rb".freeze, "spec/addressable/security_spec.rb".freeze, "spec/addressable/template_spec.rb".freeze, "spec/addressable/uri_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tasks/clobber.rake".freeze, "tasks/gem.rake".freeze, "tasks/git.rake".freeze, "tasks/metrics.rake".freeze, "tasks/profile.rake".freeze, "tasks/rspec.rake".freeze, "tasks/yard.rake".freeze]
17
+ s.homepage = "https://github.com/sporkmonger/addressable".freeze
18
+ s.licenses = ["Apache-2.0".freeze]
19
+ s.rdoc_options = ["--main".freeze, "README.md".freeze]
20
+ s.required_ruby_version = Gem::Requirement.new(">= 2.2".freeze)
21
+ s.rubygems_version = "3.4.8".freeze
22
+ s.summary = "URI Implementation".freeze
23
+
24
+ s.specification_version = 4
25
+
26
+ s.add_runtime_dependency(%q<public_suffix>.freeze, [">= 2.0.2", "< 6.0"])
27
+ s.add_development_dependency(%q<bundler>.freeze, [">= 1.0", "< 3.0"])
28
+ end
@@ -29,10 +29,6 @@ module Addressable
29
29
  IDN::Punycode.decode(value.to_s)
30
30
  end
31
31
 
32
- def self.unicode_normalize_kc(value)
33
- IDN::Stringprep.nfkc_normalize(value.to_s)
34
- end
35
-
36
32
  def self.to_ascii(value)
37
33
  value.to_s.split('.', -1).map do |segment|
38
34
  if segment.size > 0 && segment.size < 64
@@ -66,7 +66,7 @@ module Addressable
66
66
  # domain name as described in RFC 3490.
67
67
  def self.to_ascii(input)
68
68
  input = input.to_s unless input.is_a?(String)
69
- input = input.dup
69
+ input = input.dup.force_encoding(Encoding::UTF_8).unicode_normalize(:nfkc)
70
70
  if input.respond_to?(:force_encoding)
71
71
  input.force_encoding(Encoding::ASCII_8BIT)
72
72
  end
@@ -77,7 +77,7 @@ module Addressable
77
77
  part.force_encoding(Encoding::ASCII_8BIT)
78
78
  end
79
79
  if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE
80
- ACE_PREFIX + punycode_encode(unicode_normalize_kc(part))
80
+ ACE_PREFIX + punycode_encode(part)
81
81
  else
82
82
  part
83
83
  end
@@ -112,15 +112,6 @@ module Addressable
112
112
  output
113
113
  end
114
114
 
115
- # Unicode normalization form KC.
116
- def self.unicode_normalize_kc(input)
117
- input = input.to_s unless input.is_a?(String)
118
- unpacked = input.unpack("U*")
119
- unpacked =
120
- unicode_compose(unicode_sort_canonical(unicode_decompose(unpacked)))
121
- return unpacked.pack("U*")
122
- end
123
-
124
115
  ##
125
116
  # Unicode aware downcase method.
126
117
  #
@@ -136,164 +127,6 @@ module Addressable
136
127
  end
137
128
  private_class_method :unicode_downcase
138
129
 
139
- def self.unicode_compose(unpacked)
140
- unpacked_result = []
141
- length = unpacked.length
142
-
143
- return unpacked if length == 0
144
-
145
- starter = unpacked[0]
146
- starter_cc = lookup_unicode_combining_class(starter)
147
- starter_cc = 256 if starter_cc != 0
148
- for i in 1...length
149
- ch = unpacked[i]
150
-
151
- if (starter_cc == 0 &&
152
- (composite = unicode_compose_pair(starter, ch)) != nil)
153
- starter = composite
154
- else
155
- unpacked_result << starter
156
- starter = ch
157
- end
158
- end
159
- unpacked_result << starter
160
- return unpacked_result
161
- end
162
- private_class_method :unicode_compose
163
-
164
- def self.unicode_compose_pair(ch_one, ch_two)
165
- if ch_one >= HANGUL_LBASE && ch_one < HANGUL_LBASE + HANGUL_LCOUNT &&
166
- ch_two >= HANGUL_VBASE && ch_two < HANGUL_VBASE + HANGUL_VCOUNT
167
- # Hangul L + V
168
- return HANGUL_SBASE + (
169
- (ch_one - HANGUL_LBASE) * HANGUL_VCOUNT + (ch_two - HANGUL_VBASE)
170
- ) * HANGUL_TCOUNT
171
- elsif ch_one >= HANGUL_SBASE &&
172
- ch_one < HANGUL_SBASE + HANGUL_SCOUNT &&
173
- (ch_one - HANGUL_SBASE) % HANGUL_TCOUNT == 0 &&
174
- ch_two >= HANGUL_TBASE && ch_two < HANGUL_TBASE + HANGUL_TCOUNT
175
- # Hangul LV + T
176
- return ch_one + (ch_two - HANGUL_TBASE)
177
- end
178
-
179
- p = []
180
-
181
- ucs4_to_utf8(ch_one, p)
182
- ucs4_to_utf8(ch_two, p)
183
-
184
- return lookup_unicode_composition(p)
185
- end
186
- private_class_method :unicode_compose_pair
187
-
188
- def self.ucs4_to_utf8(char, buffer)
189
- if char < 128
190
- buffer << char
191
- elsif char < 2048
192
- buffer << (char >> 6 | 192)
193
- buffer << (char & 63 | 128)
194
- elsif char < 0x10000
195
- buffer << (char >> 12 | 224)
196
- buffer << (char >> 6 & 63 | 128)
197
- buffer << (char & 63 | 128)
198
- elsif char < 0x200000
199
- buffer << (char >> 18 | 240)
200
- buffer << (char >> 12 & 63 | 128)
201
- buffer << (char >> 6 & 63 | 128)
202
- buffer << (char & 63 | 128)
203
- elsif char < 0x4000000
204
- buffer << (char >> 24 | 248)
205
- buffer << (char >> 18 & 63 | 128)
206
- buffer << (char >> 12 & 63 | 128)
207
- buffer << (char >> 6 & 63 | 128)
208
- buffer << (char & 63 | 128)
209
- elsif char < 0x80000000
210
- buffer << (char >> 30 | 252)
211
- buffer << (char >> 24 & 63 | 128)
212
- buffer << (char >> 18 & 63 | 128)
213
- buffer << (char >> 12 & 63 | 128)
214
- buffer << (char >> 6 & 63 | 128)
215
- buffer << (char & 63 | 128)
216
- end
217
- end
218
- private_class_method :ucs4_to_utf8
219
-
220
- def self.unicode_sort_canonical(unpacked)
221
- unpacked = unpacked.dup
222
- i = 1
223
- length = unpacked.length
224
-
225
- return unpacked if length < 2
226
-
227
- while i < length
228
- last = unpacked[i-1]
229
- ch = unpacked[i]
230
- last_cc = lookup_unicode_combining_class(last)
231
- cc = lookup_unicode_combining_class(ch)
232
- if cc != 0 && last_cc != 0 && last_cc > cc
233
- unpacked[i] = last
234
- unpacked[i-1] = ch
235
- i -= 1 if i > 1
236
- else
237
- i += 1
238
- end
239
- end
240
- return unpacked
241
- end
242
- private_class_method :unicode_sort_canonical
243
-
244
- def self.unicode_decompose(unpacked)
245
- unpacked_result = []
246
- for cp in unpacked
247
- if cp >= HANGUL_SBASE && cp < HANGUL_SBASE + HANGUL_SCOUNT
248
- l, v, t = unicode_decompose_hangul(cp)
249
- unpacked_result << l
250
- unpacked_result << v if v
251
- unpacked_result << t if t
252
- else
253
- dc = lookup_unicode_compatibility(cp)
254
- unless dc
255
- unpacked_result << cp
256
- else
257
- unpacked_result.concat(unicode_decompose(dc.unpack("U*")))
258
- end
259
- end
260
- end
261
- return unpacked_result
262
- end
263
- private_class_method :unicode_decompose
264
-
265
- def self.unicode_decompose_hangul(codepoint)
266
- sindex = codepoint - HANGUL_SBASE;
267
- if sindex < 0 || sindex >= HANGUL_SCOUNT
268
- l = codepoint
269
- v = t = nil
270
- return l, v, t
271
- end
272
- l = HANGUL_LBASE + sindex / HANGUL_NCOUNT
273
- v = HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
274
- t = HANGUL_TBASE + sindex % HANGUL_TCOUNT
275
- if t == HANGUL_TBASE
276
- t = nil
277
- end
278
- return l, v, t
279
- end
280
- private_class_method :unicode_decompose_hangul
281
-
282
- def self.lookup_unicode_combining_class(codepoint)
283
- codepoint_data = UNICODE_DATA[codepoint]
284
- (codepoint_data ?
285
- (codepoint_data[UNICODE_DATA_COMBINING_CLASS] || 0) :
286
- 0)
287
- end
288
- private_class_method :lookup_unicode_combining_class
289
-
290
- def self.lookup_unicode_compatibility(codepoint)
291
- codepoint_data = UNICODE_DATA[codepoint]
292
- (codepoint_data ?
293
- codepoint_data[UNICODE_DATA_COMPATIBILITY] : nil)
294
- end
295
- private_class_method :lookup_unicode_compatibility
296
-
297
130
  def self.lookup_unicode_lowercase(codepoint)
298
131
  codepoint_data = UNICODE_DATA[codepoint]
299
132
  (codepoint_data ?
@@ -302,21 +135,6 @@ module Addressable
302
135
  end
303
136
  private_class_method :lookup_unicode_lowercase
304
137
 
305
- def self.lookup_unicode_composition(unpacked)
306
- return COMPOSITION_TABLE[unpacked]
307
- end
308
- private_class_method :lookup_unicode_composition
309
-
310
- HANGUL_SBASE = 0xac00
311
- HANGUL_LBASE = 0x1100
312
- HANGUL_LCOUNT = 19
313
- HANGUL_VBASE = 0x1161
314
- HANGUL_VCOUNT = 21
315
- HANGUL_TBASE = 0x11a7
316
- HANGUL_TCOUNT = 28
317
- HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT # 588
318
- HANGUL_SCOUNT = HANGUL_LCOUNT * HANGUL_NCOUNT # 11172
319
-
320
138
  UNICODE_DATA_COMBINING_CLASS = 0
321
139
  UNICODE_DATA_EXCLUSION = 1
322
140
  UNICODE_DATA_CANONICAL = 2
@@ -892,7 +892,7 @@ module Addressable
892
892
  # operator.
893
893
  #
894
894
  # @param [Hash, Array, String] value
895
- # Normalizes keys and values with IDNA#unicode_normalize_kc
895
+ # Normalizes unicode keys and values with String#unicode_normalize (NFC)
896
896
  #
897
897
  # @return [Hash, Array, String] The normalized values
898
898
  def normalize_value(value)
@@ -902,15 +902,17 @@ module Addressable
902
902
 
903
903
  # Handle unicode normalization
904
904
  if value.kind_of?(Array)
905
- value.map! { |val| Addressable::IDNA.unicode_normalize_kc(val) }
905
+ value.map! { |val| normalize_value(val) }
906
906
  elsif value.kind_of?(Hash)
907
907
  value = value.inject({}) { |acc, (k, v)|
908
- acc[Addressable::IDNA.unicode_normalize_kc(k)] =
909
- Addressable::IDNA.unicode_normalize_kc(v)
908
+ acc[normalize_value(k)] = normalize_value(v)
910
909
  acc
911
910
  }
912
911
  else
913
- value = Addressable::IDNA.unicode_normalize_kc(value)
912
+ if value.encoding != Encoding::UTF_8
913
+ value = value.dup.force_encoding(Encoding::UTF_8)
914
+ end
915
+ value = value.unicode_normalize(:nfc)
914
916
  end
915
917
  value
916
918
  end
@@ -53,7 +53,7 @@ module Addressable
53
53
  PCHAR = (UNRESERVED + SUB_DELIMS + "\\:\\@").freeze
54
54
  SCHEME = (ALPHA + DIGIT + "\\-\\+\\.").freeze
55
55
  HOST = (UNRESERVED + SUB_DELIMS + "\\[\\:\\]").freeze
56
- AUTHORITY = (PCHAR + "\\[\\:\\]").freeze
56
+ AUTHORITY = (PCHAR + "\\[\\]").freeze
57
57
  PATH = (PCHAR + "\\/").freeze
58
58
  QUERY = (PCHAR + "\\/\\?").freeze
59
59
  FRAGMENT = (PCHAR + "\\/\\?").freeze
@@ -117,7 +117,7 @@ module Addressable
117
117
  uri = uri.to_str
118
118
  rescue TypeError, NoMethodError
119
119
  raise TypeError, "Can't convert #{uri.class} into String."
120
- end if not uri.is_a? String
120
+ end unless uri.is_a?(String)
121
121
 
122
122
  # This Regexp supplied as an example in RFC 3986, and it works great.
123
123
  scan = uri.scan(URIREGEX)
@@ -138,15 +138,15 @@ module Addressable
138
138
  user = userinfo.strip[/^([^:]*):?/, 1]
139
139
  password = userinfo.strip[/:(.*)$/, 1]
140
140
  end
141
+
141
142
  host = authority.sub(
142
143
  /^([^\[\]]*)@/, EMPTY_STR
143
144
  ).sub(
144
145
  /:([^:@\[\]]*?)$/, EMPTY_STR
145
146
  )
147
+
146
148
  port = authority[/:([^:@\[\]]*?)$/, 1]
147
- end
148
- if port == EMPTY_STR
149
- port = nil
149
+ port = nil if port == EMPTY_STR
150
150
  end
151
151
 
152
152
  return new(
@@ -189,7 +189,7 @@ module Addressable
189
189
  uri = uri.to_s
190
190
  end
191
191
 
192
- if !uri.respond_to?(:to_str)
192
+ unless uri.respond_to?(:to_str)
193
193
  raise TypeError, "Can't convert #{uri.class} into String."
194
194
  end
195
195
  # Otherwise, convert to a String
@@ -281,7 +281,7 @@ module Addressable
281
281
  return nil unless path
282
282
  # If a URI object is passed, just return itself.
283
283
  return path if path.kind_of?(self)
284
- if !path.respond_to?(:to_str)
284
+ unless path.respond_to?(:to_str)
285
285
  raise TypeError, "Can't convert #{path.class} into String."
286
286
  end
287
287
  # Otherwise, convert to a String
@@ -329,13 +329,13 @@ module Addressable
329
329
  # #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
330
330
  def self.join(*uris)
331
331
  uri_objects = uris.collect do |uri|
332
- if !uri.respond_to?(:to_str)
332
+ unless uri.respond_to?(:to_str)
333
333
  raise TypeError, "Can't convert #{uri.class} into String."
334
334
  end
335
335
  uri.kind_of?(self) ? uri : self.parse(uri.to_str)
336
336
  end
337
337
  result = uri_objects.shift.dup
338
- for uri in uri_objects
338
+ uri_objects.each do |uri|
339
339
  result.join!(uri)
340
340
  end
341
341
  return result
@@ -481,7 +481,7 @@ module Addressable
481
481
  leave_encoded.include?(c) ? sequence : c
482
482
  end
483
483
 
484
- result.force_encoding("utf-8")
484
+ result.force_encoding(Encoding::UTF_8)
485
485
  if return_type == String
486
486
  return result
487
487
  elsif return_type == ::Addressable::URI
@@ -579,7 +579,7 @@ module Addressable
579
579
  unencoded = self.unencode_component(component, String, leave_encoded)
580
580
  begin
581
581
  encoded = self.encode_component(
582
- Addressable::IDNA.unicode_normalize_kc(unencoded),
582
+ unencoded.unicode_normalize(:nfc),
583
583
  character_class,
584
584
  leave_encoded
585
585
  )
@@ -687,8 +687,7 @@ module Addressable
687
687
  components.each do |key, value|
688
688
  if value != nil
689
689
  begin
690
- components[key] =
691
- Addressable::IDNA.unicode_normalize_kc(value.to_str)
690
+ components[key] = value.to_str.unicode_normalize(:nfc)
692
691
  rescue ArgumentError
693
692
  # Likely a malformed UTF-8 character, skip unicode normalization
694
693
  components[key] = value.to_str
@@ -836,7 +835,9 @@ module Addressable
836
835
  end
837
836
  end
838
837
 
839
- self.defer_validation do
838
+ reset_ivs
839
+
840
+ defer_validation do
840
841
  # Bunch of crazy logic required because of the composite components
841
842
  # like userinfo and authority.
842
843
  self.scheme = options[:scheme] if options[:scheme]
@@ -851,7 +852,8 @@ module Addressable
851
852
  self.query_values = options[:query_values] if options[:query_values]
852
853
  self.fragment = options[:fragment] if options[:fragment]
853
854
  end
854
- self.to_s
855
+
856
+ to_s # force path validation
855
857
  end
856
858
 
857
859
  ##
@@ -878,9 +880,7 @@ module Addressable
878
880
  # The scheme component for this URI.
879
881
  #
880
882
  # @return [String] The scheme component.
881
- def scheme
882
- return defined?(@scheme) ? @scheme : nil
883
- end
883
+ attr_reader :scheme
884
884
 
885
885
  ##
886
886
  # The scheme component for this URI, normalized.
@@ -888,8 +888,8 @@ module Addressable
888
888
  # @return [String] The scheme component, normalized.
889
889
  def normalized_scheme
890
890
  return nil unless self.scheme
891
- @normalized_scheme ||= begin
892
- if self.scheme =~ /^\s*ssh\+svn\s*$/i
891
+ if @normalized_scheme == NONE
892
+ @normalized_scheme = if self.scheme =~ /^\s*ssh\+svn\s*$/i
893
893
  "svn+ssh".dup
894
894
  else
895
895
  Addressable::URI.normalize_component(
@@ -920,7 +920,7 @@ module Addressable
920
920
  @scheme = nil if @scheme.to_s.strip.empty?
921
921
 
922
922
  # Reset dependent values
923
- remove_instance_variable(:@normalized_scheme) if defined?(@normalized_scheme)
923
+ @normalized_scheme = NONE
924
924
  remove_composite_values
925
925
 
926
926
  # Ensure we haven't created an invalid URI
@@ -931,9 +931,7 @@ module Addressable
931
931
  # The user component for this URI.
932
932
  #
933
933
  # @return [String] The user component.
934
- def user
935
- return defined?(@user) ? @user : nil
936
- end
934
+ attr_reader :user
937
935
 
938
936
  ##
939
937
  # The user component for this URI, normalized.
@@ -941,8 +939,8 @@ module Addressable
941
939
  # @return [String] The user component, normalized.
942
940
  def normalized_user
943
941
  return nil unless self.user
944
- return @normalized_user if defined?(@normalized_user)
945
- @normalized_user ||= begin
942
+ return @normalized_user unless @normalized_user == NONE
943
+ @normalized_user = begin
946
944
  if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
947
945
  (!self.password || self.password.strip.empty?)
948
946
  nil
@@ -970,14 +968,14 @@ module Addressable
970
968
 
971
969
  # You can't have a nil user with a non-nil password
972
970
  if password != nil
973
- @user = EMPTY_STR if @user.nil?
971
+ @user = EMPTY_STR unless user
974
972
  end
975
973
 
976
974
  # Reset dependent values
977
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
978
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
979
- remove_instance_variable(:@authority) if defined?(@authority)
980
- remove_instance_variable(:@normalized_user) if defined?(@normalized_user)
975
+ @userinfo = nil
976
+ @normalized_userinfo = NONE
977
+ @authority = nil
978
+ @normalized_user = NONE
981
979
  remove_composite_values
982
980
 
983
981
  # Ensure we haven't created an invalid URI
@@ -988,9 +986,7 @@ module Addressable
988
986
  # The password component for this URI.
989
987
  #
990
988
  # @return [String] The password component.
991
- def password
992
- return defined?(@password) ? @password : nil
993
- end
989
+ attr_reader :password
994
990
 
995
991
  ##
996
992
  # The password component for this URI, normalized.
@@ -998,8 +994,8 @@ module Addressable
998
994
  # @return [String] The password component, normalized.
999
995
  def normalized_password
1000
996
  return nil unless self.password
1001
- return @normalized_password if defined?(@normalized_password)
1002
- @normalized_password ||= begin
997
+ return @normalized_password unless @normalized_password == NONE
998
+ @normalized_password = begin
1003
999
  if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
1004
1000
  (!self.user || self.user.strip.empty?)
1005
1001
  nil
@@ -1026,17 +1022,15 @@ module Addressable
1026
1022
  @password = new_password ? new_password.to_str : nil
1027
1023
 
1028
1024
  # You can't have a nil user with a non-nil password
1029
- @password ||= nil
1030
- @user ||= nil
1031
1025
  if @password != nil
1032
- @user = EMPTY_STR if @user.nil?
1026
+ self.user = EMPTY_STR if user.nil?
1033
1027
  end
1034
1028
 
1035
1029
  # Reset dependent values
1036
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
1037
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
1038
- remove_instance_variable(:@authority) if defined?(@authority)
1039
- remove_instance_variable(:@normalized_password) if defined?(@normalized_password)
1030
+ @userinfo = nil
1031
+ @normalized_userinfo = NONE
1032
+ @authority = nil
1033
+ @normalized_password = NONE
1040
1034
  remove_composite_values
1041
1035
 
1042
1036
  # Ensure we haven't created an invalid URI
@@ -1066,8 +1060,8 @@ module Addressable
1066
1060
  # @return [String] The userinfo component, normalized.
1067
1061
  def normalized_userinfo
1068
1062
  return nil unless self.userinfo
1069
- return @normalized_userinfo if defined?(@normalized_userinfo)
1070
- @normalized_userinfo ||= begin
1063
+ return @normalized_userinfo unless @normalized_userinfo == NONE
1064
+ @normalized_userinfo = begin
1071
1065
  current_user = self.normalized_user
1072
1066
  current_password = self.normalized_password
1073
1067
  if !current_user && !current_password
@@ -1105,7 +1099,7 @@ module Addressable
1105
1099
  self.user = new_user
1106
1100
 
1107
1101
  # Reset dependent values
1108
- remove_instance_variable(:@authority) if defined?(@authority)
1102
+ @authority = nil
1109
1103
  remove_composite_values
1110
1104
 
1111
1105
  # Ensure we haven't created an invalid URI
@@ -1116,9 +1110,7 @@ module Addressable
1116
1110
  # The host component for this URI.
1117
1111
  #
1118
1112
  # @return [String] The host component.
1119
- def host
1120
- return defined?(@host) ? @host : nil
1121
- end
1113
+ attr_reader :host
1122
1114
 
1123
1115
  ##
1124
1116
  # The host component for this URI, normalized.
@@ -1161,8 +1153,8 @@ module Addressable
1161
1153
  @host = new_host ? new_host.to_str : nil
1162
1154
 
1163
1155
  # Reset dependent values
1164
- remove_instance_variable(:@authority) if defined?(@authority)
1165
- remove_instance_variable(:@normalized_host) if defined?(@normalized_host)
1156
+ @authority = nil
1157
+ @normalized_host = nil
1166
1158
  remove_composite_values
1167
1159
 
1168
1160
  # Ensure we haven't created an invalid URI
@@ -1293,14 +1285,14 @@ module Addressable
1293
1285
  end
1294
1286
 
1295
1287
  # Password assigned first to ensure validity in case of nil
1296
- self.password = defined?(new_password) ? new_password : nil
1297
- self.user = defined?(new_user) ? new_user : nil
1298
- self.host = defined?(new_host) ? new_host : nil
1299
- self.port = defined?(new_port) ? new_port : nil
1288
+ self.password = new_password
1289
+ self.user = new_user
1290
+ self.host = new_host
1291
+ self.port = new_port
1300
1292
 
1301
1293
  # Reset dependent values
1302
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
1303
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
1294
+ @userinfo = nil
1295
+ @normalized_userinfo = NONE
1304
1296
  remove_composite_values
1305
1297
 
1306
1298
  # Ensure we haven't created an invalid URI
@@ -1348,16 +1340,16 @@ module Addressable
1348
1340
  new_port = new_origin[/:([^:@\[\]\/]*?)$/, 1]
1349
1341
  end
1350
1342
 
1351
- self.scheme = defined?(new_scheme) ? new_scheme : nil
1352
- self.host = defined?(new_host) ? new_host : nil
1353
- self.port = defined?(new_port) ? new_port : nil
1343
+ self.scheme = new_scheme
1344
+ self.host = new_host
1345
+ self.port = new_port
1354
1346
  self.userinfo = nil
1355
1347
 
1356
1348
  # Reset dependent values
1357
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
1358
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
1359
- remove_instance_variable(:@authority) if defined?(@authority)
1360
- remove_instance_variable(:@normalized_authority) if defined?(@normalized_authority)
1349
+ @userinfo = nil
1350
+ @normalized_userinfo = NONE
1351
+ @authority = nil
1352
+ @normalized_authority = nil
1361
1353
  remove_composite_values
1362
1354
 
1363
1355
  # Ensure we haven't created an invalid URI
@@ -1384,9 +1376,7 @@ module Addressable
1384
1376
  # infer port numbers from default values.
1385
1377
  #
1386
1378
  # @return [Integer] The port component.
1387
- def port
1388
- return defined?(@port) ? @port : nil
1389
- end
1379
+ attr_reader :port
1390
1380
 
1391
1381
  ##
1392
1382
  # The port component for this URI, normalized.
@@ -1394,8 +1384,8 @@ module Addressable
1394
1384
  # @return [Integer] The port component, normalized.
1395
1385
  def normalized_port
1396
1386
  return nil unless self.port
1397
- return @normalized_port if defined?(@normalized_port)
1398
- @normalized_port ||= begin
1387
+ return @normalized_port unless @normalized_port == NONE
1388
+ @normalized_port = begin
1399
1389
  if URI.port_mapping[self.normalized_scheme] == self.port
1400
1390
  nil
1401
1391
  else
@@ -1426,8 +1416,8 @@ module Addressable
1426
1416
  @port = nil if @port == 0
1427
1417
 
1428
1418
  # Reset dependent values
1429
- remove_instance_variable(:@authority) if defined?(@authority)
1430
- remove_instance_variable(:@normalized_port) if defined?(@normalized_port)
1419
+ @authority = nil
1420
+ @normalized_port = NONE
1431
1421
  remove_composite_values
1432
1422
 
1433
1423
  # Ensure we haven't created an invalid URI
@@ -1528,9 +1518,7 @@ module Addressable
1528
1518
  # The path component for this URI.
1529
1519
  #
1530
1520
  # @return [String] The path component.
1531
- def path
1532
- return defined?(@path) ? @path : EMPTY_STR
1533
- end
1521
+ attr_reader :path
1534
1522
 
1535
1523
  NORMPATH = /^(?!\/)[^\/:]*:.*$/
1536
1524
  ##
@@ -1579,7 +1567,7 @@ module Addressable
1579
1567
  end
1580
1568
 
1581
1569
  # Reset dependent values
1582
- remove_instance_variable(:@normalized_path) if defined?(@normalized_path)
1570
+ @normalized_path = nil
1583
1571
  remove_composite_values
1584
1572
 
1585
1573
  # Ensure we haven't created an invalid URI
@@ -1609,9 +1597,7 @@ module Addressable
1609
1597
  # The query component for this URI.
1610
1598
  #
1611
1599
  # @return [String] The query component.
1612
- def query
1613
- return defined?(@query) ? @query : nil
1614
- end
1600
+ attr_reader :query
1615
1601
 
1616
1602
  ##
1617
1603
  # The query component for this URI, normalized.
@@ -1619,8 +1605,8 @@ module Addressable
1619
1605
  # @return [String] The query component, normalized.
1620
1606
  def normalized_query(*flags)
1621
1607
  return nil unless self.query
1622
- return @normalized_query if defined?(@normalized_query)
1623
- @normalized_query ||= begin
1608
+ return @normalized_query unless @normalized_query == NONE
1609
+ @normalized_query = begin
1624
1610
  modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
1625
1611
  # Make sure possible key-value pair delimiters are escaped.
1626
1612
  modified_query_class.sub!("\\&", "").sub!("\\;", "")
@@ -1652,7 +1638,7 @@ module Addressable
1652
1638
  @query = new_query ? new_query.to_str : nil
1653
1639
 
1654
1640
  # Reset dependent values
1655
- remove_instance_variable(:@normalized_query) if defined?(@normalized_query)
1641
+ @normalized_query = NONE
1656
1642
  remove_composite_values
1657
1643
  end
1658
1644
 
@@ -1814,9 +1800,7 @@ module Addressable
1814
1800
  # The fragment component for this URI.
1815
1801
  #
1816
1802
  # @return [String] The fragment component.
1817
- def fragment
1818
- return defined?(@fragment) ? @fragment : nil
1819
- end
1803
+ attr_reader :fragment
1820
1804
 
1821
1805
  ##
1822
1806
  # The fragment component for this URI, normalized.
@@ -1824,8 +1808,8 @@ module Addressable
1824
1808
  # @return [String] The fragment component, normalized.
1825
1809
  def normalized_fragment
1826
1810
  return nil unless self.fragment
1827
- return @normalized_fragment if defined?(@normalized_fragment)
1828
- @normalized_fragment ||= begin
1811
+ return @normalized_fragment unless @normalized_fragment == NONE
1812
+ @normalized_fragment = begin
1829
1813
  component = Addressable::URI.normalize_component(
1830
1814
  self.fragment,
1831
1815
  Addressable::URI::NormalizeCharacterClasses::FRAGMENT
@@ -1848,7 +1832,7 @@ module Addressable
1848
1832
  @fragment = new_fragment ? new_fragment.to_str : nil
1849
1833
 
1850
1834
  # Reset dependent values
1851
- remove_instance_variable(:@normalized_fragment) if defined?(@normalized_fragment)
1835
+ @normalized_fragment = NONE
1852
1836
  remove_composite_values
1853
1837
 
1854
1838
  # Ensure we haven't created an invalid URI
@@ -2014,7 +1998,7 @@ module Addressable
2014
1998
  #
2015
1999
  # @see Hash#merge
2016
2000
  def merge(hash)
2017
- if !hash.respond_to?(:to_hash)
2001
+ unless hash.respond_to?(:to_hash)
2018
2002
  raise TypeError, "Can't convert #{hash.class} into Hash."
2019
2003
  end
2020
2004
  hash = hash.to_hash
@@ -2408,7 +2392,8 @@ module Addressable
2408
2392
  yield
2409
2393
  @validation_deferred = false
2410
2394
  validate
2411
- return nil
2395
+ ensure
2396
+ @validation_deferred = false
2412
2397
  end
2413
2398
 
2414
2399
  protected
@@ -2507,11 +2492,7 @@ module Addressable
2507
2492
  # @return [Addressable::URI] <code>self</code>.
2508
2493
  def replace_self(uri)
2509
2494
  # Reset dependent values
2510
- instance_variables.each do |var|
2511
- if instance_variable_defined?(var) && var != :@validation_deferred
2512
- remove_instance_variable(var)
2513
- end
2514
- end
2495
+ reset_ivs
2515
2496
 
2516
2497
  @scheme = uri.scheme
2517
2498
  @user = uri.user
@@ -2543,8 +2524,8 @@ module Addressable
2543
2524
  #
2544
2525
  # @api private
2545
2526
  def remove_composite_values
2546
- remove_instance_variable(:@uri_string) if defined?(@uri_string)
2547
- remove_instance_variable(:@hash) if defined?(@hash)
2527
+ @uri_string = nil
2528
+ @hash = nil
2548
2529
  end
2549
2530
 
2550
2531
  ##
@@ -2556,5 +2537,40 @@ module Addressable
2556
2537
  str.force_encoding(Encoding::UTF_8)
2557
2538
  end
2558
2539
  end
2540
+
2541
+ private
2542
+
2543
+ ##
2544
+ # Resets instance variables
2545
+ #
2546
+ # @api private
2547
+ def reset_ivs
2548
+ @scheme = nil
2549
+ @user = nil
2550
+ @normalized_scheme = NONE
2551
+ @normalized_user = NONE
2552
+ @uri_string = nil
2553
+ @hash = nil
2554
+ @userinfo = nil
2555
+ @normalized_userinfo = NONE
2556
+ @authority = nil
2557
+ @password = nil
2558
+ @normalized_authority = nil
2559
+ @port = nil
2560
+ @normalized_password = NONE
2561
+ @host = nil
2562
+ @normalized_host = nil
2563
+ @normalized_port = NONE
2564
+ @path = EMPTY_STR
2565
+ @normalized_path = nil
2566
+ @normalized_query = NONE
2567
+ @fragment = nil
2568
+ @normalized_fragment = NONE
2569
+ @query = nil
2570
+ end
2571
+
2572
+ NONE = Object.new.freeze
2573
+
2574
+ private_constant :NONE
2559
2575
  end
2560
2576
  end
@@ -23,7 +23,7 @@ if !defined?(Addressable::VERSION)
23
23
  module VERSION
24
24
  MAJOR = 2
25
25
  MINOR = 8
26
- TINY = 1
26
+ TINY = 2
27
27
 
28
28
  STRING = [MAJOR, MINOR, TINY].join('.')
29
29
  end
@@ -38,6 +38,12 @@ shared_examples_for "converting from unicode to ASCII" do
38
38
  )).to eq("www.xn--8ws00zhy3a.com")
39
39
  end
40
40
 
41
+ it "also accepts unicode strings encoded as ascii-8bit" do
42
+ expect(Addressable::IDNA.to_ascii(
43
+ "www.詹姆斯.com".b
44
+ )).to eq("www.xn--8ws00zhy3a.com")
45
+ end
46
+
41
47
  it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do
42
48
  "www.Iñtërnâtiônàlizætiøn.com"
43
49
  expect(Addressable::IDNA.to_ascii(
@@ -249,11 +255,6 @@ shared_examples_for "converting from ASCII to unicode" do
249
255
  "example..host"
250
256
  )).to eq("example..host")
251
257
  end
252
-
253
- it "should normalize 'string' correctly" do
254
- expect(Addressable::IDNA.unicode_normalize_kc(:'string')).to eq("string")
255
- expect(Addressable::IDNA.unicode_normalize_kc("string")).to eq("string")
256
- end
257
258
  end
258
259
 
259
260
  describe Addressable::IDNA, "when using the pure-Ruby implementation" do
@@ -1021,6 +1021,19 @@ describe Addressable::Template do
1021
1021
  )
1022
1022
  end
1023
1023
 
1024
+ it "normalizes as unicode even with wrong encoding specified" do
1025
+ template = subject.partial_expand("query" => "Cafe\u0301".b)
1026
+ expect(template.pattern).to eq(
1027
+ "http://example.com/{resource}/Caf%C3%A9/"
1028
+ )
1029
+ end
1030
+
1031
+ it "raises on invalid unicode input" do
1032
+ expect {
1033
+ subject.partial_expand("query" => "M\xE9thode".b)
1034
+ }.to raise_error(ArgumentError, "invalid byte sequence in UTF-8")
1035
+ end
1036
+
1024
1037
  it "does not normalize unicode when byte semantics requested" do
1025
1038
  template = subject.partial_expand({"query" => "Cafe\u0301"}, nil, false)
1026
1039
  expect(template.pattern).to eq(
@@ -1081,6 +1094,17 @@ describe Addressable::Template do
1081
1094
  expect(uri).to eq("http://example.com/search/Caf%C3%A9/")
1082
1095
  end
1083
1096
 
1097
+ it "normalizes as unicode even with wrong encoding specified" do
1098
+ uri = subject.expand("query" => "Cafe\u0301".b).to_str
1099
+ expect(uri).to eq("http://example.com/search/Caf%C3%A9/")
1100
+ end
1101
+
1102
+ it "raises on invalid unicode input" do
1103
+ expect {
1104
+ subject.expand("query" => "M\xE9thode".b).to_str
1105
+ }.to raise_error(ArgumentError, "invalid byte sequence in UTF-8")
1106
+ end
1107
+
1084
1108
  it "does not normalize unicode when byte semantics requested" do
1085
1109
  uri = subject.expand({ "query" => "Cafe\u0301" }, nil, false).to_str
1086
1110
  expect(uri).to eq("http://example.com/search/Cafe%CC%81/")
@@ -3021,6 +3021,20 @@ describe Addressable::URI, "when parsed from " +
3021
3021
  end
3022
3022
  end
3023
3023
 
3024
+ describe Addressable::URI, "when parsed with empty port" do
3025
+ subject(:uri) do
3026
+ Addressable::URI.parse("//example.com:")
3027
+ end
3028
+
3029
+ it "should not infer a port" do
3030
+ expect(uri.port).to be(nil)
3031
+ end
3032
+
3033
+ it "should have a site value of '//example.com'" do
3034
+ expect(uri.site).to eq("//example.com")
3035
+ end
3036
+ end
3037
+
3024
3038
  describe Addressable::URI, "when parsed from " +
3025
3039
  "'http://example.com/%2E/'" do
3026
3040
  before do
@@ -5939,6 +5953,26 @@ describe Addressable::URI, "when normalizing a path with an encoded slash" do
5939
5953
  end
5940
5954
  end
5941
5955
 
5956
+ describe Addressable::URI, "when normalizing a path with special unicode" do
5957
+ it "does not stop at or ignore null bytes" do
5958
+ expect(Addressable::URI.parse("/path%00segment/").normalize.path).to eq(
5959
+ "/path%00segment/"
5960
+ )
5961
+ end
5962
+
5963
+ it "does apply NFC unicode normalization" do
5964
+ expect(Addressable::URI.parse("/%E2%84%A6").normalize.path).to eq(
5965
+ "/%CE%A9"
5966
+ )
5967
+ end
5968
+
5969
+ it "does not apply NFKC unicode normalization" do
5970
+ expect(Addressable::URI.parse("/%C2%AF%C2%A0").normalize.path).to eq(
5971
+ "/%C2%AF%C2%A0"
5972
+ )
5973
+ end
5974
+ end
5975
+
5942
5976
  describe Addressable::URI, "when normalizing a partially encoded string" do
5943
5977
  it "should result in correct percent encoded sequence" do
5944
5978
  expect(Addressable::URI.normalize_component(
@@ -6743,3 +6777,25 @@ describe Addressable::URI, "when initialized in a non-main `Ractor`" do
6743
6777
  ).to eq(main)
6744
6778
  end
6745
6779
  end
6780
+
6781
+ describe Addressable::URI, "when deferring validation" do
6782
+ subject(:deferred) { uri.instance_variable_get(:@validation_deferred) }
6783
+
6784
+ let(:uri) { Addressable::URI.parse("http://example.com") }
6785
+
6786
+ it "defers validation within the block" do
6787
+ uri.defer_validation do
6788
+ expect(deferred).to be true
6789
+ end
6790
+ end
6791
+
6792
+ it "always resets deferral afterward" do
6793
+ expect { uri.defer_validation { raise "boom" } }.to raise_error("boom")
6794
+ expect(deferred).to be false
6795
+ end
6796
+
6797
+ it "returns nil" do
6798
+ res = uri.defer_validation {}
6799
+ expect(res).to be nil
6800
+ end
6801
+ end
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.8.1
4
+ version: 2.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Aman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-19 00:00:00.000000000 Z
11
+ date: 2023-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: public_suffix
@@ -65,6 +65,7 @@ files:
65
65
  - LICENSE.txt
66
66
  - README.md
67
67
  - Rakefile
68
+ - addressable.gemspec
68
69
  - data/unicode.data
69
70
  - lib/addressable.rb
70
71
  - lib/addressable/idna.rb
@@ -108,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
109
  - !ruby/object:Gem::Version
109
110
  version: '0'
110
111
  requirements: []
111
- rubygems_version: 3.3.7
112
+ rubygems_version: 3.4.8
112
113
  signing_key:
113
114
  specification_version: 4
114
115
  summary: URI Implementation