rack-berater 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7db43d04f14f84dd3e2f3f96352226a48d4e36bfe17467fd1e4eb77015ca6148
4
- data.tar.gz: 40f79342ca6cd92145a32e2d0b76455850cc4bdd25d701e83157a7acf330bc15
3
+ metadata.gz: 5781e3d2d2b482f08801da66f94cda264ece7e1310bb0f5adaafefc12e5a61a5
4
+ data.tar.gz: b32888c349ced36fbdfb1b4c941dd32a45ba8a19a61c0cd553d3e208cbd39126
5
5
  SHA512:
6
- metadata.gz: 1f8b3b29d9d62796a49701340fb53446cfca19258f2083c5973bd17e9a9314abb28c7faf7070a260cd4128bb9028346d18f24c98d96d5d8a9e503e5d113f8456
7
- data.tar.gz: 7ee2bef14d6c4334ef2d65b67a60fe432047a0ce8581f62461cf1fd0c65264083833bbd9f3fe45f2909be3b52cb2147db9704a44e2f9cbc9017b83f69da182d4
6
+ metadata.gz: 92bc69dd2604250f0cdda1f5ab28ea3af4bb1059d6e284b5e51f70e15b5011a7a5f9bb2429a843b4b361081d95076f9483181d072617d1695e281630455b8101
7
+ data.tar.gz: 8ff2a5d2f76aabcd7b6e3a84a79be08591d8deaf995bbd99490be2e4bd9094406403ca161cb67aa1e30cb74325743373a391091d4285b233ba0adf5dc8578fab
@@ -0,0 +1,76 @@
1
+ require 'rack'
2
+
3
+ module Rack
4
+ class Berater
5
+ class Prioritizer
6
+ ENV_KEY = 'berater_priority'
7
+ HEADER = 'X-Berater-Priority'
8
+
9
+ def initialize(app, options = {})
10
+ @app = app
11
+ @header = options[:header] || HEADER
12
+
13
+ synchronize { @@cache ||= {} }
14
+ end
15
+
16
+ def call(env)
17
+ priority = env[@header] || env["HTTP_#{@header.upcase.tr('-', '_')}"]
18
+
19
+ if priority
20
+ set_priority(priority)
21
+ return @app.call(env)
22
+ end
23
+
24
+ cache_key = cache_key_for(env)
25
+ cached_priority = cache_get(cache_key)
26
+
27
+ if cached_priority
28
+ set_priority(cached_priority)
29
+ end
30
+
31
+ @app.call(env).tap do |status, headers, body|
32
+ app_priority = headers.delete(@header) if headers
33
+
34
+ if app_priority && app_priority != cached_priority
35
+ # update cache for next time
36
+ cache_set(cache_key, app_priority)
37
+ end
38
+ end
39
+ ensure
40
+ Thread.current[ENV_KEY] = nil
41
+ end
42
+
43
+ def self.current_priority
44
+ Thread.current[ENV_KEY]
45
+ end
46
+
47
+ protected
48
+
49
+ def set_priority(priority)
50
+ Thread.current[ENV_KEY] = priority
51
+ end
52
+
53
+ def cache_key_for(env)
54
+ [
55
+ env[Rack::REQUEST_METHOD].downcase,
56
+
57
+ # normalize RESTful paths
58
+ env['PATH_INFO'].gsub(%r{/[0-9]+(/|$)}, '/x\1'),
59
+ ].join(':')
60
+ end
61
+
62
+ def cache_get(key)
63
+ synchronize { @@cache[key] }
64
+ end
65
+
66
+ def cache_set(key, priority)
67
+ synchronize { @@cache[key] = priority }
68
+ end
69
+
70
+ @@lock = Thread::Mutex.new
71
+ def synchronize(&block)
72
+ @@lock.synchronize(&block)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,17 @@
1
+ require 'action_controller/metal'
2
+ require 'action_dispatch'
3
+
4
+ module Rack
5
+ class Berater
6
+ class RailsPrioritizer < Prioritizer
7
+ def cache_key_for(env)
8
+ Rails.application.routes.recognize_path(
9
+ env[Rack::PATH_INFO],
10
+ method: env[Rack::REQUEST_METHOD],
11
+ ).values_at(:controller, :action).compact.join('#')
12
+ rescue ActionController::RoutingError
13
+ super
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,9 +1,13 @@
1
- require "rails/railtie"
1
+ require 'rails/railtie'
2
2
 
3
3
  module Rack
4
4
  class Berater
5
5
  class Railtie < Rails::Railtie
6
- initializer "rack.berater" do |app|
6
+ initializer 'rack.berater' do |app|
7
+ if ::Berater.middleware.include?(::Berater::Middleware::LoadShedder)
8
+ app.middleware.use Rack::Berater::RailsPrioritizer
9
+ end
10
+
7
11
  app.middleware.use Rack::Berater
8
12
  end
9
13
  end
@@ -0,0 +1,9 @@
1
+ require 'rack/berater'
2
+ require 'rspec/core'
3
+
4
+ RSpec.configure do |config|
5
+ config.after do
6
+ Thread.current[Rack::Berater::Prioritizer::ENV_KEY] = nil
7
+ Rack::Berater::Prioritizer.class_variable_set(:@@cache, nil)
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Berater
3
- VERSION = "0.2.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
data/lib/rack/berater.rb CHANGED
@@ -1,11 +1,13 @@
1
- require "berater"
2
- require "rack"
3
- require "rack/berater/version"
4
- require "set"
1
+ require 'berater'
2
+ require 'rack'
3
+ require 'rack/berater/version'
4
+ require 'set'
5
5
 
6
6
  module Rack
7
7
  class Berater
8
- autoload :Railtie, "rack/berater/railtie"
8
+ autoload :Prioritizer, 'rack/berater/prioritizer'
9
+ autoload :RailsPrioritizer, 'rack/berater/rails_prioritizer'
10
+ autoload :Railtie, 'rack/berater/railtie'
9
11
 
10
12
  ERRORS = Set[ ::Berater::Overloaded ]
11
13
 
@@ -27,12 +29,12 @@ module Rack
27
29
  when String
28
30
  options[:body]
29
31
  else
30
- raise ArgumentError, "invalid :body option: #{options[:body]}"
32
+ raise ArgumentError, 'invalid :body option: #{options[:body]}'
31
33
  end
32
34
 
33
35
  # configure headers
34
36
  if @options[:body]
35
- @options[:headers][Rack::CONTENT_TYPE] = "text/plain"
37
+ @options[:headers][Rack::CONTENT_TYPE] = 'text/plain'
36
38
  end
37
39
  @options[:headers].update(options.fetch(:headers, {}))
38
40
  end
data/lib/rack-berater.rb CHANGED
@@ -1 +1 @@
1
- require "rack/berater"
1
+ require 'rack/berater'
data/spec/limiter_spec.rb CHANGED
@@ -9,108 +9,108 @@ describe Rack::Berater do
9
9
  Rack::Builder.new do
10
10
  use Rack::Lint
11
11
  run (lambda do |env|
12
- [200, {"Content-Type" => "text/plain"}, ["OK"]]
12
+ [200, {'Content-Type' => 'text/plain'}, ['OK']]
13
13
  end)
14
14
  end
15
15
  end
16
- let(:response) { get "/" }
16
+ let(:response) { get '/' }
17
17
 
18
- shared_examples "works nominally" do
19
- it "has the correct status code" do
18
+ shared_examples 'works nominally' do
19
+ it 'has the correct status code' do
20
20
  expect(response.status).to eq 200
21
21
  end
22
22
 
23
- it "has the correct headers" do
23
+ it 'has the correct headers' do
24
24
  expect(response.headers).to eq({
25
- "Content-Type" => "text/plain",
26
- "Content-Length" => "2",
25
+ 'Content-Type' => 'text/plain',
26
+ 'Content-Length' => '2',
27
27
  })
28
28
  end
29
29
 
30
- it "has the correct body" do
31
- expect(response.body).to eq "OK"
30
+ it 'has the correct body' do
31
+ expect(response.body).to eq 'OK'
32
32
  end
33
33
  end
34
34
 
35
- context "without a limiter" do
35
+ context 'without a limiter' do
36
36
  before { Berater.test_mode = :fail }
37
37
 
38
- include_examples "works nominally"
38
+ include_examples 'works nominally'
39
39
  end
40
40
 
41
- describe "limiter option" do
42
- context "when limiter is a limiter" do
41
+ describe 'limiter option' do
42
+ context 'when limiter is a limiter' do
43
43
  let(:limiter) { ::Berater::Unlimiter.new }
44
44
 
45
- include_examples "works nominally"
45
+ include_examples 'works nominally'
46
46
 
47
- it "calls the limiter" do
47
+ it 'calls the limiter' do
48
48
  expect(limiter).to receive(:limit).and_call_original
49
49
  response
50
50
  end
51
51
 
52
- context "when operating beyond limits" do
52
+ context 'when operating beyond limits' do
53
53
  before { Berater.test_mode = :fail }
54
54
 
55
- it "returns an error" do
55
+ it 'returns an error' do
56
56
  expect(response.status).to eq 429
57
57
  end
58
58
  end
59
59
  end
60
60
 
61
- context "when limiter is a proc" do
61
+ context 'when limiter is a proc' do
62
62
  let(:limiter_instance) { ::Berater::Unlimiter.new }
63
63
  let(:limiter) { Proc.new { limiter_instance } }
64
64
 
65
- include_examples "works nominally"
65
+ include_examples 'works nominally'
66
66
 
67
- it "calls the proc with env" do
67
+ it 'calls the proc with env' do
68
68
  expect(limiter).to receive(:call).with(Hash).and_call_original
69
69
  response
70
70
  end
71
71
 
72
- context "when operating beyond limits" do
72
+ context 'when operating beyond limits' do
73
73
  before { Berater.test_mode = :fail }
74
74
 
75
- it "returns an error" do
75
+ it 'returns an error' do
76
76
  expect(response.status).to eq 429
77
77
  end
78
78
  end
79
79
  end
80
80
  end
81
81
 
82
- describe "enabled? option" do
82
+ describe 'enabled? option' do
83
83
  after { expect(response.status).to eq 200 }
84
84
 
85
85
  let(:enabled?) { double }
86
86
 
87
- context "when there is a limiter" do
87
+ context 'when there is a limiter' do
88
88
  let(:limiter) { ::Berater::Unlimiter.new }
89
89
 
90
- it "should be called with the env hash" do
90
+ it 'should be called with the env hash' do
91
91
  expect(enabled?).to receive(:call) do |env|
92
92
  expect(env).to be_a Hash
93
- expect(Rack::Request.new(env).path).to eq "/"
93
+ expect(Rack::Request.new(env).path).to eq '/'
94
94
  end
95
95
  end
96
96
 
97
- context "when enabled" do
98
- it "should call the limiter" do
97
+ context 'when enabled' do
98
+ it 'should call the limiter' do
99
99
  expect(enabled?).to receive(:call).and_return(true)
100
100
  expect(limiter).to receive(:limit).and_call_original
101
101
  end
102
102
  end
103
103
 
104
- context "when disabled" do
105
- it "should not call the limiter" do
104
+ context 'when disabled' do
105
+ it 'should not call the limiter' do
106
106
  expect(enabled?).to receive(:call).and_return(false)
107
107
  expect(limiter).not_to receive(:limit)
108
108
  end
109
109
  end
110
110
  end
111
111
 
112
- context "when there is no limiter" do
113
- it "should not call enabled?" do
112
+ context 'when there is no limiter' do
113
+ it 'should not call enabled?' do
114
114
  expect(enabled?).not_to receive(:call)
115
115
  end
116
116
  end
@@ -0,0 +1,213 @@
1
+ describe Rack::Berater::Prioritizer do
2
+ let(:cache) { described_class.class_variable_get(:@@cache) }
3
+
4
+ describe '#call' do
5
+ subject { described_class.new(app) }
6
+
7
+ let(:app) { ->(*) { [200, {'Content-Type' => 'text/plain'}, ['OK']] } }
8
+ let(:cache_key) { subject.send(:cache_key_for, env) }
9
+ let(:env) { Rack::MockRequest.env_for('/') }
10
+
11
+ specify 'sanity check' do
12
+ expect(app).to receive(:call).and_call_original
13
+
14
+ Rack::Lint.new(subject).call(env)
15
+ end
16
+
17
+ it 'checks the cache' do
18
+ is_expected.to receive(:cache_get)
19
+
20
+ subject.call(env)
21
+ end
22
+
23
+ context 'with a cached priority' do
24
+ before do
25
+ allow(subject).to receive(:cache_get).with(cache_key).and_return(priority)
26
+ end
27
+
28
+ let(:priority) { '3' }
29
+
30
+ after { subject.call(env) }
31
+
32
+ it 'sets the priority accordingly' do
33
+ is_expected.to receive(:set_priority).with(priority)
34
+ end
35
+
36
+ it 'updates the global priority during the request' do
37
+ expect(app).to receive(:call) do
38
+ expect(described_class.current_priority).to eq priority
39
+ end
40
+ end
41
+
42
+ it 'resets the priority after the request completes' do
43
+ subject.call(env)
44
+ expect(described_class.current_priority).to be nil
45
+ end
46
+ end
47
+
48
+ context 'with an incoming priority header' do
49
+ let(:env) do
50
+ Rack::MockRequest.env_for(
51
+ '/',
52
+ described_class::HEADER => priority,
53
+ )
54
+ end
55
+ let(:priority) { '2' }
56
+
57
+ after { subject.call(env) }
58
+
59
+ it 'uses the header' do
60
+ is_expected.to receive(:set_priority).with(priority)
61
+ end
62
+
63
+ it 'ignores any cached value' do
64
+ allow(subject).to receive(:cache_get).with(cache_key).and_return('123')
65
+ is_expected.to receive(:set_priority).with(priority)
66
+ end
67
+
68
+ it 'resets the priority after the request completes' do
69
+ subject.call(env)
70
+ expect(described_class.current_priority).to be nil
71
+ end
72
+ end
73
+
74
+ context 'when the app returns a priority header' do
75
+ let(:app) do
76
+ ->(*) { [200, { described_class::HEADER => priority }, ['OK']] }
77
+ end
78
+
79
+ let(:priority) { '5' }
80
+
81
+ after { subject.call(env) }
82
+
83
+ it 'caches the priority' do
84
+ is_expected.to receive(:cache_set).with(cache_key, priority)
85
+ end
86
+
87
+ it 'removes the header' do
88
+ _, headers, _ = subject.call(env)
89
+ expect(headers).not_to include described_class::HEADER
90
+ end
91
+
92
+ it 'updates the cache when a different priority is returned' do
93
+ expect(subject).to receive(:cache_get).and_return('123')
94
+ is_expected.to receive(:cache_set).with(cache_key, priority)
95
+ end
96
+
97
+ it 'does not update the cache when the same priority is returned' do
98
+ expect(subject).to receive(:cache_get).and_return(priority)
99
+ is_expected.not_to receive(:cache_set)
100
+ end
101
+ end
102
+
103
+ it 'does not update the cache when no priority is returned' do
104
+ is_expected.not_to receive(:cache_set)
105
+ subject.call(env)
106
+ end
107
+ end
108
+
109
+ describe '#cache_key_for' do
110
+ subject{ described_class.new(nil).send(:cache_key_for, env) }
111
+
112
+ context 'with a basic env' do
113
+ let(:env) { Rack::MockRequest.env_for('/') }
114
+
115
+ it 'combines the verb and path' do
116
+ is_expected.to match %r{get:/$}
117
+ end
118
+ end
119
+
120
+ context 'with a different verb' do
121
+ let(:env) { Rack::MockRequest.env_for('/', method: 'PUT') }
122
+
123
+ it 'combines the verb and path' do
124
+ is_expected.to match %r{put:/$}
125
+ end
126
+ end
127
+
128
+ context 'with a RESTful path' do
129
+ let(:env) { Rack::MockRequest.env_for('/user/123') }
130
+
131
+ it 'normalizes the id' do
132
+ is_expected.to match %r{get:/user/x$}
133
+ end
134
+ end
135
+
136
+ context 'with a RESTful path and trailing slash' do
137
+ let(:env) { Rack::MockRequest.env_for('/user/123/') }
138
+
139
+ it 'normalizes the id and keeps the trailing slash' do
140
+ is_expected.to match %r{get:/user/x/$}
141
+ end
142
+ end
143
+
144
+ context 'with a very RESTful path' do
145
+ let(:env) { Rack::MockRequest.env_for('/user/123/friend/456') }
146
+
147
+ it 'normalizes both ids' do
148
+ is_expected.to match %r{get:/user/x/friend/x$}
149
+ end
150
+ end
151
+ end
152
+
153
+ context 'as Rack middleware' do
154
+ def call(path = '/')
155
+ get(path).body
156
+ end
157
+
158
+ let(:app) do
159
+ headers = {
160
+ 'Content-Type' => 'text/plain',
161
+ described_class::HEADER => app_priority,
162
+ }.compact
163
+
164
+ Rack::Builder.new do
165
+ use Rack::Lint
166
+ use Rack::Berater::Prioritizer
167
+
168
+ run (lambda do |env|
169
+ [200, headers, [ Rack::Berater::Prioritizer.current_priority.to_s ]]
170
+ end)
171
+ end
172
+ end
173
+
174
+ let(:app_priority) { nil }
175
+
176
+ it 'starts empty' do
177
+ expect(call).to be_empty
178
+ end
179
+
180
+ it 'parses incoming priority header' do
181
+ header described_class::HEADER, '7'
182
+
183
+ expect(call).to eq '7'
184
+ end
185
+
186
+ context 'when app returns a priority header' do
187
+ let(:app_priority) { '8' }
188
+
189
+ it 'parses the priority returned from the app' do
190
+ expect(call).to be_empty
191
+ expect(cache.values).to include app_priority
192
+ end
193
+
194
+ it 'uses the cached priority for subsequent calls' do
195
+ expect(call).to be_empty
196
+ expect(call).to eq app_priority
197
+ end
198
+ end
199
+
200
+ # context 'when two different endpoints are called' do
201
+ # fit 'parses and caches each priority' do
202
+ # @app_priority = '6'
203
+ # expect(call('/six')).to be_empty
204
+
205
+ # expect(call('/six')).to eq '6'
206
+
207
+ # @app_priority = '9'
208
+ # expect(call('/nine')).to be_empty
209
+ # expect(call('/nine')).to '9'
210
+ # end
211
+ # end
212
+ end
213
+ end
@@ -0,0 +1,100 @@
1
+ require 'rspec/rails'
2
+
3
+ describe Rack::Berater::RailsPrioritizer do
4
+ before do
5
+ class EchoController < ActionController::Base
6
+ def index
7
+ render plain: Rack::Berater::Prioritizer.current_priority
8
+ end
9
+
10
+ def six
11
+ response.set_header(Rack::Berater::Prioritizer::HEADER, '6')
12
+ index
13
+ end
14
+
15
+ def nine
16
+ response.set_header(Rack::Berater::Prioritizer::HEADER, '9')
17
+ index
18
+ end
19
+ end
20
+
21
+ Rails.application = Class.new(Rails::Application) do
22
+ config.eager_load = false
23
+ config.hosts.clear # disable hostname filtering
24
+ # config.logger = ActiveSupport::Logger.new($stdout)
25
+ end
26
+ Rails.application.middleware.use described_class
27
+ Rails.initialize!
28
+
29
+ Rails.application.routes.draw do
30
+ get '/' => 'echo#index'
31
+ get '/six' => 'echo#six'
32
+ post '/nine' => 'echo#nine'
33
+ get '/redirect' => redirect('/')
34
+ end
35
+ end
36
+
37
+ let(:app) { Rails.application }
38
+ let(:middleware) { described_class.new(app) }
39
+
40
+ after do
41
+ Rails.application = nil
42
+ end
43
+
44
+ let(:cache) { described_class.class_variable_get(:@@cache) }
45
+
46
+ describe '#cache_key_for' do
47
+ subject { described_class.new(app).method(:cache_key_for) }
48
+
49
+ it 'uses the controller and action name' do
50
+ expect(
51
+ subject.call(Rack::MockRequest.env_for('/'))
52
+ ).to match /echo#index/
53
+
54
+ expect(
55
+ subject.call(Rack::MockRequest.env_for('/six'))
56
+ ).to match /echo#six/
57
+
58
+ expect(
59
+ subject.call(Rack::MockRequest.env_for('/nine', method: 'POST'))
60
+ ).to match /echo#nine/
61
+ end
62
+
63
+ it 'falls back to Rack style names' do
64
+ expect(
65
+ subject.call(Rack::MockRequest.env_for('/nine'))
66
+ ).to match %r{get:/nine}
67
+ end
68
+
69
+ it 'works with redirects' do
70
+ expect(
71
+ subject.call(Rack::MockRequest.env_for('/redirect'))
72
+ ).to match %r{get:/redirect}
73
+ end
74
+ end
75
+
76
+ context 'when a priority header is sent' do
77
+ before { header described_class::HEADER, priority }
78
+
79
+ let(:priority) { '6' }
80
+
81
+ it 'sets the priority' do
82
+ expect(get('/six').body).to eq priority
83
+ end
84
+ end
85
+
86
+ context 'when the app returns a priority' do
87
+ it 'does not know the first time the controller is called' do
88
+ expect(get('/six').body).to be_empty
89
+ expect(post('/nine').body).to be_empty
90
+ end
91
+
92
+ it 'caches the repsonses for the second time' do
93
+ expect(get('/six').body).to be_empty
94
+ expect(post('/nine').body).to be_empty
95
+
96
+ expect(get('/six').body).to eq '6'
97
+ expect(post('/nine').body).to eq '9'
98
+ end
99
+ end
100
+ end
data/spec/railtie_spec.rb CHANGED
@@ -1,5 +1,5 @@
1
- require "rails"
2
- require "rack/berater/railtie"
1
+ require 'rails'
2
+ require 'rack/berater/railtie'
3
3
 
4
4
  RSpec.describe Rack::Berater::Railtie do
5
5
  subject { Rails.initialize! }
@@ -7,11 +7,10 @@ RSpec.describe Rack::Berater::Railtie do
7
7
  before do
8
8
  Rails.application = Class.new(Rails::Application) do
9
9
  config.eager_load = false
10
- config.logger = ActiveSupport::Logger.new($stdout)
11
10
  end
12
11
  end
13
12
 
14
- it "adds middleware automatically" do
13
+ it 'adds middleware automatically' do
15
14
  expect(subject.middleware).to include(Rack::Berater)
16
15
  end
17
16
  end
data/spec/rescuer_spec.rb CHANGED
@@ -4,34 +4,34 @@ describe Rack::Berater do
4
4
  use Rack::Lint
5
5
  run (lambda do |env|
6
6
  Berater::Unlimiter() do
7
- [200, {"Content-Type" => "text/plain"}, ["OK"]]
7
+ [200, {'Content-Type' => 'text/plain'}, ['OK']]
8
8
  end
9
9
  end)
10
10
  end
11
11
  end
12
- let(:response) { get "/" }
12
+ let(:response) { get '/' }
13
13
 
14
- shared_examples "works nominally" do
15
- it "has the correct status code" do
14
+ shared_examples 'works nominally' do
15
+ it 'has the correct status code' do
16
16
  expect(response.status).to eq 200
17
17
  end
18
18
 
19
- it "has the correct headers" do
19
+ it 'has the correct headers' do
20
20
  expect(response.headers).to eq({
21
- "Content-Type" => "text/plain",
22
- "Content-Length" => "2",
21
+ 'Content-Type' => 'text/plain',
22
+ 'Content-Length' => '2',
23
23
  })
24
24
  end
25
25
 
26
- it "has the correct body" do
27
- expect(response.body).to eq "OK"
26
+ it 'has the correct body' do
27
+ expect(response.body).to eq 'OK'
28
28
  end
29
29
  end
30
30
 
31
- context "without middleware" do
32
- include_examples "works nominally"
31
+ context 'without middleware' do
32
+ include_examples 'works nominally'
33
33
 
34
- it "does not catch limit errors" do
34
+ it 'does not catch limit errors' do
35
35
  Berater.test_mode = :fail
36
36
  expect {
37
37
  response
@@ -39,55 +39,55 @@ describe Rack::Berater do
39
39
  end
40
40
  end
41
41
 
42
- context "with middleware using default settings" do
42
+ context 'with middleware using default settings' do
43
43
  before { app.use described_class }
44
44
 
45
- include_examples "works nominally"
45
+ include_examples 'works nominally'
46
46
 
47
- it "catches and transforms limit errors" do
47
+ it 'catches and transforms limit errors' do
48
48
  Berater.test_mode = :fail
49
49
  expect(response.status).to eq 429
50
- expect(response.body).to eq "Too Many Requests"
50
+ expect(response.body).to eq 'Too Many Requests'
51
51
  end
52
52
  end
53
53
 
54
- context "with middleware using custom settings" do
54
+ context 'with middleware using custom settings' do
55
55
  before do
56
56
  app.use described_class, options
57
57
  Berater.test_mode = :fail
58
58
  end
59
59
 
60
- context "with a custom body" do
61
- context "with body nil" do
60
+ context 'with a custom body' do
61
+ context 'with body nil' do
62
62
  let(:options) { { body: nil } }
63
63
 
64
- it "falls back to the default" do
65
- expect(response.body).to eq "Too Many Requests"
64
+ it 'falls back to the default' do
65
+ expect(response.body).to eq 'Too Many Requests'
66
66
  end
67
67
  end
68
68
 
69
- context "with body disabled" do
69
+ context 'with body disabled' do
70
70
  let(:options) { { body: false } }
71
71
 
72
- it "should not send a body" do
72
+ it 'should not send a body' do
73
73
  expect(response.body).to be_empty
74
74
  end
75
75
 
76
- it "should not send the Content-Type header" do
76
+ it 'should not send the Content-Type header' do
77
77
  expect(response.headers.keys).not_to include(Rack::CONTENT_TYPE)
78
78
  end
79
79
  end
80
80
 
81
- context "with a string" do
82
- let(:body) { "none shall pass!" }
81
+ context 'with a string' do
82
+ let(:body) { 'none shall pass!' }
83
83
  let(:options) { { body: body } }
84
84
 
85
- it "should send the custom string" do
85
+ it 'should send the custom string' do
86
86
  expect(response.body).to eq body
87
87
  end
88
88
  end
89
89
 
90
- context "with an erroneous value" do
90
+ context 'with an erroneous value' do
91
91
  let(:options) { { body: 123 } }
92
92
 
93
93
  it 'should raise an error' do
@@ -98,56 +98,56 @@ describe Rack::Berater do
98
98
  end
99
99
  end
100
100
 
101
- context "with custom headers" do
102
- context "with an extra header" do
103
- let(:options) { { headers: { Rack::CACHE_CONTROL => "no-cache" } } }
101
+ context 'with custom headers' do
102
+ context 'with an extra header' do
103
+ let(:options) { { headers: { Rack::CACHE_CONTROL => 'no-cache' } } }
104
104
 
105
- it "should contain the default headers" do
105
+ it 'should contain the default headers' do
106
106
  expect(response.headers.keys).to include(Rack::CONTENT_TYPE)
107
107
  end
108
108
 
109
- it "should also contain the custom header" do
109
+ it 'should also contain the custom header' do
110
110
  expect(response.headers).to include(options[:headers])
111
111
  end
112
112
  end
113
113
 
114
- context "with a new content type" do
115
- let(:options) { { headers: { Rack::CONTENT_TYPE => "application/json" } } }
114
+ context 'with a new content type' do
115
+ let(:options) { { headers: { Rack::CONTENT_TYPE => 'application/json' } } }
116
116
 
117
- it "should override the Content-Type header" do
117
+ it 'should override the Content-Type header' do
118
118
  expect(response.headers).to include(options[:headers])
119
119
  end
120
120
  end
121
121
  end
122
122
 
123
- context "with custom status code" do
123
+ context 'with custom status code' do
124
124
  let(:options) { { status_code: 503 } }
125
125
 
126
- it "catches and transforms limit errors" do
126
+ it 'catches and transforms limit errors' do
127
127
  expect(response.status).to eq 503
128
- expect(response.body).to eq "Service Unavailable"
128
+ expect(response.body).to eq 'Service Unavailable'
129
129
  end
130
130
  end
131
131
  end
132
132
 
133
- context "with custom error type" do
133
+ context 'with custom error type' do
134
134
  before do
135
135
  app.use described_class
136
136
  expect(Berater::Limiter).to receive(:new).and_raise(IOError)
137
137
  end
138
138
 
139
- it "normally crashes the app" do
139
+ it 'normally crashes the app' do
140
140
  expect { response }.to raise_error(IOError)
141
141
  end
142
142
 
143
- context "when an error type is registered with middleware" do
143
+ context 'when an error type is registered with middleware' do
144
144
  around do |example|
145
145
  Rack::Berater::ERRORS << IOError
146
146
  example.run
147
147
  Rack::Berater::ERRORS.delete(IOError)
148
148
  end
149
149
 
150
- it "catches and transforms limit errors" do
150
+ it 'catches and transforms limit errors' do
151
151
  expect(response.status).to eq 429
152
152
  end
153
153
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-berater
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Pepper
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-18 00:00:00.000000000 Z
11
+ date: 2021-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: berater
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec-rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: simplecov
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -130,9 +144,14 @@ extra_rdoc_files: []
130
144
  files:
131
145
  - lib/rack-berater.rb
132
146
  - lib/rack/berater.rb
147
+ - lib/rack/berater/prioritizer.rb
148
+ - lib/rack/berater/rails_prioritizer.rb
133
149
  - lib/rack/berater/railtie.rb
150
+ - lib/rack/berater/rspec.rb
134
151
  - lib/rack/berater/version.rb
135
152
  - spec/limiter_spec.rb
153
+ - spec/prioritizer_spec.rb
154
+ - spec/rails_prioritizer_spec.rb
136
155
  - spec/railtie_spec.rb
137
156
  - spec/rescuer_spec.rb
138
157
  homepage: https://github.com/dpep/rack-berater
@@ -159,6 +178,8 @@ signing_key:
159
178
  specification_version: 4
160
179
  summary: Rack::Berater
161
180
  test_files:
181
+ - spec/rails_prioritizer_spec.rb
162
182
  - spec/railtie_spec.rb
163
183
  - spec/rescuer_spec.rb
184
+ - spec/prioritizer_spec.rb
164
185
  - spec/limiter_spec.rb