lib/prop/limiter.rb in prop-1.2.0 vs lib/prop/limiter.rb in prop-2.0.0
- old
+ new
@@ -6,33 +6,41 @@
module Prop
class Limiter
class << self
- attr_accessor :handles, :reader, :writer, :before_throttle_callback
+ attr_accessor :handles, :before_throttle_callback, :cache
def read(&blk)
- self.reader = blk
+ raise "Use .cache = "
end
def write(&blk)
- self.writer = blk
+ raise "Use .cache = "
end
+ def cache=(cache)
+ [:read, :write, :increment].each do |method|
+ next if cache.respond_to?(method)
+ raise ArgumentError, "Cache needs to respond to #{method}"
+ end
+ @cache = cache
+ end
+
def before_throttle(&blk)
self.before_throttle_callback = blk
end
# Public: Registers a handle for rate limiting
#
# handle - the name of the handle you wish to use in your code, e.g. :login_attempt
- # defaults - the settings for this handle, e.g. { :threshold => 5, :interval => 5.minutes }
+ # defaults - the settings for this handle, e.g. { threshold: 5, interval: 5.minutes }
#
# Raises Prop::RateLimited if the number if the threshold for this handle has been reached
def configure(handle, defaults)
- raise RuntimeError.new("Invalid threshold setting") unless defaults[:threshold].to_i > 0
- raise RuntimeError.new("Invalid interval setting") unless defaults[:interval].to_i > 0
+ raise ArgumentError.new("Invalid threshold setting") unless defaults[:threshold].to_i > 0
+ raise ArgumentError.new("Invalid interval setting") unless defaults[:interval].to_i > 0
self.handles ||= {}
self.handles[handle] = defaults
end
@@ -53,69 +61,65 @@
# options - request specific overrides to the defaults configured for this handle
# (optional) a block of code that this throttle is guarding
#
# Returns true if the threshold for this handle has been reached, else returns false
def throttle(handle, key = nil, options = {})
+ return false if disabled?
+
options, cache_key = prepare(handle, key, options)
- counter = @strategy.counter(cache_key, options)
+ counter = @strategy.increment(cache_key, options)
- unless disabled?
- if @strategy.at_threshold?(counter, options)
- unless before_throttle_callback.nil?
- before_throttle_callback.call(handle, key, options[:threshold], options[:interval])
- end
+ if @strategy.compare_threshold?(counter, :>, options)
+ before_throttle_callback &&
+ before_throttle_callback.call(handle, key, options[:threshold], options[:interval])
- true
- else
- @strategy.increment(cache_key, options, counter)
-
- yield if block_given?
-
- false
- end
+ true
+ else
+ yield if block_given?
+ false
end
end
# Public: Records a single action for the given handle/key combination.
#
# handle - the registered handle associated with the action
# key - a custom request specific key, e.g. [ account.id, "download", request.remote_ip ]
# options - request specific overrides to the defaults configured for this handle
# (optional) a block of code that this throttle is guarding
#
- # Raises Prop::RateLimited if the number if the threshold for this handle has been reached
+ # Raises Prop::RateLimited if the threshold for this handle has been reached
# Returns the value of the block if given a such, otherwise the current count of the throttle
def throttle!(handle, key = nil, options = {})
options, cache_key = prepare(handle, key, options)
if throttle(handle, key, options)
- raise Prop::RateLimited.new(options.merge(:cache_key => cache_key, :handle => handle))
+ raise Prop::RateLimited.new(options.merge(cache_key: cache_key, handle: handle))
end
block_given? ? yield : @strategy.counter(cache_key, options)
end
- # Public: Allows to query whether the given handle/key combination is currently throttled
+ # Public: Is the given handle/key combination currently throttled ?
#
# handle - the throttle identifier
# key - the associated key
#
# Returns true if a call to `throttle!` with same parameters would raise, otherwise false
def throttled?(handle, key = nil, options = {})
options, cache_key = prepare(handle, key, options)
counter = @strategy.counter(cache_key, options)
- @strategy.at_threshold?(counter, options)
+ @strategy.compare_threshold?(counter, :>=, options)
end
# Public: Resets a specific throttle
#
# handle - the throttle identifier
# key - the associated key
#
# Returns nothing
def reset(handle, key = nil, options = {})
- options, cache_key = prepare(handle, key, options)
+ _options, cache_key = prepare(handle, key, options)
@strategy.reset(cache_key)
end
# Public: Counts the number of times the given handle/key combination has been hit in the current window
#
@@ -139,17 +143,18 @@
def disabled?
!!@disabled
end
def prepare(handle, key, params)
- raise RuntimeError.new("No such handle configured: #{handle.inspect}") unless (handles || {}).key?(handle)
+ unless defaults = handles[handle]
+ raise KeyError.new("No such handle configured: #{handle.inspect}")
+ end
- defaults = handles[handle]
- options = Prop::Options.build(:key => key, :params => params, :defaults => defaults)
+ options = Prop::Options.build(key: key, params: params, defaults: defaults)
@strategy = options.fetch(:strategy)
- cache_key = @strategy.build(:key => key, :handle => handle, :interval => options[:interval])
+ cache_key = @strategy.build(key: key, handle: handle, interval: options[:interval])
[ options, cache_key ]
end
end
end