uri 0.12.3 → 0.13.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e5eb539bccaa2d0f90ead026894ac1120ef94cffc908cc98450e4fe3e261119
4
- data.tar.gz: f5708e8f3e332c3e05cd39b1147ba732645986599e59952b2ede2d9e2051d3a0
3
+ metadata.gz: 3aa899933652aa155b60babf100c7b892a976a70121c16a408313687b85be675
4
+ data.tar.gz: 2c1f5cdfa19249b82f3c84f7c090eb389e9079147d22db436265b43c2cf283f1
5
5
  SHA512:
6
- metadata.gz: 2f3d2a24d61837a54530718294785ab82a12c28681de29fc5afe8cd0aa70e9ed046c8d023bfec55e0a484ef20aa74601479003d9781ed4ac9f78fb9338ecd937
7
- data.tar.gz: 765df4b7b63d7b102cf22e0ca54a8eef49b3ab3f4bbb5655f4beb003608a2caa3110f4d71f5b5b502b6a17203260dda72c0a16d6bf23a79001de010a3ee7504a
6
+ metadata.gz: 709ffc67b67a2de85fc86c8317537e6aced97c17abf8111f9c682d0232a46610ef0f769a9fdaf0802689e08a81b83a6cb2d9a9981b0c335513203171966e7251
7
+ data.tar.gz: f92761f35747c21ed9e5e11d7390c786be62548bc9f83a5de9b7c25fb0b9c3378ee8c20d1938dcc8e88a7d47de2996ed8c7fed6b970e96a9c967e5ce7f27a41a
@@ -0,0 +1,46 @@
1
+ name: Deploy RDoc site to Pages
2
+
3
+ on:
4
+ push:
5
+ branches: [ 'master' ]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+ pages: write
11
+ id-token: write
12
+
13
+ concurrency:
14
+ group: "pages"
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - name: Checkout
22
+ uses: actions/checkout@v4
23
+ - name: Setup Ruby
24
+ uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # v1.152.0
25
+ with:
26
+ ruby-version: '3.2'
27
+ bundler-cache: true
28
+ - name: Setup Pages
29
+ id: pages
30
+ uses: actions/configure-pages@v3
31
+ - name: Build with RDoc
32
+ # Outputs to the './_site' directory by default
33
+ run: bundle exec rake rdoc
34
+ - name: Upload artifact
35
+ uses: actions/upload-pages-artifact@v2
36
+
37
+ deploy:
38
+ environment:
39
+ name: github-pages
40
+ url: ${{ steps.deployment.outputs.page_url }}
41
+ runs-on: ubuntu-latest
42
+ needs: build
43
+ steps:
44
+ - name: Deploy to GitHub Pages
45
+ id: deployment
46
+ uses: actions/deploy-pages@v2
@@ -3,20 +3,24 @@ name: CI
3
3
  on: [push, pull_request]
4
4
 
5
5
  jobs:
6
+ ruby-versions:
7
+ uses: ruby/actions/.github/workflows/ruby_versions.yml@master
8
+ with:
9
+ min_version: 2.5
10
+
6
11
  build:
12
+ needs: ruby-versions
7
13
  name: build (${{ matrix.ruby }} / ${{ matrix.os }})
8
14
  strategy:
9
15
  matrix:
10
- ruby: [ 3.1, '3.0', 2.7, 2.6, 2.5, 2.4, head, truffleruby ]
16
+ ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
11
17
  os: [ ubuntu-latest, macos-latest ]
12
18
  exclude:
13
- - ruby: 2.4
14
- os: macos-latest
15
19
  - ruby: 2.5
16
20
  os: macos-latest
17
21
  runs-on: ${{ matrix.os }}
18
22
  steps:
19
- - uses: actions/checkout@v3
23
+ - uses: actions/checkout@v4
20
24
  - name: Set up Ruby
21
25
  uses: ruby/setup-ruby@v1
22
26
  with:
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  Gemfile.lock
10
+ /_site
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # URI
2
2
 
3
3
  [![CI](https://github.com/ruby/uri/actions/workflows/test.yml/badge.svg)](https://github.com/ruby/uri/actions/workflows/test.yml)
4
+ [![Yard Docs](https://img.shields.io/badge/docs-exist-blue.svg)](https://ruby.github.io/uri/)
4
5
 
5
6
  URI is a module providing classes to handle Uniform Resource Identifiers [RFC2396](http://tools.ietf.org/html/rfc2396).
6
7
 
data/Rakefile CHANGED
@@ -7,11 +7,12 @@ Rake::TestTask.new(:test) do |t|
7
7
  t.test_files = FileList["test/**/test_*.rb"]
8
8
  end
9
9
 
10
- task :sync_tool do
11
- require 'fileutils'
12
- FileUtils.cp "../ruby/tool/lib/test/unit/core_assertions.rb", "./test/lib"
13
- FileUtils.cp "../ruby/tool/lib/envutil.rb", "./test/lib"
14
- FileUtils.cp "../ruby/tool/lib/find_executable.rb", "./test/lib"
10
+ require "rdoc/task"
11
+ RDoc::Task.new do |doc|
12
+ doc.main = "README.md"
13
+ doc.title = "URI - handle Uniform Resource Identifiers"
14
+ doc.rdoc_files = FileList.new %w[lib README.md LICENSE.txt]
15
+ doc.rdoc_dir = "_site" # for github pages
15
16
  end
16
17
 
17
18
  task :default => :test
data/lib/uri/common.rb CHANGED
@@ -70,16 +70,32 @@ module URI
70
70
  end
71
71
  private_constant :Schemes
72
72
 
73
+ # Registers the given +klass+ as the class to be instantiated
74
+ # when parsing a \URI with the given +scheme+:
73
75
  #
74
- # Register the given +klass+ to be instantiated when parsing URLs with the given +scheme+.
75
- # Note that currently only schemes which after .upcase are valid constant names
76
- # can be registered (no -/+/. allowed).
76
+ # URI.register_scheme('MS_SEARCH', URI::Generic) # => URI::Generic
77
+ # URI.scheme_list['MS_SEARCH'] # => URI::Generic
77
78
  #
79
+ # Note that after calling String#upcase on +scheme+, it must be a valid
80
+ # constant name.
78
81
  def self.register_scheme(scheme, klass)
79
82
  Schemes.const_set(scheme.to_s.upcase, klass)
80
83
  end
81
84
 
82
- # 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.
83
99
  def self.scheme_list
84
100
  Schemes.constants.map { |name|
85
101
  [name.to_s.upcase, Schemes.const_get(name)]
@@ -90,9 +106,21 @@ module URI
90
106
  private_constant :INITIAL_SCHEMES
91
107
  Ractor.make_shareable(INITIAL_SCHEMES) if defined?(Ractor)
92
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.
116
+ #
117
+ # Examples:
93
118
  #
94
- # Construct a URI instance, using the scheme to detect the appropriate class
95
- # from +URI.scheme_list+.
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>
96
124
  #
97
125
  def self.for(scheme, *arguments, default: Generic)
98
126
  const_name = scheme.to_s.upcase
@@ -123,95 +151,49 @@ module URI
123
151
  #
124
152
  class BadURIError < Error; end
125
153
 
126
- #
127
- # == Synopsis
128
- #
129
- # URI::split(uri)
130
- #
131
- # == Args
132
- #
133
- # +uri+::
134
- # String with URI.
135
- #
136
- # == Description
137
- #
138
- # Splits the string on following parts and returns array with result:
139
- #
140
- # * Scheme
141
- # * Userinfo
142
- # * Host
143
- # * Port
144
- # * Registry
145
- # * Path
146
- # * Opaque
147
- # * Query
148
- # * Fragment
149
- #
150
- # == Usage
151
- #
152
- # require 'uri'
153
- #
154
- # URI.split("http://www.ruby-lang.org/")
155
- # # => ["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"]]
156
171
  #
157
172
  def self.split(uri)
158
173
  RFC3986_PARSER.split(uri)
159
174
  end
160
175
 
176
+ # Returns a new \URI object constructed from the given string +uri+:
161
177
  #
162
- # == Synopsis
163
- #
164
- # URI::parse(uri_str)
165
- #
166
- # == Args
167
- #
168
- # +uri_str+::
169
- # String with URI.
170
- #
171
- # == Description
172
- #
173
- # Creates one of the URI's subclasses instance from the string.
174
- #
175
- # == Raises
176
- #
177
- # URI::InvalidURIError::
178
- # Raised if URI given is not a correct one.
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>
179
182
  #
180
- # == Usage
181
- #
182
- # require 'uri'
183
- #
184
- # uri = URI.parse("http://www.ruby-lang.org/")
185
- # # => #<URI::HTTP http://www.ruby-lang.org/>
186
- # uri.scheme
187
- # # => "http"
188
- # uri.host
189
- # # => "www.ruby-lang.org"
190
- #
191
- # It's recommended to first ::escape the provided +uri_str+ if there are any
192
- # invalid URI characters.
183
+ # It's recommended to first ::escape string +uri+
184
+ # if it may contain invalid URI characters.
193
185
  #
194
186
  def self.parse(uri)
195
187
  RFC3986_PARSER.parse(uri)
196
188
  end
197
189
 
190
+ # Merges the given URI strings +str+
191
+ # per {RFC 2396}[https://www.rfc-editor.org/rfc/rfc2396.html].
198
192
  #
199
- # == Synopsis
200
- #
201
- # URI::join(str[, str, ...])
193
+ # Each string in +str+ is converted to an
194
+ # {RFC3986 URI}[https://www.rfc-editor.org/rfc/rfc3986.html] before being merged.
202
195
  #
203
- # == Args
204
- #
205
- # +str+::
206
- # String(s) to work with, will be converted to RFC3986 URIs before merging.
207
- #
208
- # == Description
209
- #
210
- # Joins URIs.
211
- #
212
- # == Usage
213
- #
214
- # require 'uri'
196
+ # Examples:
215
197
  #
216
198
  # URI.join("http://example.com/","main.rbx")
217
199
  # # => #<URI::HTTP http://example.com/main.rbx>
@@ -256,7 +238,7 @@ module URI
256
238
  # URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.")
257
239
  # # => ["http://foo.example.com/bla", "mailto:test@example.com"]
258
240
  #
259
- def self.extract(str, schemes = nil, &block)
241
+ def self.extract(str, schemes = nil, &block) # :nodoc:
260
242
  warn "URI.extract is obsolete", uplevel: 1 if $VERBOSE
261
243
  DEFAULT_PARSER.extract(str, schemes, &block)
262
244
  end
@@ -293,7 +275,7 @@ module URI
293
275
  # p $&
294
276
  # end
295
277
  #
296
- def self.regexp(schemes = nil)
278
+ def self.regexp(schemes = nil)# :nodoc:
297
279
  warn "URI.regexp is obsolete", uplevel: 1 if $VERBOSE
298
280
  DEFAULT_PARSER.make_regexp(schemes)
299
281
  end
@@ -316,40 +298,86 @@ module URI
316
298
  TBLDECWWWCOMP_['+'] = ' '
317
299
  TBLDECWWWCOMP_.freeze
318
300
 
319
- # 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:
320
312
  #
321
- # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
322
- # (ASCII space) to + and converts others to %XX.
313
+ # URI.encode_www_form_component('*.-_azAZ09')
314
+ # # => "*.-_azAZ09"
323
315
  #
324
- # If +enc+ is given, convert +str+ to the encoding before percent encoding.
316
+ # - Converts:
325
317
  #
326
- # This is an implementation of
327
- # https://www.w3.org/TR/2013/CR-html5-20130806/forms.html#url-encoded-form-data.
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>.
328
321
  #
329
- # See URI.decode_www_form_component, URI.encode_www_form.
322
+ # Example:
323
+ #
324
+ # URI.encode_www_form_component('Here are some punctuation characters: ,;?:')
325
+ # # => "Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A"
326
+ #
327
+ # Encoding:
328
+ #
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>).
330
337
  def self.encode_www_form_component(str, enc=nil)
331
338
  _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc)
332
339
  end
333
340
 
334
- # Decodes given +str+ of URL-encoded form data.
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.
335
363
  #
336
- # This decodes + to SP.
364
+ # Example:
337
365
  #
338
- # See URI.encode_www_form_component, URI.decode_www_form.
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>).
339
370
  def self.decode_www_form_component(str, enc=Encoding::UTF_8)
340
371
  _decode_uri_component(/\+|%\h\h/, str, enc)
341
372
  end
342
373
 
343
- # Encodes +str+ using URL encoding
344
- #
345
- # This encodes SP to %20 instead of +.
374
+ # Like URI.encode_www_form_component, except that <tt>' '</tt> (space)
375
+ # is encoded as <tt>'%20'</tt> (instead of <tt>'+'</tt>).
346
376
  def self.encode_uri_component(str, enc=nil)
347
377
  _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCURICOMP_, str, enc)
348
378
  end
349
379
 
350
- # Decodes given +str+ of URL-encoded data.
351
- #
352
- # This does not decode + to SP.
380
+ # Like URI.decode_www_form_component, except that <tt>'+'</tt> is preserved.
353
381
  def self.decode_uri_component(str, enc=Encoding::UTF_8)
354
382
  _decode_uri_component(/%\h\h/, str, enc)
355
383
  end
@@ -374,33 +402,104 @@ module URI
374
402
  end
375
403
  private_class_method :_decode_uri_component
376
404
 
377
- # 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:
426
+ #
427
+ # URI.encode_www_form('f#o': '/', 'b-r': '$', 'b z': '@')
428
+ # # => "f%23o=%2F&b-r=%24&b+z=%40"
429
+ #
430
+ # When +enum+ is Array-like, each element +ele+ is converted to a field:
431
+ #
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):
435
+ #
436
+ # name = URI.encode_www_form_component(ele[0], enc)
437
+ # value = URI.encode_www_form_component(ele[1], enc)
438
+ # "#{name}=#{value}"
378
439
  #
379
- # This generates application/x-www-form-urlencoded data defined in HTML5
380
- # from given an Enumerable object.
440
+ # Examples:
381
441
  #
382
- # This internally uses URI.encode_www_form_component(str).
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"
383
446
  #
384
- # This method doesn't convert the encoding of given items, so convert them
385
- # before calling this method if you want to send data as other than original
386
- # encoding or mixed encoding data. (Strings which are encoded in an HTML5
387
- # ASCII incompatible encoding are converted to UTF-8.)
447
+ # - If +ele+ is an array of one element,
448
+ # the field is formed from <tt>ele[0]</tt>:
388
449
  #
389
- # This method doesn't handle files. When you send a file, use
390
- # multipart/form-data.
450
+ # URI.encode_www_form_component(ele[0])
391
451
  #
392
- # This refers https://url.spec.whatwg.org/#concept-urlencoded-serializer
452
+ # Example:
393
453
  #
394
- # URI.encode_www_form([["q", "ruby"], ["lang", "en"]])
395
- # #=> "q=ruby&lang=en"
396
- # URI.encode_www_form("q" => "ruby", "lang" => "en")
397
- # #=> "q=ruby&lang=en"
398
- # URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en")
399
- # #=> "q=ruby&q=perl&lang=en"
400
- # URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]])
401
- # #=> "q=ruby&q=perl&lang=en"
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"
402
502
  #
403
- # See URI.encode_www_form_component, URI.decode_www_form.
404
503
  def self.encode_www_form(enum, enc=nil)
405
504
  enum.map do |k,v|
406
505
  if v.nil?
@@ -421,22 +520,39 @@ module URI
421
520
  end.join('&')
422
521
  end
423
522
 
424
- # 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>.
528
+ #
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].
425
534
  #
426
- # This decodes application/x-www-form-urlencoded data
427
- # and returns an array of key-value arrays.
535
+ # A simple example:
428
536
  #
429
- # This refers http://url.spec.whatwg.org/#concept-urlencoded-parser,
430
- # so this supports only &-separator, and doesn't support ;-separator.
537
+ # URI.decode_www_form('foo=0&bar=1&baz')
538
+ # # => [["foo", "0"], ["bar", "1"], ["baz", ""]]
431
539
  #
432
- # ary = URI.decode_www_form("a=1&a=2&b=3")
433
- # ary #=> [['a', '1'], ['a', '2'], ['b', '3']]
434
- # ary.assoc('a').last #=> '1'
435
- # ary.assoc('b').last #=> '3'
436
- # ary.rassoc('a').last #=> '2'
437
- # Hash[ary] #=> {"a"=>"2", "b"=>"3"}
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", ""]]
438
555
  #
439
- # See URI.decode_www_form_component, URI.encode_www_form.
440
556
  def self.decode_www_form(str, enc=Encoding::UTF_8, separator: '&', use__charset_: false, isindex: false)
441
557
  raise ArgumentError, "the input of #{self.name}.#{__method__} must be ASCII only string" unless str.ascii_only?
442
558
  ary = []
@@ -715,7 +831,15 @@ end # module URI
715
831
  module Kernel
716
832
 
717
833
  #
718
- # 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>
719
843
  #
720
844
  def URI(uri)
721
845
  if uri.is_a?(URI::Generic)
data/lib/uri/generic.rb CHANGED
@@ -1376,6 +1376,7 @@ module URI
1376
1376
  end
1377
1377
  str
1378
1378
  end
1379
+ alias to_str to_s
1379
1380
 
1380
1381
  #
1381
1382
  # Compares two URIs.
@@ -1,9 +1,73 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
  module URI
3
3
  class RFC3986_Parser # :nodoc:
4
4
  # URI defined in RFC3986
5
- RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+))(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
6
- RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
5
+ HOST = %r[
6
+ (?<IP-literal>\[(?:
7
+ (?<IPv6address>
8
+ (?:\h{1,4}:){6}
9
+ (?<ls32>\h{1,4}:\h{1,4}
10
+ | (?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)
11
+ \.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>)
12
+ )
13
+ | ::(?:\h{1,4}:){5}\g<ls32>
14
+ | \h{1,4}?::(?:\h{1,4}:){4}\g<ls32>
15
+ | (?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>
16
+ | (?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>
17
+ | (?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>
18
+ | (?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>
19
+ | (?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}
20
+ | (?:(?:\h{1,4}:){,6}\h{1,4})?::
21
+ )
22
+ | (?<IPvFuture>v\h++\.[!$&-.0-9:;=A-Z_a-z~]++)
23
+ )\])
24
+ | \g<IPv4address>
25
+ | (?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+)
26
+ ]x
27
+
28
+ USERINFO = /(?:%\h\h|[!$&-.0-9:;=A-Z_a-z~])*+/
29
+
30
+ SCHEME = %r[[A-Za-z][+\-.0-9A-Za-z]*+].source
31
+ SEG = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/])].source
32
+ SEG_NC = %r[(?:%\h\h|[!$&-.0-9;=@A-Z_a-z~])].source
33
+ FRAGMENT = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+].source
34
+
35
+ RFC3986_URI = %r[\A
36
+ (?<seg>#{SEG}){0}
37
+ (?<URI>
38
+ (?<scheme>#{SCHEME}):
39
+ (?<hier-part>//
40
+ (?<authority>
41
+ (?:(?<userinfo>#{USERINFO.source})@)?
42
+ (?<host>#{HOST.source.delete(" \n")})
43
+ (?::(?<port>\d*+))?
44
+ )
45
+ (?<path-abempty>(?:/\g<seg>*+)?)
46
+ | (?<path-absolute>/((?!/)\g<seg>++)?)
47
+ | (?<path-rootless>(?!/)\g<seg>++)
48
+ | (?<path-empty>)
49
+ )
50
+ (?:\?(?<query>[^\#]*+))?
51
+ (?:\#(?<fragment>#{FRAGMENT}))?
52
+ )\z]x
53
+
54
+ RFC3986_relative_ref = %r[\A
55
+ (?<seg>#{SEG}){0}
56
+ (?<relative-ref>
57
+ (?<relative-part>//
58
+ (?<authority>
59
+ (?:(?<userinfo>#{USERINFO.source})@)?
60
+ (?<host>#{HOST.source.delete(" \n")}(?<!/))?
61
+ (?::(?<port>\d*+))?
62
+ )
63
+ (?<path-abempty>(?:/\g<seg>*+)?)
64
+ | (?<path-absolute>/\g<seg>*+)
65
+ | (?<path-noscheme>#{SEG_NC}++(?:/\g<seg>*+)?)
66
+ | (?<path-empty>)
67
+ )
68
+ (?:\?(?<query>[^#]*+))?
69
+ (?:\#(?<fragment>#{FRAGMENT}))?
70
+ )\z]x
7
71
  attr_reader :regexp
8
72
 
9
73
  def initialize
@@ -19,9 +83,9 @@ module URI
19
83
  uri.ascii_only? or
20
84
  raise InvalidURIError, "URI must be ascii only #{uri.dump}"
21
85
  if m = RFC3986_URI.match(uri)
22
- query = m["query".freeze]
23
- scheme = m["scheme".freeze]
24
- opaque = m["path-rootless".freeze]
86
+ query = m["query"]
87
+ scheme = m["scheme"]
88
+ opaque = m["path-rootless"]
25
89
  if opaque
26
90
  opaque << "?#{query}" if query
27
91
  [ scheme,
@@ -32,35 +96,35 @@ module URI
32
96
  nil, # path
33
97
  opaque,
34
98
  nil, # query
35
- m["fragment".freeze]
99
+ m["fragment"]
36
100
  ]
37
101
  else # normal
38
102
  [ scheme,
39
- m["userinfo".freeze],
40
- m["host".freeze],
41
- m["port".freeze],
103
+ m["userinfo"],
104
+ m["host"],
105
+ m["port"],
42
106
  nil, # registry
43
- (m["path-abempty".freeze] ||
44
- m["path-absolute".freeze] ||
45
- m["path-empty".freeze]),
107
+ (m["path-abempty"] ||
108
+ m["path-absolute"] ||
109
+ m["path-empty"]),
46
110
  nil, # opaque
47
111
  query,
48
- m["fragment".freeze]
112
+ m["fragment"]
49
113
  ]
50
114
  end
51
115
  elsif m = RFC3986_relative_ref.match(uri)
52
116
  [ nil, # scheme
53
- m["userinfo".freeze],
54
- m["host".freeze],
55
- m["port".freeze],
117
+ m["userinfo"],
118
+ m["host"],
119
+ m["port"],
56
120
  nil, # registry,
57
- (m["path-abempty".freeze] ||
58
- m["path-absolute".freeze] ||
59
- m["path-noscheme".freeze] ||
60
- m["path-empty".freeze]),
121
+ (m["path-abempty"] ||
122
+ m["path-absolute"] ||
123
+ m["path-noscheme"] ||
124
+ m["path-empty"]),
61
125
  nil, # opaque
62
- m["query".freeze],
63
- m["fragment".freeze]
126
+ m["query"],
127
+ m["fragment"]
64
128
  ]
65
129
  else
66
130
  raise InvalidURIError, "bad URI(is not URI?): #{uri.inspect}"
@@ -92,14 +156,14 @@ module URI
92
156
 
93
157
  def default_regexp # :nodoc:
94
158
  {
95
- SCHEME: /\A[A-Za-z][A-Za-z0-9+\-.]*\z/,
96
- USERINFO: /\A(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*\z/,
97
- HOST: /\A(?:(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{,4}::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))\z/,
98
- ABS_PATH: /\A\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/,
99
- REL_PATH: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/,
100
- QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
101
- FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
102
- OPAQUE: /\A(?:[^\/].*)?\z/,
159
+ SCHEME: %r[\A#{SCHEME}\z]o,
160
+ USERINFO: %r[\A#{USERINFO}\z]o,
161
+ HOST: %r[\A#{HOST}\z]o,
162
+ ABS_PATH: %r[\A/#{SEG}*+\z]o,
163
+ REL_PATH: %r[\A(?!/)#{SEG}++\z]o,
164
+ QUERY: %r[\A(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+\z],
165
+ FRAGMENT: %r[\A#{FRAGMENT}\z]o,
166
+ OPAQUE: %r[\A(?:[^/].*)?\z],
103
167
  PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
104
168
  }
105
169
  end
data/lib/uri/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module URI
2
2
  # :stopdoc:
3
- VERSION_CODE = '001203'.freeze
3
+ VERSION_CODE = '001301'.freeze
4
4
  VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
5
5
  # :startdoc:
6
6
  end
@@ -0,0 +1,17 @@
1
+ task :sync_tool, [:from] do |t, from: nil|
2
+ from ||= (File.identical?(__dir__, "rakelib") ? "../ruby/tool" : File.dirname(__dir__))
3
+
4
+ require 'fileutils'
5
+
6
+ {
7
+ "rakelib/sync_tool.rake" => "rakelib",
8
+ "lib/core_assertions.rb" => "test/lib",
9
+ "lib/envutil.rb" => "test/lib",
10
+ "lib/find_executable.rb" => "test/lib",
11
+ "lib/helper.rb" => "test/lib",
12
+ }.each do |src, dest|
13
+ FileUtils.mkpath(dest)
14
+ FileUtils.cp "#{from}/#{src}", dest
15
+ rescue Errno::ENOENT
16
+ end
17
+ end
data/uri.gemspec CHANGED
@@ -12,18 +12,26 @@ Gem::Specification.new do |spec|
12
12
 
13
13
  spec.summary = %q{URI is a module providing classes to handle Uniform Resource Identifiers}
14
14
  spec.description = spec.summary
15
- spec.homepage = "https://github.com/ruby/uri"
15
+
16
+ github_link = "https://github.com/ruby/uri"
17
+
18
+ spec.homepage = github_link
16
19
  spec.licenses = ["Ruby", "BSD-2-Clause"]
17
20
 
18
- spec.required_ruby_version = '>= 2.4'
21
+ spec.required_ruby_version = '>= 2.5'
19
22
 
20
- spec.metadata["homepage_uri"] = spec.homepage
21
- spec.metadata["source_code_uri"] = spec.homepage
23
+ spec.metadata = {
24
+ "bug_tracker_uri" => "#{github_link}/issues",
25
+ "changelog_uri" => "#{github_link}/releases",
26
+ "documentation_uri" => "https://ruby.github.io/uri/",
27
+ "homepage_uri" => spec.homepage,
28
+ "source_code_uri" => github_link
29
+ }
22
30
 
23
31
  # Specify which files should be added to the gem when it is released.
24
32
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
33
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
26
- `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
34
+ `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
35
  end
28
36
  spec.bindir = "exe"
29
37
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uri
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.3
4
+ version: 0.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Akira Yamada
@@ -17,6 +17,7 @@ extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
19
  - ".github/dependabot.yml"
20
+ - ".github/workflows/gh-pages.yml"
20
21
  - ".github/workflows/test.yml"
21
22
  - ".gitignore"
22
23
  - Gemfile
@@ -40,12 +41,16 @@ files:
40
41
  - lib/uri/version.rb
41
42
  - lib/uri/ws.rb
42
43
  - lib/uri/wss.rb
44
+ - rakelib/sync_tool.rake
43
45
  - uri.gemspec
44
46
  homepage: https://github.com/ruby/uri
45
47
  licenses:
46
48
  - Ruby
47
49
  - BSD-2-Clause
48
50
  metadata:
51
+ bug_tracker_uri: https://github.com/ruby/uri/issues
52
+ changelog_uri: https://github.com/ruby/uri/releases
53
+ documentation_uri: https://ruby.github.io/uri/
49
54
  homepage_uri: https://github.com/ruby/uri
50
55
  source_code_uri: https://github.com/ruby/uri
51
56
  rdoc_options: []
@@ -55,7 +60,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
55
60
  requirements:
56
61
  - - ">="
57
62
  - !ruby/object:Gem::Version
58
- version: '2.4'
63
+ version: '2.5'
59
64
  required_rubygems_version: !ruby/object:Gem::Requirement
60
65
  requirements:
61
66
  - - ">="