esirb 0.3.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +98 -0
- data/README.md +404 -20
- data/lib/esirb/client.rb +137 -32
- data/lib/esirb/compatibility_date.rb +41 -0
- data/lib/esirb/configuration.rb +59 -0
- data/lib/esirb/error_limit_guard.rb +62 -0
- data/lib/esirb/errors.rb +107 -0
- data/lib/esirb/pagination.rb +105 -0
- data/lib/esirb/path.rb +90 -14
- data/lib/esirb/raise_error.rb +27 -0
- data/lib/esirb/rate_limit.rb +58 -0
- data/lib/esirb/retry.rb +74 -0
- data/lib/esirb/vary_by_authorization.rb +19 -0
- data/lib/esirb/version.rb +1 -1
- data/lib/esirb.rb +9 -4
- data/lib/generators/esirb/install/install_generator.rb +35 -0
- data/lib/generators/esirb/install/templates/esirb.rb.tt +35 -0
- metadata +20 -10
- data/.rspec +0 -3
- data/.rubocop.yml +0 -8
- data/.ruby-version +0 -1
- data/Rakefile +0 -12
- data/sig/esirb.rbs +0 -4
data/lib/esirb/path.rb
CHANGED
|
@@ -1,31 +1,107 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "erb"
|
|
4
|
+
require_relative "pagination"
|
|
5
|
+
|
|
3
6
|
module Esirb
|
|
7
|
+
# An ESI endpoint path built by chaining method calls, sent with an HTTP verb method.
|
|
8
|
+
#
|
|
9
|
+
# Every call returns a new Path, so a partial path can be reused:
|
|
10
|
+
#
|
|
11
|
+
# character = client.characters(123)
|
|
12
|
+
# character.location.to_s # => "/characters/123/location"
|
|
13
|
+
# character.skills.to_s # => "/characters/123/skills"
|
|
4
14
|
class Path
|
|
5
|
-
|
|
15
|
+
include Pagination
|
|
16
|
+
|
|
17
|
+
VERBS = %i[get head delete post put patch].freeze
|
|
18
|
+
|
|
19
|
+
# Method names that become path segments. Conversion hooks like to_ary and to_str are left to Ruby.
|
|
20
|
+
SEGMENT_NAME = /\A(?!to_)[a-z][a-z0-9_]*\z/
|
|
21
|
+
SEGMENT_TYPES = [String, Symbol, Integer].freeze
|
|
22
|
+
|
|
23
|
+
# ESI segments with hyphens, which can't be Ruby method names. Calling the underscored name
|
|
24
|
+
# (client.freelance_jobs) sends the hyphenated segment. Segments keep their spelling across
|
|
25
|
+
# compatibility dates, so this covers every date. `rake esi:check_segments` compares it with
|
|
26
|
+
# ESI's OpenAPI spec for each date.
|
|
27
|
+
HYPHENATED_SEGMENTS = %w[
|
|
28
|
+
access-lists
|
|
29
|
+
compatibility-dates
|
|
30
|
+
freelance-jobs
|
|
31
|
+
mercenary-dens
|
|
32
|
+
mercenary-tactical-operations
|
|
33
|
+
military-campaigns
|
|
34
|
+
paragon-hub
|
|
35
|
+
recommended-fit
|
|
36
|
+
sovereignty-hubs
|
|
37
|
+
].to_h { |segment| [segment.tr("-", "_"), segment] }.freeze
|
|
38
|
+
|
|
39
|
+
NO_BODY = Object.new.freeze
|
|
40
|
+
private_constant :NO_BODY
|
|
41
|
+
|
|
42
|
+
def self.segment_name?(name)
|
|
43
|
+
SEGMENT_NAME.match?(name.to_s)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
attr_reader :client
|
|
47
|
+
|
|
48
|
+
def initialize(client:, segments: [])
|
|
49
|
+
raise ArgumentError, "client is required" if client.nil?
|
|
6
50
|
|
|
7
|
-
def initialize(client: nil)
|
|
8
51
|
@client = client
|
|
9
|
-
@
|
|
52
|
+
@segments = segments.freeze
|
|
53
|
+
@path = segments.map { |segment| "/#{segment}" }.join.freeze
|
|
54
|
+
freeze
|
|
10
55
|
end
|
|
11
56
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
57
|
+
# get, head, delete, post, put and patch send the request and return the Faraday response.
|
|
58
|
+
# params: become the query string, body: is sent as JSON, and headers: are added to the
|
|
59
|
+
# client's defaults, replacing any with the same name.
|
|
60
|
+
VERBS.each do |verb|
|
|
61
|
+
define_method verb do |params: nil, body: NO_BODY, headers: nil|
|
|
62
|
+
client.connection.public_send(verb, to_s) do |req|
|
|
63
|
+
req.params.update(params) if params
|
|
64
|
+
req.body = body unless body.equal?(NO_BODY)
|
|
65
|
+
req.headers.update(headers) if headers
|
|
18
66
|
end
|
|
19
67
|
end
|
|
20
68
|
end
|
|
21
69
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
70
|
+
# The path, e.g. "/characters/123/location".
|
|
71
|
+
def to_s
|
|
72
|
+
@path
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Adds a segment and its path params to a copy of this path, exactly as given. Use it for
|
|
76
|
+
# segments that can't be reached by method name.
|
|
77
|
+
def segment(name, *params)
|
|
78
|
+
self.class.new(client:, segments: @segments + [name, *params].map { |value| encode(value) })
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def method_missing(name, *params, **kwargs)
|
|
82
|
+
return super unless self.class.segment_name?(name)
|
|
83
|
+
if kwargs.any?
|
|
84
|
+
raise ArgumentError, "path segments only take positional params; pass query params to the HTTP verb as params:"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
segment(HYPHENATED_SEGMENTS.fetch(name.to_s, name), *params)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def respond_to_missing?(name, include_private = false)
|
|
91
|
+
self.class.segment_name?(name) || super
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def encode(value)
|
|
97
|
+
unless SEGMENT_TYPES.any? { |type| value.is_a?(type) }
|
|
98
|
+
raise ArgumentError, "path segments must be strings, symbols or integers, got #{value.inspect}"
|
|
26
99
|
end
|
|
27
100
|
|
|
28
|
-
|
|
101
|
+
text = value.to_s
|
|
102
|
+
raise ArgumentError, "path segments can't be #{text.inspect}" if ["", ".", ".."].include?(text)
|
|
103
|
+
|
|
104
|
+
ERB::Util.url_encode(text)
|
|
29
105
|
end
|
|
30
106
|
end
|
|
31
107
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
require_relative "errors"
|
|
5
|
+
|
|
6
|
+
module Esirb
|
|
7
|
+
# Raises esirb's errors in place of Faraday's: a ResponseError subclass for error statuses, and
|
|
8
|
+
# ConnectionFailed, TimeoutError or Error for failures with no response. The Faraday error is
|
|
9
|
+
# kept as the exception's cause.
|
|
10
|
+
class RaiseError < Faraday::Middleware
|
|
11
|
+
def call(env)
|
|
12
|
+
super
|
|
13
|
+
rescue Faraday::TimeoutError => e
|
|
14
|
+
raise Esirb::TimeoutError, e.message
|
|
15
|
+
rescue Faraday::ConnectionFailed, Faraday::SSLError => e
|
|
16
|
+
raise Esirb::ConnectionFailed, e.message
|
|
17
|
+
rescue Faraday::Error => e
|
|
18
|
+
raise Esirb::Error, e.message
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def on_complete(env)
|
|
22
|
+
return unless env.status.to_i >= 400
|
|
23
|
+
|
|
24
|
+
raise ResponseError.for_status(env.status), env
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
|
|
5
|
+
module Esirb
|
|
6
|
+
# ESI's limits as reported on one response. A route uses either the per-group rate limit
|
|
7
|
+
# (X-Ratelimit-*) or the older error limit (X-ESI-Error-Limit-*), never both, so the other
|
|
8
|
+
# set of values is nil. See https://developers.eveonline.com/docs/services/esi/rate-limiting/
|
|
9
|
+
#
|
|
10
|
+
# Esirb::RateLimit.new(response).remaining # => 1195
|
|
11
|
+
class RateLimit
|
|
12
|
+
WINDOW_UNITS = { "s" => 1, "m" => 60, "h" => 60 * 60 }.freeze
|
|
13
|
+
# With fewer errors than this left in the error limit window, esirb stops retrying, and with a
|
|
14
|
+
# cache store stops sending requests: running out blocks every ESI route until the window resets.
|
|
15
|
+
ERROR_LIMIT_FLOOR = 20
|
|
16
|
+
# Each reader and the header its whole-number value comes from.
|
|
17
|
+
HEADERS_BY_READER = {
|
|
18
|
+
remaining: "X-Ratelimit-Remaining",
|
|
19
|
+
used: "X-Ratelimit-Used",
|
|
20
|
+
error_limit_remaining: "X-ESI-Error-Limit-Remain",
|
|
21
|
+
error_limit_reset: "X-ESI-Error-Limit-Reset"
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
# group, limit (tokens per window), window (seconds), remaining and used (what this request
|
|
25
|
+
# cost) come from the rate limit; error_limit_remaining and error_limit_reset (seconds until
|
|
26
|
+
# the window resets) from the error limit.
|
|
27
|
+
attr_reader :group, :limit, :window, :remaining, :used, :error_limit_remaining, :error_limit_reset
|
|
28
|
+
|
|
29
|
+
# Takes a response (or anything with headers) or the headers themselves.
|
|
30
|
+
def initialize(source)
|
|
31
|
+
headers = Faraday::Utils::Headers.new.update(source.respond_to?(:headers) ? source.headers : source.to_h)
|
|
32
|
+
|
|
33
|
+
@group = headers["X-Ratelimit-Group"]
|
|
34
|
+
@limit, @window = parse_limit(headers["X-Ratelimit-Limit"])
|
|
35
|
+
HEADERS_BY_READER.each { |reader, header| instance_variable_set(:"@#{reader}", integer(headers[header])) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Whether the error limit is nearly used up. Routes on the newer rate limit don't send error
|
|
39
|
+
# limit headers, so this is false for them.
|
|
40
|
+
def error_limit_low?
|
|
41
|
+
!error_limit_remaining.nil? && error_limit_remaining < ERROR_LIMIT_FLOOR
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# "150/15m" => [150, 900]
|
|
47
|
+
def parse_limit(value)
|
|
48
|
+
match = value.to_s.match(%r{\A(\d+)/(\d+)([smh])\z})
|
|
49
|
+
return [nil, nil] unless match
|
|
50
|
+
|
|
51
|
+
[match[1].to_i, match[2].to_i * WINDOW_UNITS.fetch(match[3])]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def integer(value)
|
|
55
|
+
Integer(value, exception: false) if value
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/esirb/retry.rb
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
require_relative "errors"
|
|
5
|
+
|
|
6
|
+
module Esirb
|
|
7
|
+
# Retries requests that failed for reasons that usually clear up on their own, up to `retries`
|
|
8
|
+
# times in all, whatever the reason. Sits outside RaiseError, so it sees esirb's errors.
|
|
9
|
+
class Retry < Faraday::Middleware
|
|
10
|
+
# Usually transient: ESI tends to recover within a second or two.
|
|
11
|
+
TRANSIENT = [BadGateway, ServiceUnavailable, GatewayTimeout, TimeoutError, ConnectionFailed].freeze
|
|
12
|
+
# Safe to repeat. POST isn't, since it might do something twice, like sending a mail.
|
|
13
|
+
METHODS = %i[delete get head put].freeze
|
|
14
|
+
# Longest Retry-After worth waiting for, in seconds. Anything longer is raised for the app to handle.
|
|
15
|
+
MAX_WAIT = 10
|
|
16
|
+
|
|
17
|
+
# A method of its own, so specs can skip the wait.
|
|
18
|
+
def self.pause(seconds)
|
|
19
|
+
sleep(seconds)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(app, retries:)
|
|
23
|
+
super(app)
|
|
24
|
+
@retries = retries
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# body is the original request body: after a failure, env.body holds the response's.
|
|
28
|
+
def call(env, body: env.body, attempt: 0)
|
|
29
|
+
env.body = body
|
|
30
|
+
@app.call(env)
|
|
31
|
+
rescue Error => e
|
|
32
|
+
wait = wait_for(env, e, attempt)
|
|
33
|
+
raise unless wait
|
|
34
|
+
|
|
35
|
+
self.class.pause(wait)
|
|
36
|
+
call(env, body:, attempt: attempt + 1)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# Seconds to wait before retrying, or nil to raise the error.
|
|
42
|
+
def wait_for(env, error, attempt)
|
|
43
|
+
return unless retry_allowed?(env, error, attempt)
|
|
44
|
+
return retry_after(error) if error.is_a?(TooManyRequests)
|
|
45
|
+
return unless TRANSIENT.any? { |error_class| error.is_a?(error_class) }
|
|
46
|
+
|
|
47
|
+
retry_after_header?(error) ? retry_after(error) : backoff(attempt)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def retry_allowed?(env, error, attempt)
|
|
51
|
+
attempt < @retries && METHODS.include?(env.method) && !error_limit_low?(error)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Each failed retry is another error, so stop when the error limit is nearly used up.
|
|
55
|
+
def error_limit_low?(error)
|
|
56
|
+
error.is_a?(ResponseError) && error.rate_limit.error_limit_low?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# About 0.5s, then 1s, then 2s..., plus up to 50% so many clients don't retry in step.
|
|
60
|
+
def backoff(attempt)
|
|
61
|
+
0.5 * (2**attempt) * (1 + (rand * 0.5))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def retry_after_header?(error)
|
|
65
|
+
error.is_a?(ResponseError) && !error.headers["Retry-After"].nil?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# ESI's Retry-After, or nil if it's missing or too long to wait for.
|
|
69
|
+
def retry_after(error)
|
|
70
|
+
seconds = Float(error.headers["Retry-After"], exception: false) if retry_after_header?(error)
|
|
71
|
+
seconds if seconds && seconds <= MAX_WAIT
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
|
|
5
|
+
module Esirb
|
|
6
|
+
# Adds Authorization to a response's Vary header when the request carried a token, so the
|
|
7
|
+
# ByVary cache strategy keys authenticated responses by token. ESI's Vary doesn't name it,
|
|
8
|
+
# which would let a cache store shared between clients serve one character's data to another.
|
|
9
|
+
class VaryByAuthorization < Faraday::Middleware
|
|
10
|
+
def on_complete(env)
|
|
11
|
+
return unless env.request_headers["Authorization"]
|
|
12
|
+
|
|
13
|
+
names = env.response_headers["Vary"].to_s.split(/[\s,]+/).reject(&:empty?)
|
|
14
|
+
return if names.include?("*") || names.any? { |name| name.casecmp?("Authorization") }
|
|
15
|
+
|
|
16
|
+
env.response_headers["Vary"] = [*names, "Authorization"].join(", ")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/esirb/version.rb
CHANGED
data/lib/esirb.rb
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "esirb/version"
|
|
4
|
+
require_relative "esirb/rate_limit"
|
|
5
|
+
require_relative "esirb/errors"
|
|
6
|
+
require_relative "esirb/configuration"
|
|
7
|
+
require_relative "esirb/compatibility_date"
|
|
8
|
+
require_relative "esirb/raise_error"
|
|
9
|
+
require_relative "esirb/retry"
|
|
10
|
+
require_relative "esirb/error_limit_guard"
|
|
11
|
+
require_relative "esirb/vary_by_authorization"
|
|
4
12
|
require_relative "esirb/client"
|
|
13
|
+
require_relative "esirb/pagination"
|
|
5
14
|
require_relative "esirb/path"
|
|
6
|
-
|
|
7
|
-
module Esirb
|
|
8
|
-
class Error < StandardError; end
|
|
9
|
-
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "esirb"
|
|
5
|
+
|
|
6
|
+
module Esirb
|
|
7
|
+
module Generators
|
|
8
|
+
# rails generate esirb:install
|
|
9
|
+
#
|
|
10
|
+
# Writes config/initializers/esirb.rb with esirb's settings and a note on each. Rails finds this
|
|
11
|
+
# by its path, so esirb has no Rails dependency, and nothing here loads outside a Rails generator.
|
|
12
|
+
class InstallGenerator < Rails::Generators::Base
|
|
13
|
+
source_root File.expand_path("templates", __dir__)
|
|
14
|
+
desc "Creates config/initializers/esirb.rb with esirb's settings for ESI."
|
|
15
|
+
|
|
16
|
+
def create_initializer
|
|
17
|
+
template "esirb.rb.tt", "config/initializers/esirb.rb"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# For the User-Agent placeholder, e.g. "my-app" for a MyApp application.
|
|
23
|
+
def app_name
|
|
24
|
+
application = Rails.application if Rails.respond_to?(:application)
|
|
25
|
+
return "my-app" unless application
|
|
26
|
+
|
|
27
|
+
application.class.module_parent_name.underscore.dasherize
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def defaults
|
|
31
|
+
Configuration::DEFAULTS
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Settings for esirb, the client for EVE Online's ESI API. Options passed to Esirb::Client.new
|
|
4
|
+
# override these for that client. See https://github.com/sicks/esirb#configuration
|
|
5
|
+
Esirb.configure do |config|
|
|
6
|
+
# CCP asks every app using ESI to identify itself, ideally with a contact email. esirb adds its
|
|
7
|
+
# own name after this.
|
|
8
|
+
config.user_agent = "<%= app_name %>/1.0 (you@example.com)"
|
|
9
|
+
|
|
10
|
+
# The ESI behavior to use. A newer date can change response shapes, so check your code against
|
|
11
|
+
# it before moving it. Dates are listed at https://esi.evetech.net/meta/compatibility-dates
|
|
12
|
+
config.compatibility_date = "<%= defaults[:compatibility_date] %>"
|
|
13
|
+
|
|
14
|
+
# Where to cache ESI responses, shared by every client. Without a store nothing is cached, and
|
|
15
|
+
# esirb can't pause requests when ESI's error limit is nearly used up. Keep entries for longer
|
|
16
|
+
# than a day, so static data like universe types can be revalidated cheaply, e.g.
|
|
17
|
+
# ActiveSupport::Cache.lookup_store(:solid_cache_store, namespace: "esirb", expires_in: 2.days)
|
|
18
|
+
config.cache_store = Rails.cache
|
|
19
|
+
|
|
20
|
+
# Seconds to wait for a response, and to open the connection.
|
|
21
|
+
# config.timeout = <%= defaults[:timeout] %>
|
|
22
|
+
# config.open_timeout = <%= defaults[:open_timeout] %>
|
|
23
|
+
|
|
24
|
+
# Times to retry a 502, 503 or 504, a timeout, a connection failure or a short 429, on GET, HEAD,
|
|
25
|
+
# PUT and DELETE. 0 turns retries off.
|
|
26
|
+
# config.retries = <%= defaults[:retries] %>
|
|
27
|
+
|
|
28
|
+
# ESI's tenant and response language.
|
|
29
|
+
# config.tenant = "<%= defaults[:tenant] %>"
|
|
30
|
+
# config.language = "<%= defaults[:language] %>"
|
|
31
|
+
|
|
32
|
+
# Add Faraday middleware, change the adapter or set other connection options, e.g. logging in
|
|
33
|
+
# development. headers: false keeps access tokens out of the log.
|
|
34
|
+
# config.faraday = ->(f) { f.response :logger, Rails.logger, headers: false } if Rails.env.development?
|
|
35
|
+
end
|
metadata
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: esirb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- sicks
|
|
8
|
-
bindir:
|
|
8
|
+
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
@@ -37,32 +37,42 @@ dependencies:
|
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '2.7'
|
|
40
|
+
description: A light client for EVE Online's ESI API. Build any endpoint's path from
|
|
41
|
+
method calls, with caching, retries, rate and error limit handling, and pagination
|
|
42
|
+
helpers built in.
|
|
40
43
|
email:
|
|
41
44
|
- esirb@sicks.dev
|
|
42
45
|
executables: []
|
|
43
46
|
extensions: []
|
|
44
47
|
extra_rdoc_files: []
|
|
45
48
|
files:
|
|
46
|
-
- ".rspec"
|
|
47
|
-
- ".rubocop.yml"
|
|
48
|
-
- ".ruby-version"
|
|
49
49
|
- CHANGELOG.md
|
|
50
50
|
- LICENSE.txt
|
|
51
51
|
- README.md
|
|
52
|
-
- Rakefile
|
|
53
52
|
- lib/esirb.rb
|
|
54
53
|
- lib/esirb/client.rb
|
|
54
|
+
- lib/esirb/compatibility_date.rb
|
|
55
|
+
- lib/esirb/configuration.rb
|
|
56
|
+
- lib/esirb/error_limit_guard.rb
|
|
57
|
+
- lib/esirb/errors.rb
|
|
58
|
+
- lib/esirb/pagination.rb
|
|
55
59
|
- lib/esirb/path.rb
|
|
60
|
+
- lib/esirb/raise_error.rb
|
|
61
|
+
- lib/esirb/rate_limit.rb
|
|
62
|
+
- lib/esirb/retry.rb
|
|
63
|
+
- lib/esirb/vary_by_authorization.rb
|
|
56
64
|
- lib/esirb/version.rb
|
|
57
|
-
-
|
|
65
|
+
- lib/generators/esirb/install/install_generator.rb
|
|
66
|
+
- lib/generators/esirb/install/templates/esirb.rb.tt
|
|
58
67
|
homepage: https://github.com/sicks/esirb
|
|
59
68
|
licenses:
|
|
60
69
|
- MIT
|
|
61
70
|
metadata:
|
|
62
71
|
allowed_push_host: https://rubygems.org
|
|
63
|
-
homepage_uri: https://github.com/sicks/esirb
|
|
64
72
|
source_code_uri: https://github.com/sicks/esirb
|
|
65
73
|
changelog_uri: https://github.com/sicks/esirb/blob/main/CHANGELOG.md
|
|
74
|
+
bug_tracker_uri: https://github.com/sicks/esirb/issues
|
|
75
|
+
rubygems_mfa_required: 'true'
|
|
66
76
|
rdoc_options: []
|
|
67
77
|
require_paths:
|
|
68
78
|
- lib
|
|
@@ -70,7 +80,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
70
80
|
requirements:
|
|
71
81
|
- - ">="
|
|
72
82
|
- !ruby/object:Gem::Version
|
|
73
|
-
version: 3.
|
|
83
|
+
version: 3.3.0
|
|
74
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
85
|
requirements:
|
|
76
86
|
- - ">="
|
|
@@ -79,5 +89,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
79
89
|
requirements: []
|
|
80
90
|
rubygems_version: 4.0.15
|
|
81
91
|
specification_version: 4
|
|
82
|
-
summary: Ruby
|
|
92
|
+
summary: Ruby client for EVE Online's ESI API
|
|
83
93
|
test_files: []
|
data/.rspec
DELETED
data/.rubocop.yml
DELETED
data/.ruby-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
ruby-3.4.7
|
data/Rakefile
DELETED
data/sig/esirb.rbs
DELETED