module WithRateLimit

Constants

VERSION

Public Instance Methods

with_rate_limit(interval, limit, options = {}) { || ... } click to toggle source
# File lib/with_rate_limit.rb, line 12
def with_rate_limit(interval, limit, options = {}, &block)
  options = default_options(interval, limit).merge(options.transform_keys &:to_sym)
  validate_options(options)
  
  strategy = options[:strategy]
  cache_key = options[:cache_key]
  cache = options[:cache]
  cache_data = cache.get(cache_key).transform_keys(&:to_sym)
  
  last_interval_started_at = cache_data[:last_interval_started_at] || current_timestamp
  operations_count = cache_data[:operations_count].to_i
  timestamp = current_timestamp
  count_reset_delta = timestamp - last_interval_started_at
  interval = interval * 1000

  if (count_reset_delta < interval) && operations_count >= limit
    strategy.execute((interval - count_reset_delta) / 1000.0)
    return with_rate_limit(interval / 1000, limit, options, &block)
  elsif count_reset_delta > interval
    cache.set(cache_key, {last_interval_started_at: timestamp, operations_count: 1})
  else
    cache.set(cache_key, {last_interval_started_at: last_interval_started_at, operations_count: operations_count + 1})
  end

  begin
    yield
  rescue StandardError => e
    raise e
  end
end

Private Instance Methods

current_timestamp() click to toggle source
# File lib/with_rate_limit.rb, line 45
def current_timestamp
  DateTime.now.strftime('%Q').to_i
end
default_options(interval, limit) click to toggle source
# File lib/with_rate_limit.rb, line 49
def default_options(interval, limit)
  {
    cache_key: "#{limit}-operations-per-#{interval}",
    strategy: WithRateLimit::Strategy::Sleep,
    cache: WithRateLimit::Cache::Memory
  }
end
redis_client() click to toggle source
# File lib/with_rate_limit.rb, line 57
def redis_client
  @redis_client ||= Redis.new(url: ENV['REDIS_HOST'])
end
validate_options(options) click to toggle source
# File lib/with_rate_limit.rb, line 61
def validate_options(options)
  raise "Invalid 'cache' option value.  Must be a subclass of WithRateLimit::Cache" unless options[:cache] < WithRateLimit::Cache
  raise "Invalid 'strategy' option value.  Must be a subclass of WithRateLimit::Strategy" unless options[:strategy] < WithRateLimit::Strategy
end