diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index f59e155..b1b07c5 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -19,6 +19,7 @@ class Rack::Attack autoload :StoreProxy, 'rack/attack/store_proxy' autoload :DalliProxy, 'rack/attack/store_proxy/dalli_proxy' autoload :MemCacheProxy, 'rack/attack/store_proxy/mem_cache_proxy' + autoload :MemCacheStoreProxy, 'rack/attack/store_proxy/mem_cache_store_proxy' autoload :RedisProxy, 'rack/attack/store_proxy/redis_proxy' autoload :RedisStoreProxy, 'rack/attack/store_proxy/redis_store_proxy' autoload :RedisCacheStoreProxy, 'rack/attack/store_proxy/redis_cache_store_proxy' diff --git a/lib/rack/attack/store_proxy.rb b/lib/rack/attack/store_proxy.rb index 29bef49..fdd0e23 100644 --- a/lib/rack/attack/store_proxy.rb +++ b/lib/rack/attack/store_proxy.rb @@ -3,10 +3,7 @@ module Rack class Attack module StoreProxy - PROXIES = [DalliProxy, MemCacheProxy, RedisStoreProxy, RedisProxy, RedisCacheStoreProxy].freeze - - ACTIVE_SUPPORT_WRAPPER_CLASSES = Set.new(['ActiveSupport::Cache::MemCacheStore', 'ActiveSupport::Cache::RedisStore', 'ActiveSupport::Cache::RedisCacheStore']).freeze - ACTIVE_SUPPORT_CLIENTS = Set.new(['Redis::Store', 'Dalli::Client', 'MemCache']).freeze + PROXIES = [DalliProxy, MemCacheStoreProxy, MemCacheProxy, RedisStoreProxy, RedisProxy, RedisCacheStoreProxy].freeze def self.build(store) client = unwrap_active_support_stores(store) @@ -17,15 +14,8 @@ module Rack def self.unwrap_active_support_stores(store) # ActiveSupport::Cache::RedisStore doesn't expose any way to set an expiry, # so use the raw Redis::Store instead. - # We also want to use the underlying Dalli client instead of ::ActiveSupport::Cache::MemCacheStore, - # and the MemCache client if using Rails 3.x - - if store.instance_variable_defined?(:@data) - client = store.instance_variable_get(:@data) - end - - if ACTIVE_SUPPORT_WRAPPER_CLASSES.include?(store.class.to_s) && ACTIVE_SUPPORT_CLIENTS.include?(client.class.to_s) - client + if store.class.name == 'ActiveSupport::Cache::RedisStore' + store.instance_variable_get(:@data) else store end diff --git a/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb b/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb new file mode 100644 index 0000000..eebfed7 --- /dev/null +++ b/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'delegate' + +module Rack + class Attack + module StoreProxy + class MemCacheStoreProxy < SimpleDelegator + def self.handle?(store) + defined?(::Dalli) && defined?(::ActiveSupport::Cache::MemCacheStore) && store.is_a?(::ActiveSupport::Cache::MemCacheStore) + end + + def write(name, value, options = {}) + super(name, value, options.merge!(raw: true)) + end + end + end + end +end diff --git a/spec/acceptance/stores/active_support_mem_cache_store_pooled_spec.rb b/spec/acceptance/stores/active_support_mem_cache_store_pooled_spec.rb new file mode 100644 index 0000000..c477380 --- /dev/null +++ b/spec/acceptance/stores/active_support_mem_cache_store_pooled_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require_relative "../../spec_helper" + +if defined?(::ConnectionPool) && defined?(::Dalli) + require_relative "../../support/cache_store_helper" + require "timecop" + + describe "ActiveSupport::Cache::MemCacheStore (pooled) as a cache backend" do + before do + Rack::Attack.cache.store = ActiveSupport::Cache::MemCacheStore.new(pool_size: 2) + end + + after do + Rack::Attack.cache.store.clear + end + + it_works_for_cache_backed_features(fetch_from_store: ->(key) { + Rack::Attack.cache.store.read(key) + }) + end +end diff --git a/spec/acceptance/stores/active_support_mem_cache_store_spec.rb b/spec/acceptance/stores/active_support_mem_cache_store_spec.rb index 74e40f0..65abe7d 100644 --- a/spec/acceptance/stores/active_support_mem_cache_store_spec.rb +++ b/spec/acceptance/stores/active_support_mem_cache_store_spec.rb @@ -12,9 +12,9 @@ if defined?(::Dalli) end after do - Rack::Attack.cache.store.flush_all + Rack::Attack.cache.store.clear end - it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.get(key) }) + it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.read(key) }) end end