prop 0.7.0 → 0.7.1
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 +22 -1
- data/lib/prop.rb +1 -1
- data/lib/prop/middleware.rb +18 -9
- data/prop.gemspec +1 -1
- data/test/test_middleware.rb +10 -0
- data/test/test_options.rb +5 -1
- metadata +3 -3
data/README.md
CHANGED
@@ -60,10 +60,31 @@ If the throttle! method gets called more than "threshold" times within "interval
|
|
60
60
|
if e.handle == :authorization_attempt
|
61
61
|
render :status => :forbidden, :message => I18n.t(e.description)
|
62
62
|
elsif ...
|
63
|
-
|
63
|
+
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
+
### Using the Middleware
|
68
|
+
|
69
|
+
Prop ships with a built in Rack middleware that you can use to do all the exception handling. When a `Prop::RateLimited` error is caught, it will build an HTTP [429 Too Many Requests](http://tools.ietf.org/html/draft-nottingham-http-new-status-02#section-4) response and set the following headers:
|
70
|
+
|
71
|
+
Retry-After: 32
|
72
|
+
Content-Type: text/plain
|
73
|
+
Content-Length: 72
|
74
|
+
|
75
|
+
Where `Retry-After` is the number of seconds the client has to wait before retrying this end point. The body of this response is whatever description Prop has configured for the throttle that got violated, or a default string if there's none configured.
|
76
|
+
|
77
|
+
If you wish to do manual error messaging in these cases, you can define an error handler in your Prop configuration. Here's how the default error handler looks, feel free to replace it with your own - you can set the error handler to anything that responds to `.call` and takes a `RateLimited` instance as argument:
|
78
|
+
|
79
|
+
error_handler = Proc.new do |error|
|
80
|
+
body = error.description || "This action has been rate limited"
|
81
|
+
headers = { "Content-Type" => "text/plain", "Content-Length" => body.size, "Retry-After" => error.retry_after }
|
82
|
+
|
83
|
+
[ 429, headers, [ body ]]
|
84
|
+
end
|
85
|
+
|
86
|
+
ActionController::Dispatcher.middleware.insert_before(ActionController::ParamsParser, :error_handler => error_handler)
|
87
|
+
|
67
88
|
## Disabling Prop
|
68
89
|
|
69
90
|
In case you need to perform e.g. a manual bulk operation:
|
data/lib/prop.rb
CHANGED
data/lib/prop/middleware.rb
CHANGED
@@ -1,23 +1,32 @@
|
|
1
1
|
module Prop
|
2
|
+
|
2
3
|
# Convenience middleware that conveys the message configured on a Prop handle as well
|
3
4
|
# as time left before the current window has passed in a Retry-After header.
|
4
5
|
class Middleware
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
# Default error handler
|
8
|
+
class DefaultErrorHandler
|
9
|
+
def self.call(error)
|
10
|
+
body = error.description || "This action has been rate limited"
|
11
|
+
headers = { "Content-Type" => "text/plain", "Content-Length" => body.size, "Retry-After" => error.retry_after }
|
12
|
+
|
13
|
+
[ 429, headers, [ body ]]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(app, options = {})
|
18
|
+
@app = app
|
19
|
+
@options = options
|
20
|
+
@handler = options[:error_handler] || DefaultErrorHandler
|
8
21
|
end
|
9
22
|
|
10
|
-
def call(env
|
23
|
+
def call(env)
|
11
24
|
begin
|
12
25
|
@app.call(env)
|
13
26
|
rescue Prop::RateLimited => e
|
14
|
-
|
15
|
-
|
16
|
-
headers = { "Content-Type" => "text/plain", "Content-Length" => body.size }
|
17
|
-
headers["Retry-After"] = e.retry_after if e.retry_after > 0
|
18
|
-
|
19
|
-
[ 429, headers, [ body ]]
|
27
|
+
@handler.call(e)
|
20
28
|
end
|
21
29
|
end
|
22
30
|
end
|
31
|
+
|
23
32
|
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.1'
|
17
17
|
s.date = '2012-04-06'
|
18
18
|
s.rubyforge_project = 'prop'
|
19
19
|
|
data/test/test_middleware.rb
CHANGED
@@ -32,6 +32,16 @@ class TestMiddleware < Test::Unit::TestCase
|
|
32
32
|
assert_equal 429, response[0]
|
33
33
|
assert_equal ["Boom!"], response[2]
|
34
34
|
end
|
35
|
+
|
36
|
+
context "with a custom error handler" do
|
37
|
+
setup do
|
38
|
+
@middleware = Prop::Middleware.new(@app, :error_handler => Proc.new { |error| "Oops" })
|
39
|
+
end
|
40
|
+
|
41
|
+
should "allow setting a custom error handler" do
|
42
|
+
assert_equal "Oops", @middleware.call(@env)
|
43
|
+
end
|
44
|
+
end
|
35
45
|
end
|
36
46
|
end
|
37
47
|
end
|
data/test/test_options.rb
CHANGED
@@ -35,7 +35,11 @@ class TestOptions < Test::Unit::TestCase
|
|
35
35
|
|
36
36
|
should "raise when not given a key" do
|
37
37
|
@args.delete(:key)
|
38
|
-
|
38
|
+
begin
|
39
|
+
Prop::Options.build(@args)
|
40
|
+
fail "Should puke when not given a valid key"
|
41
|
+
rescue
|
42
|
+
end
|
39
43
|
end
|
40
44
|
end
|
41
45
|
end
|
metadata
CHANGED