# frozen_string_literal: true class RateLimiter::InMemory < RateLimiter attr_reader :list def initialize super @list = {} end def get_global(name, ip) list[global_key(name, ip)].try(:[], :count) end def get_route(name, domain, path, ip) list[route_key(name, domain, path, ip)].try(:[], :count) end def get_global_ttl(name, ip) list[global_key(name, ip)].try(:[], :expiry) end def get_route_ttl(name, domain, path, ip) list[route_key(name, domain, path, ip)].try(:[], :expiry) end def consume_global(name, window, limit, ip) consume(global_key(name, ip), window, limit) end def consume_route(name, domain, path, window, limit, ip) consume(route_key(name, domain, path, ip), window, limit) end def fix_infinite_expiry(_key, val, expiry) # noop end private def consume(key, window, limit) (list[key] || { count: 0, expiry: nil }) => { count:, expiry: } if expiry && expiry < Time.now.to_i list.delete(key) count = expiry = nil end return count + 1 if count && count < limit count = (count || 0) + 1 expiry ||= Time.now.to_i + window list[key] = { count: count, expiry: expiry } count end end