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