prop 0.7.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|