Skip to content

Commit c90893f

Browse files
committed
Move from proxies to well-defined adapters [skip ci]
1 parent 95347e3 commit c90893f

File tree

11 files changed

+144
-125
lines changed

11 files changed

+144
-125
lines changed

lib/rack/attack.rb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@
1010

1111
module Rack
1212
class Attack
13-
class MisconfiguredStoreError < StandardError; end
14-
class MissingStoreError < StandardError; end
15-
1613
autoload :Cache, 'rack/attack/cache'
1714
autoload :Check, 'rack/attack/check'
1815
autoload :Throttle, 'rack/attack/throttle'

lib/rack/attack/cache.rb

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ def initialize
1313

1414
attr_reader :store
1515
def store=(store)
16-
@store = StoreProxy.build(store)
16+
adapter = StoreAdapter.lookup(store)
17+
if adapter
18+
@store = adapter.new(store)
19+
elsif store?(store)
20+
@store = store
21+
else
22+
raise ArgumentError
23+
end
1724
end
1825

1926
def count(unprefixed_key, period)
@@ -22,9 +29,6 @@ def count(unprefixed_key, period)
2229
end
2330

2431
def read(unprefixed_key)
25-
enforce_store_presence!
26-
enforce_store_method_presence!(:read)
27-
2832
store.read("#{prefix}:#{unprefixed_key}")
2933
end
3034

@@ -51,33 +55,19 @@ def key_and_expiry(unprefixed_key, period)
5155
end
5256

5357
def do_count(key, expires_in)
54-
enforce_store_presence!
55-
enforce_store_method_presence!(:increment)
56-
5758
result = store.increment(key, 1, expires_in: expires_in)
5859

5960
# NB: Some stores return nil when incrementing uninitialized values
6061
if result.nil?
61-
enforce_store_method_presence!(:write)
62-
6362
store.write(key, 1, expires_in: expires_in)
6463
end
6564
result || 1
6665
end
6766

68-
def enforce_store_presence!
69-
if store.nil?
70-
raise Rack::Attack::MissingStoreError
71-
end
72-
end
67+
STORE_METHODS = [:read, :write, :increment, :delete].freeze
7368

74-
def enforce_store_method_presence!(method_name)
75-
if !store.respond_to?(method_name)
76-
raise(
77-
Rack::Attack::MisconfiguredStoreError,
78-
"Configured store #{store.class.name} doesn't respond to ##{method_name} method"
79-
)
80-
end
69+
def store?(object)
70+
STORE_METHODS.all? { |meth| object.respond_to?(meth) }
8171
end
8272
end
8373
end

lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb renamed to lib/rack/attack/store_adapters/active_support_redis_store_adapter.rb

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
# frozen_string_literal: true
22

3-
require 'delegate'
4-
53
module Rack
64
class Attack
7-
module StoreProxy
8-
class ActiveSupportRedisStoreProxy < SimpleDelegator
5+
module StoreAdapters
6+
class ActiveSupportRedisStoreAdapter < StoreAdapter
97
def self.handle?(store)
108
defined?(::Redis) &&
119
defined?(::ActiveSupport::Cache::RedisStore) &&
1210
store.is_a?(::ActiveSupport::Cache::RedisStore)
1311
end
1412

15-
def increment(name, amount = 1, options = {})
13+
def increment(key, amount = 1, options = {})
1614
# #increment ignores options[:expires_in].
1715
#
1816
# So in order to workaround this we use #write (which sets expiration) to initialize
1917
# the counter. After that we continue using the original #increment.
20-
if options[:expires_in] && !read(name)
21-
write(name, amount, options)
18+
if options[:expires_in] && !read(key)
19+
write(key, amount, options)
2220

2321
amount
2422
else
25-
super
23+
store.increment(key, amount, options)
2624
end
2725
end
2826

29-
def read(name, options = {})
30-
super(name, options.merge!(raw: true))
27+
def read(key, options = {})
28+
store.read(key, options.merge!(raw: true))
29+
end
30+
31+
def write(key, value, options = {})
32+
store.write(key, value, options.merge!(raw: true))
3133
end
3234

33-
def write(name, value, options = {})
34-
super(name, value, options.merge!(raw: true))
35+
def delete(key, options = {})
36+
store.delete(key, options)
3537
end
3638
end
3739
end

lib/rack/attack/store_proxy/dalli_proxy.rb renamed to lib/rack/attack/store_adapters/dalli_adapter.rb

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
# frozen_string_literal: true
22

3-
require 'delegate'
4-
53
module Rack
64
class Attack
7-
module StoreProxy
8-
class DalliProxy < SimpleDelegator
5+
module StoreAdapters
6+
class DalliAdapter < StoreAdapter
97
def self.handle?(store)
108
return false unless defined?(::Dalli)
119

@@ -18,38 +16,38 @@ def self.handle?(store)
1816
end
1917
end
2018

21-
def initialize(client)
22-
super(client)
19+
def initialize(store)
20+
super
2321
stub_with_if_missing
2422
end
2523

2624
def read(key)
2725
rescuing do
28-
with do |client|
26+
store.with do |client|
2927
client.get(key)
3028
end
3129
end
3230
end
3331

3432
def write(key, value, options = {})
3533
rescuing do
36-
with do |client|
34+
store.with do |client|
3735
client.set(key, value, options.fetch(:expires_in, 0), raw: true)
3836
end
3937
end
4038
end
4139

4240
def increment(key, amount, options = {})
4341
rescuing do
44-
with do |client|
42+
store.with do |client|
4543
client.incr(key, amount, options.fetch(:expires_in, 0), amount)
4644
end
4745
end
4846
end
4947

5048
def delete(key)
5149
rescuing do
52-
with do |client|
50+
store.with do |client|
5351
client.delete(key)
5452
end
5553
end
@@ -58,11 +56,9 @@ def delete(key)
5856
private
5957

6058
def stub_with_if_missing
61-
unless __getobj__.respond_to?(:with)
62-
class << self
63-
def with
64-
yield __getobj__
65-
end
59+
unless store.respond_to?(:with)
60+
def store.with
61+
yield store
6662
end
6763
end
6864
end
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# frozen_string_literal: true
2+
3+
module Rack
4+
class Attack
5+
module StoreAdapters
6+
class MemCacheStoreAdapter < StoreAdapter
7+
def self.handle?(store)
8+
defined?(::Dalli) &&
9+
defined?(::ActiveSupport::Cache::MemCacheStore) &&
10+
store.is_a?(::ActiveSupport::Cache::MemCacheStore)
11+
end
12+
13+
def read(key, options = {})
14+
store.read(key, options)
15+
end
16+
17+
def write(key, value, options = {})
18+
store.write(key, value, options.merge!(raw: true))
19+
end
20+
21+
def increment(*args)
22+
store.increment(*args)
23+
end
24+
25+
def delete(*args)
26+
store.delete(*args)
27+
end
28+
end
29+
end
30+
end
31+
end

lib/rack/attack/store_proxy/redis_proxy.rb renamed to lib/rack/attack/store_adapters/redis_adapter.rb

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,48 @@
11
# frozen_string_literal: true
22

3-
require 'delegate'
4-
53
module Rack
64
class Attack
7-
module StoreProxy
8-
class RedisProxy < SimpleDelegator
9-
def initialize(*args)
5+
module StoreAdapters
6+
class RedisProxy < StoreAdapter
7+
def initialize(store)
108
if Gem::Version.new(Redis::VERSION) < Gem::Version.new("3")
119
warn 'RackAttack requires Redis gem >= 3.0.0.'
1210
end
1311

14-
super(*args)
12+
super
1513
end
1614

1715
def self.handle?(store)
1816
defined?(::Redis) && store.is_a?(::Redis)
1917
end
2018

2119
def read(key)
22-
rescuing { get(key) }
20+
rescuing { store.get(key) }
2321
end
2422

2523
def write(key, value, options = {})
2624
if (expires_in = options[:expires_in])
27-
rescuing { setex(key, expires_in, value) }
25+
rescuing { store.setex(key, expires_in, value) }
2826
else
29-
rescuing { set(key, value) }
27+
rescuing { store.set(key, value) }
3028
end
3129
end
3230

3331
def increment(key, amount, options = {})
3432
count = nil
3533

3634
rescuing do
37-
pipelined do
38-
count = incrby(key, amount)
39-
expire(key, options[:expires_in]) if options[:expires_in]
35+
store.pipelined do
36+
count = store.incrby(key, amount)
37+
store.expire(key, options[:expires_in]) if options[:expires_in]
4038
end
4139
end
4240

4341
count.value if count
4442
end
4543

4644
def delete(key, _options = {})
47-
rescuing { del(key) }
45+
rescuing { store.del(key) }
4846
end
4947

5048
private

lib/rack/attack/store_proxy/redis_cache_store_proxy.rb renamed to lib/rack/attack/store_adapters/redis_cache_store_adapter.rb

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,43 @@
11
# frozen_string_literal: true
22

3-
require 'delegate'
4-
53
module Rack
64
class Attack
7-
module StoreProxy
8-
class RedisCacheStoreProxy < SimpleDelegator
5+
module StoreAdapters
6+
class RedisCacheStoreAdapter < StoreAdapter
97
def self.handle?(store)
108
store.class.name == "ActiveSupport::Cache::RedisCacheStore"
119
end
1210

13-
def increment(name, amount = 1, options = {})
11+
def increment(key, amount = 1, options = {})
1412
# RedisCacheStore#increment ignores options[:expires_in].
1513
#
1614
# So in order to workaround this we use RedisCacheStore#write (which sets expiration) to initialize
1715
# the counter. After that we continue using the original RedisCacheStore#increment.
1816
rescuing do
19-
if options[:expires_in] && !read(name)
20-
write(name, amount, options)
17+
if options[:expires_in] && !read(key)
18+
write(key, amount, options)
2119

2220
amount
2321
else
24-
super
22+
store.increment(key, amount, options)
2523
end
2624
end
2725
end
2826

29-
def read(*_args)
30-
rescuing { super }
27+
def read(*args)
28+
rescuing { store.read(*args) }
3129
end
3230

33-
def write(name, value, options = {})
31+
def write(key, value, options = {})
3432
rescuing do
35-
super(name, value, options.merge!(raw: true))
33+
store.write(key, value, options.merge!(raw: true))
3634
end
3735
end
3836

37+
def delete(*args)
38+
rescuing { store.delete(*args) }
39+
end
40+
3941
private
4042

4143
def rescuing

lib/rack/attack/store_proxy/redis_store_proxy.rb renamed to lib/rack/attack/store_adapters/redis_store_adapter.rb

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
# frozen_string_literal: true
22

3-
require 'delegate'
4-
53
module Rack
64
class Attack
7-
module StoreProxy
8-
class RedisStoreProxy < RedisProxy
5+
module StoreAdapters
6+
class RedisStoreAdapter < RedisAdapter
97
def self.handle?(store)
108
defined?(::Redis::Store) && store.is_a?(::Redis::Store)
119
end
1210

1311
def read(key)
14-
rescuing { get(key, raw: true) }
12+
rescuing { store.get(key, raw: true) }
1513
end
1614

1715
def write(key, value, options = {})
1816
if (expires_in = options[:expires_in])
19-
rescuing { setex(key, expires_in, value, raw: true) }
17+
rescuing { store.setex(key, expires_in, value, raw: true) }
2018
else
21-
rescuing { set(key, value, raw: true) }
19+
rescuing { store.set(key, value, raw: true) }
2220
end
2321
end
2422
end

0 commit comments

Comments
 (0)