diff --git a/lib/cached_resource/caching.rb b/lib/cached_resource/caching.rb index e2b465a..803b534 100644 --- a/lib/cached_resource/caching.rb +++ b/lib/cached_resource/caching.rb @@ -148,19 +148,31 @@ def cache_clear(options=nil) cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} CLEAR ALL") end else - cached_resource.cache.delete_matched("^#{name_key}/*").tap do |result| - cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} CLEAR #{name_key}/*") + cached_resource.cache.delete_matched(cache_key_delete_pattern).tap do |result| + cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} CLEAR #{cache_key_delete_pattern}") end end end + def cache_key_delete_pattern + case cached_resource.cache.class.to_s + when "ActiveSupport::Cache::MemoryStore", "ActiveSupport::Cache::FileStore" + /^#{name_key}\// + else + "#{name_key}/*" + end + end + # Generate the request cache key. def cache_key(*arguments) "#{name_key}/#{arguments.join('/')}".downcase.delete(' ') end def name_key - name.parameterize.gsub("-", "/") + @name_key ||= begin + prefix = cached_resource.cache_key_prefix.nil? ? "" : "#{cached_resource.cache_key_prefix}/" + prefix + name.parameterize.gsub("-", "/") + end end # Make a full duplicate of an ActiveResource record. diff --git a/lib/cached_resource/configuration.rb b/lib/cached_resource/configuration.rb index 7374ed6..2c8655a 100644 --- a/lib/cached_resource/configuration.rb +++ b/lib/cached_resource/configuration.rb @@ -27,12 +27,13 @@ class Configuration < OpenStruct # :concurrent_write, default: false def initialize(options={}) super({ - :enabled => true, :cache => defined?(Rails.cache) && Rails.cache || CACHE, :cache_collections => true, + :cache_key_prefix => nil, :collection_arguments => [:all], :collection_synchronize => false, :concurrent_write => false, + :enabled => true, :logger => defined?(Rails.logger) && Rails.logger || LOGGER, :race_condition_ttl => 86400, :ttl => 604800, diff --git a/spec/cached_resource/caching_spec.rb b/spec/cached_resource/caching_spec.rb index 6d144b7..efc927e 100644 --- a/spec/cached_resource/caching_spec.rb +++ b/spec/cached_resource/caching_spec.rb @@ -85,8 +85,70 @@ class NotTheThing < ActiveResource::Base include_examples "caching" + # NOTE: Possibly flaky, but best easy/cheap way to test concurrency. context 'when concurrency is turned on' do - include_examples "caching" + before do + # it's on by default, but lets call the method + # to make sure it works + Thing.cached_resource.cache.clear + Thing.cached_resource.on! + NotTheThing.cached_resource.cache.clear + NotTheThing.cached_resource.on! + + ActiveResource::HttpMock.reset! + ActiveResource::HttpMock.respond_to do |mock| + mock.get "/things/5.json", {}, {:thing => {:id => 1, :name => ("x" * 1_000_000) }}.to_json + end + end + + after do + Thing.cached_resource.concurrent_write = false + end + + it "should cache a response asynchronusly when on" do + Thing.cached_resource.concurrent_write = true + result = Thing.find(5) + read_from_cache("thing/5").should == nil + loops = 0 + begin + Timeout.timeout(5) do + loop do + loops += 1 + if read_from_cache("thing/5") == result + break + else + sleep 0.5 + end + end + end + rescue Timeout::Error + RSpec::Expectations.fail_with("Concurrency failed: Cache was not populated within the expected time") + end + + loops.should > 0 + read_from_cache("thing/5").should == result + end + + it "will cache a response synchronously when off" do + Thing.cached_resource.concurrent_write = false + result = Thing.find(5) + read_from_cache("thing/5").should == result + end + end + + context "When there is a cache prefix" do + before do + Thing.cached_resource.cache_key_prefix = "prefix123" + end + + after do + Thing.cached_resource.cache_key_prefix = nil + end + + it "caches with the cache_key_prefix" do + result = Thing.find(1) + read_from_cache("prefix123/thing/1").should == result + end end it "should cache a response for a string primary key" do