From 7b0bd1001d41d50ff035ac3f80af05241e37abc9 Mon Sep 17 00:00:00 2001 From: Morgan Brown Date: Mon, 9 Jan 2012 16:38:33 -0800 Subject: [PATCH] Update cache entries when a subset of all is retrieved Signed-off-by: David Souza --- lib/cached_resource/caching.rb | 35 ++++++++++++++++++++++++---- spec/cached_resource/caching_spec.rb | 20 ++++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/lib/cached_resource/caching.rb b/lib/cached_resource/caching.rb index 29fc3e6..d8d7096 100644 --- a/lib/cached_resource/caching.rb +++ b/lib/cached_resource/caching.rb @@ -43,14 +43,39 @@ def find_via_reload(key, *arguments) # write cache entries for all its members # otherwise update an existing collection if possible. def cache_collection_synchronize(object, *arguments) - if arguments.length == 1 && arguments[0] == :all - object.each {|r| cache_write(r.send(primary_key), r)} - elsif !arguments.include?(:all) && (collection = cache_read(:all)) - collection.each_with_index {|member, i| collection[i] = object if member.send(primary_key) == object.send(primary_key)} - cache_write(:all, collection) + if object.is_a? Array + update_singles_cache(object) + update_collection_cache(object) unless is_collection?(*arguments) + else + update_collection_cache(object) end end + # Update the cache of singles with an array of updates. + def update_singles_cache(updates) + updates = Array(updates) + updates.each {|object| cache_write(object.send(primary_key), object)} + end + + # Update the "mother" collection with an array of updates. + def update_collection_cache(updates) + updates = Array(updates) + collection = cache_read(:all) + + if collection && !updates.empty? + store = RUBY_VERSION.to_f < 1.9 ? ActiveSupport::OrderedHash.new : {} + index = collection.inject(store) {|hash, object| hash[object.send(primary_key)] = object; hash} + updates.each {|object| index[object.send(primary_key)] = object} + cache_write(:all, index.values) + end + end + + # Determine if the given arguments represent + # the entire collection of objects. + def is_collection?(*arguments) + arguments.length == 1 && arguments[0] == :all + end + # Read a entry from the cache for the given key. # The key is processed to make sure it is valid. def cache_read(key) diff --git a/spec/cached_resource/caching_spec.rb b/spec/cached_resource/caching_spec.rb index 9051060..abf7ea5 100644 --- a/spec/cached_resource/caching_spec.rb +++ b/spec/cached_resource/caching_spec.rb @@ -138,6 +138,26 @@ class Thing < ActiveResource::Base Thing.cached_resource.cache.read("thing/all")[0].should == result Thing.cached_resource.cache.read("thing/all")[0].name.should == result.name end + + it "should update both the collection and the member cache entries when a subset of the collection is retrieved" do + # create cache entries for + old_individual = Thing.find(1) + old_collection = Thing.all + + # change the server + ActiveResource::HttpMock.respond_to do |mock| + mock.get "/things.json?name=Ari", {}, [@other_thing[:thing]].to_json(:root => :thing) + end + + # make a request for a subset of the "mother" collection + result = Thing.find(:all, :params => {:name => "Ari"}) + # the collection should be updated to reflect the server change + Thing.cached_resource.cache.read("thing/all")[0].should == result[0] + Thing.cached_resource.cache.read("thing/all")[0].name.should == result[0].name + # the individual should be updated to reflect the server change + Thing.cached_resource.cache.read("thing/1").should == result[0] + Thing.cached_resource.cache.read("thing/1").name.should == result[0].name + end end describe "when collection synchronize is disabled" do