improved-rack-throttle 0.7.1 → 0.8.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dea077855e9d5f1e7aa8126af311c58f518e2abf
4
+ data.tar.gz: d1c0ce949e3a7152adfb1b9b6ae96896021d5e81
5
+ SHA512:
6
+ metadata.gz: 77a62c9ad00ee3aff04a413e5e186a261cd925c9b46e281dd167604fee5b3df2451daf4eb775f1c3b94a4b1e1fb16feddd35828763ed33666dc62df7c5bfc6cb
7
+ data.tar.gz: ab2f078f3380a13826fbb30f47292540ae59c8cf3269f1fa4ce051825962574d7d9e6021709058381dabb7035cd610b4ce05d2575e57f196292abb4aa00bde8f
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.2"
5
+ - "1.9.3"
6
+ - "2.0.0"
7
+ - "2.1.3"
8
+ # uncomment this line if your project needs to run something other than `rake`:
9
+ # script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -3,10 +3,11 @@ source "http://rubygems.org"
3
3
  gem "rack", ">= 1.0.0"
4
4
 
5
5
  group :development, :test do
6
- gem 'timecop', '~> 0.5.2'
7
- gem 'rack-test', '~> 0.6.2'
8
- gem 'rspec', '~> 2.11.0'
9
- gem 'yard' , '>= 0.5.5'
6
+ gem 'timecop', "<= 0.6.2.2"
7
+ gem 'rack-test'
8
+ gem 'rspec'
9
+ gem 'yard'
10
+ gem "simplecov", :require => false
10
11
  gem 'redcarpet'
11
12
  gem 'rake'
12
13
  gem 'jeweler'
@@ -1,31 +1,40 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- diff-lcs (1.1.3)
4
+ diff-lcs (1.2.5)
5
5
  git (1.2.5)
6
6
  jeweler (1.8.4)
7
7
  bundler (~> 1.0)
8
8
  git (>= 1.2.5)
9
9
  rake
10
10
  rdoc
11
- json (1.7.6)
12
- rack (1.4.4)
11
+ json (1.8.0)
12
+ multi_json (1.7.6)
13
+ rack (1.5.2)
13
14
  rack-test (0.6.2)
14
15
  rack (>= 1.0)
15
- rake (10.0.3)
16
- rdoc (3.12)
16
+ rake (10.0.4)
17
+ rdoc (4.0.1)
17
18
  json (~> 1.4)
18
- redcarpet (2.2.2)
19
- rspec (2.11.0)
20
- rspec-core (~> 2.11.0)
21
- rspec-expectations (~> 2.11.0)
22
- rspec-mocks (~> 2.11.0)
23
- rspec-core (2.11.1)
24
- rspec-expectations (2.11.3)
25
- diff-lcs (~> 1.1.3)
26
- rspec-mocks (2.11.3)
27
- timecop (0.5.9.1)
28
- yard (0.8.3)
19
+ redcarpet (2.3.0)
20
+ rspec (3.0.0)
21
+ rspec-core (~> 3.0.0)
22
+ rspec-expectations (~> 3.0.0)
23
+ rspec-mocks (~> 3.0.0)
24
+ rspec-core (3.0.1)
25
+ rspec-support (~> 3.0.0)
26
+ rspec-expectations (3.0.1)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.0.0)
29
+ rspec-mocks (3.0.1)
30
+ rspec-support (~> 3.0.0)
31
+ rspec-support (3.0.0)
32
+ simplecov (0.7.1)
33
+ multi_json (~> 1.0)
34
+ simplecov-html (~> 0.7.1)
35
+ simplecov-html (0.7.1)
36
+ timecop (0.6.1)
37
+ yard (0.8.6.1)
29
38
 
30
39
  PLATFORMS
31
40
  ruby
@@ -33,9 +42,10 @@ PLATFORMS
33
42
  DEPENDENCIES
34
43
  jeweler
35
44
  rack (>= 1.0.0)
36
- rack-test (~> 0.6.2)
45
+ rack-test
37
46
  rake
38
47
  redcarpet
39
- rspec (~> 2.11.0)
40
- timecop (~> 0.5.2)
41
- yard (>= 0.5.5)
48
+ rspec
49
+ simplecov
50
+ timecop (<= 0.6.2.2)
51
+ yard
data/README.md CHANGED
@@ -3,6 +3,7 @@ HTTP Request Rate Limiter for Rack Applications
3
3
 
4
4
  [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/bensomers/improved-rack-throttle)
5
5
  [![Dependency Status](https://gemnasium.com/bensomers/improved-rack-throttle.png)](https://gemnasium.com/bensomers/improved-rack-throttle)
6
+ [![Build Status](https://travis-ci.org/bensomers/improved-rack-throttle.png?branch=master)](https://travis-ci.org/bensomers/improved-rack-throttle)
6
7
 
7
8
  This is a [Rack][] middleware that provides logic for rate-limiting incoming
8
9
  HTTP requests to Rack applications. You can use `Rack::Throttle` with any
@@ -37,7 +38,7 @@ Examples
37
38
 
38
39
  # config/application.rb
39
40
  require 'rack/throttle'
40
-
41
+
41
42
  class Application < Rails::Application
42
43
  config.middleware.use Rack::Throttle::Interval
43
44
  end
@@ -47,18 +48,18 @@ Examples
47
48
  #!/usr/bin/env ruby -rubygems
48
49
  require 'sinatra'
49
50
  require 'rack/throttle'
50
-
51
+
51
52
  use Rack::Throttle::Interval
52
-
53
+
53
54
  get('/hello') { "Hello, world!\n" }
54
55
 
55
56
  ### Adding throttling to a Rackup application
56
57
 
57
58
  #!/usr/bin/env rackup
58
59
  require 'rack/throttle'
59
-
60
+
60
61
  use Rack::Throttle::Interval
61
-
62
+
62
63
  run lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, world!\n"] }
63
64
 
64
65
  ### Enforcing a minimum 3-second interval between requests
@@ -86,19 +87,19 @@ Examples
86
87
  ### Storing the rate-limiting counters in a GDBM database
87
88
 
88
89
  require 'gdbm'
89
-
90
+
90
91
  use Rack::Throttle::Interval, :cache => GDBM.new('tmp/throttle.db')
91
92
 
92
93
  ### Storing the rate-limiting counters on a Memcached server
93
94
 
94
95
  require 'memcached'
95
-
96
+
96
97
  use Rack::Throttle::Interval, :cache => Memcached.new, :key_prefix => :throttle
97
98
 
98
99
  ### Storing the rate-limiting counters on a Redis server
99
100
 
100
101
  require 'redis'
101
-
102
+
102
103
  use Rack::Throttle::Interval, :cache => Redis.new, :key_prefix => :throttle
103
104
 
104
105
  ### Scoping the rate-limit to a specific path and method
@@ -188,8 +189,6 @@ status code by passing in a `:code => 503` option when constructing a
188
189
 
189
190
  Documentation
190
191
  -------------
191
- UNDER DEVELOPMENT
192
-
193
192
  <http://rubydoc.info/gems/improved-rack-throttle>
194
193
 
195
194
  * {Rack::Throttle}
@@ -229,7 +228,7 @@ as follows:
229
228
 
230
229
  Authors
231
230
  -------
232
- * [Ben Somers](mailto:somers.ben@gmail.com) - <http://www.somanyrobots.com>
231
+ * [Ben Somers](mailto:somers.ben@gmail.com) - <http://www.github.com/bensomers>
233
232
  * [Arto Bendiken](mailto:arto.bendiken@gmail.com) - <http://ar.to/>
234
233
  * [Brendon Murphy](mailto:disposable.20.xternal@spamourmet.com>) - <http://www.techfreak.net/>
235
234
 
data/Rakefile CHANGED
@@ -28,8 +28,6 @@ Jeweler::Tasks.new do |gem|
28
28
  end
29
29
  Jeweler::RubygemsDotOrgTasks.new
30
30
 
31
- task :default => :spec
32
-
33
31
  require 'rdoc/task'
34
32
  Rake::RDocTask.new do |rdoc|
35
33
  version = File.exist?('VERSION') ? File.read('VERSION') : ""
@@ -39,3 +37,7 @@ Rake::RDocTask.new do |rdoc|
39
37
  rdoc.rdoc_files.include('README*')
40
38
  rdoc.rdoc_files.include('lib/**/*.rb')
41
39
  end
40
+
41
+ require 'rspec/core/rake_task'
42
+ RSpec::Core::RakeTask.new(:spec)
43
+ task :default => :spec
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "improved-rack-throttle"
8
- s.version = "0.7.1"
8
+ s.version = "0.8.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Ben Somers", "Arto Bendiken", "Brendon Murphy"]
12
- s.date = "2013-05-09"
12
+ s.date = "2014-09-22"
13
13
  s.description = "Rack middleware for rate-limiting incoming HTTP requests."
14
14
  s.email = "somers.ben@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
17
17
  ]
18
18
  s.files = [
19
19
  ".document",
20
+ ".travis.yml",
20
21
  "Gemfile",
21
22
  "Gemfile.lock",
22
23
  "README.md",
@@ -55,37 +56,40 @@ Gem::Specification.new do |s|
55
56
  s.homepage = "http://github.com/bensomers/improved-rack-throttle"
56
57
  s.licenses = ["Public Domain"]
57
58
  s.require_paths = ["lib"]
58
- s.rubygems_version = "1.8.25"
59
+ s.rubygems_version = "2.0.3"
59
60
  s.summary = "HTTP request rate limiter for Rack applications."
60
61
 
61
62
  if s.respond_to? :specification_version then
62
- s.specification_version = 3
63
+ s.specification_version = 4
63
64
 
64
65
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
65
66
  s.add_runtime_dependency(%q<rack>, [">= 1.0.0"])
66
- s.add_development_dependency(%q<timecop>, ["~> 0.5.2"])
67
- s.add_development_dependency(%q<rack-test>, ["~> 0.6.2"])
68
- s.add_development_dependency(%q<rspec>, ["~> 2.11.0"])
69
- s.add_development_dependency(%q<yard>, [">= 0.5.5"])
67
+ s.add_development_dependency(%q<timecop>, ["<= 0.6.2.2"])
68
+ s.add_development_dependency(%q<rack-test>, [">= 0"])
69
+ s.add_development_dependency(%q<rspec>, [">= 0"])
70
+ s.add_development_dependency(%q<yard>, [">= 0"])
71
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
70
72
  s.add_development_dependency(%q<redcarpet>, [">= 0"])
71
73
  s.add_development_dependency(%q<rake>, [">= 0"])
72
74
  s.add_development_dependency(%q<jeweler>, [">= 0"])
73
75
  else
74
76
  s.add_dependency(%q<rack>, [">= 1.0.0"])
75
- s.add_dependency(%q<timecop>, ["~> 0.5.2"])
76
- s.add_dependency(%q<rack-test>, ["~> 0.6.2"])
77
- s.add_dependency(%q<rspec>, ["~> 2.11.0"])
78
- s.add_dependency(%q<yard>, [">= 0.5.5"])
77
+ s.add_dependency(%q<timecop>, ["<= 0.6.2.2"])
78
+ s.add_dependency(%q<rack-test>, [">= 0"])
79
+ s.add_dependency(%q<rspec>, [">= 0"])
80
+ s.add_dependency(%q<yard>, [">= 0"])
81
+ s.add_dependency(%q<simplecov>, [">= 0"])
79
82
  s.add_dependency(%q<redcarpet>, [">= 0"])
80
83
  s.add_dependency(%q<rake>, [">= 0"])
81
84
  s.add_dependency(%q<jeweler>, [">= 0"])
82
85
  end
83
86
  else
84
87
  s.add_dependency(%q<rack>, [">= 1.0.0"])
85
- s.add_dependency(%q<timecop>, ["~> 0.5.2"])
86
- s.add_dependency(%q<rack-test>, ["~> 0.6.2"])
87
- s.add_dependency(%q<rspec>, ["~> 2.11.0"])
88
- s.add_dependency(%q<yard>, [">= 0.5.5"])
88
+ s.add_dependency(%q<timecop>, ["<= 0.6.2.2"])
89
+ s.add_dependency(%q<rack-test>, [">= 0"])
90
+ s.add_dependency(%q<rspec>, [">= 0"])
91
+ s.add_dependency(%q<yard>, [">= 0"])
92
+ s.add_dependency(%q<simplecov>, [">= 0"])
89
93
  s.add_dependency(%q<redcarpet>, [">= 0"])
90
94
  s.add_dependency(%q<rake>, [">= 0"])
91
95
  s.add_dependency(%q<jeweler>, [">= 0"])
@@ -23,7 +23,6 @@ module Rack; module Throttle
23
23
  def initialize(app, options = {})
24
24
  rules = options.delete(:rules) || {}
25
25
  @app, @options, @matchers = app, options, []
26
- @matchers += Array(rules[:ip]).map { |rule| IpMatcher.new(rule) } if rules[:ip]
27
26
  @matchers += Array(rules[:url]).map { |rule| UrlMatcher.new(rule) } if rules[:url]
28
27
  @matchers += Array(rules[:user_agent]).map { |rule| UserAgentMatcher.new(rule) } if rules[:user_agent]
29
28
  @matchers += Array(rules[:method]).map { |rule| MethodMatcher.new(rule) } if rules[:method]
@@ -197,7 +196,8 @@ module Rack; module Throttle
197
196
  #
198
197
  # @return [Array(Integer, Hash, #each)]
199
198
  def rate_limit_exceeded
200
- headers = respond_to?(:retry_after) ? {'Retry-After' => retry_after.to_f.ceil.to_s} : {}
199
+ return_retry_after = options[:return_retry_after] || false
200
+ headers = (respond_to?(:retry_after) && return_retry_after) ? {'Retry-After' => retry_after.to_f.ceil.to_s} : {}
201
201
  http_error(options[:code] || 403, options[:message] || 'Rate Limit Exceeded', headers)
202
202
  end
203
203
 
@@ -2,8 +2,8 @@ module Rack; module Throttle
2
2
  ##
3
3
  # This rate limiter strategy throttles the application with
4
4
  # a sliding window (implemented as a leaky bucket). It operates
5
- # on second-level resolution. It takes :burst and :average
6
- # options, which correspond to the maximum size of a traffic
5
+ # on second-level resolution. It takes :burst and :average
6
+ # options, which correspond to the maximum size of a traffic
7
7
  # burst, and the maximum allowed average traffic level.
8
8
  class SlidingWindow < Limiter
9
9
  ##
@@ -18,7 +18,7 @@ module Rack; module Throttle
18
18
  end
19
19
 
20
20
  ##
21
- # Returns `true` if the request conforms to the
21
+ # Returns `true` if the request conforms to the
22
22
  # specified :average and :burst rules
23
23
  #
24
24
  # @param [Rack::Request] request
@@ -29,7 +29,7 @@ module Rack; module Throttle
29
29
  bucket = cache_get(key) rescue nil
30
30
  bucket ||= LeakyBucket.new(options[:burst], options[:average])
31
31
  bucket.maximum, bucket.outflow = options[:burst], options[:average]
32
- bucket.leak!
32
+ bucket.leak!
33
33
  bucket.increment!
34
34
  allowed = !bucket.full?
35
35
  begin
@@ -44,6 +44,15 @@ module Rack; module Throttle
44
44
  end
45
45
  end
46
46
 
47
+ ##
48
+ # Returns the number of seconds before the client is allowed to retry an
49
+ # HTTP request.
50
+ #
51
+ # @return [Float]
52
+ def retry_after
53
+ @retry_after ||= (1.0 / options[:average].to_f)
54
+ end
55
+
47
56
  ###
48
57
  # LeakyBucket is an internal class used to implement the
49
58
  # SlidingWindow limiter strategy. It is a (slightly tweaked)
@@ -67,7 +76,7 @@ module Rack; module Throttle
67
76
  loss = (outflow * time).to_f
68
77
  if loss > 0
69
78
  @count -= loss
70
- @last_touched = t
79
+ @last_touched = t
71
80
  end
72
81
  end
73
82
 
@@ -4,7 +4,7 @@ module Rack; module Throttle
4
4
  # requested. For instance, you may care about limiting requests
5
5
  # to a machine-consumed API, but not be concerned about requests
6
6
  # coming from browsers.
7
- # UrlMatchers take Regexp object to matcha gainst the request path.
7
+ # UrlMatchers take Regexp object to match against the request path.
8
8
  class UrlMatcher < Matcher
9
9
  ###
10
10
  # @param [Rack::Request] request
@@ -1,8 +1,8 @@
1
1
  module Rack; module Throttle
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 7
5
- TINY = 1
4
+ MINOR = 8
5
+ TINY = 0
6
6
  EXTRA = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY].join('.')
@@ -10,18 +10,18 @@ describe Rack::Throttle::Daily do
10
10
 
11
11
  it "should be allowed if not seen this day" do
12
12
  get "/foo"
13
- last_response.body.should show_allowed_response
13
+ expect(last_response.body).to show_allowed_response
14
14
  end
15
-
15
+
16
16
  it "should be allowed if seen fewer than the max allowed per day" do
17
17
  2.times { get "/foo" }
18
- last_response.body.should show_allowed_response
18
+ expect(last_response.body).to show_allowed_response
19
19
  end
20
-
20
+
21
21
  it "should not be allowed if seen more times than the max allowed per day" do
22
22
  4.times { get "/foo" }
23
- last_response.body.should show_throttled_response
23
+ expect(last_response.body).to show_throttled_response
24
24
  end
25
-
25
+
26
26
  # TODO mess with time travelling and requests to make sure no overlap
27
27
  end
@@ -10,18 +10,18 @@ describe Rack::Throttle::Hourly do
10
10
 
11
11
  it "should be allowed if not seen this hour" do
12
12
  get "/foo"
13
- last_response.body.should show_allowed_response
13
+ expect(last_response.body).to show_allowed_response
14
14
  end
15
-
15
+
16
16
  it "should be allowed if seen fewer than the max allowed per hour" do
17
17
  2.times { get "/foo" }
18
- last_response.body.should show_allowed_response
18
+ expect(last_response.body).to show_allowed_response
19
19
  end
20
-
20
+
21
21
  it "should not be allowed if seen more times than the max allowed per hour" do
22
22
  4.times { get "/foo" }
23
- last_response.body.should show_throttled_response
23
+ expect(last_response.body).to show_throttled_response
24
24
  end
25
-
25
+
26
26
  # TODO mess with time travelling and requests to make sure no overlap
27
27
  end
@@ -3,7 +3,7 @@ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
3
3
 
4
4
  describe Rack::Throttle::Interval do
5
5
  include Rack::Test::Methods
6
-
6
+
7
7
  def app
8
8
  @target_app ||= example_target_app
9
9
  @app ||= Rack::Throttle::Interval.new(@target_app, :min => 0.1)
@@ -11,9 +11,9 @@ describe Rack::Throttle::Interval do
11
11
 
12
12
  it "should allow the request if the source has not been seen" do
13
13
  get "/foo"
14
- last_response.body.should show_allowed_response
14
+ expect(last_response.body).to show_allowed_response
15
15
  end
16
-
16
+
17
17
  it "should allow the request if the source has not been seen in the current interval" do
18
18
  Timecop.freeze do
19
19
  get "/foo"
@@ -21,25 +21,25 @@ describe Rack::Throttle::Interval do
21
21
  get "/foo"
22
22
  end
23
23
  end
24
- last_response.body.should show_allowed_response
24
+ expect(last_response.body).to show_allowed_response
25
25
  end
26
-
26
+
27
27
  it "should not allow the request if the source has been seen inside the current interval" do
28
28
  Timecop.freeze do
29
29
  2.times { get "/foo" }
30
30
  end
31
- last_response.body.should show_throttled_response
31
+ expect(last_response.body).to show_throttled_response
32
32
  end
33
-
33
+
34
34
  it "should gracefully allow the request if the cache bombs on getting" do
35
- app.should_receive(:cache_get).and_raise(StandardError)
35
+ expect(app).to receive(:cache_get).and_raise(StandardError)
36
36
  get "/foo"
37
- last_response.body.should show_allowed_response
37
+ expect(last_response.body).to show_allowed_response
38
38
  end
39
-
39
+
40
40
  it "should gracefully allow the request if the cache bombs on setting" do
41
- app.should_receive(:cache_set).and_raise(StandardError)
41
+ expect(app).to receive(:cache_set).and_raise(StandardError)
42
42
  get "/foo"
43
- last_response.body.should show_allowed_response
43
+ expect(last_response.body).to show_allowed_response
44
44
  end
45
45
  end
@@ -7,44 +7,44 @@ describe Rack::Throttle::Limiter do
7
7
  @target_app ||= example_target_app
8
8
  @app ||= Rack::Throttle::Limiter.new(@target_app)
9
9
  end
10
-
10
+
11
11
  describe "basic calling" do
12
12
  it "should return the example app" do
13
13
  get "/foo"
14
- last_response.body.should show_allowed_response
14
+ expect(last_response.body).to show_allowed_response
15
15
  end
16
-
16
+
17
17
  it "should call the application if allowed" do
18
- app.should_receive(:allowed?).and_return(true)
18
+ expect(app).to receive(:allowed?).and_return(true)
19
19
  get "/foo"
20
- last_response.body.should show_allowed_response
20
+ expect(last_response.body).to show_allowed_response
21
21
  end
22
-
22
+
23
23
  it "should give a rate limit exceeded message if not allowed" do
24
- app.should_receive(:allowed?).and_return(false)
24
+ expect(app).to receive(:allowed?).and_return(false)
25
25
  get "/foo"
26
- last_response.body.should show_throttled_response
26
+ expect(last_response.body).to show_throttled_response
27
27
  end
28
28
  end
29
-
29
+
30
30
  describe "allowed?" do
31
31
  it "should return true if whitelisted" do
32
- app.should_receive(:whitelisted?).and_return(true)
32
+ expect(app).to receive(:whitelisted?).and_return(true)
33
33
  get "/foo"
34
- last_response.body.should show_allowed_response
34
+ expect(last_response.body).to show_allowed_response
35
35
  end
36
-
36
+
37
37
  it "should return false if blacklisted" do
38
- app.should_receive(:blacklisted?).and_return(true)
38
+ expect(app).to receive(:blacklisted?).and_return(true)
39
39
  get "/foo"
40
- last_response.body.should show_throttled_response
40
+ expect(last_response.body).to show_throttled_response
41
41
  end
42
-
42
+
43
43
  it "should return true if not whitelisted or blacklisted" do
44
- app.should_receive(:whitelisted?).and_return(false)
45
- app.should_receive(:blacklisted?).and_return(false)
44
+ expect(app).to receive(:whitelisted?).and_return(false)
45
+ expect(app).to receive(:blacklisted?).and_return(false)
46
46
  get "/foo"
47
- last_response.body.should show_allowed_response
47
+ expect(last_response.body).to show_allowed_response
48
48
  end
49
49
  end
50
50
 
@@ -19,28 +19,28 @@ describe Rack::Throttle::SlidingWindow do
19
19
 
20
20
  it "should allow the request if the source has not been seen at all" do
21
21
  get "/foo"
22
- last_response.body.should show_allowed_response
22
+ expect(last_response.body).to show_allowed_response
23
23
  end
24
-
24
+
25
25
  it "should allow the request if the rate is above-average but within the burst rule" do
26
26
  Timecop.freeze(@time) { get "/foo" }
27
27
  Timecop.freeze(@time + 0.5) { get "/foo" }
28
- last_response.body.should show_allowed_response
28
+ expect(last_response.body).to show_allowed_response
29
29
  end
30
30
 
31
31
  it "should not allow the request if the rate is greater than the burst rule" do
32
32
  Timecop.freeze(@time) { get "/foo" }
33
33
  Timecop.freeze(@time + 0.3) { get "/foo" }
34
34
  Timecop.freeze(@time + 0.6) { get "/foo" }
35
- last_response.body.should show_throttled_response
35
+ expect(last_response.body).to show_throttled_response
36
36
  end
37
-
37
+
38
38
  it "should allow the request if the rate is less than the average" do
39
39
  Timecop.freeze(@time) { get "/foo" }
40
40
  Timecop.freeze(@time + 0.5) { get "/foo" }
41
41
  Timecop.freeze(@time + 2) { get "/foo" }
42
42
 
43
- last_response.body.should show_allowed_response
43
+ expect(last_response.body).to show_allowed_response
44
44
  end
45
45
 
46
46
  it "should not allow the request if the rate is more than the average" do
@@ -49,19 +49,19 @@ describe Rack::Throttle::SlidingWindow do
49
49
  Timecop.freeze(@time + 1) { get "/foo" }
50
50
  Timecop.freeze(@time + 1.5) { get "/foo" }
51
51
 
52
- last_response.body.should show_throttled_response
52
+ expect(last_response.body).to show_throttled_response
53
53
  end
54
54
 
55
55
  it "should gracefully allow the request if the cache bombs on getting" do
56
- app.should_receive(:cache_get).and_raise(StandardError)
56
+ expect(app).to receive(:cache_get).and_raise(StandardError)
57
57
  get "/foo"
58
- last_response.body.should show_allowed_response
58
+ expect(last_response.body).to show_allowed_response
59
59
  end
60
-
60
+
61
61
  it "should gracefully allow the request if the cache bombs on setting" do
62
- app.should_receive(:cache_set).and_raise(StandardError)
62
+ expect(app).to receive(:cache_set).and_raise(StandardError)
63
63
  get "/foo"
64
- last_response.body.should show_allowed_response
64
+ expect(last_response.body).to show_allowed_response
65
65
  end
66
66
  end
67
67
 
@@ -9,19 +9,19 @@ describe Rack::Throttle::MethodMatcher do
9
9
  end
10
10
 
11
11
  it "should not bother checking if the path doesn't match the rule" do
12
- app.should_not_receive(:allowed?)
12
+ expect(app).not_to receive(:allowed?)
13
13
  get "/foo"
14
- last_response.body.should show_allowed_response
14
+ expect(last_response.body).to show_allowed_response
15
15
  end
16
-
16
+
17
17
  it "should check if the path matches the rule" do
18
- app.should_receive(:allowed?).and_return(false)
18
+ expect(app).to receive(:allowed?).and_return(false)
19
19
  post "/foo"
20
- last_response.body.should show_throttled_response
20
+ expect(last_response.body).to show_throttled_response
21
21
  end
22
22
 
23
23
  it "should append the rule to the cache key" do
24
24
  post "/foo"
25
- app.send(:cache_key, last_request).should == "127.0.0.1:meth-post"
25
+ expect(app.send(:cache_key, last_request)).to eq "127.0.0.1:meth-post"
26
26
  end
27
27
  end
@@ -9,20 +9,20 @@ describe Rack::Throttle::UrlMatcher do
9
9
  end
10
10
 
11
11
  it "should not bother checking if the path doesn't match the rule" do
12
- app.should_not_receive(:allowed?)
12
+ expect(app).not_to receive(:allowed?)
13
13
  get "/bar"
14
- last_response.body.should show_allowed_response
14
+ expect(last_response.body).to show_allowed_response
15
15
  end
16
-
16
+
17
17
  it "should check if the path matches the rule" do
18
- app.should_receive(:allowed?).and_return(false)
18
+ expect(app).to receive(:allowed?).and_return(false)
19
19
  get "/foo"
20
- last_response.body.should show_throttled_response
20
+ expect(last_response.body).to show_throttled_response
21
21
  end
22
22
 
23
23
  it "should append the rule to the cache key" do
24
24
  get "/foo"
25
- app.send(:cache_key, last_request).should == "127.0.0.1:url-/foo/"
25
+ expect(app.send(:cache_key, last_request)).to eq "127.0.0.1:url-/foo/"
26
26
  end
27
27
  end
28
28
 
@@ -9,20 +9,20 @@ describe Rack::Throttle::UserAgentMatcher do
9
9
  end
10
10
 
11
11
  it "should not bother checking if the path doesn't match the rule" do
12
- app.should_not_receive(:allowed?)
12
+ expect(app).not_to receive(:allowed?)
13
13
  get "/foo"
14
- last_response.body.should show_allowed_response
14
+ expect(last_response.body).to show_allowed_response
15
15
  end
16
16
 
17
17
  it "should check if the path matches the rule" do
18
- app.should_receive(:allowed?).and_return(false)
18
+ expect(app).to receive(:allowed?).and_return(false)
19
19
  header 'User-Agent', 'blahdeblah GoogleBot owns your soul'
20
20
  get "/foo"
21
- last_response.body.should show_throttled_response
21
+ expect(last_response.body).to show_throttled_response
22
22
  end
23
23
 
24
24
  it "should append the rule to the cache key" do
25
25
  get "/foo"
26
- app.send(:cache_key, last_request).should == "127.0.0.1:ua-/google/i"
26
+ expect(app.send(:cache_key, last_request)).to eq "127.0.0.1:ua-/google/i"
27
27
  end
28
28
  end
@@ -3,9 +3,14 @@ require "rack/test"
3
3
  require "rack/throttle"
4
4
  require "timecop"
5
5
 
6
+ unless RUBY_VERSION.match(/1\.8/)
7
+ require 'simplecov'
8
+ SimpleCov.start
9
+ end
10
+
6
11
  def example_target_app
7
12
  @target_app = double("Example Rack App")
8
- @target_app.stub(:call).with(any_args()).and_return([200, {}, "Example App Body"])
13
+ allow(@target_app).to receive(:call).with(any_args()).and_return([200, {}, "Example App Body"])
9
14
  @target_app
10
15
  end
11
16
 
@@ -13,34 +18,34 @@ RSpec::Matchers.define :show_allowed_response do
13
18
  match do |body|
14
19
  body.include?("Example App Body")
15
20
  end
16
-
17
- failure_message_for_should do
18
- "expected response to show the allowed response"
19
- end
20
21
 
21
- failure_message_for_should_not do
22
- "expected response not to show the allowed response"
22
+ failure_message do
23
+ "expected response to show the allowed response"
23
24
  end
24
-
25
+
26
+ failure_message_when_negated do
27
+ "expected response not to show the allowed response"
28
+ end
29
+
25
30
  description do
26
31
  "expected the allowed response"
27
- end
32
+ end
28
33
  end
29
34
 
30
35
  RSpec::Matchers.define :show_throttled_response do
31
36
  match do |body|
32
37
  body.include?("Rate Limit Exceeded")
33
38
  end
34
-
35
- failure_message_for_should do
36
- "expected response to show the throttled response"
37
- end
38
39
 
39
- failure_message_for_should_not do
40
- "expected response not to show the throttled response"
40
+ failure_message do
41
+ "expected response to show the throttled response"
42
+ end
43
+
44
+ failure_message do
45
+ "expected response not to show the throttled response"
41
46
  end
42
-
47
+
43
48
  description do
44
49
  "expected the throttled response"
45
- end
50
+ end
46
51
  end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: improved-rack-throttle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
5
- prerelease:
4
+ version: 0.8.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Ben Somers
@@ -11,134 +10,132 @@ authors:
11
10
  autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2013-05-09 00:00:00.000000000 Z
13
+ date: 2014-09-22 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: rack
18
17
  requirement: !ruby/object:Gem::Requirement
19
- none: false
20
18
  requirements:
21
- - - ! '>='
19
+ - - '>='
22
20
  - !ruby/object:Gem::Version
23
21
  version: 1.0.0
24
22
  type: :runtime
25
23
  prerelease: false
26
24
  version_requirements: !ruby/object:Gem::Requirement
27
- none: false
28
25
  requirements:
29
- - - ! '>='
26
+ - - '>='
30
27
  - !ruby/object:Gem::Version
31
28
  version: 1.0.0
32
29
  - !ruby/object:Gem::Dependency
33
30
  name: timecop
34
31
  requirement: !ruby/object:Gem::Requirement
35
- none: false
36
32
  requirements:
37
- - - ~>
33
+ - - <=
38
34
  - !ruby/object:Gem::Version
39
- version: 0.5.2
35
+ version: 0.6.2.2
40
36
  type: :development
41
37
  prerelease: false
42
38
  version_requirements: !ruby/object:Gem::Requirement
43
- none: false
44
39
  requirements:
45
- - - ~>
40
+ - - <=
46
41
  - !ruby/object:Gem::Version
47
- version: 0.5.2
42
+ version: 0.6.2.2
48
43
  - !ruby/object:Gem::Dependency
49
44
  name: rack-test
50
45
  requirement: !ruby/object:Gem::Requirement
51
- none: false
52
46
  requirements:
53
- - - ~>
47
+ - - '>='
54
48
  - !ruby/object:Gem::Version
55
- version: 0.6.2
49
+ version: '0'
56
50
  type: :development
57
51
  prerelease: false
58
52
  version_requirements: !ruby/object:Gem::Requirement
59
- none: false
60
53
  requirements:
61
- - - ~>
54
+ - - '>='
62
55
  - !ruby/object:Gem::Version
63
- version: 0.6.2
56
+ version: '0'
64
57
  - !ruby/object:Gem::Dependency
65
58
  name: rspec
66
59
  requirement: !ruby/object:Gem::Requirement
67
- none: false
68
60
  requirements:
69
- - - ~>
61
+ - - '>='
70
62
  - !ruby/object:Gem::Version
71
- version: 2.11.0
63
+ version: '0'
72
64
  type: :development
73
65
  prerelease: false
74
66
  version_requirements: !ruby/object:Gem::Requirement
75
- none: false
76
67
  requirements:
77
- - - ~>
68
+ - - '>='
78
69
  - !ruby/object:Gem::Version
79
- version: 2.11.0
70
+ version: '0'
80
71
  - !ruby/object:Gem::Dependency
81
72
  name: yard
82
73
  requirement: !ruby/object:Gem::Requirement
83
- none: false
84
74
  requirements:
85
- - - ! '>='
75
+ - - '>='
86
76
  - !ruby/object:Gem::Version
87
- version: 0.5.5
77
+ version: '0'
88
78
  type: :development
89
79
  prerelease: false
90
80
  version_requirements: !ruby/object:Gem::Requirement
91
- none: false
92
81
  requirements:
93
- - - ! '>='
82
+ - - '>='
94
83
  - !ruby/object:Gem::Version
95
- version: 0.5.5
84
+ version: '0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: simplecov
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
96
99
  - !ruby/object:Gem::Dependency
97
100
  name: redcarpet
98
101
  requirement: !ruby/object:Gem::Requirement
99
- none: false
100
102
  requirements:
101
- - - ! '>='
103
+ - - '>='
102
104
  - !ruby/object:Gem::Version
103
105
  version: '0'
104
106
  type: :development
105
107
  prerelease: false
106
108
  version_requirements: !ruby/object:Gem::Requirement
107
- none: false
108
109
  requirements:
109
- - - ! '>='
110
+ - - '>='
110
111
  - !ruby/object:Gem::Version
111
112
  version: '0'
112
113
  - !ruby/object:Gem::Dependency
113
114
  name: rake
114
115
  requirement: !ruby/object:Gem::Requirement
115
- none: false
116
116
  requirements:
117
- - - ! '>='
117
+ - - '>='
118
118
  - !ruby/object:Gem::Version
119
119
  version: '0'
120
120
  type: :development
121
121
  prerelease: false
122
122
  version_requirements: !ruby/object:Gem::Requirement
123
- none: false
124
123
  requirements:
125
- - - ! '>='
124
+ - - '>='
126
125
  - !ruby/object:Gem::Version
127
126
  version: '0'
128
127
  - !ruby/object:Gem::Dependency
129
128
  name: jeweler
130
129
  requirement: !ruby/object:Gem::Requirement
131
- none: false
132
130
  requirements:
133
- - - ! '>='
131
+ - - '>='
134
132
  - !ruby/object:Gem::Version
135
133
  version: '0'
136
134
  type: :development
137
135
  prerelease: false
138
136
  version_requirements: !ruby/object:Gem::Requirement
139
- none: false
140
137
  requirements:
141
- - - ! '>='
138
+ - - '>='
142
139
  - !ruby/object:Gem::Version
143
140
  version: '0'
144
141
  description: Rack middleware for rate-limiting incoming HTTP requests.
@@ -149,6 +146,7 @@ extra_rdoc_files:
149
146
  - README.md
150
147
  files:
151
148
  - .document
149
+ - .travis.yml
152
150
  - Gemfile
153
151
  - Gemfile.lock
154
152
  - README.md
@@ -186,29 +184,26 @@ files:
186
184
  homepage: http://github.com/bensomers/improved-rack-throttle
187
185
  licenses:
188
186
  - Public Domain
187
+ metadata: {}
189
188
  post_install_message:
190
189
  rdoc_options: []
191
190
  require_paths:
192
191
  - lib
193
192
  required_ruby_version: !ruby/object:Gem::Requirement
194
- none: false
195
193
  requirements:
196
- - - ! '>='
194
+ - - '>='
197
195
  - !ruby/object:Gem::Version
198
196
  version: '0'
199
- segments:
200
- - 0
201
- hash: -4496223408765979053
202
197
  required_rubygems_version: !ruby/object:Gem::Requirement
203
- none: false
204
198
  requirements:
205
- - - ! '>='
199
+ - - '>='
206
200
  - !ruby/object:Gem::Version
207
201
  version: '0'
208
202
  requirements: []
209
203
  rubyforge_project:
210
- rubygems_version: 1.8.25
204
+ rubygems_version: 2.0.3
211
205
  signing_key:
212
- specification_version: 3
206
+ specification_version: 4
213
207
  summary: HTTP request rate limiter for Rack applications.
214
208
  test_files: []
209
+ has_rdoc: