prop 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -34,7 +34,15 @@ In many cases you will want to tie a specific key to a defined throttle, for exa
34
34
 
35
35
  Prop.throttle_mails_per_hour!(mail.from)
36
36
 
37
- If this method gets called more than "threshold" times within "interval in seconds" Prop throws a Prop::RateLimitExceededError.
37
+ If this method gets called more than "threshold" times within "interval in seconds" Prop throws a Prop::RateLimitExceededError. You can change the message of this error, which is handy when you are using Prop in multiple locations and want to be able to differentiate further up the stack. For example:
38
+
39
+ Prop.setup(:logins, :threshold => 5, :interval => 5.minutes, :message => "Too many invalid login attempts.")
40
+
41
+ In Rails you can use this in e.g. ApplicationController:
42
+
43
+ rescue_from Prop::RateLimitExceededError do |exception|
44
+ render :status => 403, :message => exception.message
45
+ end
38
46
 
39
47
  You can chose to override the threshold for a given key:
40
48
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.3
1
+ 0.3.4
data/lib/prop.rb CHANGED
@@ -8,6 +8,14 @@ end
8
8
 
9
9
  class Prop
10
10
  class RateLimitExceededError < RuntimeError
11
+ attr_reader :root_message, :retry_after
12
+
13
+ def initialize(key, threshold, message)
14
+ @root_message = "#{key} threshold #{threshold} exceeded"
15
+ @retry_after = Time.now.to_i % threshold.to_i
16
+
17
+ super(message || @root_message)
18
+ end
11
19
  end
12
20
 
13
21
  class << self
@@ -46,7 +54,7 @@ class Prop
46
54
  counter = count(options)
47
55
 
48
56
  if counter >= options[:threshold]
49
- raise Prop::RateLimitExceededError.new("#{options[:key]} threshold #{options[:threshold]} exceeded")
57
+ raise Prop::RateLimitExceededError.new(options[:key], options[:threshold], options[:message])
50
58
  else
51
59
  writer.call(sanitized_prop_key(options), counter + 1)
52
60
  end
@@ -73,7 +81,10 @@ class Prop
73
81
  # Sanitizes the option set and sets defaults
74
82
  def sanitized_prop_options(args, defaults)
75
83
  options = args.last.is_a?(Hash) ? args.pop : {}
76
- { :key => normalize_cache_key(args), :threshold => defaults[:threshold].to_i, :interval => defaults[:interval].to_i }.merge(options)
84
+ return {
85
+ :key => normalize_cache_key(args), :message => defaults[:message],
86
+ :threshold => defaults[:threshold].to_i, :interval => defaults[:interval].to_i
87
+ }.merge(options)
77
88
  end
78
89
 
79
90
  # Simple key expansion only supports arrays and primitives
data/prop.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{prop}
8
- s.version = "0.3.3"
8
+ s.version = "0.3.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Morten Primdahl"]
data/test/test_prop.rb CHANGED
@@ -126,6 +126,15 @@ class TestProp < Test::Unit::TestCase
126
126
  assert_raises(Prop::RateLimitExceededError) do
127
127
  Prop.throttle!(:key => 'hello', :threshold => 5, :interval => 10)
128
128
  end
129
+
130
+ begin
131
+ Prop.throttle!(:key => 'hello', :threshold => 5, :interval => 10, :message => "Wibble")
132
+ fail
133
+ rescue Prop::RateLimitExceededError => e
134
+ assert_equal "Wibble", e.message
135
+ assert_equal "hello threshold 5 exceeded", e.root_message
136
+ assert e.retry_after
137
+ end
129
138
  end
130
139
  end
131
140
 
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: 21
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 3
10
- version: 0.3.3
9
+ - 4
10
+ version: 0.3.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Morten Primdahl