fetch-api 0.3.4 → 0.4.0
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 +4 -4
- data/README.md +13 -0
- data/lib/fetch/client.rb +11 -8
- data/lib/fetch/config.rb +3 -0
- data/lib/fetch/connection_pool.rb +86 -0
- data/lib/fetch/form_data.rb +7 -5
- data/lib/fetch/headers.rb +7 -5
- data/lib/fetch/response.rb +14 -2
- data/lib/fetch/url_search_params.rb +6 -5
- data/lib/fetch/version.rb +1 -1
- data/lib/fetch.rb +16 -0
- data/sig/fetch/api.rbs +11 -0
- data/sig/fetch/client.rbs +19 -0
- data/sig/fetch/config.rbs +6 -0
- data/sig/fetch/connection_pool.rbs +21 -0
- data/sig/fetch/errors.rbs +7 -0
- data/sig/fetch/form_data.rbs +24 -0
- data/sig/fetch/headers.rbs +20 -0
- data/sig/fetch/response.rbs +14 -0
- data/sig/fetch/url_search_params.rbs +24 -0
- data/sig/fetch.rbs +4 -1
- metadata +43 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dbef9662b23db6e0efca2b67264d6450553991b551ec94f1b7099c0b4c2eda55
|
4
|
+
data.tar.gz: 0456d3ff62bcceeaa780ff9cb42c3ff231d667b977ef968e22bb5b2a5578e358
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 516ebe663dc6f346a8acc56f7a1c8e1a3bc11f3e133f6ca0c10a8544516a6dc8a8e34efd660585de627c595ba66ed17b6df3f3415ec90a439be84e21e50d7343
|
7
|
+
data.tar.gz: 3b0630efe83154f9a71df06aec28b551720a6041e24ce1797626a105eedacc54a96495362e061b391018847cbfd2021e4fd750854b75a546841738f2a58d441c
|
data/README.md
CHANGED
@@ -102,6 +102,19 @@ res = fetch('http://example.com', **{
|
|
102
102
|
})
|
103
103
|
```
|
104
104
|
|
105
|
+
## Connection pooling
|
106
|
+
|
107
|
+
Fetch API automatically pools and reuses connections to the same origin. Connections are closed after a certain amount of time has passed since the last use, and a new connection is used the next time. Different connections are returned for different threads (in other words, Fetch API is thread-safe).
|
108
|
+
|
109
|
+
These values (in seconds) can be configured as follows:
|
110
|
+
|
111
|
+
``` ruby
|
112
|
+
Fetch.configure do |config|
|
113
|
+
config.connection_max_idle_time = 10 # default
|
114
|
+
config.keep_alive_timeout = 2 # default
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
105
118
|
## Development
|
106
119
|
|
107
120
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/fetch/client.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require_relative 'connection_pool'
|
1
2
|
require_relative 'errors'
|
2
3
|
require_relative 'form_data'
|
3
4
|
require_relative 'headers'
|
@@ -6,6 +7,7 @@ require_relative 'url_search_params'
|
|
6
7
|
|
7
8
|
require 'mini_mime'
|
8
9
|
require 'net/http'
|
10
|
+
require 'net/https'
|
9
11
|
require 'singleton'
|
10
12
|
require 'uri'
|
11
13
|
|
@@ -13,12 +15,16 @@ module Fetch
|
|
13
15
|
class Client
|
14
16
|
include Singleton
|
15
17
|
|
18
|
+
def initialize
|
19
|
+
@pool = ConnectionPool.new
|
20
|
+
end
|
21
|
+
|
16
22
|
def fetch(resource, method: :get, headers: [], body: nil, redirect: :follow, _redirected: false)
|
17
23
|
uri = URI.parse(resource)
|
18
24
|
req = Net::HTTP.const_get(method.capitalize).new(uri)
|
19
25
|
|
20
26
|
headers.each do |k, v|
|
21
|
-
req[k] = v
|
27
|
+
req[k.to_s] = v.to_s
|
22
28
|
end
|
23
29
|
|
24
30
|
case body
|
@@ -39,16 +45,13 @@ module Fetch
|
|
39
45
|
req.body = body
|
40
46
|
end
|
41
47
|
|
42
|
-
|
43
|
-
http.use_ssl = uri.scheme == 'https'
|
44
|
-
|
45
|
-
res = http.start { _1.request(req) }
|
48
|
+
res = @pool.with_connection(uri) { _1.request(req) }
|
46
49
|
|
47
50
|
case res
|
48
51
|
when Net::HTTPRedirection
|
49
52
|
case redirect.to_s
|
50
53
|
when 'follow'
|
51
|
-
fetch(res['Location'], method:, headers:, body:, redirect:, _redirected: true)
|
54
|
+
fetch(res['Location'], method:, headers:, body:, redirect:, _redirected: true) # steep:ignore ArgumentTypeMismatch
|
52
55
|
when 'error'
|
53
56
|
raise RedirectError, "redirected to #{res['Location']}"
|
54
57
|
when 'manual'
|
@@ -65,9 +68,9 @@ module Fetch
|
|
65
68
|
|
66
69
|
def to_response(url, res, redirected)
|
67
70
|
Response.new(
|
68
|
-
url: ,
|
71
|
+
url: url.to_str,
|
69
72
|
status: res.code.to_i,
|
70
|
-
headers: Headers.new(res),
|
73
|
+
headers: Headers.new(res.each),
|
71
74
|
body: res.body,
|
72
75
|
redirected:
|
73
76
|
)
|
data/lib/fetch/config.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative '../fetch'
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
module Fetch
|
6
|
+
class ConnectionPool
|
7
|
+
Entry = Struct.new(:connection, :in_use, :last_used, keyword_init: true)
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@connections = {}
|
11
|
+
@mutex = Mutex.new
|
12
|
+
|
13
|
+
@sweeper = Thread.new {
|
14
|
+
loop do
|
15
|
+
sleep 1
|
16
|
+
|
17
|
+
sweep
|
18
|
+
|
19
|
+
Thread.stop if @connections.empty?
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_connection(uri, &block)
|
25
|
+
conn = acquire(uri)
|
26
|
+
|
27
|
+
begin
|
28
|
+
block.call(conn)
|
29
|
+
ensure
|
30
|
+
release uri
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def acquire(uri)
|
37
|
+
@mutex.synchronize {
|
38
|
+
entry = @connections[key(uri)]
|
39
|
+
|
40
|
+
if entry
|
41
|
+
entry.in_use = true
|
42
|
+
|
43
|
+
entry.connection
|
44
|
+
else
|
45
|
+
Net::HTTP.new(uri.host, uri.port).tap {|http| # steep:ignore ArgumentTypeMismatch
|
46
|
+
http.use_ssl = uri.scheme == 'https'
|
47
|
+
http.keep_alive_timeout = Fetch.config.keep_alive_timeout
|
48
|
+
|
49
|
+
@connections[key(uri)] = Entry.new(connection: http, in_use: true)
|
50
|
+
|
51
|
+
http.start
|
52
|
+
}
|
53
|
+
end
|
54
|
+
}.tap {
|
55
|
+
@sweeper.wakeup
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def release(uri)
|
60
|
+
@mutex.synchronize do
|
61
|
+
if entry = @connections[key(uri)]
|
62
|
+
entry.in_use = false
|
63
|
+
entry.last_used = Time.now
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def sweep
|
69
|
+
@mutex.synchronize do
|
70
|
+
@connections.each do |key, entry|
|
71
|
+
next if entry.in_use
|
72
|
+
|
73
|
+
if entry.last_used + Fetch.config.connection_max_idle_time < Time.now
|
74
|
+
entry.connection.finish
|
75
|
+
|
76
|
+
@connections.delete key
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def key(uri)
|
83
|
+
"#{Thread.current.object_id}/#{uri.origin}".freeze
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/fetch/form_data.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Fetch
|
2
4
|
class FormData
|
3
5
|
include Enumerable
|
4
6
|
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :entries, :each
|
10
|
+
|
5
11
|
def self.build(enumerable)
|
6
12
|
data = FormData.new
|
7
13
|
|
@@ -19,7 +25,7 @@ module Fetch
|
|
19
25
|
attr_reader :entries
|
20
26
|
|
21
27
|
def append(key, value)
|
22
|
-
@entries.push [key.to_s, value]
|
28
|
+
@entries.push [key.to_s, File === value ? value : value.to_s]
|
23
29
|
end
|
24
30
|
|
25
31
|
def delete(key)
|
@@ -50,9 +56,5 @@ module Fetch
|
|
50
56
|
def values
|
51
57
|
@entries.map(&:last)
|
52
58
|
end
|
53
|
-
|
54
|
-
def each(&block)
|
55
|
-
@entries.each(&block)
|
56
|
-
end
|
57
59
|
end
|
58
60
|
end
|
data/lib/fetch/headers.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Fetch
|
2
4
|
class Headers
|
3
5
|
include Enumerable
|
4
6
|
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :entries, :each
|
10
|
+
|
5
11
|
def initialize(init = [])
|
6
12
|
@data = {}
|
7
13
|
|
@@ -11,7 +17,7 @@ module Fetch
|
|
11
17
|
end
|
12
18
|
|
13
19
|
def append(key, value)
|
14
|
-
(@data[key.to_s.downcase] ||= []) << value
|
20
|
+
(@data[key.to_s.downcase] ||= []) << value.to_s
|
15
21
|
end
|
16
22
|
|
17
23
|
def delete(key)
|
@@ -41,9 +47,5 @@ module Fetch
|
|
41
47
|
def values
|
42
48
|
@data.values.map { _1.join(', ') }
|
43
49
|
end
|
44
|
-
|
45
|
-
def each(&block)
|
46
|
-
entries.each(&block)
|
47
|
-
end
|
48
50
|
end
|
49
51
|
end
|
data/lib/fetch/response.rb
CHANGED
@@ -2,7 +2,17 @@ require 'json'
|
|
2
2
|
require 'rack/utils'
|
3
3
|
|
4
4
|
module Fetch
|
5
|
-
Response
|
5
|
+
class Response
|
6
|
+
def initialize(url:, status:, headers:, body:, redirected:)
|
7
|
+
@url = url
|
8
|
+
@status = status
|
9
|
+
@headers = headers
|
10
|
+
@body = body
|
11
|
+
@redirected = redirected
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :url, :status, :headers, :body, :redirected
|
15
|
+
|
6
16
|
def ok
|
7
17
|
status.between?(200, 299)
|
8
18
|
end
|
@@ -12,7 +22,9 @@ module Fetch
|
|
12
22
|
end
|
13
23
|
|
14
24
|
def json(...)
|
25
|
+
return nil unless body
|
26
|
+
|
15
27
|
JSON.parse(body, ...)
|
16
28
|
end
|
17
|
-
|
29
|
+
end
|
18
30
|
end
|
@@ -1,9 +1,14 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
require 'uri'
|
2
3
|
|
3
4
|
module Fetch
|
4
5
|
class URLSearchParams
|
5
6
|
include Enumerable
|
6
7
|
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :entries, :each
|
11
|
+
|
7
12
|
def initialize(options = [])
|
8
13
|
@entries = []
|
9
14
|
|
@@ -22,7 +27,7 @@ module Fetch
|
|
22
27
|
attr_reader :entries
|
23
28
|
|
24
29
|
def append(key, value)
|
25
|
-
@entries.push [key.to_s, value]
|
30
|
+
@entries.push [key.to_s, value.to_s]
|
26
31
|
end
|
27
32
|
|
28
33
|
def delete(key)
|
@@ -61,9 +66,5 @@ module Fetch
|
|
61
66
|
def values
|
62
67
|
@entries.map(&:last)
|
63
68
|
end
|
64
|
-
|
65
|
-
def each(&block)
|
66
|
-
@entries.each(&block)
|
67
|
-
end
|
68
69
|
end
|
69
70
|
end
|
data/lib/fetch/version.rb
CHANGED
data/lib/fetch.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'fetch/config'
|
2
|
+
|
3
|
+
module Fetch
|
4
|
+
singleton_class.attr_accessor :config
|
5
|
+
|
6
|
+
self.config = Config.new
|
7
|
+
|
8
|
+
def self.configure(&block)
|
9
|
+
block.call(config)
|
10
|
+
end
|
11
|
+
|
12
|
+
configure do |config|
|
13
|
+
config.connection_max_idle_time = 10
|
14
|
+
config.keep_alive_timeout = 2
|
15
|
+
end
|
16
|
+
end
|
data/sig/fetch/api.rbs
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Fetch
|
2
|
+
class API
|
3
|
+
def self?.fetch: (
|
4
|
+
string,
|
5
|
+
?method: String | Symbol,
|
6
|
+
?headers: _Each[[_ToS, _ToS]],
|
7
|
+
?body: (String | FormData | URLSearchParams)?,
|
8
|
+
?redirect: 'follow' | 'error' | 'manual' | :follow | :error | :manual,
|
9
|
+
) -> Response
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Fetch
|
2
|
+
class Client
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def fetch: (
|
6
|
+
string,
|
7
|
+
?method: String | Symbol,
|
8
|
+
?headers: _Each[[_ToS, _ToS]],
|
9
|
+
?body: (String | FormData | URLSearchParams)?,
|
10
|
+
?redirect: 'follow' | 'error' | 'manual' | :follow | :error | :manual,
|
11
|
+
?_redirected: bool
|
12
|
+
) -> Response
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def initialize: () -> void
|
17
|
+
def to_response: (string, Net::HTTPResponse, bool) -> Response
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fetch
|
2
|
+
class ConnectionPool
|
3
|
+
class Entry
|
4
|
+
attr_accessor connection: Net::HTTP
|
5
|
+
attr_accessor in_use: bool
|
6
|
+
attr_accessor last_used: Time
|
7
|
+
|
8
|
+
def initialize: (connection: Net::HTTP, in_use: bool, ?last_used: Time) -> void
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize: () -> void
|
12
|
+
def with_connection: [T] (URI::HTTP) { (Net::HTTP) -> T } -> T
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def acquire: (URI::HTTP) -> Net::HTTP
|
17
|
+
def release: (URI::HTTP) -> void
|
18
|
+
def sweep: () -> void
|
19
|
+
def key: (URI::HTTP) -> String
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Fetch
|
2
|
+
class FormData
|
3
|
+
include Enumerable[[String, String | File]]
|
4
|
+
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader entries: Array[[String, String | File]]
|
8
|
+
|
9
|
+
def self.build: (_Each[[_ToS, _ToS | File]]) -> FormData
|
10
|
+
|
11
|
+
def initialize: () -> void
|
12
|
+
def append: (_ToS, _ToS | File) -> void
|
13
|
+
def delete: (_ToS) -> void
|
14
|
+
def get: (_ToS) -> (String | File)?
|
15
|
+
def get_all: (_ToS) -> Array[String | File]
|
16
|
+
def has: (_ToS) -> bool
|
17
|
+
def keys: -> Array[String]
|
18
|
+
def set: (_ToS, _ToS | File) -> void
|
19
|
+
def values: () -> Array[String | File]
|
20
|
+
|
21
|
+
def each: () { ([String, String | File]) -> void } -> Array[[String, String | File]]
|
22
|
+
| () -> Enumerator[[String, String | File], Array[[String, String | File]]]
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Fetch
|
2
|
+
class Headers
|
3
|
+
include Enumerable[[String, String]]
|
4
|
+
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def initialize: (_Each[[_ToS, _ToS]]) -> void
|
8
|
+
def append: (_ToS, _ToS) -> void
|
9
|
+
def delete: (_ToS) -> void
|
10
|
+
def entries: () -> Array[[String, String]]
|
11
|
+
def get: (_ToS) -> String?
|
12
|
+
def has: (_ToS) -> bool
|
13
|
+
def keys: -> Array[String]
|
14
|
+
def set: (_ToS, _ToS) -> void
|
15
|
+
def values: () -> Array[String]
|
16
|
+
|
17
|
+
def each: () { ([String, String]) -> void } -> Array[[String, String]]
|
18
|
+
| () -> Enumerator[[String, String], Array[[String, String]]]
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Fetch
|
2
|
+
class Response
|
3
|
+
attr_reader url: String
|
4
|
+
attr_reader status: Integer
|
5
|
+
attr_reader headers: Headers
|
6
|
+
attr_reader body: String?
|
7
|
+
attr_reader redirected: bool
|
8
|
+
|
9
|
+
def initialize: (url: String, status: Integer, headers: Headers, body: String?, redirected: bool) -> void
|
10
|
+
def ok: () -> bool
|
11
|
+
def status_text: () -> String
|
12
|
+
def json: (json_options) -> untyped
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Fetch
|
2
|
+
class URLSearchParams
|
3
|
+
include Enumerable[[String, String]]
|
4
|
+
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader entries: Array[[String, String]]
|
8
|
+
|
9
|
+
def self.build: (_Each[[_ToS, _ToS]]) -> FormData
|
10
|
+
|
11
|
+
def initialize: (String | _Each[[_ToS, _ToS]]) -> void
|
12
|
+
def append: (_ToS, _ToS) -> void
|
13
|
+
def delete: (_ToS) -> void
|
14
|
+
def get: (_ToS) -> String?
|
15
|
+
def get_all: (_ToS) -> Array[String]
|
16
|
+
def has: (_ToS) -> bool
|
17
|
+
def keys: -> Array[String]
|
18
|
+
def set: (_ToS, _ToS) -> void
|
19
|
+
def values: () -> Array[String]
|
20
|
+
|
21
|
+
def each: () { ([String, String]) -> void } -> Array[[String, String]]
|
22
|
+
| () -> Enumerator[[String, String], Array[[String, String]]]
|
23
|
+
end
|
24
|
+
end
|
data/sig/fetch.rbs
CHANGED
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fetch-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keita Urashima
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: forwardable
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: mini_mime
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -90,8 +118,11 @@ files:
|
|
90
118
|
- LICENSE.txt
|
91
119
|
- README.md
|
92
120
|
- lib/fetch-api.rb
|
121
|
+
- lib/fetch.rb
|
93
122
|
- lib/fetch/api.rb
|
94
123
|
- lib/fetch/client.rb
|
124
|
+
- lib/fetch/config.rb
|
125
|
+
- lib/fetch/connection_pool.rb
|
95
126
|
- lib/fetch/errors.rb
|
96
127
|
- lib/fetch/form_data.rb
|
97
128
|
- lib/fetch/headers.rb
|
@@ -99,6 +130,15 @@ files:
|
|
99
130
|
- lib/fetch/url_search_params.rb
|
100
131
|
- lib/fetch/version.rb
|
101
132
|
- sig/fetch.rbs
|
133
|
+
- sig/fetch/api.rbs
|
134
|
+
- sig/fetch/client.rbs
|
135
|
+
- sig/fetch/config.rbs
|
136
|
+
- sig/fetch/connection_pool.rbs
|
137
|
+
- sig/fetch/errors.rbs
|
138
|
+
- sig/fetch/form_data.rbs
|
139
|
+
- sig/fetch/headers.rbs
|
140
|
+
- sig/fetch/response.rbs
|
141
|
+
- sig/fetch/url_search_params.rbs
|
102
142
|
homepage: https://github.com/ursm/fetch-api
|
103
143
|
licenses:
|
104
144
|
- MIT
|
@@ -113,7 +153,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
153
|
requirements:
|
114
154
|
- - ">="
|
115
155
|
- !ruby/object:Gem::Version
|
116
|
-
version: 3.
|
156
|
+
version: 3.1.0
|
117
157
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
158
|
requirements:
|
119
159
|
- - ">="
|