Skip to content

Commit 75e40da

Browse files
author
Martín Peñaloza
committed
feat: Route DalliProxy errors through handle_store_error
Replaces the bespoke rescuing helper with handle_store_error and declares ['Dalli::DalliError'] as DalliProxy.default_bypassable_store_errors. Because the entry is a String, loading DalliProxy does not require the dalli gem to be present.
1 parent 97c224e commit 75e40da

2 files changed

Lines changed: 95 additions & 12 deletions

File tree

lib/rack/attack/store_proxy/dalli_proxy.rb

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,37 +18,41 @@ def self.handle?(store)
1818
end
1919
end
2020

21-
def initialize(client)
22-
super(client)
21+
def self.default_bypassable_store_errors
22+
['Dalli::DalliError']
23+
end
24+
25+
def initialize(client, **options)
26+
super(client, **options)
2327
stub_with_if_missing
2428
end
2529

2630
def read(key)
27-
rescuing do
31+
handle_store_error do
2832
with do |client|
2933
client.get(key)
3034
end
3135
end
3236
end
3337

3438
def write(key, value, options = {})
35-
rescuing do
39+
handle_store_error do
3640
with do |client|
3741
client.set(key, value, options.fetch(:expires_in, 0), raw: true)
3842
end
3943
end
4044
end
4145

4246
def increment(key, amount, options = {})
43-
rescuing do
47+
handle_store_error do
4448
with do |client|
4549
client.incr(key, amount, options.fetch(:expires_in, 0), amount)
4650
end
4751
end
4852
end
4953

5054
def delete(key)
51-
rescuing do
55+
handle_store_error do
5256
with do |client|
5357
client.delete(key)
5458
end
@@ -66,12 +70,6 @@ def with
6670
end
6771
end
6872
end
69-
70-
def rescuing
71-
yield
72-
rescue Dalli::DalliError
73-
nil
74-
end
7573
end
7674
end
7775
end

spec/rack_attack_dalli_proxy_spec.rb

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,94 @@
22

33
require_relative 'spec_helper'
44

5+
module DalliProxySpec
6+
class FakeDalliClient
7+
def initialize(raises: nil)
8+
@raises = raises
9+
end
10+
11+
def get(_key)
12+
bomb_or(:value)
13+
end
14+
15+
def set(*_args)
16+
bomb_or(:ok)
17+
end
18+
19+
def incr(*_args)
20+
bomb_or(1)
21+
end
22+
23+
def delete(_key)
24+
bomb_or(:ok)
25+
end
26+
27+
def with
28+
yield(self)
29+
end
30+
31+
private
32+
33+
def bomb_or(value)
34+
raise @raises if @raises
35+
36+
value
37+
end
38+
end
39+
end
40+
541
describe Rack::Attack::StoreProxy::DalliProxy do
642
it 'should stub Dalli::Client#with on older clients' do
743
proxy = Rack::Attack::StoreProxy::DalliProxy.new(Class.new)
844
proxy.with {} # will not raise an error
945
end
46+
47+
describe "bypassable_store_errors" do
48+
it "uses ['Dalli::DalliError'] as the default bypass list (as a String)" do
49+
_(Rack::Attack::StoreProxy::DalliProxy.default_bypassable_store_errors)
50+
.must_equal ['Dalli::DalliError']
51+
end
52+
53+
it "bypasses Dalli::DalliError by default on #read" do
54+
skip 'dalli gem not available' unless defined?(::Dalli::DalliError)
55+
client = DalliProxySpec::FakeDalliClient.new(raises: Dalli::DalliError.new("server down"))
56+
proxy = Rack::Attack::StoreProxy::DalliProxy.new(client)
57+
_(proxy.read("k")).must_be_nil
58+
end
59+
60+
it "re-raises non-Dalli errors by default" do
61+
client = DalliProxySpec::FakeDalliClient.new(raises: ArgumentError.new("boom"))
62+
proxy = Rack::Attack::StoreProxy::DalliProxy.new(client)
63+
_(-> { proxy.read("k") }).must_raise ArgumentError
64+
end
65+
66+
it "expresses its default as a String class name so no const is resolved eagerly" do
67+
default = Rack::Attack::StoreProxy::DalliProxy.default_bypassable_store_errors
68+
_(default).must_be_kind_of Array
69+
_(default.all? { |entry| entry.is_a?(String) }).must_equal true
70+
end
71+
72+
it "user :none disables the default Dalli bypass" do
73+
skip 'dalli gem not available' unless defined?(::Dalli::DalliError)
74+
client = DalliProxySpec::FakeDalliClient.new(raises: Dalli::DalliError.new("server down"))
75+
proxy = Rack::Attack::StoreProxy::DalliProxy.new(client, bypassable_store_errors: :none)
76+
_(-> { proxy.read("k") }).must_raise Dalli::DalliError
77+
end
78+
79+
it "user Array overrides the default Dalli bypass" do
80+
skip 'dalli gem not available' unless defined?(::Dalli::DalliError)
81+
client = DalliProxySpec::FakeDalliClient.new(raises: Dalli::DalliError.new("server down"))
82+
proxy = Rack::Attack::StoreProxy::DalliProxy.new(
83+
client,
84+
bypassable_store_errors: [ArgumentError]
85+
)
86+
_(-> { proxy.read("k") }).must_raise Dalli::DalliError
87+
end
88+
89+
it "user :all bypasses any error" do
90+
client = DalliProxySpec::FakeDalliClient.new(raises: RuntimeError.new("boom"))
91+
proxy = Rack::Attack::StoreProxy::DalliProxy.new(client, bypassable_store_errors: :all)
92+
_(proxy.read("k")).must_be_nil
93+
end
94+
end
1095
end

0 commit comments

Comments
 (0)