prop 0.3.5 → 0.3.6

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.
Files changed (5) hide show
  1. data/VERSION +1 -1
  2. data/lib/prop.rb +11 -3
  3. data/prop.gemspec +2 -2
  4. data/test/test_prop.rb +29 -0
  5. metadata +4 -4
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.5
1
+ 0.3.6
data/lib/prop.rb CHANGED
@@ -13,7 +13,7 @@ class Prop
13
13
  def self.create(key, threshold, message)
14
14
  default = "#{key} threshold #{threshold} exceeded"
15
15
  error = new(message || default)
16
- error.retry_after = threshold - Time.now.to_i % threshold
16
+ error.retry_after = threshold - Time.now.to_i % threshold if threshold > 0
17
17
  error.root_message = default
18
18
 
19
19
  raise error
@@ -46,6 +46,10 @@ class Prop
46
46
  define_prop_class_method "reset_#{handle}" do |*args|
47
47
  reset(sanitized_prop_options([ handle ] + args, defaults))
48
48
  end
49
+
50
+ define_prop_class_method "count_#{handle}" do |*args|
51
+ count(sanitized_prop_options([ handle ] + args, defaults))
52
+ end
49
53
  end
50
54
 
51
55
  def throttle?(options)
@@ -56,6 +60,9 @@ class Prop
56
60
  counter = count(options)
57
61
 
58
62
  if counter >= options[:threshold]
63
+ if options[:progressive]
64
+ writer.call(sanitized_prop_key(options.merge(:window_modifier => 1)), counter)
65
+ end
59
66
  raise Prop::RateLimitExceededError.create(options[:key], options[:threshold], options[:message])
60
67
  else
61
68
  writer.call(sanitized_prop_key(options), counter + 1)
@@ -76,7 +83,8 @@ class Prop
76
83
 
77
84
  # Builds the expiring cache key
78
85
  def sanitized_prop_key(options)
79
- cache_key = "#{normalize_cache_key(options[:key])}/#{Time.now.to_i / options[:interval]}"
86
+ window = (Time.now.to_i / options[:interval]) + options[:window_modifier].to_i
87
+ cache_key = "#{normalize_cache_key(options[:key])}/#{ window }"
80
88
  "prop/#{Digest::MD5.hexdigest(cache_key)}"
81
89
  end
82
90
 
@@ -84,7 +92,7 @@ class Prop
84
92
  def sanitized_prop_options(args, defaults)
85
93
  options = args.last.is_a?(Hash) ? args.pop : {}
86
94
  return {
87
- :key => normalize_cache_key(args), :message => defaults[:message],
95
+ :key => normalize_cache_key(args), :message => defaults[:message], :progressive => defaults[:progressive],
88
96
  :threshold => defaults[:threshold].to_i, :interval => defaults[:interval].to_i
89
97
  }.merge(options)
90
98
  end
data/prop.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{prop}
8
- s.version = "0.3.5"
8
+ s.version = "0.3.6"
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"]
12
- s.date = %q{2010-07-22}
12
+ s.date = %q{2010-08-03}
13
13
  s.description = %q{A gem for implementing rate limiting}
14
14
  s.email = %q{morten@zendesk.com}
15
15
  s.extra_rdoc_files = [
data/test/test_prop.rb CHANGED
@@ -111,6 +111,35 @@ class TestProp < Test::Unit::TestCase
111
111
  end
112
112
  end
113
113
 
114
+ should "not reset counter when violations happen during the time window for a progressive cache" do
115
+ start = Time.parse("2006-05-04 03:02:01")
116
+ Time.stubs(:now).returns(start)
117
+
118
+ Prop.setup :not_progressive, :threshold => 5, :interval => 10
119
+
120
+ assert_raises(Prop::RateLimitExceededError) do
121
+ 6.times { Prop.throttle_not_progressive! }
122
+ end
123
+
124
+ Time.stubs(:now).returns(start + 5)
125
+ assert_raises(Prop::RateLimitExceededError) { Prop.throttle_not_progressive! }
126
+ Time.stubs(:now).returns(start + 15)
127
+ assert Prop.throttle_not_progressive!
128
+
129
+ Time.stubs(:now).returns(start)
130
+ Prop.setup :progressive, :threshold => 5, :interval => 10, :progressive => true
131
+
132
+ assert_raises(Prop::RateLimitExceededError) do
133
+ 6.times { Prop.throttle_progressive! }
134
+ end
135
+
136
+ Time.stubs(:now).returns(start + 5)
137
+ assert_raises(Prop::RateLimitExceededError) { Prop.throttle_progressive! }
138
+
139
+ Time.stubs(:now).returns(start + 11)
140
+ assert_raises(Prop::RateLimitExceededError) { Prop.throttle_progressive! }
141
+ end
142
+
114
143
  should "not increment the counter beyon the threshold" do
115
144
  10.times do |i|
116
145
  Prop.throttle!(:key => 'hello', :threshold => 5, :interval => 10) rescue nil
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: 25
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 5
10
- version: 0.3.5
9
+ - 6
10
+ version: 0.3.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Morten Primdahl
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-07-22 00:00:00 -07:00
18
+ date: 2010-08-03 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency