x 0.9.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -27
- data/README.md +6 -2
- data/lib/x/authenticator.rb +8 -0
- data/lib/x/bearer_token_authenticator.rb +5 -3
- data/lib/x/cgi.rb +15 -0
- data/lib/x/client.rb +40 -37
- data/lib/x/connection.rb +42 -65
- data/lib/x/errors/bad_gateway.rb +5 -0
- data/lib/x/errors/{not_found_error.rb → bad_request.rb} +1 -1
- data/lib/x/errors/connection_exception.rb +5 -0
- data/lib/x/errors/error.rb +1 -9
- data/lib/x/errors/{forbidden_error.rb → forbidden.rb} +1 -1
- data/lib/x/errors/gateway_timeout.rb +5 -0
- data/lib/x/errors/{bad_request_error.rb → gone.rb} +1 -1
- data/lib/x/errors/internal_server_error.rb +5 -0
- data/lib/x/errors/network_error.rb +1 -1
- data/lib/x/errors/not_acceptable.rb +5 -0
- data/lib/x/errors/not_found.rb +5 -0
- data/lib/x/errors/payload_too_large.rb +5 -0
- data/lib/x/errors/service_unavailable.rb +5 -0
- data/lib/x/errors/too_many_redirects.rb +5 -0
- data/lib/x/errors/too_many_requests.rb +29 -0
- data/lib/x/errors/unauthorized.rb +5 -0
- data/lib/x/errors/unprocessable_entity.rb +5 -0
- data/lib/x/media_uploader.rb +122 -0
- data/lib/x/oauth_authenticator.rb +10 -15
- data/lib/x/redirect_handler.rb +26 -24
- data/lib/x/request_builder.rb +22 -28
- data/lib/x/response_parser.rb +92 -0
- data/lib/x/version.rb +1 -1
- data/sig/x.rbs +160 -71
- metadata +22 -11
- data/lib/x/errors/authentication_error.rb +0 -5
- data/lib/x/errors/service_unavailable_error.rb +0 -5
- data/lib/x/errors/too_many_redirects_error.rb +0 -5
- data/lib/x/errors/too_many_requests_error.rb +0 -29
- data/lib/x/response_handler.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5af820e6e3cd8b04becf201ad7af96c8d95d9206112673888f91c5ecf923187
|
4
|
+
data.tar.gz: 6f0f561be192153eca2cc7356c823c87bd98f0987256875892c0a2e9ea12b82d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b208d5ba10604a34a57b0dd2ee5dc0992eb801cfbcc287a7f1b0d54444f214050a2c25b2ec6291868a8c35fff09260ad91a82641be830b87d3e6abf0207fc3e
|
7
|
+
data.tar.gz: 24c6a1083358b60065698320372d53404bab9c319aaad13b097508a7041653ac9d45aabd8e6f7a99204accddfc1beeeb2bce18bd67fa9ba816eb6282cbab4227
|
data/CHANGELOG.md
CHANGED
@@ -1,66 +1,82 @@
|
|
1
|
-
## [
|
1
|
+
## [0.11.0] - 2023-10-24
|
2
|
+
|
3
|
+
* Add base Authenticator class (8c66ce2)
|
4
|
+
* Consistently use keyword arguments (3beb271)
|
5
|
+
* Use patern matching to build request (4d001c7)
|
6
|
+
* Rename ResponseHandler to ResponseParser (498e890)
|
7
|
+
* Rename methods to be more consistent (5b8c655)
|
8
|
+
* Rename MediaUpload to MediaUploader (84f0c15)
|
9
|
+
* Add mutant and kill mutants (b124968)
|
10
|
+
* Fix authentication bug with request URLs that contain spaces (8de3174)
|
11
|
+
* Refactor errors (853d39c)
|
12
|
+
* Make Connection class threadsafe (d95d285)
|
13
|
+
|
14
|
+
## [0.10.0] - 2023-10-08
|
15
|
+
|
16
|
+
* Add media upload helper methods (6c6a267)
|
17
|
+
* Add PayloadTooLargeError class (cd61850)
|
2
18
|
|
3
19
|
## [0.9.1] - 2023-10-06
|
4
20
|
|
5
|
-
|
6
|
-
|
7
|
-
|
21
|
+
* Allow successful empty responses (06bf7db)
|
22
|
+
* Update default User-Agent string (296b36a)
|
23
|
+
* Move query parameter escaping into RequestBuilder (56d6bd2)
|
8
24
|
|
9
25
|
## [0.9.0] - 2023-09-26
|
10
26
|
|
11
|
-
|
27
|
+
* Add support for HTTP proxies (3740f4f)
|
12
28
|
|
13
29
|
## [0.8.1] - 2023-09-20
|
14
30
|
|
15
|
-
|
31
|
+
* Fix bug where setting Connection#base_uri= doesn't update the HTTP client (d5a89db)
|
16
32
|
|
17
33
|
## [0.8.0] - 2023-09-14
|
18
34
|
|
19
|
-
|
20
|
-
|
21
|
-
|
35
|
+
* Add (back) bearer token authentication (62e141d)
|
36
|
+
* Follow redirects (90a8c55)
|
37
|
+
* Parse error responses with Content-Type: application/problem+json (0b697d9)
|
22
38
|
|
23
39
|
## [0.7.1] - 2023-09-02
|
24
40
|
|
25
|
-
|
41
|
+
* Fix bug in X::Authenticator#split_uri (ebc9d5f)
|
26
42
|
|
27
43
|
## [0.7.0] - 2023-09-02
|
28
44
|
|
29
|
-
|
45
|
+
* Remove OAuth gem (7c29bb1)
|
30
46
|
|
31
47
|
## [0.6.0] - 2023-08-30
|
32
48
|
|
33
|
-
|
34
|
-
|
35
|
-
|
49
|
+
* Add configurable debug output stream for logging (fd2d4b0)
|
50
|
+
* Remove bearer token authentication (efff940)
|
51
|
+
* Define RBS type signatures (d7f63ba)
|
36
52
|
|
37
53
|
## [0.5.1] - 2023-08-16
|
38
54
|
|
39
|
-
|
55
|
+
* Fix bearer token authentication (1a1ca93)
|
40
56
|
|
41
57
|
## [0.5.0] - 2023-08-10
|
42
58
|
|
43
|
-
|
44
|
-
|
59
|
+
* Add configurable write timeout (2a31f84)
|
60
|
+
* Use built-in Gem::Version class (066e0b6)
|
45
61
|
|
46
62
|
## [0.4.0] - 2023-08-06
|
47
63
|
|
48
|
-
|
49
|
-
|
50
|
-
|
64
|
+
* Refactor Client into Authenticator, RequestBuilder, Connection, ResponseHandler (6bee1e9)
|
65
|
+
* Add configurable open timeout (1000f9d)
|
66
|
+
* Allow configuration of content type (f33a732)
|
51
67
|
|
52
68
|
## [0.3.0] - 2023-08-04
|
53
69
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
70
|
+
* Add accessors to X::Client (e61fa73)
|
71
|
+
* Add configurable read timeout (41502b9)
|
72
|
+
* Handle network-related errors (9ed1fb4)
|
73
|
+
* Include response body in errors (a203e6a)
|
58
74
|
|
59
75
|
## [0.2.0] - 2023-08-02
|
60
76
|
|
61
|
-
|
62
|
-
|
77
|
+
* Allow configuration of base URL (4bc0531)
|
78
|
+
* Improve error handling (14dc0cd)
|
63
79
|
|
64
80
|
## [0.1.0] - 2023-08-02
|
65
81
|
|
66
|
-
|
82
|
+
* Initial release
|
data/README.md
CHANGED
@@ -130,11 +130,15 @@ Pull requests will only be accepted if they meet all the following criteria:
|
|
130
130
|
|
131
131
|
bundle exec rake standard
|
132
132
|
|
133
|
-
2.
|
133
|
+
2. 100% C0 code coverage. This can be verified with:
|
134
134
|
|
135
135
|
bundle exec rake test
|
136
136
|
|
137
|
-
3.
|
137
|
+
3. 100% mutation coverage. This can be verified with:
|
138
|
+
|
139
|
+
bundle exec rake mutant
|
140
|
+
|
141
|
+
4. RBS type signatures (in `sig/x.rbs`). This can be verified with:
|
138
142
|
|
139
143
|
bundle exec rake steep
|
140
144
|
|
@@ -1,14 +1,16 @@
|
|
1
|
+
require_relative "authenticator"
|
2
|
+
|
1
3
|
module X
|
2
4
|
# Handles bearer token authentication
|
3
|
-
class BearerTokenAuthenticator
|
5
|
+
class BearerTokenAuthenticator < Authenticator
|
4
6
|
attr_accessor :bearer_token
|
5
7
|
|
6
|
-
def initialize(bearer_token)
|
8
|
+
def initialize(bearer_token:) # rubocop:disable Lint/MissingSuper
|
7
9
|
@bearer_token = bearer_token
|
8
10
|
end
|
9
11
|
|
10
12
|
def header(_request)
|
11
|
-
"Bearer #{bearer_token}"
|
13
|
+
{"Authorization" => "Bearer #{bearer_token}"}
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
data/lib/x/cgi.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "cgi"
|
2
|
+
|
3
|
+
module X
|
4
|
+
# Namespaced CGI class
|
5
|
+
class CGI
|
6
|
+
# TODO: Replace CGI.escape with CGI.escapeURIComponent when support for Ruby 3.1 is dropped
|
7
|
+
def self.escape(value)
|
8
|
+
::CGI.escape(value).gsub("+", "%20")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.escape_params(params)
|
12
|
+
params.map { |k, v| "#{k}=#{escape(v)}" }.join("&")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/x/client.rb
CHANGED
@@ -1,84 +1,87 @@
|
|
1
1
|
require "forwardable"
|
2
2
|
require_relative "bearer_token_authenticator"
|
3
|
-
require_relative "oauth_authenticator"
|
4
3
|
require_relative "connection"
|
4
|
+
require_relative "oauth_authenticator"
|
5
5
|
require_relative "redirect_handler"
|
6
6
|
require_relative "request_builder"
|
7
|
-
require_relative "
|
7
|
+
require_relative "response_parser"
|
8
8
|
|
9
9
|
module X
|
10
10
|
# Main public interface
|
11
11
|
class Client
|
12
12
|
extend Forwardable
|
13
13
|
|
14
|
+
DEFAULT_BASE_URL = "https://api.twitter.com/2/".freeze
|
15
|
+
|
16
|
+
attr_accessor :base_url
|
17
|
+
|
14
18
|
def_delegators :@authenticator, :bearer_token, :api_key, :api_key_secret, :access_token, :access_token_secret
|
15
19
|
def_delegators :@authenticator, :bearer_token=, :api_key=, :api_key_secret=, :access_token=, :access_token_secret=
|
16
|
-
def_delegators :@connection, :
|
17
|
-
def_delegators :@connection, :
|
18
|
-
def_delegators :@
|
19
|
-
def_delegators :@
|
20
|
-
def_delegators :@
|
21
|
-
def_delegators :@
|
20
|
+
def_delegators :@connection, :open_timeout, :read_timeout, :write_timeout, :proxy_url, :debug_output
|
21
|
+
def_delegators :@connection, :open_timeout=, :read_timeout=, :write_timeout=, :proxy_url=, :debug_output=
|
22
|
+
def_delegators :@redirect_handler, :max_redirects
|
23
|
+
def_delegators :@redirect_handler, :max_redirects=
|
24
|
+
def_delegators :@response_parser, :array_class, :object_class
|
25
|
+
def_delegators :@response_parser, :array_class=, :object_class=
|
22
26
|
|
23
27
|
def initialize(bearer_token: nil,
|
24
28
|
api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
|
25
|
-
base_url:
|
29
|
+
base_url: DEFAULT_BASE_URL,
|
26
30
|
open_timeout: Connection::DEFAULT_OPEN_TIMEOUT,
|
27
31
|
read_timeout: Connection::DEFAULT_READ_TIMEOUT,
|
28
32
|
write_timeout: Connection::DEFAULT_WRITE_TIMEOUT,
|
29
33
|
proxy_url: nil,
|
30
|
-
content_type: RequestBuilder::DEFAULT_CONTENT_TYPE,
|
31
|
-
user_agent: RequestBuilder::DEFAULT_USER_AGENT,
|
32
34
|
debug_output: nil,
|
33
|
-
array_class:
|
34
|
-
object_class:
|
35
|
+
array_class: nil,
|
36
|
+
object_class: nil,
|
35
37
|
max_redirects: RedirectHandler::DEFAULT_MAX_REDIRECTS)
|
36
38
|
|
39
|
+
@base_url = base_url
|
37
40
|
initialize_authenticator(bearer_token, api_key, api_key_secret, access_token, access_token_secret)
|
38
|
-
@connection = Connection.new(
|
41
|
+
@connection = Connection.new(open_timeout: open_timeout, read_timeout: read_timeout,
|
39
42
|
write_timeout: write_timeout, debug_output: debug_output, proxy_url: proxy_url)
|
40
|
-
@request_builder = RequestBuilder.new
|
41
|
-
@redirect_handler = RedirectHandler.new(@authenticator,
|
42
|
-
max_redirects: max_redirects)
|
43
|
-
@
|
43
|
+
@request_builder = RequestBuilder.new
|
44
|
+
@redirect_handler = RedirectHandler.new(authenticator: @authenticator, connection: @connection,
|
45
|
+
request_builder: @request_builder, max_redirects: max_redirects)
|
46
|
+
@response_parser = ResponseParser.new(array_class: array_class, object_class: object_class)
|
44
47
|
end
|
45
48
|
|
46
|
-
def get(endpoint)
|
47
|
-
|
49
|
+
def get(endpoint, headers: {})
|
50
|
+
execute_request(:get, endpoint, headers: headers)
|
48
51
|
end
|
49
52
|
|
50
|
-
def post(endpoint, body = nil)
|
51
|
-
|
53
|
+
def post(endpoint, body = nil, headers: {})
|
54
|
+
execute_request(:post, endpoint, body: body, headers: headers)
|
52
55
|
end
|
53
56
|
|
54
|
-
def put(endpoint, body = nil)
|
55
|
-
|
57
|
+
def put(endpoint, body = nil, headers: {})
|
58
|
+
execute_request(:put, endpoint, body: body, headers: headers)
|
56
59
|
end
|
57
60
|
|
58
|
-
def delete(endpoint)
|
59
|
-
|
61
|
+
def delete(endpoint, headers: {})
|
62
|
+
execute_request(:delete, endpoint, headers: headers)
|
60
63
|
end
|
61
64
|
|
62
65
|
private
|
63
66
|
|
64
67
|
def initialize_authenticator(bearer_token, api_key, api_key_secret, access_token, access_token_secret)
|
65
68
|
@authenticator = if bearer_token
|
66
|
-
BearerTokenAuthenticator.new(bearer_token)
|
69
|
+
BearerTokenAuthenticator.new(bearer_token: bearer_token)
|
67
70
|
elsif api_key && api_key_secret && access_token && access_token_secret
|
68
|
-
|
71
|
+
OAuthAuthenticator.new(api_key: api_key, api_key_secret: api_key_secret, access_token: access_token,
|
72
|
+
access_token_secret: access_token_secret)
|
69
73
|
else
|
70
|
-
|
71
|
-
"Client must be initialized with either a bearer_token or " \
|
72
|
-
"an api_key, api_key_secret, access_token, and access_token_secret"
|
74
|
+
Authenticator.new
|
73
75
|
end
|
74
76
|
end
|
75
77
|
|
76
|
-
def
|
77
|
-
uri = URI.join(
|
78
|
-
request = @request_builder.build(@authenticator, http_method, uri, body: body
|
79
|
-
|
80
|
-
|
81
|
-
@
|
78
|
+
def execute_request(http_method, endpoint, headers:, body: nil)
|
79
|
+
uri = URI.join(base_url, endpoint)
|
80
|
+
request = @request_builder.build(authenticator: @authenticator, http_method: http_method, uri: uri, body: body,
|
81
|
+
headers: headers)
|
82
|
+
response = @connection.perform(request: request)
|
83
|
+
response = @redirect_handler.handle(response: response, request: request, base_url: base_url)
|
84
|
+
@response_parser.parse(response: response)
|
82
85
|
end
|
83
86
|
end
|
84
87
|
end
|
data/lib/x/connection.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "forwardable"
|
2
2
|
require "net/http"
|
3
|
+
require "openssl"
|
3
4
|
require "uri"
|
4
5
|
require_relative "errors/network_error"
|
5
6
|
|
@@ -8,96 +9,72 @@ module X
|
|
8
9
|
class Connection
|
9
10
|
extend Forwardable
|
10
11
|
|
11
|
-
|
12
|
-
DEFAULT_HOST = "https://api.twitter.com".freeze
|
12
|
+
DEFAULT_HOST = "api.twitter.com".freeze
|
13
13
|
DEFAULT_PORT = 443
|
14
14
|
DEFAULT_OPEN_TIMEOUT = 60 # seconds
|
15
15
|
DEFAULT_READ_TIMEOUT = 60 # seconds
|
16
16
|
DEFAULT_WRITE_TIMEOUT = 60 # seconds
|
17
|
+
DEFAULT_DEBUG_OUTPUT = File.open(File::NULL, "w")
|
17
18
|
NETWORK_ERRORS = [
|
18
19
|
Errno::ECONNREFUSED,
|
20
|
+
Errno::ECONNRESET,
|
19
21
|
Net::OpenTimeout,
|
20
|
-
Net::ReadTimeout
|
22
|
+
Net::ReadTimeout,
|
23
|
+
OpenSSL::SSL::SSLError
|
21
24
|
].freeze
|
22
25
|
|
23
|
-
|
26
|
+
attr_accessor :open_timeout, :read_timeout, :write_timeout, :debug_output
|
27
|
+
attr_reader :proxy_url, :proxy_uri
|
24
28
|
|
25
|
-
|
26
|
-
|
27
|
-
def_delegator
|
29
|
+
def_delegator :proxy_uri, :host, :proxy_host
|
30
|
+
def_delegator :proxy_uri, :port, :proxy_port
|
31
|
+
def_delegator :proxy_uri, :user, :proxy_user
|
32
|
+
def_delegator :proxy_uri, :password, :proxy_pass
|
28
33
|
|
29
|
-
def initialize(
|
30
|
-
|
31
|
-
@
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
write_timeout: write_timeout,
|
37
|
-
debug_output: debug_output
|
38
|
-
)
|
34
|
+
def initialize(open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT,
|
35
|
+
write_timeout: DEFAULT_WRITE_TIMEOUT, debug_output: DEFAULT_DEBUG_OUTPUT, proxy_url: nil)
|
36
|
+
@open_timeout = open_timeout
|
37
|
+
@read_timeout = read_timeout
|
38
|
+
@write_timeout = write_timeout
|
39
|
+
@debug_output = debug_output
|
40
|
+
self.proxy_url = proxy_url unless proxy_url.nil?
|
39
41
|
end
|
40
42
|
|
41
|
-
def
|
42
|
-
|
43
|
+
def perform(request:)
|
44
|
+
host = request.uri.host || DEFAULT_HOST
|
45
|
+
port = request.uri.port || DEFAULT_PORT
|
46
|
+
http_client = build_http_client(host, port)
|
47
|
+
http_client.use_ssl = request.uri.scheme.eql?("https")
|
48
|
+
http_client.request(request)
|
43
49
|
rescue *NETWORK_ERRORS => e
|
44
|
-
raise NetworkError, "Network error: #{e
|
50
|
+
raise NetworkError, "Network error: #{e}"
|
45
51
|
end
|
46
52
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
53
|
+
def proxy_url=(proxy_url)
|
54
|
+
@proxy_url = proxy_url
|
55
|
+
proxy_uri = URI(proxy_url)
|
56
|
+
raise ArgumentError, "Invalid proxy URL: #{proxy_uri}" unless proxy_uri.is_a?(URI::HTTP)
|
50
57
|
|
51
|
-
@
|
52
|
-
update_http_client_settings
|
53
|
-
end
|
54
|
-
|
55
|
-
def debug_output
|
56
|
-
@http_client.instance_variable_get(:@debug_output)
|
58
|
+
@proxy_uri = proxy_uri
|
57
59
|
end
|
58
60
|
|
59
61
|
private
|
60
62
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
@http_client.write_timeout = write_timeout
|
65
|
-
@http_client.set_debug_output(debug_output) if debug_output
|
66
|
-
end
|
67
|
-
|
68
|
-
def current_http_client_settings
|
69
|
-
{
|
70
|
-
open_timeout: @http_client.open_timeout,
|
71
|
-
read_timeout: @http_client.read_timeout,
|
72
|
-
write_timeout: @http_client.write_timeout,
|
73
|
-
debug_output: debug_output
|
74
|
-
}
|
75
|
-
end
|
76
|
-
|
77
|
-
def update_http_client_settings
|
78
|
-
conditionally_apply_http_client_settings do
|
79
|
-
host = @base_uri.host || DEFAULT_HOST
|
80
|
-
port = @base_uri.port || DEFAULT_PORT
|
81
|
-
@http_client = build_http_client(host: host, port: port)
|
82
|
-
@http_client.use_ssl = @base_uri.scheme == "https"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def build_http_client(host:, port:)
|
87
|
-
if @proxy_uri.nil?
|
88
|
-
Net::HTTP.new(host, port)
|
63
|
+
def build_http_client(host = DEFAULT_HOST, port = DEFAULT_PORT)
|
64
|
+
http_client = if proxy_uri
|
65
|
+
Net::HTTP.new(host, port, proxy_host, proxy_port, proxy_user, proxy_pass)
|
89
66
|
else
|
90
|
-
Net::HTTP.new(host, port
|
67
|
+
Net::HTTP.new(host, port)
|
91
68
|
end
|
69
|
+
configure_http_client(http_client)
|
92
70
|
end
|
93
71
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
yield
|
72
|
+
def configure_http_client(http_client)
|
73
|
+
http_client.tap do |c|
|
74
|
+
c.open_timeout = open_timeout
|
75
|
+
c.read_timeout = read_timeout
|
76
|
+
c.write_timeout = write_timeout
|
77
|
+
c.set_debug_output(debug_output)
|
101
78
|
end
|
102
79
|
end
|
103
80
|
end
|
data/lib/x/errors/error.rb
CHANGED
@@ -1,15 +1,7 @@
|
|
1
|
-
require "json"
|
2
|
-
require "net/http"
|
3
|
-
|
4
1
|
module X
|
5
2
|
# Base error class
|
6
3
|
class Error < ::StandardError
|
7
|
-
|
8
|
-
|
9
|
-
attr_reader :object
|
10
|
-
|
11
|
-
def initialize(msg, response:)
|
12
|
-
@object = JSON.parse(response.body || "{}") if JSON_CONTENT_TYPE_REGEXP.match?(response["content-type"])
|
4
|
+
def initialize(msg, _response = nil)
|
13
5
|
super(msg)
|
14
6
|
end
|
15
7
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "client_error"
|
2
|
+
|
3
|
+
module X
|
4
|
+
# Rate limit error
|
5
|
+
class TooManyRequests < ClientError
|
6
|
+
def initialize(msg, response)
|
7
|
+
@response = response
|
8
|
+
super(msg)
|
9
|
+
end
|
10
|
+
|
11
|
+
def limit
|
12
|
+
@response["x-rate-limit-limit"].to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
def remaining
|
16
|
+
@response["x-rate-limit-remaining"].to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset_at
|
20
|
+
Time.at(@response["x-rate-limit-reset"].to_i)
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset_in
|
24
|
+
[(reset_at - Time.now).ceil, 0].max
|
25
|
+
end
|
26
|
+
|
27
|
+
alias_method :retry_after, :reset_in
|
28
|
+
end
|
29
|
+
end
|