Skip to content

Commit 14d9cb5

Browse files
committed
add after_evaluated callback
1 parent ba539ca commit 14d9cb5

File tree

4 files changed

+55
-3
lines changed

4 files changed

+55
-3
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ Prop.before_throttle do |handle, key, threshold, interval|
3131
end
3232
```
3333

34+
## Setting an After Evaluated Callback
35+
36+
You can define an optional callback that is invoked when a rate limit is checked. The callback will be invoked regardless
37+
of the result of the evaluation.
38+
39+
```ruby
40+
Prop.after_evaluated do |handle, counter, options|
41+
Rails.logger.info "Prop #{handle} has just been check. current value: #{counter}"
42+
end
43+
````
44+
3445
## Defining thresholds
3546

3647
Example: Limit on accepted emails per hour from a given user, by defining a threshold and interval (in seconds):

lib/prop.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ module Prop
99
class << self
1010
extend Forwardable
1111
def_delegators :"Prop::Limiter", :read, :write, :cache, :cache=, :configure, :configurations, :disabled, :before_throttle
12-
def_delegators :"Prop::Limiter", :throttle, :throttle!, :throttled?, :count, :query, :reset
12+
def_delegators :"Prop::Limiter", :throttle, :throttle!, :throttled?, :count, :query, :reset, :after_evaluated
1313
end
1414
end

lib/prop/limiter.rb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module Prop
99
class Limiter
1010

1111
class << self
12-
attr_accessor :handles, :before_throttle_callback, :cache
12+
attr_accessor :handles, :before_throttle_callback, :cache, :after_evaluated_callback
1313

1414
def read(&blk)
1515
raise "Use .cache = "
@@ -39,6 +39,10 @@ def before_throttle(&blk)
3939
self.before_throttle_callback = blk
4040
end
4141

42+
def after_evaluated(&blk)
43+
self.after_evaluated_callback = blk
44+
end
45+
4246
# Public: Registers a handle for rate limiting
4347
#
4448
# handle - the name of the handle you wish to use in your code, e.g. :login_attempt
@@ -150,13 +154,18 @@ def _throttle(strategy, handle, key, cache_key, options)
150154
return [false, strategy.zero_counter] if disabled?
151155

152156
if leaky_bucket_strategy?(strategy)
153-
return Prop::LeakyBucketStrategy._throttle_leaky_bucket(handle, key, cache_key, options)
157+
is_over_limit, bucket_info_hash = Prop::LeakyBucketStrategy._throttle_leaky_bucket(handle, key, cache_key, options)
158+
bucket_counter = bucket_info_hash.fetch(:bucket)
159+
after_evaluated_callback.call(handle, bucket_counter, options.merge(bucket_info_hash)) if after_evaluated_callback
160+
return [is_over_limit, bucket_info_hash]
154161
end
155162

156163
counter = options.key?(:decrement) ?
157164
strategy.decrement(cache_key, options.fetch(:decrement), options) :
158165
strategy.increment(cache_key, options.fetch(:increment, 1), options)
159166

167+
after_evaluated_callback.call(handle, counter, options) if after_evaluated_callback
168+
160169
if strategy.compare_threshold?(counter, :>, options)
161170
before_throttle_callback &&
162171
before_throttle_callback.call(handle, key, options[:threshold], options[:interval])

test/test_limiter.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ def self.it_handles_concurrency(method)
6767
Prop.count(:something).must_equal -4
6868
end
6969

70+
describe "when there is a after_evaluated callback" do
71+
it "invokes the callback" do
72+
Prop.after_evaluated do |handle, counter, options|
73+
@handle = handle
74+
@counter = counter
75+
@options = options
76+
end
77+
78+
Prop.throttle(:something)
79+
80+
@handle.must_equal :something
81+
@counter.must_equal 1
82+
@options.must_equal({ threshold: 10, interval: 10, key: "", strategy: Prop::IntervalStrategy })
83+
end
84+
end
85+
7086
describe "and the threshold has been reached" do
7187
before { Prop::IntervalStrategy.stubs(:compare_threshold?).returns(true) }
7288

@@ -190,6 +206,22 @@ def self.it_handles_concurrency(method)
190206
)
191207
end
192208
end
209+
210+
describe "when there is a after_evaluated callback" do
211+
it "invokes the callback" do
212+
Prop.after_evaluated do |handle, counter, options|
213+
@handle = handle
214+
@counter = counter
215+
@options = options
216+
end
217+
218+
Prop.throttle(:something)
219+
220+
@handle.must_equal :something
221+
@counter.must_equal 1
222+
@options.keys.sort.must_equal [:bucket, :burst_rate, :interval, :key, :last_leak_time, :over_limit, :strategy, :threshold]
223+
end
224+
end
193225
end
194226

195227
describe "#throttle!" do

0 commit comments

Comments
 (0)