rack-berater 0.2.0 → 0.3.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: 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