From 90c09c8ae592cc2422f4f54eedb5efed62d45818 Mon Sep 17 00:00:00 2001 From: twinturbo Date: Tue, 8 May 2012 09:33:46 +0200 Subject: [PATCH] Expand cache keys like ActiveSupport::Cache --- lib/active_support/cache/dalli_store.rb | 46 +++++++++++++++++++++++-- test/test_active_support.rb | 32 +++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/lib/active_support/cache/dalli_store.rb b/lib/active_support/cache/dalli_store.rb index 38ccefb8..c201ec1f 100644 --- a/lib/active_support/cache/dalli_store.rb +++ b/lib/active_support/cache/dalli_store.rb @@ -46,6 +46,8 @@ def initialize(*addresses) def fetch(name, options=nil) options ||= {} + name = namespaced_key(name, options) + if block_given? unless options[:force] entry = instrument(:read, name, options) do |payload| @@ -71,6 +73,8 @@ def fetch(name, options=nil) def read(name, options=nil) options ||= {} + name = namespaced_key(name, options) + instrument(:read, name, options) do |payload| entry = read_entry(name, options) payload[:hit] = !!entry if payload @@ -80,6 +84,8 @@ def read(name, options=nil) def write(name, value, options=nil) options ||= {} + name = namespaced_key(name, options) + instrument(:write, name, options) do |payload| write_entry(name, value, options) end @@ -87,20 +93,24 @@ def write(name, value, options=nil) def exist?(name, options=nil) options ||= {} + name = namespaced_key(name, options) + !read_entry(name, options).nil? end def delete(name, options=nil) + options ||= {} + name = namespaced_key(name, options) + delete_entry(name, options) end # Reads multiple keys from the cache using a single call to the # servers for all keys. Keys must be Strings. def read_multi(*names) - names.extract_options! + options = names.extract_options! names = names.flatten - - mapping = names.inject({}) { |memo, name| memo[escape(name)] = name; memo } + mapping = names.inject({}) { |memo, name| memo[escape(namespaced_key(name, options))] = name; memo } instrument(:read_multi, names) do results = @data.get_multi(mapping.keys) results.inject({}) do |memo, (inner, value)| @@ -119,6 +129,7 @@ def read_multi(*names) # memcached counters cannot hold negative values. def increment(name, amount = 1, options=nil) options ||= {} + name = namespaced_key(name, options) initial = options.has_key?(:initial) ? options[:initial] : amount expires_in = options[:expires_in] instrument(:increment, name, :amount => amount) do @@ -137,6 +148,7 @@ def increment(name, amount = 1, options=nil) # memcached counters cannot hold negative values. def decrement(name, amount = 1, options=nil) options ||= {} + name = namespaced_key(name, options) initial = options.has_key?(:initial) ? options[:initial] : 0 expires_in = options[:expires_in] instrument(:decrement, name, :amount => amount) do @@ -197,6 +209,34 @@ def delete_entry(key, options) # :nodoc: end private + # Expand key to be a consistent string value. Invoke +cache_key+ if + # object responds to +cache_key+. Otherwise, to_param method will be + # called. If the key is a Hash, then keys will be sorted alphabetically. + def expanded_key(key) # :nodoc: + return key.cache_key.to_s if key.respond_to?(:cache_key) + + case key + when Array + if key.size > 1 + key = key.collect{|element| expanded_key(element)} + else + key = key.first + end + when Hash + key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"} + end + + key.to_param + end + + # Prefix a key with the namespace. Namespace and key will be delimited with a colon. + def namespaced_key(key, options = {}) + key = expanded_key(key) + namespace = options[:namespace] if options + prefix = namespace.is_a?(Proc) ? namespace.call : namespace + key = "#{prefix}:#{key}" if prefix + key + end def escape(key) key = key.to_s.dup diff --git a/test/test_active_support.rb b/test/test_active_support.rb index 9207907b..a243af98 100644 --- a/test/test_active_support.rb +++ b/test/test_active_support.rb @@ -1,6 +1,12 @@ # encoding: utf-8 require 'helper' +class MockUser + def cache_key + "users/1/21348793847982314" + end +end + describe 'ActiveSupport' do context 'active_support caching' do @@ -50,6 +56,11 @@ @dalli.write('false', false) dvalue = @dalli.fetch('false') { flunk } assert_equal false, dvalue + + user = MockUser.new + @dalli.write(user.cache_key, false) + dvalue = @dalli.fetch(user) { flunk } + assert_equal false, dvalue end end end @@ -118,6 +129,16 @@ dres = @dalli.delete(y) assert_equal true, dres + + user = MockUser.new + dres = @dalli.write(user.cache_key, "foo") + assert_equal true, dres + + dres = @dalli.read(user) + assert_equal "foo", dres + + dres = @dalli.delete(user) + assert_equal true, dres end end end @@ -149,6 +170,13 @@ assert_equal nil, @dalli.decrement('counterZ2', 1, :initial => nil) assert_equal nil, @dalli.read('counterZ2') + + user = MockUser.new + assert_equal true, @dalli.write(user, 0, :raw => true) + assert_equal 1, @dalli.increment(user) + assert_equal 2, @dalli.increment(user) + assert_equal 1, @dalli.decrement(user) + assert_equal "1", @dalli.read(user, :raw => true) end end end @@ -164,6 +192,10 @@ assert_equal true, @dalli.exist?(:false_value) assert_equal false, @dalli.exist?(:bar) + + user = MockUser.new + @dalli.write(user, 'foo') + assert_equal true, @dalli.exist?(user) end end end