# frozen_string_literal: true module ErpIntegration class RateLimiter class MissingMethodError < StandardError; end # The `api_key_fragment` method should return a string that is used to identify # the rate limiter by the API key. # # The `within_limit` method should yield to the block if the rate limit is not exceeded. REQUIRED_METHODS = %i[api_key_fragment within_limit].freeze class << self # Validates that the rate limiter object responds to the required methods. # It requires the `api_key_fragment` and `within_limit` methods to be present. # # @raise [MissingMethodError] # @param rate_limiter [Object] def validate!(rate_limiter) REQUIRED_METHODS.each do |method| next if rate_limiter.respond_to?(method) raise MissingMethodError, "'#{rate_limiter.class}##{method}' method is required." end end # Finds the rate limiter by the API key. # If the API key is not found, it returns an unlimited rate limiter. # # @param api_key [String] # @return [Object] The rate limiter object. def find_by_api_key(api_key) rate_limiters[api_key] || unlimited end # Returns an unlimited rate limiter. # # @return [RateLimiter::Unlimited] The unlimited rate limiter object. def unlimited @unlimited ||= Unlimited.new end private # The `rate_limiters` hash stores the rate limiter objects found by the API key. # # @return [Hash] The rate limiters hash. def rate_limiters @rate_limiters ||= Hash.new do |h, api_key| h[api_key] = ErpIntegration.config.rate_limiters.find do |rate_limiter| api_key.end_with?(rate_limiter.api_key_fragment) end end end end # {Unlimited} is a rate limiter that allows all requests. # When the rate limiter wasn't found by the API key, it defaults to this class. # Or if the `rate_limiters` option is not set in the configuration. class Unlimited # Executes the block without any restrictions. def within_limit yield end end end end