internetdata 2.8.4 → 2.8.6

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: b1201aae6e9b3b660aebec9dca3ac9ce92fc708b917f53247254fb2b1f4d2a7a
4
- data.tar.gz: 8d6e23b3ee8ee61b78ef947a4ac3c34940c511d916a66a5b1264cee7fd507e83
3
+ metadata.gz: 60cb6997f887f4ac598e856721bfc16b49d84059390ce32d1c1a13721cf3079a
4
+ data.tar.gz: 129161d2e8eede6fc3aa00ee2daf764f3177dab897b7af0a79d7188241430cce
5
5
  SHA512:
6
- metadata.gz: 3569b4ee80466547aef9c6608d724ee47113d428b0396e59a6db1dada5f3fea827c2af2ec1c5a73e0a156bc67b737518fa5bbf38cc2c3386e5eeb18e8d251123
7
- data.tar.gz: 7d3b5df4695bfe420527e1d3e9926d5f5e66343a34463cae7c2b38e480942e73e95940cd66ada729967b408fda8d12dadc904b85394a63f5d0e220cf16cfa27e
6
+ metadata.gz: 63b12de438f16533af87d36997d2a66a53b7e5b98330b6a4175c42c6ab481f27ed4bf742646674b51a5eae3567db6fcce25b267cf237bd019369577ebac9f476
7
+ data.tar.gz: c9c6f3cef56834177ea1f5806ebadb0c31aa7ce40817c7e19b3e579f309b64fb68ffaa474f64c503ce8a11c59792e0bb4acf6e678cd4d3fa5f23e044907957d9
data/README.md CHANGED
@@ -118,7 +118,7 @@ end
118
118
 
119
119
  `kind` is one of `:bad_request`, `:unauthorized`, `:forbidden`, `:rate_limited`, `:quota_exceeded`, `:server_error` or `:network`. `rc` is the API's own result code, such as `NOT_LICENSED` or `LICENSE_EXPIRED`, which is usually the specific thing you want to read.
120
120
 
121
- Note that `:rate_limited` and `:quota_exceeded` both arrive as HTTP 429 and are not the same thing. A rate limit is the API facing a burst, so retrying later works; a spent quota needs your allowance raised or the window to roll over. The library retries the first for you and never the second. It waits the `Retry-After` the API sent, or its own backoff from 250 ms when that's past about 24.8 days.
121
+ `:rate_limited` is a 429 that carries `Retry-After`, and the library retries it for you, waiting as long as it says, or its own backoff from 250 ms when that's past about 24.8 days. `:quota_exceeded` is a 429 without one, which it never retries. InternetData's database API has no quota, so you won't see `:quota_exceeded` from it.
122
122
 
123
123
  ### Sign in with OAuth (device flow)
124
124
 
@@ -21,16 +21,20 @@ module InternetData
21
21
 
22
22
  # @param api_key [String, nil] a key carrying the `db.download` scope. Omit
23
23
  # it and no credential is sent; every database published today answers 401.
24
- # @param retries [Integer] extra attempts for a transient failure. Each waits
25
- # the server's `Retry-After` when it sent one of at most about 24.8 days,
26
- # and otherwise a backoff of 250 ms that doubles per retry.
24
+ # The blanks around it are dropped, and blanks alone are no key.
25
+ # @param retries [Integer] extra attempts for a transient failure, from 0. Each
26
+ # waits the server's `Retry-After` when it sent one of at most about 24.8
27
+ # days, and otherwise a backoff of 250 ms that doubles per retry.
27
28
  # @param timeout [Numeric] seconds allowed for one API call, 0 for no bound. It
28
29
  # also bounds the CONNECT phase of a transfer, which is otherwise unlimited.
29
30
  # @raise [ArgumentError] for a `timeout` that is negative, not a finite number,
30
- # or past 2147483.647 seconds (2**31 - 1 ms), the longest curl holds.
31
+ # or past 2147483.647 seconds (2**31 - 1 ms), the longest curl holds; for a
32
+ # `retries` that is not an Integer from 0; and for an `api_key` holding a
33
+ # control character, without the key in the message.
31
34
  # @param transport [Transport, nil] override the HTTP layer, mostly for tests.
32
35
  def initialize(api_key: nil, base_url: DEFAULT_BASE_URL, retries: DEFAULT_RETRIES,
33
36
  timeout: DEFAULT_TIMEOUT, transport: nil)
37
+ retries = Retries.checked_count(retries)
34
38
  @transport = transport || Transport.new(
35
39
  Transport::Config.new(api_key: api_key, base_url: base_url, timeout: timeout),
36
40
  )
@@ -15,6 +15,15 @@ module InternetData
15
15
 
16
16
  module_function
17
17
 
18
+ # `retries`, once it is a count. Refused where it is set: unchecked, -1 and
19
+ # NaN read as none, 1.5 as two, Infinity retried a failing call for good, and
20
+ # a String, nil or true raised a raw ArgumentError out of every failing call.
21
+ def checked_count(retries)
22
+ return retries if retries.is_a?(Integer) && retries >= 0
23
+
24
+ raise ArgumentError, "retries must be an Integer from 0, not #{retries.inspect}"
25
+ end
26
+
18
27
  # `retry_if`, when given, is asked after each retryable failure, and a false
19
28
  # answer ends the attempts there.
20
29
  def with_retries(retries, retry_if: nil)
@@ -24,6 +24,19 @@ module InternetData
24
24
  "timeout must be a number of seconds from 0 (no bound) to #{LONGEST_TIMEOUT}, not #{timeout.inspect}"
25
25
  end
26
26
 
27
+ # `api_key` without the blanks around it, nil for blanks alone. A control
28
+ # character is refused where it is set, and the key is left out of the
29
+ # message: a CR or LF failed every call with Ethon's raw InvalidValue, whose
30
+ # message quoted the key, and a tab, NUL or DEL went out inside it (2.8.5).
31
+ def self.checked_key(api_key)
32
+ return nil if api_key.nil?
33
+
34
+ key = api_key.to_s.strip
35
+ raise ArgumentError, 'api_key must not contain a control character' if key.match?(/[[:cntrl:]]/)
36
+
37
+ key.empty? ? nil : key
38
+ end
39
+
27
40
  # The generated Configuration applies a security scheme whatever it holds, so
28
41
  # a keyless client would send `Authorization: Bearer ` - an empty credential,
29
42
  # which the API answers 401 to rather than treating as no credential at all.
@@ -35,7 +48,7 @@ module InternetData
35
48
  self.scheme = uri.scheme
36
49
  self.host = uri.port == uri.default_port ? uri.host : "#{uri.host}:#{uri.port}"
37
50
  self.base_path = uri.path
38
- self.access_token = api_key
51
+ self.access_token = Transport.checked_key(api_key)
39
52
  self.timeout = timeout.nil? ? nil : Transport.checked_timeout(timeout)
40
53
  end
41
54
 
@@ -216,12 +229,13 @@ module InternetData
216
229
  }.freeze
217
230
 
218
231
  # RFC 3339's full-date, the spec's `date`, and its date-time, the spec's
219
- # `date-time`, in the upper case the API sends. Anything else reached the
232
+ # `date-time`, its `T` and `Z` in either case, as RFC 3339 allows: through
233
+ # 2.8.4 a lowercase one failed the call. Anything else reached the
220
234
  # generated reader's Date.parse or Time.parse, which read "monday" as this
221
235
  # week's, "10:00" as today in the process's zone and 30 February as 2 March
222
236
  # (2.8.2, measured 2026-10-20).
223
237
  RFC3339_DATE = /\A(\d{4})-(\d{2})-(\d{2})\z/
224
- RFC3339_DATE_TIME = /\A(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-](\d{2}):(\d{2}))\z/
238
+ RFC3339_DATE_TIME = /\A(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:[Zz]|[+-](\d{2}):(\d{2}))\z/
225
239
 
226
240
  private
227
241
 
@@ -239,7 +253,9 @@ module InternetData
239
253
  when 'Integer' then value.is_a?(Integer)
240
254
  when 'Float' then value.is_a?(Numeric)
241
255
  when 'Boolean' then [true, false].include?(value)
242
- when 'Object' then true
256
+ # The spec's `type: object`, a sample row: through 2.8.5 a row of 1, "x"
257
+ # or [] came back as the answer.
258
+ when 'Object' then value.is_a?(Hash)
243
259
  when /\AArray<(?<inner>.+)>\z/
244
260
  inner = Regexp.last_match(:inner)
245
261
  value.is_a?(Array) && value.each_with_index.all? { |v, i| check_type!(v, inner, "#{where}[#{i}]") }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module InternetData
4
- VERSION = '2.8.4'
4
+ VERSION = '2.8.6'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: internetdata
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.4
4
+ version: 2.8.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mslm Dev