prop 0.7.2 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -1
- data/lib/prop.rb +1 -1
- data/lib/prop/limiter.rb +17 -6
- data/prop.gemspec +2 -1
- data/test/test_limiter.rb +83 -0
- data/test/test_prop.rb +1 -0
- metadata +5 -3
data/README.md
CHANGED
@@ -56,7 +56,7 @@ The throttle scope can also be an array of values, e.g.:
|
|
56
56
|
|
57
57
|
If the throttle! method gets called more than "threshold" times within "interval in seconds" for a given handle and key combination, Prop throws a Prop::RateLimited error which is a subclass of StandardError. This exception contains a "handle" reference and a "description" if specified during the configuration. The handle allows you to rescue Prop::RateLimited and differentiate action depending on the handle. For example, in Rails you can use this in e.g. ApplicationController:
|
58
58
|
|
59
|
-
rescue_from Prop::
|
59
|
+
rescue_from Prop::RateLimited do |e|
|
60
60
|
if e.handle == :authorization_attempt
|
61
61
|
render :status => :forbidden, :message => I18n.t(e.description)
|
62
62
|
elsif ...
|
data/lib/prop.rb
CHANGED
data/lib/prop/limiter.rb
CHANGED
@@ -45,19 +45,26 @@ module Prop
|
|
45
45
|
# handle - the registered handle associated with the action
|
46
46
|
# key - a custom request specific key, e.g. [ account.id, "download", request.remote_ip ]
|
47
47
|
# options - request specific overrides to the defaults configured for this handle
|
48
|
+
# blk - an optional block of code that this throttle is guarding
|
48
49
|
#
|
49
50
|
# Raises Prop::RateLimited if the number if the threshold for this handle has been reached
|
50
|
-
|
51
|
+
# Returns the value of the block if given a such, otherwise the current count of the throttle
|
52
|
+
def throttle!(handle, key = nil, options = {}, &blk)
|
51
53
|
options, cache_key = prepare(handle, key, options)
|
52
|
-
|
53
54
|
counter = reader.call(cache_key).to_i
|
54
55
|
|
55
|
-
|
56
|
+
unless disabled?
|
57
|
+
if at_threshold?(counter, options[:threshold])
|
58
|
+
raise Prop::RateLimited.new(options.merge(:cache_key => cache_key, :handle => handle))
|
59
|
+
else
|
60
|
+
counter = writer.call(cache_key, counter + [ 1, options[:increment].to_i ].max)
|
61
|
+
end
|
62
|
+
end
|
56
63
|
|
57
|
-
if
|
58
|
-
|
64
|
+
if block_given?
|
65
|
+
yield
|
59
66
|
else
|
60
|
-
|
67
|
+
counter
|
61
68
|
end
|
62
69
|
end
|
63
70
|
|
@@ -97,6 +104,10 @@ module Prop
|
|
97
104
|
|
98
105
|
private
|
99
106
|
|
107
|
+
def at_threshold?(mark, threshold)
|
108
|
+
mark >= threshold
|
109
|
+
end
|
110
|
+
|
100
111
|
def disabled?
|
101
112
|
!!@disabled
|
102
113
|
end
|
data/prop.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
## If your rubyforge_project name is different, then edit it and comment out
|
14
14
|
## the sub! line in the Rakefile
|
15
15
|
s.name = 'prop'
|
16
|
-
s.version = '0.7.
|
16
|
+
s.version = '0.7.3'
|
17
17
|
s.date = '2012-04-06'
|
18
18
|
s.rubyforge_project = 'prop'
|
19
19
|
|
@@ -74,6 +74,7 @@ Gem::Specification.new do |s|
|
|
74
74
|
prop.gemspec
|
75
75
|
test/helper.rb
|
76
76
|
test/test_key.rb
|
77
|
+
test/test_limiter.rb
|
77
78
|
test/test_middleware.rb
|
78
79
|
test/test_options.rb
|
79
80
|
test/test_prop.rb
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestLimiter < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context Prop::Limiter do
|
6
|
+
setup do
|
7
|
+
@store = {}
|
8
|
+
|
9
|
+
Prop::Limiter.read { |key| @store[key] }
|
10
|
+
Prop::Limiter.write { |key, value| @store[key] = value }
|
11
|
+
Prop::Limiter.configure(:something, :threshold => 10, :interval => 10)
|
12
|
+
|
13
|
+
@start = Time.now
|
14
|
+
Time.stubs(:now).returns(@start)
|
15
|
+
end
|
16
|
+
|
17
|
+
context "#throttle!" do
|
18
|
+
setup do
|
19
|
+
Prop.reset(:something)
|
20
|
+
end
|
21
|
+
|
22
|
+
context "when disabled" do
|
23
|
+
setup { Prop::Limiter.expects(:disabled?).returns(true) }
|
24
|
+
|
25
|
+
[ true, false ].each do |threshold_reached|
|
26
|
+
context "and threshold has #{"not " unless threshold_reached}been reached" do
|
27
|
+
setup { Prop::Limiter.stubs(:at_threshold?).returns(threshold_reached) }
|
28
|
+
|
29
|
+
context "given a block" do
|
30
|
+
should "execute that block" do
|
31
|
+
assert_equal "wibble", Prop.throttle!(:something) { "wibble" }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "not given a block" do
|
36
|
+
should "return the current throttle count" do
|
37
|
+
assert_equal Prop.count(:something), Prop.throttle!(:something)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when not disabled" do
|
45
|
+
setup { Prop::Limiter.expects(:disabled?).returns(false) }
|
46
|
+
|
47
|
+
context "and threshold has been reached" do
|
48
|
+
setup { Prop::Limiter.expects(:at_threshold?).returns(true) }
|
49
|
+
|
50
|
+
context "given a block" do
|
51
|
+
should "raise Prop::RateLimited" do
|
52
|
+
assert_raises(Prop::RateLimited) { Prop.throttle!(:something) { "wibble" }}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "not given a block" do
|
57
|
+
should "raise Prop::RateLimited" do
|
58
|
+
assert_raises(Prop::RateLimited) { Prop.throttle!(:something) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "and threshold has not been reached" do
|
64
|
+
setup do
|
65
|
+
Prop::Limiter.expects(:at_threshold?).returns(false)
|
66
|
+
end
|
67
|
+
|
68
|
+
context "given a block" do
|
69
|
+
should "execute that block" do
|
70
|
+
assert_equal "wibble", Prop.throttle!(:something) { "wibble" }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "not given a block" do
|
75
|
+
should "return the updated throttle count" do
|
76
|
+
assert_equal Prop.count(:something) + 1, Prop.throttle!(:something)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/test/test_prop.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 7
|
9
|
-
-
|
10
|
-
version: 0.7.
|
9
|
+
- 3
|
10
|
+
version: 0.7.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Morten Primdahl
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- prop.gemspec
|
97
97
|
- test/helper.rb
|
98
98
|
- test/test_key.rb
|
99
|
+
- test/test_limiter.rb
|
99
100
|
- test/test_middleware.rb
|
100
101
|
- test/test_options.rb
|
101
102
|
- test/test_prop.rb
|
@@ -135,6 +136,7 @@ specification_version: 2
|
|
135
136
|
summary: Gem for implementing rate limits.
|
136
137
|
test_files:
|
137
138
|
- test/test_key.rb
|
139
|
+
- test/test_limiter.rb
|
138
140
|
- test/test_middleware.rb
|
139
141
|
- test/test_options.rb
|
140
142
|
- test/test_prop.rb
|