fetch-api 0.6.0 → 0.7.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 +9 -3
- data/lib/fetch/client.rb +88 -30
- data/lib/fetch/connection_pool.rb +93 -41
- data/lib/fetch/headers.rb +1 -1
- data/lib/fetch/multi_map.rb +2 -2
- data/lib/fetch/response.rb +2 -2
- data/lib/fetch/version.rb +1 -1
- data/sig/fetch/api.rbs +1 -1
- data/sig/fetch/client.rbs +18 -5
- data/sig/fetch/config.rbs +2 -2
- data/sig/fetch/connection_pool.rbs +13 -1
- data/sig/fetch/headers.rbs +3 -1
- data/sig/fetch/multi_map.rbs +3 -1
- data/sig/fetch/response.rbs +2 -2
- metadata +5 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bda47bde351ccf1a6f948922ec3feacc448084da4c00314ed2917cbf5f3e3b90
|
|
4
|
+
data.tar.gz: b4dcd409c7ed44f98cf5d40c3dca16fc0daa0e4ed255f1b965694cfa42d0d4dd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3cea9f26869b527d4dc03afdb8a4a6dd57d72c5cae14d42399feef89c5f9ccc56bf9a2afcfea6875b0f3aead7360b7af77b449bba0839b5faa239f0a1073910d
|
|
7
|
+
data.tar.gz: 6a37f3138b20dd12228a29960959a834e0e2306230c21b8783831231569ae79cdd909a402055bdba920617c091f6660f9a521ebcbcf244ad0716301114e042a2
|
data/README.md
CHANGED
|
@@ -50,6 +50,12 @@ Options for `fetch` method:
|
|
|
50
50
|
- `body`: Request body (default: `nil`)
|
|
51
51
|
- `redirect`: Follow redirects (one of `:follow`, `:error`, `:manual`, default: `:follow`)
|
|
52
52
|
|
|
53
|
+
When following redirects, `fetch` behaves like the Fetch Standard:
|
|
54
|
+
|
|
55
|
+
- Gives up with `Fetch::RedirectError` after 20 redirects
|
|
56
|
+
- Changes the method to `GET` and drops the body on 301 / 302 (for `POST`) and 303 (for anything but `GET` / `HEAD`)
|
|
57
|
+
- Removes the `Authorization` header when redirected to a different origin
|
|
58
|
+
|
|
53
59
|
Methods of `Fetch::Response` object:
|
|
54
60
|
|
|
55
61
|
- `body`: Response body (String)
|
|
@@ -104,13 +110,13 @@ res = fetch('http://example.com', **{
|
|
|
104
110
|
|
|
105
111
|
### Connection pooling
|
|
106
112
|
|
|
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.
|
|
113
|
+
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. The pool is shared by all threads and a connection is never used by more than one thread at a time (in other words, Fetch::API is thread-safe). A forked process does not reuse the connections of its parent. The number of connections is not limited: as many connections as concurrent requests are opened, and they stay in the pool until they expire.
|
|
108
114
|
|
|
109
115
|
These values can be configured as follows (in seconds):
|
|
110
116
|
|
|
111
117
|
``` ruby
|
|
112
118
|
Fetch.configure do |config|
|
|
113
|
-
config.connection_max_idle_time = 30 # default
|
|
119
|
+
config.connection_max_idle_time = 30 # default (Float::INFINITY to keep connections forever)
|
|
114
120
|
end
|
|
115
121
|
```
|
|
116
122
|
|
|
@@ -145,7 +151,7 @@ end
|
|
|
145
151
|
|
|
146
152
|
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.
|
|
147
153
|
|
|
148
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then
|
|
154
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then push a `v*` tag for the version. GitHub Actions will push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
149
155
|
|
|
150
156
|
## Contributing
|
|
151
157
|
|
data/lib/fetch/client.rb
CHANGED
|
@@ -15,12 +15,70 @@ module Fetch
|
|
|
15
15
|
class Client
|
|
16
16
|
include Singleton
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
MAX_REDIRECTS = 20
|
|
19
|
+
REDIRECT_STATUSES = [301, 302, 303, 307, 308]
|
|
20
|
+
REDIRECT_MODES = %w(follow error manual)
|
|
21
|
+
|
|
22
|
+
BODY_HEADERS = %w(
|
|
23
|
+
content-encoding
|
|
24
|
+
content-language
|
|
25
|
+
content-length
|
|
26
|
+
content-location
|
|
27
|
+
content-type
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def initialize
|
|
31
|
+
@pool = ConnectionPool.new
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def fetch(resource, method:, headers:, body:, redirect:)
|
|
35
|
+
raise ArgumentError, "invalid redirect option: #{redirect.inspect}" unless REDIRECT_MODES.include?(redirect.to_s)
|
|
36
|
+
|
|
37
|
+
uri = URI.parse(resource)
|
|
38
|
+
method = method.to_s.upcase
|
|
39
|
+
headers = Headers.new(headers)
|
|
40
|
+
|
|
41
|
+
raise ArgumentError, "not an HTTP URL: #{uri}" unless uri.is_a?(URI::HTTP)
|
|
42
|
+
|
|
43
|
+
MAX_REDIRECTS.succ.times do |redirect_count|
|
|
44
|
+
res = request(uri, method, headers, body)
|
|
45
|
+
response = to_response(uri, res, redirect_count > 0)
|
|
46
|
+
location = res['Location']
|
|
47
|
+
|
|
48
|
+
return response if !REDIRECT_STATUSES.include?(response.status) || redirect.to_s == 'manual'
|
|
49
|
+
|
|
50
|
+
raise RedirectError, location ? "redirected to #{location}" : 'redirected without a location' if redirect.to_s == 'error'
|
|
51
|
+
|
|
52
|
+
return response unless location
|
|
53
|
+
|
|
54
|
+
redirected_uri = resolve(uri, location)
|
|
55
|
+
|
|
56
|
+
if rewrite_to_get?(response.status, method)
|
|
57
|
+
method = 'GET'
|
|
58
|
+
body = nil
|
|
59
|
+
|
|
60
|
+
BODY_HEADERS.each do |name|
|
|
61
|
+
headers.delete name
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
headers.delete 'authorization' unless redirected_uri.origin == uri.origin
|
|
66
|
+
|
|
67
|
+
rewind body
|
|
68
|
+
|
|
69
|
+
uri = redirected_uri
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
raise RedirectError, "too many redirects (more than #{MAX_REDIRECTS})"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def request(uri, method, headers, body)
|
|
20
78
|
req = Net::HTTP.const_get(method.capitalize).new(uri)
|
|
21
79
|
|
|
22
80
|
headers.each do |k, v|
|
|
23
|
-
req[k
|
|
81
|
+
req[k] = v
|
|
24
82
|
end
|
|
25
83
|
|
|
26
84
|
case body
|
|
@@ -41,42 +99,42 @@ module Fetch
|
|
|
41
99
|
req.body = body
|
|
42
100
|
end
|
|
43
101
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
102
|
+
@pool.with_connection(uri) { _1.request(req) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Like browsers, percent-encodes the characters that are not allowed in a URL instead of rejecting them.
|
|
106
|
+
def resolve(uri, location)
|
|
107
|
+
redirected_uri = URI.join(uri, location.b.gsub(/[^\x21-\x7e]/n) { format('%%%02X', _1.ord) })
|
|
108
|
+
|
|
109
|
+
raise RedirectError, "redirected to unsupported URL: #{location}" unless redirected_uri.is_a?(URI::HTTP) && !redirected_uri.host.to_s.empty?
|
|
110
|
+
|
|
111
|
+
redirected_uri
|
|
112
|
+
rescue URI::InvalidURIError
|
|
113
|
+
raise RedirectError, "redirected to invalid URL: #{location}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def rewrite_to_get?(status, method)
|
|
117
|
+
case status
|
|
118
|
+
when 301, 302
|
|
119
|
+
method == 'POST'
|
|
120
|
+
when 303
|
|
121
|
+
!%w(GET HEAD).include?(method)
|
|
62
122
|
else
|
|
63
|
-
|
|
123
|
+
false
|
|
64
124
|
end
|
|
65
125
|
end
|
|
66
126
|
|
|
67
|
-
|
|
127
|
+
def rewind(body)
|
|
128
|
+
return unless body.is_a?(FormData)
|
|
68
129
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
pool
|
|
72
|
-
else
|
|
73
|
-
Thread.current.thread_variable_set(:fetch_connection_pool, ConnectionPool.new)
|
|
130
|
+
body.each do |_, v|
|
|
131
|
+
v.rewind if v.is_a?(File)
|
|
74
132
|
end
|
|
75
133
|
end
|
|
76
134
|
|
|
77
|
-
def to_response(
|
|
135
|
+
def to_response(uri, res, redirected)
|
|
78
136
|
Response.new(
|
|
79
|
-
url:
|
|
137
|
+
url: uri.to_s,
|
|
80
138
|
status: res.code.to_i,
|
|
81
139
|
headers: Headers.new(res.each),
|
|
82
140
|
body: res.body,
|
|
@@ -1,77 +1,129 @@
|
|
|
1
1
|
require_relative '../fetch'
|
|
2
2
|
|
|
3
|
-
require 'thread'
|
|
4
|
-
|
|
5
3
|
module Fetch
|
|
6
4
|
class ConnectionPool
|
|
5
|
+
SWEEP_INTERVAL = 1
|
|
6
|
+
|
|
7
7
|
def initialize
|
|
8
8
|
@connections = {}
|
|
9
9
|
@mutex = Mutex.new
|
|
10
|
-
|
|
11
|
-
@sweeper = Thread.new {
|
|
12
|
-
loop do
|
|
13
|
-
sweep
|
|
14
|
-
|
|
15
|
-
if @connections.empty?
|
|
16
|
-
Thread.stop
|
|
17
|
-
else
|
|
18
|
-
sleep 1
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
}
|
|
10
|
+
@pid = Process.pid
|
|
22
11
|
end
|
|
23
12
|
|
|
13
|
+
# The connection goes back to the pool only if the block completes. Otherwise it may still have a response in
|
|
14
|
+
# flight, which must not leak into the next request.
|
|
24
15
|
def with_connection(uri, &block)
|
|
25
|
-
conn
|
|
16
|
+
conn = checkout(uri)
|
|
17
|
+
completed = false
|
|
26
18
|
|
|
27
19
|
begin
|
|
28
|
-
block.call(conn)
|
|
20
|
+
result = block.call(conn)
|
|
21
|
+
completed = true
|
|
22
|
+
|
|
23
|
+
result
|
|
29
24
|
ensure
|
|
30
|
-
checkin
|
|
25
|
+
completed ? checkin(uri, conn) : close([conn])
|
|
31
26
|
end
|
|
32
27
|
end
|
|
33
28
|
|
|
34
29
|
private
|
|
35
30
|
|
|
36
31
|
def checkout(uri)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# @type var host: String
|
|
41
|
-
host = uri.host
|
|
32
|
+
expired, entry = synchronize { [sweep, @connections[uri.origin]&.pop] }
|
|
33
|
+
|
|
34
|
+
close expired
|
|
42
35
|
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
entry&.first || connect(uri)
|
|
37
|
+
end
|
|
45
38
|
|
|
46
|
-
|
|
39
|
+
def checkin(uri, conn)
|
|
40
|
+
synchronize do
|
|
41
|
+
(@connections[uri.origin] ||= []) << [conn, now]
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
}
|
|
43
|
+
start_sweeper unless @sweeper&.alive?
|
|
50
44
|
end
|
|
51
45
|
end
|
|
52
46
|
|
|
53
|
-
def
|
|
54
|
-
@
|
|
55
|
-
|
|
47
|
+
def connect(uri)
|
|
48
|
+
# @type var host: String
|
|
49
|
+
host = uri.host
|
|
50
|
+
|
|
51
|
+
Net::HTTP.new(host, uri.port).tap {|http|
|
|
52
|
+
http.use_ssl = uri.scheme == 'https'
|
|
53
|
+
|
|
54
|
+
Fetch.config.on_connection_create.call http, uri
|
|
55
|
+
|
|
56
|
+
http.start
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def close(conns)
|
|
61
|
+
conns.each do |conn|
|
|
62
|
+
conn.finish
|
|
63
|
+
rescue StandardError
|
|
64
|
+
# The connection is discarded anyway.
|
|
56
65
|
end
|
|
66
|
+
end
|
|
57
67
|
|
|
58
|
-
|
|
68
|
+
def now
|
|
69
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
59
70
|
end
|
|
60
71
|
|
|
61
|
-
|
|
72
|
+
# Connections inherited from the parent process share their sockets with it, so a forked process starts over.
|
|
73
|
+
def synchronize(&block)
|
|
62
74
|
@mutex.synchronize do
|
|
63
|
-
@
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
unless @pid == Process.pid
|
|
76
|
+
@connections = {}
|
|
77
|
+
@pid = Process.pid
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
block.call
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The sweeper retires once the pool is empty. It clears @sweeper under the lock so that a concurrent checkin never
|
|
85
|
+
# mistakes a retiring sweeper for a running one.
|
|
86
|
+
def start_sweeper
|
|
87
|
+
@sweeper = Thread.new {
|
|
88
|
+
loop do
|
|
89
|
+
# @type var expired: Array[Net::HTTP]
|
|
90
|
+
expired = []
|
|
91
|
+
retired = false
|
|
92
|
+
|
|
93
|
+
synchronize do
|
|
94
|
+
expired = sweep
|
|
95
|
+
retired = @connections.empty?
|
|
96
|
+
@sweeper = nil if retired
|
|
72
97
|
end
|
|
98
|
+
|
|
99
|
+
close expired
|
|
100
|
+
|
|
101
|
+
break if retired
|
|
102
|
+
|
|
103
|
+
sleep SWEEP_INTERVAL
|
|
73
104
|
end
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Removes the expired connections from the pool and returns them. They are closed outside the lock because closing
|
|
109
|
+
# may take a while.
|
|
110
|
+
def sweep
|
|
111
|
+
max_idle_time = Fetch.config.connection_max_idle_time
|
|
112
|
+
current = now
|
|
113
|
+
|
|
114
|
+
expired = @connections.flat_map {|_, entries|
|
|
115
|
+
expired, idle = entries.partition {|_, last_used| current - last_used > max_idle_time }
|
|
116
|
+
|
|
117
|
+
entries.replace idle
|
|
118
|
+
|
|
119
|
+
expired.map(&:first)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@connections.delete_if do |_, entries|
|
|
123
|
+
entries.empty?
|
|
74
124
|
end
|
|
125
|
+
|
|
126
|
+
expired
|
|
75
127
|
end
|
|
76
128
|
end
|
|
77
129
|
end
|
data/lib/fetch/headers.rb
CHANGED
data/lib/fetch/multi_map.rb
CHANGED
data/lib/fetch/response.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require 'json'
|
|
2
|
-
require '
|
|
2
|
+
require 'net/http/status'
|
|
3
3
|
|
|
4
4
|
module Fetch
|
|
5
5
|
class Response
|
|
@@ -18,7 +18,7 @@ module Fetch
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def status_text
|
|
21
|
-
|
|
21
|
+
Net::HTTP::STATUS_CODES[status]
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def json(**json_parse_options)
|
data/lib/fetch/version.rb
CHANGED
data/sig/fetch/api.rbs
CHANGED
data/sig/fetch/client.rbs
CHANGED
|
@@ -2,19 +2,32 @@ module Fetch
|
|
|
2
2
|
class Client
|
|
3
3
|
include Singleton
|
|
4
4
|
|
|
5
|
+
MAX_REDIRECTS: Integer
|
|
6
|
+
REDIRECT_STATUSES: Array[Integer]
|
|
7
|
+
REDIRECT_MODES: Array[String]
|
|
8
|
+
BODY_HEADERS: Array[String]
|
|
9
|
+
|
|
10
|
+
type body = (String | FormData | URLSearchParams)?
|
|
11
|
+
|
|
12
|
+
@pool: ConnectionPool
|
|
13
|
+
|
|
14
|
+
def self.instance: () -> Client
|
|
15
|
+
|
|
5
16
|
def fetch: (
|
|
6
17
|
string,
|
|
7
18
|
method: String | Symbol,
|
|
8
19
|
headers: _Each[[_ToS, _ToS]],
|
|
9
|
-
body:
|
|
10
|
-
redirect: 'follow' | 'error' | 'manual' | :follow | :error | :manual
|
|
11
|
-
?redirected: bool
|
|
20
|
+
body: body,
|
|
21
|
+
redirect: 'follow' | 'error' | 'manual' | :follow | :error | :manual
|
|
12
22
|
) -> Response
|
|
13
23
|
|
|
14
24
|
private
|
|
15
25
|
|
|
16
26
|
def initialize: () -> void
|
|
17
|
-
def
|
|
18
|
-
def
|
|
27
|
+
def request: (URI::HTTP, String, Headers, body) -> Net::HTTPResponse
|
|
28
|
+
def resolve: (URI::HTTP, String) -> URI::HTTP
|
|
29
|
+
def rewrite_to_get?: (Integer, String) -> bool
|
|
30
|
+
def rewind: (body) -> void
|
|
31
|
+
def to_response: (URI::HTTP, Net::HTTPResponse, bool) -> Response
|
|
19
32
|
end
|
|
20
33
|
end
|
data/sig/fetch/config.rbs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Fetch
|
|
2
2
|
class Config
|
|
3
|
-
attr_accessor connection_max_idle_time: Integer
|
|
3
|
+
attr_accessor connection_max_idle_time: Integer | Float
|
|
4
4
|
attr_accessor on_connection_create: ^(Net::HTTP, URI::HTTP) -> void
|
|
5
|
-
attr_accessor json_parse_options:
|
|
5
|
+
attr_accessor json_parse_options: JSON::options
|
|
6
6
|
end
|
|
7
7
|
end
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
module Fetch
|
|
2
2
|
class ConnectionPool
|
|
3
|
+
SWEEP_INTERVAL: Integer | Float
|
|
4
|
+
|
|
5
|
+
@connections: Hash[String, Array[[Net::HTTP, Float]]]
|
|
6
|
+
@mutex: Thread::Mutex
|
|
7
|
+
@pid: Integer
|
|
8
|
+
@sweeper: Thread?
|
|
9
|
+
|
|
3
10
|
def initialize: () -> void
|
|
4
11
|
def with_connection: [T] (URI::HTTP) { (Net::HTTP) -> T } -> T
|
|
5
12
|
|
|
@@ -7,6 +14,11 @@ module Fetch
|
|
|
7
14
|
|
|
8
15
|
def checkout: (URI::HTTP) -> Net::HTTP
|
|
9
16
|
def checkin: (URI::HTTP, Net::HTTP) -> void
|
|
10
|
-
def
|
|
17
|
+
def connect: (URI::HTTP) -> Net::HTTP
|
|
18
|
+
def close: (Array[Net::HTTP]) -> void
|
|
19
|
+
def now: () -> Float
|
|
20
|
+
def synchronize: [T] () { () -> T } -> T
|
|
21
|
+
def start_sweeper: () -> void
|
|
22
|
+
def sweep: () -> Array[Net::HTTP]
|
|
11
23
|
end
|
|
12
24
|
end
|
data/sig/fetch/headers.rbs
CHANGED
|
@@ -2,7 +2,9 @@ module Fetch
|
|
|
2
2
|
class Headers
|
|
3
3
|
include Enumerable[[String, String]]
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
@data: Hash[String, Array[String]]
|
|
6
|
+
|
|
7
|
+
def initialize: (?_Each[[_ToS, _ToS]]) -> void
|
|
6
8
|
def append: (_ToS, _ToS) -> void
|
|
7
9
|
def delete: (_ToS) -> void
|
|
8
10
|
def entries: () -> Array[[String, String]]
|
data/sig/fetch/multi_map.rbs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
module Fetch
|
|
2
|
-
class MultiMap[KIN, KOUT, VIN, VOUT]
|
|
2
|
+
class MultiMap[KIN, KOUT < Comparable, VIN, VOUT]
|
|
3
3
|
include Enumerable[[KOUT, VOUT]]
|
|
4
4
|
|
|
5
|
+
@entries: Array[[KOUT, VOUT]]
|
|
6
|
+
|
|
5
7
|
def initialize: () -> void
|
|
6
8
|
def append: (KIN, VIN) -> void
|
|
7
9
|
def delete: (KIN) -> void
|
data/sig/fetch/response.rbs
CHANGED
|
@@ -8,7 +8,7 @@ module Fetch
|
|
|
8
8
|
|
|
9
9
|
def initialize: (url: String, status: Integer, headers: Headers, body: String?, redirected: bool) -> void
|
|
10
10
|
def ok: () -> bool
|
|
11
|
-
def status_text: () -> String
|
|
12
|
-
def json: (**
|
|
11
|
+
def status_text: () -> String?
|
|
12
|
+
def json: (**JSON::options) -> untyped
|
|
13
13
|
end
|
|
14
14
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fetch-api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Keita Urashima
|
|
8
|
-
|
|
9
|
-
bindir: exe
|
|
8
|
+
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: json
|
|
@@ -52,20 +51,6 @@ dependencies:
|
|
|
52
51
|
- - ">="
|
|
53
52
|
- !ruby/object:Gem::Version
|
|
54
53
|
version: '0'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: rack
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :runtime
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
69
54
|
- !ruby/object:Gem::Dependency
|
|
70
55
|
name: singleton
|
|
71
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,7 +79,6 @@ dependencies:
|
|
|
94
79
|
- - ">="
|
|
95
80
|
- !ruby/object:Gem::Version
|
|
96
81
|
version: '0'
|
|
97
|
-
description:
|
|
98
82
|
email:
|
|
99
83
|
- ursm@ursm.jp
|
|
100
84
|
executables: []
|
|
@@ -134,7 +118,6 @@ licenses:
|
|
|
134
118
|
metadata:
|
|
135
119
|
homepage_uri: https://github.com/ursm/fetch-api
|
|
136
120
|
source_code_uri: https://github.com/ursm/fetch-api.git
|
|
137
|
-
post_install_message:
|
|
138
121
|
rdoc_options: []
|
|
139
122
|
require_paths:
|
|
140
123
|
- lib
|
|
@@ -142,15 +125,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
142
125
|
requirements:
|
|
143
126
|
- - ">="
|
|
144
127
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: 3.
|
|
128
|
+
version: 3.3.0
|
|
146
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
130
|
requirements:
|
|
148
131
|
- - ">="
|
|
149
132
|
- !ruby/object:Gem::Version
|
|
150
133
|
version: '0'
|
|
151
134
|
requirements: []
|
|
152
|
-
rubygems_version:
|
|
153
|
-
signing_key:
|
|
135
|
+
rubygems_version: 4.0.20
|
|
154
136
|
specification_version: 4
|
|
155
137
|
summary: Something like the Fetch API for Ruby
|
|
156
138
|
test_files: []
|