rack-berater 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7db43d04f14f84dd3e2f3f96352226a48d4e36bfe17467fd1e4eb77015ca6148
4
- data.tar.gz: 40f79342ca6cd92145a32e2d0b76455850cc4bdd25d701e83157a7acf330bc15
3
+ metadata.gz: 5840eaff6319d5e24c78c2f50960138ca27fcce25bb3d45354e7eca195b50f0d
4
+ data.tar.gz: f9cdde9f38c97d82e03d6a1e9cea2e1939eaca13f4202a2a674a6fda4e6f530e
5
5
  SHA512:
6
- metadata.gz: 1f8b3b29d9d62796a49701340fb53446cfca19258f2083c5973bd17e9a9314abb28c7faf7070a260cd4128bb9028346d18f24c98d96d5d8a9e503e5d113f8456
7
- data.tar.gz: 7ee2bef14d6c4334ef2d65b67a60fe432047a0ce8581f62461cf1fd0c65264083833bbd9f3fe45f2909be3b52cb2147db9704a44e2f9cbc9017b83f69da182d4
6
+ metadata.gz: b0f561957104a0d049d161b720615a97474439c41f16507b3bd34a1f68269fccbea314f54736393a5e030abdf755de8ae30bc5a92724502371728975b6cf7460
7
+ data.tar.gz: f91a4cb03f24b7c18554c8145375a78f1b61cc66a2d99a0841ddca4b547dc97822bd99dfe9267ae1feeb01d3e2c8eba72d70ea075d367671f3e2077e404a8e36
@@ -0,0 +1,90 @@
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
+ end
13
+
14
+ def call(env)
15
+ priority = env[@header] || env["HTTP_#{@header.upcase.tr('-', '_')}"]
16
+
17
+ if priority
18
+ set_priority(priority)
19
+ return @app.call(env)
20
+ end
21
+
22
+ cache_key = cache_key_for(env)
23
+ cached_priority = cache_get(cache_key)
24
+
25
+ if cached_priority
26
+ set_priority(cached_priority)
27
+ end
28
+
29
+ @app.call(env).tap do |status, headers, body|
30
+ app_priority = headers.delete(@header) if headers
31
+
32
+ if app_priority && app_priority != cached_priority
33
+ # update cache for next time
34
+ cache_set(cache_key, app_priority)
35
+ end
36
+ end
37
+ ensure
38
+ Thread.current[ENV_KEY] = nil
39
+ end
40
+
41
+ def self.current_priority
42
+ Thread.current[ENV_KEY]
43
+ end
44
+
45
+ protected
46
+
47
+ def set_priority(priority)
48
+ Thread.current[ENV_KEY] = priority
49
+ end
50
+
51
+ @@cache_prefix = 'Berater:Rack:Prioritizer'
52
+ def cache_key_for(env)
53
+ req = Rack::Request.new(env)
54
+
55
+ req_method = env[Rack::REQUEST_METHOD].downcase
56
+ path = ''
57
+
58
+ if defined?(Rails) && Rails.application
59
+ res = Rails.application.routes.recognize_path(env[Rack::PATH_INFO])
60
+ path = res.values_at(:controller, :action).compact.join('#')
61
+ end
62
+
63
+ if path.empty?
64
+ path = env['PATH_INFO'].gsub(%r{/[0-9]+(/|$)}, '/x\1')
65
+ end
66
+
67
+ [
68
+ @@cache_prefix,
69
+ req_method,
70
+ path,
71
+ ].join(':')
72
+ end
73
+
74
+ @@cache = {}
75
+
76
+ def cache_get(key)
77
+ synchronize { @@cache[key] }
78
+ end
79
+
80
+ def cache_set(key, priority)
81
+ synchronize { @@cache[key] = priority }
82
+ end
83
+
84
+ @@lock = Thread::Mutex.new
85
+ def synchronize(&block)
86
+ @@lock.synchronize(&block)
87
+ end
88
+ end
89
+ end
90
+ 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::Prioritizer
9
+ end
10
+
7
11
  app.middleware.use Rack::Berater
8
12
  end
9
13
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Berater
3
- VERSION = "0.2.0"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
data/lib/rack/berater.rb CHANGED
@@ -1,11 +1,12 @@
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 :Railtie, 'rack/berater/railtie'
9
10
 
10
11
  ERRORS = Set[ ::Berater::Overloaded ]
11
12
 
@@ -27,12 +28,12 @@ module Rack
27
28
  when String
28
29
  options[:body]
29
30
  else
30
- raise ArgumentError, "invalid :body option: #{options[:body]}"
31
+ raise ArgumentError, 'invalid :body option: #{options[:body]}'
31
32
  end
32
33
 
33
34
  # configure headers
34
35
  if @options[:body]
35
- @options[:headers][Rack::CONTENT_TYPE] = "text/plain"
36
+ @options[:headers][Rack::CONTENT_TYPE] = 'text/plain'
36
37
  end
37
38
  @options[:headers].update(options.fetch(:headers, {}))
38
39
  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,295 @@
1
+ describe Rack::Berater::Prioritizer do
2
+ after do
3
+ cache.clear
4
+ Thread.current[described_class::ENV_KEY] = nil
5
+ end
6
+
7
+ let(:cache) { described_class.class_variable_get(:@@cache) }
8
+
9
+ describe '#call' do
10
+ subject { described_class.new(app) }
11
+
12
+ let(:app) { ->(*) { [200, {'Content-Type' => 'text/plain'}, ['OK']] } }
13
+ let(:cache_key) { subject.send(:cache_key_for, env) }
14
+ let(:env) { Rack::MockRequest.env_for('/') }
15
+
16
+ specify 'sanity check' do
17
+ expect(app).to receive(:call).and_call_original
18
+
19
+ Rack::Lint.new(subject).call(env)
20
+ end
21
+
22
+ it 'checks the cache' do
23
+ is_expected.to receive(:cache_get)
24
+
25
+ subject.call(env)
26
+ end
27
+
28
+ context 'with a cached priority' do
29
+ before do
30
+ allow(subject).to receive(:cache_get).with(cache_key).and_return(priority)
31
+ end
32
+
33
+ let(:priority) { '3' }
34
+
35
+ after { subject.call(env) }
36
+
37
+ it 'sets the priority accordingly' do
38
+ is_expected.to receive(:set_priority).with(priority)
39
+ end
40
+
41
+ it 'updates the global priority during the request' do
42
+ expect(app).to receive(:call) do
43
+ expect(described_class.current_priority).to eq priority
44
+ end
45
+ end
46
+
47
+ it 'resets the priority after the request completes' do
48
+ subject.call(env)
49
+ expect(described_class.current_priority).to be nil
50
+ end
51
+ end
52
+
53
+ context 'with an incoming priority header' do
54
+ let(:env) do
55
+ Rack::MockRequest.env_for(
56
+ '/',
57
+ described_class::HEADER => priority,
58
+ )
59
+ end
60
+ let(:priority) { '2' }
61
+
62
+ after { subject.call(env) }
63
+
64
+ it 'uses the header' do
65
+ is_expected.to receive(:set_priority).with(priority)
66
+ end
67
+
68
+ it 'ignores any cached value' do
69
+ allow(subject).to receive(:cache_get).with(cache_key).and_return('123')
70
+ is_expected.to receive(:set_priority).with(priority)
71
+ end
72
+
73
+ it 'resets the priority after the request completes' do
74
+ subject.call(env)
75
+ expect(described_class.current_priority).to be nil
76
+ end
77
+ end
78
+
79
+ context 'when the app returns a priority header' do
80
+ let(:app) do
81
+ ->(*) { [200, { described_class::HEADER => priority }, ['OK']] }
82
+ end
83
+
84
+ let(:priority) { '5' }
85
+
86
+ after { subject.call(env) }
87
+
88
+ it 'caches the priority' do
89
+ is_expected.to receive(:cache_set).with(cache_key, priority)
90
+ end
91
+
92
+ it 'removes the header' do
93
+ _, headers, _ = subject.call(env)
94
+ expect(headers).not_to include described_class::HEADER
95
+ end
96
+
97
+ it 'updates the cache when a different priority is returned' do
98
+ expect(subject).to receive(:cache_get).and_return('123')
99
+ is_expected.to receive(:cache_set).with(cache_key, priority)
100
+ end
101
+
102
+ it 'does not update the cache when the same priority is returned' do
103
+ expect(subject).to receive(:cache_get).and_return(priority)
104
+ is_expected.not_to receive(:cache_set)
105
+ end
106
+ end
107
+
108
+ it 'does not update the cache when no priority is returned' do
109
+ is_expected.not_to receive(:cache_set)
110
+ subject.call(env)
111
+ end
112
+ end
113
+
114
+ describe '#cache_key_for' do
115
+ subject{ described_class.new(nil).send(:cache_key_for, env) }
116
+
117
+ context 'with a basic env' do
118
+ let(:env) { Rack::MockRequest.env_for('/') }
119
+
120
+ it 'has a Berater prefix' do
121
+ is_expected.to match /^Berater:/
122
+ end
123
+
124
+ it 'combines the verb and path' do
125
+ is_expected.to match %r{:get:/$}
126
+ end
127
+ end
128
+
129
+ context 'with a different verb' do
130
+ let(:env) { Rack::MockRequest.env_for('/', method: 'PUT') }
131
+
132
+ it 'combines the verb and path' do
133
+ is_expected.to match %r{:put:/$}
134
+ end
135
+ end
136
+
137
+ context 'with a RESTful path' do
138
+ let(:env) { Rack::MockRequest.env_for('/user/123') }
139
+
140
+ it 'normalizes the id' do
141
+ is_expected.to match %r{:/user/x$}
142
+ end
143
+ end
144
+
145
+ context 'with a RESTful path and trailing slash' do
146
+ let(:env) { Rack::MockRequest.env_for('/user/123/') }
147
+
148
+ it 'normalizes the id and keeps the trailing slash' do
149
+ is_expected.to match %r{:/user/x/$}
150
+ end
151
+ end
152
+
153
+ context 'with a very RESTful path' do
154
+ let(:env) { Rack::MockRequest.env_for('/user/123/friend/456') }
155
+
156
+ it 'normalizes both ids' do
157
+ is_expected.to match %r{:/user/x/friend/x$}
158
+ end
159
+ end
160
+ end
161
+
162
+ context 'as Rack middleware' do
163
+ def call(path = '/')
164
+ get(path).body
165
+ end
166
+
167
+ let(:app) do
168
+ headers = {
169
+ 'Content-Type' => 'text/plain',
170
+ described_class::HEADER => app_priority,
171
+ }.compact
172
+
173
+ Rack::Builder.new do
174
+ use Rack::Lint
175
+ use Rack::Berater::Prioritizer
176
+
177
+ run (lambda do |env|
178
+ [200, headers, [ Rack::Berater::Prioritizer.current_priority.to_s ]]
179
+ end)
180
+ end
181
+ end
182
+
183
+ let(:app_priority) { nil }
184
+
185
+ it 'starts empty' do
186
+ expect(call).to be_empty
187
+ end
188
+
189
+ it 'parses incoming priority header' do
190
+ header described_class::HEADER, '7'
191
+
192
+ expect(call).to eq '7'
193
+ end
194
+
195
+ context 'when app returns a priority header' do
196
+ let(:app_priority) { '8' }
197
+
198
+ it 'parses the priority returned from the app' do
199
+ expect(call).to be_empty
200
+ expect(cache.values).to include app_priority
201
+ end
202
+
203
+ it 'uses the cached priority for subsequent calls' do
204
+ expect(call).to be_empty
205
+ expect(call).to eq app_priority
206
+ end
207
+ end
208
+
209
+ # context 'when two different endpoints are called' do
210
+ # fit 'parses and caches each priority' do
211
+ # @app_priority = '6'
212
+ # expect(call('/six')).to be_empty
213
+
214
+ # expect(call('/six')).to eq '6'
215
+
216
+ # @app_priority = '9'
217
+ # expect(call('/nine')).to be_empty
218
+ # expect(call('/nine')).to '9'
219
+ # end
220
+ # end
221
+ end
222
+
223
+ context 'as Rails middleware' do
224
+ before do
225
+ class EchoController < ActionController::Base
226
+ def index
227
+ render plain: Rack::Berater::Prioritizer.current_priority
228
+ end
229
+
230
+ def six
231
+ response.set_header(Rack::Berater::Prioritizer::HEADER, '6')
232
+ index
233
+ end
234
+
235
+ def nine
236
+ response.set_header(Rack::Berater::Prioritizer::HEADER, '9')
237
+ index
238
+ end
239
+ end
240
+
241
+ Rails.application = Class.new(Rails::Application) do
242
+ config.eager_load = false
243
+ config.hosts.clear # disable hostname filtering
244
+ end
245
+ Rails.application.middleware.use described_class
246
+ Rails.initialize!
247
+
248
+ Rails.application.routes.draw do
249
+ get '/' => 'echo#index'
250
+ get '/six' => 'echo#six'
251
+ get '/nine' => 'echo#nine'
252
+ end
253
+ end
254
+
255
+ let(:app) { Rails.application }
256
+ let(:middleware) { described_class.new(app) }
257
+
258
+ after { Rails.application = nil }
259
+
260
+ describe '#cache_key_for' do
261
+ subject { described_class.new(app).send(:cache_key_for, env) }
262
+
263
+ let(:env) { Rack::MockRequest.env_for('/') }
264
+
265
+ it 'uses the controller and action name' do
266
+ is_expected.to match %r{:echo#index$}
267
+ end
268
+ end
269
+
270
+ context 'when a priority header is sent' do
271
+ before { header described_class::HEADER, priority }
272
+
273
+ let(:priority) { '6' }
274
+
275
+ it 'sets the priority' do
276
+ expect(get('/six').body).to eq priority
277
+ end
278
+ end
279
+
280
+ context 'when the app returns a priority' do
281
+ it 'does not know the first time the controller is called' do
282
+ expect(get('/six').body).to be_empty
283
+ expect(get('/nine').body).to be_empty
284
+ end
285
+
286
+ it 'caches the repsonses for the second time' do
287
+ expect(get('/six').body).to be_empty
288
+ expect(get('/nine').body).to be_empty
289
+
290
+ expect(get('/six').body).to eq '6'
291
+ expect(get('/nine').body).to eq '9'
292
+ end
293
+ end
294
+ end
295
+ 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.3.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-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: berater
@@ -130,9 +130,11 @@ extra_rdoc_files: []
130
130
  files:
131
131
  - lib/rack-berater.rb
132
132
  - lib/rack/berater.rb
133
+ - lib/rack/berater/prioritizer.rb
133
134
  - lib/rack/berater/railtie.rb
134
135
  - lib/rack/berater/version.rb
135
136
  - spec/limiter_spec.rb
137
+ - spec/prioritizer_spec.rb
136
138
  - spec/railtie_spec.rb
137
139
  - spec/rescuer_spec.rb
138
140
  homepage: https://github.com/dpep/rack-berater
@@ -161,4 +163,5 @@ summary: Rack::Berater
161
163
  test_files:
162
164
  - spec/railtie_spec.rb
163
165
  - spec/rescuer_spec.rb
166
+ - spec/prioritizer_spec.rb
164
167
  - spec/limiter_spec.rb