uri 0.10.3 → 0.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/uri/common.rb CHANGED
@@ -13,9 +13,14 @@ require_relative "rfc2396_parser"
13
13
  require_relative "rfc3986_parser"
14
14
 
15
15
  module URI
16
+ include RFC2396_REGEXP
17
+
16
18
  REGEXP = RFC2396_REGEXP
17
19
  Parser = RFC2396_Parser
18
20
  RFC3986_PARSER = RFC3986_Parser.new
21
+ Ractor.make_shareable(RFC3986_PARSER) if defined?(Ractor)
22
+ RFC2396_PARSER = RFC2396_Parser.new
23
+ Ractor.make_shareable(RFC2396_PARSER) if defined?(Ractor)
19
24
 
20
25
  # URI::Parser.new
21
26
  DEFAULT_PARSER = Parser.new
@@ -27,6 +32,7 @@ module URI
27
32
  DEFAULT_PARSER.regexp.each_pair do |sym, str|
28
33
  const_set(sym, str)
29
34
  end
35
+ Ractor.make_shareable(DEFAULT_PARSER) if defined?(Ractor)
30
36
 
31
37
  module Util # :nodoc:
32
38
  def make_components_hash(klass, array_hash)
@@ -60,24 +66,70 @@ module URI
60
66
  module_function :make_components_hash
61
67
  end
62
68
 
63
- include REGEXP
69
+ module Schemes
70
+ end
71
+ private_constant :Schemes
72
+
73
+ # Registers the given +klass+ as the class to be instantiated
74
+ # when parsing a \URI with the given +scheme+:
75
+ #
76
+ # URI.register_scheme('MS_SEARCH', URI::Generic) # => URI::Generic
77
+ # URI.scheme_list['MS_SEARCH'] # => URI::Generic
78
+ #
79
+ # Note that after calling String#upcase on +scheme+, it must be a valid
80
+ # constant name.
81
+ def self.register_scheme(scheme, klass)
82
+ Schemes.const_set(scheme.to_s.upcase, klass)
83
+ end
64
84
 
65
- @@schemes = {}
66
- # Returns a Hash of the defined schemes.
85
+ # Returns a hash of the defined schemes:
86
+ #
87
+ # URI.scheme_list
88
+ # # =>
89
+ # {"MAILTO"=>URI::MailTo,
90
+ # "LDAPS"=>URI::LDAPS,
91
+ # "WS"=>URI::WS,
92
+ # "HTTP"=>URI::HTTP,
93
+ # "HTTPS"=>URI::HTTPS,
94
+ # "LDAP"=>URI::LDAP,
95
+ # "FILE"=>URI::File,
96
+ # "FTP"=>URI::FTP}
97
+ #
98
+ # Related: URI.register_scheme.
67
99
  def self.scheme_list
68
- @@schemes
100
+ Schemes.constants.map { |name|
101
+ [name.to_s.upcase, Schemes.const_get(name)]
102
+ }.to_h
69
103
  end
70
104
 
105
+ INITIAL_SCHEMES = scheme_list
106
+ private_constant :INITIAL_SCHEMES
107
+ Ractor.make_shareable(INITIAL_SCHEMES) if defined?(Ractor)
108
+
109
+ # Returns a new object constructed from the given +scheme+, +arguments+,
110
+ # and +default+:
111
+ #
112
+ # - The new object is an instance of <tt>URI.scheme_list[scheme.upcase]</tt>.
113
+ # - The object is initialized by calling the class initializer
114
+ # using +scheme+ and +arguments+.
115
+ # See URI::Generic.new.
71
116
  #
72
- # Construct a URI instance, using the scheme to detect the appropriate class
73
- # from +URI.scheme_list+.
117
+ # Examples:
118
+ #
119
+ # values = ['john.doe', 'www.example.com', '123', nil, '/forum/questions/', nil, 'tag=networking&order=newest', 'top']
120
+ # URI.for('https', *values)
121
+ # # => #<URI::HTTPS https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
122
+ # URI.for('foo', *values, default: URI::HTTP)
123
+ # # => #<URI::HTTP foo://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
74
124
  #
75
125
  def self.for(scheme, *arguments, default: Generic)
76
- if scheme
77
- uri_class = @@schemes[scheme.upcase] || default
78
- else
79
- uri_class = default
126
+ const_name = scheme.to_s.upcase
127
+
128
+ uri_class = INITIAL_SCHEMES[const_name]
129
+ uri_class ||= if /\A[A-Z]\w*\z/.match?(const_name) && Schemes.const_defined?(const_name, false)
130
+ Schemes.const_get(const_name, false)
80
131
  end
132
+ uri_class ||= default
81
133
 
82
134
  return uri_class.new(scheme, *arguments)
83
135
  end
@@ -99,95 +151,49 @@ module URI
99
151
  #
100
152
  class BadURIError < Error; end
101
153
 
102
- #
103
- # == Synopsis
104
- #
105
- # URI::split(uri)
106
- #
107
- # == Args
108
- #
109
- # +uri+::
110
- # String with URI.
111
- #
112
- # == Description
113
- #
114
- # Splits the string on following parts and returns array with result:
115
- #
116
- # * Scheme
117
- # * Userinfo
118
- # * Host
119
- # * Port
120
- # * Registry
121
- # * Path
122
- # * Opaque
123
- # * Query
124
- # * Fragment
125
- #
126
- # == Usage
127
- #
128
- # require 'uri'
129
- #
130
- # URI.split("http://www.ruby-lang.org/")
131
- # # => ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil]
154
+ # Returns a 9-element array representing the parts of the \URI
155
+ # formed from the string +uri+;
156
+ # each array element is a string or +nil+:
157
+ #
158
+ # names = %w[scheme userinfo host port registry path opaque query fragment]
159
+ # values = URI.split('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
160
+ # names.zip(values)
161
+ # # =>
162
+ # [["scheme", "https"],
163
+ # ["userinfo", "john.doe"],
164
+ # ["host", "www.example.com"],
165
+ # ["port", "123"],
166
+ # ["registry", nil],
167
+ # ["path", "/forum/questions/"],
168
+ # ["opaque", nil],
169
+ # ["query", "tag=networking&order=newest"],
170
+ # ["fragment", "top"]]
132
171
  #
133
172
  def self.split(uri)
134
173
  RFC3986_PARSER.split(uri)
135
174
  end
136
175
 
176
+ # Returns a new \URI object constructed from the given string +uri+:
137
177
  #
138
- # == Synopsis
139
- #
140
- # URI::parse(uri_str)
141
- #
142
- # == Args
143
- #
144
- # +uri_str+::
145
- # String with URI.
146
- #
147
- # == Description
148
- #
149
- # Creates one of the URI's subclasses instance from the string.
178
+ # URI.parse('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
179
+ # # => #<URI::HTTPS https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
180
+ # URI.parse('http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
181
+ # # => #<URI::HTTP http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
150
182
  #
151
- # == Raises
152
- #
153
- # URI::InvalidURIError::
154
- # Raised if URI given is not a correct one.
155
- #
156
- # == Usage
157
- #
158
- # require 'uri'
159
- #
160
- # uri = URI.parse("http://www.ruby-lang.org/")
161
- # # => #<URI::HTTP http://www.ruby-lang.org/>
162
- # uri.scheme
163
- # # => "http"
164
- # uri.host
165
- # # => "www.ruby-lang.org"
166
- #
167
- # It's recommended to first ::escape the provided +uri_str+ if there are any
168
- # invalid URI characters.
183
+ # It's recommended to first ::escape string +uri+
184
+ # if it may contain invalid URI characters.
169
185
  #
170
186
  def self.parse(uri)
171
187
  RFC3986_PARSER.parse(uri)
172
188
  end
173
189
 
190
+ # Merges the given URI strings +str+
191
+ # per {RFC 2396}[https://www.rfc-editor.org/rfc/rfc2396.html].
174
192
  #
175
- # == Synopsis
176
- #
177
- # URI::join(str[, str, ...])
178
- #
179
- # == Args
180
- #
181
- # +str+::
182
- # String(s) to work with, will be converted to RFC3986 URIs before merging.
183
- #
184
- # == Description
185
- #
186
- # Joins URIs.
187
- #
188
- # == Usage
193
+ # Each string in +str+ is converted to an
194
+ # {RFC3986 URI}[https://www.rfc-editor.org/rfc/rfc3986.html] before being merged.
189
195
  #
190
- # require 'uri'
196
+ # Examples:
191
197
  #
192
198
  # URI.join("http://example.com/","main.rbx")
193
199
  # # => #<URI::HTTP http://example.com/main.rbx>
@@ -232,7 +238,7 @@ module URI
232
238
  # URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.")
233
239
  # # => ["http://foo.example.com/bla", "mailto:test@example.com"]
234
240
  #
235
- def self.extract(str, schemes = nil, &block)
241
+ def self.extract(str, schemes = nil, &block) # :nodoc:
236
242
  warn "URI.extract is obsolete", uplevel: 1 if $VERBOSE
237
243
  DEFAULT_PARSER.extract(str, schemes, &block)
238
244
  end
@@ -269,7 +275,7 @@ module URI
269
275
  # p $&
270
276
  # end
271
277
  #
272
- def self.regexp(schemes = nil)
278
+ def self.regexp(schemes = nil)# :nodoc:
273
279
  warn "URI.regexp is obsolete", uplevel: 1 if $VERBOSE
274
280
  DEFAULT_PARSER.make_regexp(schemes)
275
281
  end
@@ -278,6 +284,7 @@ module URI
278
284
  256.times do |i|
279
285
  TBLENCWWWCOMP_[-i.chr] = -('%%%02X' % i)
280
286
  end
287
+ TBLENCURICOMP_ = TBLENCWWWCOMP_.dup.freeze
281
288
  TBLENCWWWCOMP_[' '] = '+'
282
289
  TBLENCWWWCOMP_.freeze
283
290
  TBLDECWWWCOMP_ = {} # :nodoc:
@@ -291,18 +298,91 @@ module URI
291
298
  TBLDECWWWCOMP_['+'] = ' '
292
299
  TBLDECWWWCOMP_.freeze
293
300
 
294
- # Encodes given +str+ to URL-encoded form data.
301
+ # Returns a URL-encoded string derived from the given string +str+.
302
+ #
303
+ # The returned string:
304
+ #
305
+ # - Preserves:
306
+ #
307
+ # - Characters <tt>'*'</tt>, <tt>'.'</tt>, <tt>'-'</tt>, and <tt>'_'</tt>.
308
+ # - Character in ranges <tt>'a'..'z'</tt>, <tt>'A'..'Z'</tt>,
309
+ # and <tt>'0'..'9'</tt>.
310
+ #
311
+ # Example:
312
+ #
313
+ # URI.encode_www_form_component('*.-_azAZ09')
314
+ # # => "*.-_azAZ09"
315
+ #
316
+ # - Converts:
317
+ #
318
+ # - Character <tt>' '</tt> to character <tt>'+'</tt>.
319
+ # - Any other character to "percent notation";
320
+ # the percent notation for character <i>c</i> is <tt>'%%%X' % c.ord</tt>.
295
321
  #
296
- # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
297
- # (ASCII space) to + and converts others to %XX.
322
+ # Example:
298
323
  #
299
- # If +enc+ is given, convert +str+ to the encoding before percent encoding.
324
+ # URI.encode_www_form_component('Here are some punctuation characters: ,;?:')
325
+ # # => "Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A"
300
326
  #
301
- # This is an implementation of
302
- # https://www.w3.org/TR/2013/CR-html5-20130806/forms.html#url-encoded-form-data.
327
+ # Encoding:
303
328
  #
304
- # See URI.decode_www_form_component, URI.encode_www_form.
329
+ # - If +str+ has encoding Encoding::ASCII_8BIT, argument +enc+ is ignored.
330
+ # - Otherwise +str+ is converted first to Encoding::UTF_8
331
+ # (with suitable character replacements),
332
+ # and then to encoding +enc+.
333
+ #
334
+ # In either case, the returned string has forced encoding Encoding::US_ASCII.
335
+ #
336
+ # Related: URI.encode_uri_component (encodes <tt>' '</tt> as <tt>'%20'</tt>).
305
337
  def self.encode_www_form_component(str, enc=nil)
338
+ _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc)
339
+ end
340
+
341
+ # Returns a string decoded from the given \URL-encoded string +str+.
342
+ #
343
+ # The given string is first encoded as Encoding::ASCII-8BIT (using String#b),
344
+ # then decoded (as below), and finally force-encoded to the given encoding +enc+.
345
+ #
346
+ # The returned string:
347
+ #
348
+ # - Preserves:
349
+ #
350
+ # - Characters <tt>'*'</tt>, <tt>'.'</tt>, <tt>'-'</tt>, and <tt>'_'</tt>.
351
+ # - Character in ranges <tt>'a'..'z'</tt>, <tt>'A'..'Z'</tt>,
352
+ # and <tt>'0'..'9'</tt>.
353
+ #
354
+ # Example:
355
+ #
356
+ # URI.decode_www_form_component('*.-_azAZ09')
357
+ # # => "*.-_azAZ09"
358
+ #
359
+ # - Converts:
360
+ #
361
+ # - Character <tt>'+'</tt> to character <tt>' '</tt>.
362
+ # - Each "percent notation" to an ASCII character.
363
+ #
364
+ # Example:
365
+ #
366
+ # URI.decode_www_form_component('Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A')
367
+ # # => "Here are some punctuation characters: ,;?:"
368
+ #
369
+ # Related: URI.decode_uri_component (preserves <tt>'+'</tt>).
370
+ def self.decode_www_form_component(str, enc=Encoding::UTF_8)
371
+ _decode_uri_component(/\+|%\h\h/, str, enc)
372
+ end
373
+
374
+ # Like URI.encode_www_form_component, except that <tt>' '</tt> (space)
375
+ # is encoded as <tt>'%20'</tt> (instead of <tt>'+'</tt>).
376
+ def self.encode_uri_component(str, enc=nil)
377
+ _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCURICOMP_, str, enc)
378
+ end
379
+
380
+ # Like URI.decode_www_form_component, except that <tt>'+'</tt> is preserved.
381
+ def self.decode_uri_component(str, enc=Encoding::UTF_8)
382
+ _decode_uri_component(/%\h\h/, str, enc)
383
+ end
384
+
385
+ def self._encode_uri_component(regexp, table, str, enc)
306
386
  str = str.to_s.dup
307
387
  if str.encoding != Encoding::ASCII_8BIT
308
388
  if enc && enc != Encoding::ASCII_8BIT
@@ -311,47 +391,115 @@ module URI
311
391
  end
312
392
  str.force_encoding(Encoding::ASCII_8BIT)
313
393
  end
314
- str.gsub!(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_)
394
+ str.gsub!(regexp, table)
315
395
  str.force_encoding(Encoding::US_ASCII)
316
396
  end
397
+ private_class_method :_encode_uri_component
317
398
 
318
- # Decodes given +str+ of URL-encoded form data.
319
- #
320
- # This decodes + to SP.
321
- #
322
- # See URI.encode_www_form_component, URI.decode_www_form.
323
- def self.decode_www_form_component(str, enc=Encoding::UTF_8)
324
- raise ArgumentError, "invalid %-encoding (#{str})" if /%(?!\h\h)/ =~ str
325
- str.b.gsub(/\+|%\h\h/, TBLDECWWWCOMP_).force_encoding(enc)
399
+ def self._decode_uri_component(regexp, str, enc)
400
+ raise ArgumentError, "invalid %-encoding (#{str})" if /%(?!\h\h)/.match?(str)
401
+ str.b.gsub(regexp, TBLDECWWWCOMP_).force_encoding(enc)
326
402
  end
403
+ private_class_method :_decode_uri_component
327
404
 
328
- # Generates URL-encoded form data from given +enum+.
405
+ # Returns a URL-encoded string derived from the given
406
+ # {Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-Enumerable+in+Ruby+Classes]
407
+ # +enum+.
408
+ #
409
+ # The result is suitable for use as form data
410
+ # for an \HTTP request whose <tt>Content-Type</tt> is
411
+ # <tt>'application/x-www-form-urlencoded'</tt>.
412
+ #
413
+ # The returned string consists of the elements of +enum+,
414
+ # each converted to one or more URL-encoded strings,
415
+ # and all joined with character <tt>'&'</tt>.
416
+ #
417
+ # Simple examples:
418
+ #
419
+ # URI.encode_www_form([['foo', 0], ['bar', 1], ['baz', 2]])
420
+ # # => "foo=0&bar=1&baz=2"
421
+ # URI.encode_www_form({foo: 0, bar: 1, baz: 2})
422
+ # # => "foo=0&bar=1&baz=2"
423
+ #
424
+ # The returned string is formed using method URI.encode_www_form_component,
425
+ # which converts certain characters:
329
426
  #
330
- # This generates application/x-www-form-urlencoded data defined in HTML5
331
- # from given an Enumerable object.
427
+ # URI.encode_www_form('f#o': '/', 'b-r': '$', 'b z': '@')
428
+ # # => "f%23o=%2F&b-r=%24&b+z=%40"
332
429
  #
333
- # This internally uses URI.encode_www_form_component(str).
430
+ # When +enum+ is Array-like, each element +ele+ is converted to a field:
334
431
  #
335
- # This method doesn't convert the encoding of given items, so convert them
336
- # before calling this method if you want to send data as other than original
337
- # encoding or mixed encoding data. (Strings which are encoded in an HTML5
338
- # ASCII incompatible encoding are converted to UTF-8.)
432
+ # - If +ele+ is an array of two or more elements,
433
+ # the field is formed from its first two elements
434
+ # (and any additional elements are ignored):
339
435
  #
340
- # This method doesn't handle files. When you send a file, use
341
- # multipart/form-data.
436
+ # name = URI.encode_www_form_component(ele[0], enc)
437
+ # value = URI.encode_www_form_component(ele[1], enc)
438
+ # "#{name}=#{value}"
342
439
  #
343
- # This refers https://url.spec.whatwg.org/#concept-urlencoded-serializer
440
+ # Examples:
344
441
  #
345
- # URI.encode_www_form([["q", "ruby"], ["lang", "en"]])
346
- # #=> "q=ruby&lang=en"
347
- # URI.encode_www_form("q" => "ruby", "lang" => "en")
348
- # #=> "q=ruby&lang=en"
349
- # URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en")
350
- # #=> "q=ruby&q=perl&lang=en"
351
- # URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]])
352
- # #=> "q=ruby&q=perl&lang=en"
442
+ # URI.encode_www_form([%w[foo bar], %w[baz bat bah]])
443
+ # # => "foo=bar&baz=bat"
444
+ # URI.encode_www_form([['foo', 0], ['bar', :baz, 'bat']])
445
+ # # => "foo=0&bar=baz"
446
+ #
447
+ # - If +ele+ is an array of one element,
448
+ # the field is formed from <tt>ele[0]</tt>:
449
+ #
450
+ # URI.encode_www_form_component(ele[0])
451
+ #
452
+ # Example:
453
+ #
454
+ # URI.encode_www_form([['foo'], [:bar], [0]])
455
+ # # => "foo&bar&0"
456
+ #
457
+ # - Otherwise the field is formed from +ele+:
458
+ #
459
+ # URI.encode_www_form_component(ele)
460
+ #
461
+ # Example:
462
+ #
463
+ # URI.encode_www_form(['foo', :bar, 0])
464
+ # # => "foo&bar&0"
465
+ #
466
+ # The elements of an Array-like +enum+ may be mixture:
467
+ #
468
+ # URI.encode_www_form([['foo', 0], ['bar', 1, 2], ['baz'], :bat])
469
+ # # => "foo=0&bar=1&baz&bat"
470
+ #
471
+ # When +enum+ is Hash-like,
472
+ # each +key+/+value+ pair is converted to one or more fields:
473
+ #
474
+ # - If +value+ is
475
+ # {Array-convertible}[https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html#label-Array-Convertible+Objects],
476
+ # each element +ele+ in +value+ is paired with +key+ to form a field:
477
+ #
478
+ # name = URI.encode_www_form_component(key, enc)
479
+ # value = URI.encode_www_form_component(ele, enc)
480
+ # "#{name}=#{value}"
481
+ #
482
+ # Example:
483
+ #
484
+ # URI.encode_www_form({foo: [:bar, 1], baz: [:bat, :bam, 2]})
485
+ # # => "foo=bar&foo=1&baz=bat&baz=bam&baz=2"
486
+ #
487
+ # - Otherwise, +key+ and +value+ are paired to form a field:
488
+ #
489
+ # name = URI.encode_www_form_component(key, enc)
490
+ # value = URI.encode_www_form_component(value, enc)
491
+ # "#{name}=#{value}"
492
+ #
493
+ # Example:
494
+ #
495
+ # URI.encode_www_form({foo: 0, bar: 1, baz: 2})
496
+ # # => "foo=0&bar=1&baz=2"
497
+ #
498
+ # The elements of a Hash-like +enum+ may be mixture:
499
+ #
500
+ # URI.encode_www_form({foo: [0, 1], bar: 2})
501
+ # # => "foo=0&foo=1&bar=2"
353
502
  #
354
- # See URI.encode_www_form_component, URI.decode_www_form.
355
503
  def self.encode_www_form(enum, enc=nil)
356
504
  enum.map do |k,v|
357
505
  if v.nil?
@@ -372,22 +520,39 @@ module URI
372
520
  end.join('&')
373
521
  end
374
522
 
375
- # Decodes URL-encoded form data from given +str+.
523
+ # Returns name/value pairs derived from the given string +str+,
524
+ # which must be an ASCII string.
525
+ #
526
+ # The method may be used to decode the body of Net::HTTPResponse object +res+
527
+ # for which <tt>res['Content-Type']</tt> is <tt>'application/x-www-form-urlencoded'</tt>.
376
528
  #
377
- # This decodes application/x-www-form-urlencoded data
378
- # and returns an array of key-value arrays.
529
+ # The returned data is an array of 2-element subarrays;
530
+ # each subarray is a name/value pair (both are strings).
531
+ # Each returned string has encoding +enc+,
532
+ # and has had invalid characters removed via
533
+ # {String#scrub}[https://docs.ruby-lang.org/en/master/String.html#method-i-scrub].
379
534
  #
380
- # This refers http://url.spec.whatwg.org/#concept-urlencoded-parser,
381
- # so this supports only &-separator, and doesn't support ;-separator.
535
+ # A simple example:
382
536
  #
383
- # ary = URI.decode_www_form("a=1&a=2&b=3")
384
- # ary #=> [['a', '1'], ['a', '2'], ['b', '3']]
385
- # ary.assoc('a').last #=> '1'
386
- # ary.assoc('b').last #=> '3'
387
- # ary.rassoc('a').last #=> '2'
388
- # Hash[ary] #=> {"a"=>"2", "b"=>"3"}
537
+ # URI.decode_www_form('foo=0&bar=1&baz')
538
+ # # => [["foo", "0"], ["bar", "1"], ["baz", ""]]
539
+ #
540
+ # The returned strings have certain conversions,
541
+ # similar to those performed in URI.decode_www_form_component:
542
+ #
543
+ # URI.decode_www_form('f%23o=%2F&b-r=%24&b+z=%40')
544
+ # # => [["f#o", "/"], ["b-r", "$"], ["b z", "@"]]
545
+ #
546
+ # The given string may contain consecutive separators:
547
+ #
548
+ # URI.decode_www_form('foo=0&&bar=1&&baz=2')
549
+ # # => [["foo", "0"], ["", ""], ["bar", "1"], ["", ""], ["baz", "2"]]
550
+ #
551
+ # A different separator may be specified:
552
+ #
553
+ # URI.decode_www_form('foo=0--bar=1--baz', separator: '--')
554
+ # # => [["foo", "0"], ["bar", "1"], ["baz", ""]]
389
555
  #
390
- # See URI.decode_www_form_component, URI.encode_www_form.
391
556
  def self.decode_www_form(str, enc=Encoding::UTF_8, separator: '&', use__charset_: false, isindex: false)
392
557
  raise ArgumentError, "the input of #{self.name}.#{__method__} must be ASCII only string" unless str.ascii_only?
393
558
  ary = []
@@ -653,6 +818,7 @@ module URI
653
818
  "utf-16"=>"utf-16le",
654
819
  "utf-16le"=>"utf-16le",
655
820
  } # :nodoc:
821
+ Ractor.make_shareable(WEB_ENCODINGS_) if defined?(Ractor)
656
822
 
657
823
  # :nodoc:
658
824
  # return encoding or nil
@@ -665,7 +831,15 @@ end # module URI
665
831
  module Kernel
666
832
 
667
833
  #
668
- # Returns +uri+ converted to an URI object.
834
+ # Returns a \URI object derived from the given +uri+,
835
+ # which may be a \URI string or an existing \URI object:
836
+ #
837
+ # # Returns a new URI.
838
+ # uri = URI('http://github.com/ruby/ruby')
839
+ # # => #<URI::HTTP http://github.com/ruby/ruby>
840
+ # # Returns the given URI.
841
+ # URI(uri)
842
+ # # => #<URI::HTTP http://github.com/ruby/ruby>
669
843
  #
670
844
  def URI(uri)
671
845
  if uri.is_a?(URI::Generic)
data/lib/uri/file.rb CHANGED
@@ -33,6 +33,9 @@ module URI
33
33
  # If an Array is used, the components must be passed in the
34
34
  # order <code>[host, path]</code>.
35
35
  #
36
+ # A path from e.g. the File class should be escaped before
37
+ # being passed.
38
+ #
36
39
  # Examples:
37
40
  #
38
41
  # require 'uri'
@@ -44,6 +47,9 @@ module URI
44
47
  # :path => '/ruby/src'})
45
48
  # uri2.to_s # => "file://host.example.com/ruby/src"
46
49
  #
50
+ # uri3 = URI::File.build({:path => URI::escape('/path/my file.txt')})
51
+ # uri3.to_s # => "file:///path/my%20file.txt"
52
+ #
47
53
  def self.build(args)
48
54
  tmp = Util::make_components_hash(self, args)
49
55
  super(tmp)
@@ -90,5 +96,5 @@ module URI
90
96
  end
91
97
  end
92
98
 
93
- @@schemes['FILE'] = File
99
+ register_scheme 'FILE', File
94
100
  end
data/lib/uri/ftp.rb CHANGED
@@ -262,5 +262,6 @@ module URI
262
262
  return str
263
263
  end
264
264
  end
265
- @@schemes['FTP'] = FTP
265
+
266
+ register_scheme 'FTP', FTP
266
267
  end