mirror of
https://github.com/we-promise/sure.git
synced 2026-04-17 11:04:14 +00:00
* Initial implementation * Tiingo fixes * Adds 2 providers, remove 2 * Add extra checks * FIX a big hotwire race condition // Fix hotwire_combobox race condition: when typing quickly, a slow response for // an early query (e.g. "A") can overwrite the correct results for the final query // (e.g. "AAPL"). We abort the previous in-flight request whenever a new one fires, // so stale Turbo Stream responses never reach the DOM. * pipelock * Update price_test.rb * Reviews * i8n * fixes * fixes * Update tiingo.rb * fixes * Improvements * Big revamp * optimisations * Update 20260408151837_add_offline_reason_to_securities.rb * Add missing tests, fixes * small rank tests * FIX tests * Update show.html.erb * Update resolver.rb * Update usd_converter.rb * Update holdings_controller.rb * Update holdings_controller.rb * Update holdings_controller.rb * Update holdings_controller.rb * Update holdings_controller.rb * Update _yahoo_finance_settings.html.erb
60 lines
2.5 KiB
Ruby
60 lines
2.5 KiB
Ruby
# Shared concern for providers that need interval-based request throttling
|
|
# and a standard error transformation pattern.
|
|
#
|
|
# Providers that include this concern get:
|
|
# - `throttle_request`: sleeps to enforce MIN_REQUEST_INTERVAL between calls
|
|
# - `min_request_interval`: reads from ENV with fallback to the class constant
|
|
# - `default_error_transformer`: maps Faraday/rate-limit errors to provider-scoped types
|
|
#
|
|
# The including class MUST define:
|
|
# - `MIN_REQUEST_INTERVAL` (Float) — default seconds between requests
|
|
# - `Error` (Class) — provider-scoped error class
|
|
# - `RateLimitError` (Class) — provider-scoped rate-limit error class
|
|
#
|
|
# And MAY define a `PROVIDER_ENV_PREFIX` constant (e.g. "ALPHA_VANTAGE") used
|
|
# to derive the ENV key for the min request interval override. When omitted
|
|
# the prefix is derived from the class name (Provider::AlphaVantage → "ALPHA_VANTAGE").
|
|
module Provider::RateLimitable
|
|
extend ActiveSupport::Concern
|
|
|
|
private
|
|
# Enforces a minimum interval between consecutive requests on this instance.
|
|
# Subclasses that need additional rate-limit layers (daily counters, hourly
|
|
# counters) should call `super` or invoke this via `throttle_interval` and
|
|
# add their own checks.
|
|
def throttle_request
|
|
@last_request_time ||= Time.at(0)
|
|
elapsed = Time.current - @last_request_time
|
|
sleep_time = min_request_interval - elapsed
|
|
sleep(sleep_time) if sleep_time > 0
|
|
@last_request_time = Time.current
|
|
end
|
|
|
|
def min_request_interval
|
|
ENV.fetch("#{provider_env_prefix}_MIN_REQUEST_INTERVAL", self.class::MIN_REQUEST_INTERVAL).to_f
|
|
end
|
|
|
|
def provider_env_prefix
|
|
self.class.const_defined?(:PROVIDER_ENV_PREFIX) ? self.class::PROVIDER_ENV_PREFIX : self.class.name.demodulize.underscore.upcase
|
|
end
|
|
|
|
# Standard error transformation: maps common Faraday errors to provider-scoped
|
|
# error classes. Providers with extra error types (e.g. AuthenticationError)
|
|
# should override and call `super` for the default cases.
|
|
def default_error_transformer(error)
|
|
case error
|
|
when self.class::RateLimitError
|
|
error
|
|
when Faraday::TooManyRequestsError
|
|
self.class::RateLimitError.new(
|
|
"#{self.class.name.demodulize} rate limit exceeded",
|
|
details: error.response&.dig(:body)
|
|
)
|
|
when Faraday::Error
|
|
self.class::Error.new(error.message, details: error.response&.dig(:body))
|
|
else
|
|
self.class::Error.new(error.message)
|
|
end
|
|
end
|
|
end
|