From a53ad5bba37199047ba20194933e122bf6b0252f Mon Sep 17 00:00:00 2001 From: Nahum Wild Date: Thu, 15 Jan 2009 21:28:10 -0600 Subject: Added in a local per request cache to MemCacheStore. It acts as a buffer to stop unneccessary requests being sent through to memcache [#1653 state:resolved] Signed-off-by: Joshua Peek --- .../lib/active_support/cache/mem_cache_store.rb | 65 ++++++++++- activesupport/test/caching_test.rb | 119 ++++++++++++++++++--- 2 files changed, 164 insertions(+), 20 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index f9a7fb1440..eed9faac6d 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -13,6 +13,7 @@ module ActiveSupport # server goes down, then MemCacheStore will ignore it until it goes back # online. # - Time-based expiry support. See #write and the +:expires_in+ option. + # - Per-request in memory cache for all communication with the MemCache server(s). class MemCacheStore < Store module Response # :nodoc: STORED = "STORED\r\n" @@ -22,6 +23,24 @@ module ActiveSupport DELETED = "DELETED\r\n" end + # this allows caching of the fact that there is nothing in the remote cache + NULL = 'mem_cache_store:null' + + THREAD_LOCAL_KEY = :mem_cache_store_cache + + class LocalCache + def initialize(app) + @app = app + end + + def call(env) + Thread.current[THREAD_LOCAL_KEY] = MemoryStore.new + @app.call(env) + ensure + Thread.current[THREAD_LOCAL_KEY] = nil + end + end + attr_reader :addresses # Creates a new MemCacheStore object, with the given memcached server @@ -42,7 +61,18 @@ module ActiveSupport def read(key, options = nil) # :nodoc: super - @data.get(key, raw?(options)) + + value = local_cache && local_cache.read(key) + if value == NULL + nil + elsif value.nil? + value = @data.get(key, raw?(options)) + local_cache.write(key, value || NULL) if local_cache + value + else + # forcing the value to be immutable + value.dup + end rescue MemCache::MemCacheError => e logger.error("MemCacheError (#{e}): #{e.message}") nil @@ -61,6 +91,7 @@ module ActiveSupport # memcache-client will break the connection if you send it an integer # in raw mode, so we convert it to a string to be sure it continues working. value = value.to_s if raw?(options) + local_cache.write(key, value || NULL) if local_cache response = @data.send(method, key, value, expires_in(options), raw?(options)) response == Response::STORED rescue MemCache::MemCacheError => e @@ -70,6 +101,7 @@ module ActiveSupport def delete(key, options = nil) # :nodoc: super + local_cache.write(key, NULL) if local_cache response = @data.delete(key, expires_in(options)) response == Response::DELETED rescue MemCache::MemCacheError => e @@ -80,14 +112,27 @@ module ActiveSupport def exist?(key, options = nil) # :nodoc: # Doesn't call super, cause exist? in memcache is in fact a read # But who cares? Reading is very fast anyway - !read(key, options).nil? + # Local cache is checked first, if it doesn't know then memcache itself is read from + value = local_cache.read(key) if local_cache + if value == NULL + false + elsif value + true + else + !read(key, options).nil? + end end def increment(key, amount = 1) # :nodoc: log("incrementing", key, amount) response = @data.incr(key, amount) - response == Response::NOT_FOUND ? nil : response + unless response == Response::NOT_FOUND + local_cache.write(key, response.to_s) if local_cache + response + else + nil + end rescue MemCache::MemCacheError nil end @@ -96,17 +141,25 @@ module ActiveSupport log("decrement", key, amount) response = @data.decr(key, amount) - response == Response::NOT_FOUND ? nil : response + unless response == Response::NOT_FOUND + local_cache.write(key, response.to_s) if local_cache + response + else + nil + end rescue MemCache::MemCacheError nil end def delete_matched(matcher, options = nil) # :nodoc: + # don't do any local caching at present, just pass + # through and let the error happen super raise "Not supported by Memcache" end def clear + local_cache.clear if local_cache @data.flush_all end @@ -115,6 +168,10 @@ module ActiveSupport end private + def local_cache + Thread.current[THREAD_LOCAL_KEY] + end + def expires_in(options) (options && options[:expires_in]) || 0 end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index d8506de986..5d220f4403 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -1,18 +1,18 @@ require 'abstract_unit' -class CacheKeyTest < Test::Unit::TestCase +class CacheKeyTest < ActiveSupport::TestCase def test_expand_cache_key assert_equal 'name/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true], :name) end end -class CacheStoreSettingTest < Test::Unit::TestCase +class CacheStoreSettingTest < ActiveSupport::TestCase def test_file_fragment_cache_store store = ActiveSupport::Cache.lookup_store :file_store, "/path/to/cache/directory" assert_kind_of(ActiveSupport::Cache::FileStore, store) assert_equal "/path/to/cache/directory", store.cache_path end - + def test_drb_fragment_cache_store store = ActiveSupport::Cache.lookup_store :drb_store, "druby://localhost:9192" assert_kind_of(ActiveSupport::Cache::DRbStore, store) @@ -24,13 +24,13 @@ class CacheStoreSettingTest < Test::Unit::TestCase assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) assert_equal %w(localhost), store.addresses end - + def test_mem_cache_fragment_cache_store_with_multiple_servers store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1' assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) assert_equal %w(localhost 192.168.1.1), store.addresses end - + def test_mem_cache_fragment_cache_store_with_options store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo' assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) @@ -45,7 +45,7 @@ class CacheStoreSettingTest < Test::Unit::TestCase end end -class CacheStoreTest < Test::Unit::TestCase +class CacheStoreTest < ActiveSupport::TestCase def setup @cache = ActiveSupport::Cache.lookup_store(:memory_store) end @@ -116,9 +116,15 @@ module CacheStoreBehavior assert_equal 1, @cache.decrement('foo') assert_equal 1, @cache.read('foo', :raw => true).to_i end + + def test_exist + @cache.write('foo', 'bar') + assert @cache.exist?('foo') + assert !@cache.exist?('bar') + end end -class FileStoreTest < Test::Unit::TestCase +class FileStoreTest < ActiveSupport::TestCase def setup @cache = ActiveSupport::Cache.lookup_store(:file_store, Dir.pwd) end @@ -130,7 +136,7 @@ class FileStoreTest < Test::Unit::TestCase include CacheStoreBehavior end -class MemoryStoreTest < Test::Unit::TestCase +class MemoryStoreTest < ActiveSupport::TestCase def setup @cache = ActiveSupport::Cache.lookup_store(:memory_store) end @@ -145,28 +151,109 @@ class MemoryStoreTest < Test::Unit::TestCase end uses_memcached 'memcached backed store' do - class MemCacheStoreTest < Test::Unit::TestCase + class MemCacheStoreTest < ActiveSupport::TestCase def setup @cache = ActiveSupport::Cache.lookup_store(:mem_cache_store) + @data = @cache.instance_variable_get(:@data) @cache.clear end include CacheStoreBehavior def test_store_objects_should_be_immutable - @cache.write('foo', 'bar') - @cache.read('foo').gsub!(/.*/, 'baz') - assert_equal 'bar', @cache.read('foo') + with_local_cache do + @cache.write('foo', 'bar') + @cache.read('foo').gsub!(/.*/, 'baz') + assert_equal 'bar', @cache.read('foo') + end end def test_write_should_return_true_on_success - result = @cache.write('foo', 'bar') - assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written - assert result + with_local_cache do + result = @cache.write('foo', 'bar') + assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written + assert result + end + end + + def test_local_writes_are_persistent_on_the_remote_cache + with_local_cache do + @cache.write('foo', 'bar') + end + + assert_equal 'bar', @cache.read('foo') + end + + def test_clear_also_clears_local_cache + with_local_cache do + @cache.write('foo', 'bar') + @cache.clear + assert_nil @cache.read('foo') + end end + + def test_local_cache_of_read_and_write + with_local_cache do + @cache.write('foo', 'bar') + @data.flush_all # Clear remote cache + assert_equal 'bar', @cache.read('foo') + end + end + + def test_local_cache_of_delete + with_local_cache do + @cache.write('foo', 'bar') + @cache.delete('foo') + @data.flush_all # Clear remote cache + assert_nil @cache.read('foo') + end + end + + def test_local_cache_of_exist + with_local_cache do + @cache.write('foo', 'bar') + @cache.instance_variable_set(:@data, nil) + @data.flush_all # Clear remote cache + assert @cache.exist?('foo') + end + end + + def test_local_cache_of_increment + with_local_cache do + @cache.write('foo', 1, :raw => true) + @cache.increment('foo') + @data.flush_all # Clear remote cache + assert_equal 2, @cache.read('foo', :raw => true).to_i + end + end + + def test_local_cache_of_decrement + with_local_cache do + @cache.write('foo', 1, :raw => true) + @cache.decrement('foo') + @data.flush_all # Clear remote cache + assert_equal 0, @cache.read('foo', :raw => true).to_i + end + end + + def test_exist_with_nulls_cached_locally + with_local_cache do + @cache.write('foo', 'bar') + @cache.delete('foo') + assert !@cache.exist?('foo') + end + end + + private + def with_local_cache + Thread.current[ActiveSupport::Cache::MemCacheStore::THREAD_LOCAL_KEY] = ActiveSupport::Cache::MemoryStore.new + yield + ensure + Thread.current[ActiveSupport::Cache::MemCacheStore::THREAD_LOCAL_KEY] = nil + end end - class CompressedMemCacheStore < Test::Unit::TestCase + class CompressedMemCacheStore < ActiveSupport::TestCase def setup @cache = ActiveSupport::Cache.lookup_store(:compressed_mem_cache_store) @cache.clear -- cgit v1.2.3 From 0bed5bdb213ea68e2f167ac4f61f698f37cf2d69 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Fri, 16 Jan 2009 17:37:36 +1300 Subject: Properly quote json keys. According to the RFC and the json.org site all json keys must be strings, and those strings must be quoted with double quotes. [#1755 state:committed] --- activesupport/lib/active_support/json/encoders/hash.rb | 4 ++-- activesupport/test/json/encoding_test.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/json/encoders/hash.rb b/activesupport/lib/active_support/json/encoders/hash.rb index b9bdd55fa5..16dc8337f5 100644 --- a/activesupport/lib/active_support/json/encoders/hash.rb +++ b/activesupport/lib/active_support/json/encoders/hash.rb @@ -5,7 +5,7 @@ class Hash # the hash keys. For example: # # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json - # # => {"name": "Konata Izumi", 1: 2, "age": 16} + # # => {"name": "Konata Izumi", "1": 2, "age": 16} # # The keys in the JSON string are unordered due to the nature of hashes. # @@ -39,7 +39,7 @@ class Hash returning result = '{' do result << hash_keys.map do |key| - "#{ActiveSupport::JSON.encode(key)}: #{ActiveSupport::JSON.encode(self[key], options)}" + "#{ActiveSupport::JSON.encode(key.to_s)}: #{ActiveSupport::JSON.encode(self[key], options)}" end * ', ' result << '}' end diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 8ed21cc9ad..2c5b4d0378 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -59,7 +59,7 @@ class TestJSONEncoding < Test::Unit::TestCase assert_equal %({\"a\": \"b\"}), { :a => :b }.to_json assert_equal %({\"a\": 1}), { 'a' => 1 }.to_json assert_equal %({\"a\": [1, 2]}), { 'a' => [1,2] }.to_json - assert_equal %({1: 2}), { 1 => 2 }.to_json + assert_equal %({"1": 2}), { 1 => 2 }.to_json sorted_json = '{' + {:a => :b, :c => :d}.to_json[1..-2].split(', ').sort.join(', ') + '}' assert_equal %({\"a\": \"b\", \"c\": \"d\"}), sorted_json @@ -80,7 +80,7 @@ class TestJSONEncoding < Test::Unit::TestCase def test_hash_key_identifiers_are_always_quoted values = {0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B"} - assert_equal %w( "$" "A" "A0" "A0B" "_" "a" 0 1 ), object_keys(values.to_json) + assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(values.to_json) end def test_hash_should_allow_key_filtering_with_only -- cgit v1.2.3 From 452cd74d81ecfa38d0c7d3a4dc83d2b731de9e73 Mon Sep 17 00:00:00 2001 From: Brandon Keepers Date: Sun, 4 Jan 2009 14:01:23 +0000 Subject: Dup keys in OrderedHash to prevent them from being modified [#1676 state:resolved] Signed-off-by: Frederick Cheung --- activesupport/lib/active_support/ordered_hash.rb | 31 +++++++++++++++++------- activesupport/test/ordered_hash_test.rb | 10 +++++++- 2 files changed, 31 insertions(+), 10 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 3def0be639..25ea505813 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -10,10 +10,14 @@ module ActiveSupport @keys = [] end + def initialize_copy(other) + super + # make a deep copy of keys + @keys = other.keys + end + def []=(key, value) - if !has_key?(key) - @keys << key - end + @keys << key if !has_key?(key) super end @@ -24,6 +28,12 @@ module ActiveSupport end super end + + def delete_if + super + sync_keys! + self + end def reject! super @@ -36,7 +46,7 @@ module ActiveSupport end def keys - @keys + @keys.dup end def values @@ -56,7 +66,7 @@ module ActiveSupport end def each - keys.each {|key| yield [key, self[key]]} + @keys.each {|key| yield [key, self[key]]} end alias_method :each_pair, :each @@ -73,13 +83,16 @@ module ActiveSupport [k, v] end + def merge!(other_hash) + other_hash.each {|k,v| self[k] = v } + self + end + def merge(other_hash) - result = dup - other_hash.each {|k,v| result[k]=v} - result + dup.merge!(other_hash) end - private + private def sync_keys! @keys.delete_if {|k| !has_key?(k)} diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 0e2aa4543d..fb76ca1ab6 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -98,7 +98,8 @@ class OrderedHashTest < Test::Unit::TestCase end def test_delete_if - (copy = @ordered_hash.dup).delete('pink') + copy = @ordered_hash.dup + copy.delete('pink') assert_equal copy, @ordered_hash.delete_if { |k, _| k == 'pink' } assert !@ordered_hash.keys.include?('pink') end @@ -115,6 +116,7 @@ class OrderedHashTest < Test::Unit::TestCase new_ordered_hash = @ordered_hash.reject { |k, _| k == 'pink' } assert_equal copy, @ordered_hash assert !new_ordered_hash.keys.include?('pink') + assert @ordered_hash.keys.include?('pink') end def test_clear @@ -140,4 +142,10 @@ class OrderedHashTest < Test::Unit::TestCase assert_equal [@keys.first, @values.first], pair assert !@ordered_hash.keys.include?(pair.first) end + + def test_keys + original = @ordered_hash.keys.dup + @ordered_hash.keys.pop + assert_equal original, @ordered_hash.keys + end end \ No newline at end of file -- cgit v1.2.3 From b08c96887538cf53670bb882e79996582375e6c9 Mon Sep 17 00:00:00 2001 From: Lourens Naude Date: Sat, 17 Jan 2009 18:05:48 -0600 Subject: Decouple the local cache strategy from MemCacheStore for reuse with other remote stores [#1653 state:resolved] Signed-off-by: Joshua Peek --- activesupport/lib/active_support/cache.rb | 4 + .../lib/active_support/cache/mem_cache_store.rb | 64 ++----------- .../active_support/cache/strategy/local_cache.rb | 104 +++++++++++++++++++++ activesupport/test/caching_test.rb | 36 +++---- 4 files changed, 133 insertions(+), 75 deletions(-) create mode 100644 activesupport/lib/active_support/cache/strategy/local_cache.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 6a6c861458..83174d3a85 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -10,6 +10,10 @@ module ActiveSupport autoload :MemCacheStore, 'active_support/cache/mem_cache_store' autoload :CompressedMemCacheStore, 'active_support/cache/compressed_mem_cache_store' + module Strategy + autoload :LocalCache, 'active_support/cache/strategy/local_cache' + end + # Creates a new CacheStore object according to the given options. # # If no arguments are passed to this method, then a new diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index eed9faac6d..4d8e1fdd67 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -23,24 +23,6 @@ module ActiveSupport DELETED = "DELETED\r\n" end - # this allows caching of the fact that there is nothing in the remote cache - NULL = 'mem_cache_store:null' - - THREAD_LOCAL_KEY = :mem_cache_store_cache - - class LocalCache - def initialize(app) - @app = app - end - - def call(env) - Thread.current[THREAD_LOCAL_KEY] = MemoryStore.new - @app.call(env) - ensure - Thread.current[THREAD_LOCAL_KEY] = nil - end - end - attr_reader :addresses # Creates a new MemCacheStore object, with the given memcached server @@ -57,22 +39,13 @@ module ActiveSupport addresses = ["localhost"] if addresses.empty? @addresses = addresses @data = MemCache.new(addresses, options) + + extend Strategy::LocalCache end def read(key, options = nil) # :nodoc: super - - value = local_cache && local_cache.read(key) - if value == NULL - nil - elsif value.nil? - value = @data.get(key, raw?(options)) - local_cache.write(key, value || NULL) if local_cache - value - else - # forcing the value to be immutable - value.dup - end + @data.get(key, raw?(options)) rescue MemCache::MemCacheError => e logger.error("MemCacheError (#{e}): #{e.message}") nil @@ -91,7 +64,6 @@ module ActiveSupport # memcache-client will break the connection if you send it an integer # in raw mode, so we convert it to a string to be sure it continues working. value = value.to_s if raw?(options) - local_cache.write(key, value || NULL) if local_cache response = @data.send(method, key, value, expires_in(options), raw?(options)) response == Response::STORED rescue MemCache::MemCacheError => e @@ -101,7 +73,6 @@ module ActiveSupport def delete(key, options = nil) # :nodoc: super - local_cache.write(key, NULL) if local_cache response = @data.delete(key, expires_in(options)) response == Response::DELETED rescue MemCache::MemCacheError => e @@ -113,40 +84,22 @@ module ActiveSupport # Doesn't call super, cause exist? in memcache is in fact a read # But who cares? Reading is very fast anyway # Local cache is checked first, if it doesn't know then memcache itself is read from - value = local_cache.read(key) if local_cache - if value == NULL - false - elsif value - true - else - !read(key, options).nil? - end + !read(key, options).nil? end def increment(key, amount = 1) # :nodoc: log("incrementing", key, amount) response = @data.incr(key, amount) - unless response == Response::NOT_FOUND - local_cache.write(key, response.to_s) if local_cache - response - else - nil - end + response == Response::NOT_FOUND ? nil : response rescue MemCache::MemCacheError nil end def decrement(key, amount = 1) # :nodoc: log("decrement", key, amount) - response = @data.decr(key, amount) - unless response == Response::NOT_FOUND - local_cache.write(key, response.to_s) if local_cache - response - else - nil - end + response == Response::NOT_FOUND ? nil : response rescue MemCache::MemCacheError nil end @@ -159,7 +112,6 @@ module ActiveSupport end def clear - local_cache.clear if local_cache @data.flush_all end @@ -168,10 +120,6 @@ module ActiveSupport end private - def local_cache - Thread.current[THREAD_LOCAL_KEY] - end - def expires_in(options) (options && options[:expires_in]) || 0 end diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb new file mode 100644 index 0000000000..621358d701 --- /dev/null +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -0,0 +1,104 @@ +module ActiveSupport + module Cache + module Strategy + module LocalCache + # this allows caching of the fact that there is nothing in the remote cache + NULL = 'remote_cache_store:null' + + def with_local_cache + Thread.current[thread_local_key] = MemoryStore.new + yield + ensure + Thread.current[thread_local_key] = nil + end + + def middleware + @middleware ||= begin + klass = Class.new + klass.class_eval(<<-EOS, __FILE__, __LINE__) + def initialize(app) + @app = app + end + + def call(env) + Thread.current[:#{thread_local_key}] = MemoryStore.new + @app.call(env) + ensure + Thread.current[:#{thread_local_key}] = nil + end + EOS + klass + end + end + + def read(key, options = nil) + value = local_cache && local_cache.read(key) + if value == NULL + nil + elsif value.nil? + value = super + local_cache.write(key, value || NULL) if local_cache + value + else + # forcing the value to be immutable + value.dup + end + end + + def write(key, value, options = nil) + value = value.to_s if respond_to?(:raw?) && raw?(options) + local_cache.write(key, value || NULL) if local_cache + super + end + + def delete(key, options = nil) + local_cache.write(key, NULL) if local_cache + super + end + + def exist(key, options = nil) + value = local_cache.read(key) if local_cache + if value == NULL + false + elsif value + true + else + super + end + end + + def increment(key, amount = 1) + if value = super + local_cache.write(key, value.to_s) if local_cache + value + else + nil + end + end + + def decrement(key, amount = 1) + if value = super + local_cache.write(key, value.to_s) if local_cache + value + else + nil + end + end + + def clear + local_cache.clear if local_cache + super + end + + private + def thread_local_key + @thread_local_key ||= "#{self.class.name.underscore}_local_cache".gsub("/", "_").to_sym + end + + def local_cache + Thread.current[thread_local_key] + end + end + end + end +end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 5d220f4403..e8e0b41d4d 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -161,7 +161,7 @@ uses_memcached 'memcached backed store' do include CacheStoreBehavior def test_store_objects_should_be_immutable - with_local_cache do + @cache.with_local_cache do @cache.write('foo', 'bar') @cache.read('foo').gsub!(/.*/, 'baz') assert_equal 'bar', @cache.read('foo') @@ -169,7 +169,7 @@ uses_memcached 'memcached backed store' do end def test_write_should_return_true_on_success - with_local_cache do + @cache.with_local_cache do result = @cache.write('foo', 'bar') assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written assert result @@ -177,7 +177,7 @@ uses_memcached 'memcached backed store' do end def test_local_writes_are_persistent_on_the_remote_cache - with_local_cache do + @cache.with_local_cache do @cache.write('foo', 'bar') end @@ -185,7 +185,7 @@ uses_memcached 'memcached backed store' do end def test_clear_also_clears_local_cache - with_local_cache do + @cache.with_local_cache do @cache.write('foo', 'bar') @cache.clear assert_nil @cache.read('foo') @@ -193,7 +193,7 @@ uses_memcached 'memcached backed store' do end def test_local_cache_of_read_and_write - with_local_cache do + @cache.with_local_cache do @cache.write('foo', 'bar') @data.flush_all # Clear remote cache assert_equal 'bar', @cache.read('foo') @@ -201,7 +201,7 @@ uses_memcached 'memcached backed store' do end def test_local_cache_of_delete - with_local_cache do + @cache.with_local_cache do @cache.write('foo', 'bar') @cache.delete('foo') @data.flush_all # Clear remote cache @@ -210,7 +210,7 @@ uses_memcached 'memcached backed store' do end def test_local_cache_of_exist - with_local_cache do + @cache.with_local_cache do @cache.write('foo', 'bar') @cache.instance_variable_set(:@data, nil) @data.flush_all # Clear remote cache @@ -219,7 +219,7 @@ uses_memcached 'memcached backed store' do end def test_local_cache_of_increment - with_local_cache do + @cache.with_local_cache do @cache.write('foo', 1, :raw => true) @cache.increment('foo') @data.flush_all # Clear remote cache @@ -228,7 +228,7 @@ uses_memcached 'memcached backed store' do end def test_local_cache_of_decrement - with_local_cache do + @cache.with_local_cache do @cache.write('foo', 1, :raw => true) @cache.decrement('foo') @data.flush_all # Clear remote cache @@ -237,20 +237,22 @@ uses_memcached 'memcached backed store' do end def test_exist_with_nulls_cached_locally - with_local_cache do + @cache.with_local_cache do @cache.write('foo', 'bar') @cache.delete('foo') assert !@cache.exist?('foo') end end - private - def with_local_cache - Thread.current[ActiveSupport::Cache::MemCacheStore::THREAD_LOCAL_KEY] = ActiveSupport::Cache::MemoryStore.new - yield - ensure - Thread.current[ActiveSupport::Cache::MemCacheStore::THREAD_LOCAL_KEY] = nil - end + def test_middleware + app = lambda { |env| + result = @cache.write('foo', 'bar') + assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written + assert result + } + app = @cache.middleware.new(app) + app.call({}) + end end class CompressedMemCacheStore < ActiveSupport::TestCase -- cgit v1.2.3 From 78f2c19ae7f9236591c261eecdf0c4b570e3ea1e Mon Sep 17 00:00:00 2001 From: Josh Susser Date: Fri, 16 Jan 2009 17:26:08 -0800 Subject: Refactor Object#try to use inheritance. [#1774 state:resolved] Signed-off-by: Pratik Naik --- .../lib/active_support/core_ext/object/misc.rb | 17 ------------ activesupport/lib/active_support/core_ext/try.rb | 30 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/try.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/object/misc.rb b/activesupport/lib/active_support/core_ext/object/misc.rb index c0a109ecf3..4acdfa3d6c 100644 --- a/activesupport/lib/active_support/core_ext/object/misc.rb +++ b/activesupport/lib/active_support/core_ext/object/misc.rb @@ -87,21 +87,4 @@ class Object respond_to? "acts_like_#{duck}?" end - # Tries to send the method only if object responds to it. Return +nil+ otherwise. - # It will also forward any arguments and/or block like Object#send does. - # - # ==== Example : - # - # # Without try - # @person ? @person.name : nil - # - # With try - # @person.try(:name) - # - # # try also accepts arguments/blocks for the method it is trying - # Person.try(:find, 1) - # @people.try(:map) {|p| p.name} - def try(method, *args, &block) - send(method, *args, &block) unless self.nil? - end end diff --git a/activesupport/lib/active_support/core_ext/try.rb b/activesupport/lib/active_support/core_ext/try.rb new file mode 100644 index 0000000000..0dccd40c55 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/try.rb @@ -0,0 +1,30 @@ +class Object + # Tries to send the method only if object responds to it. Return +nil+ otherwise. + # It will also forward any arguments and/or block like Object#send does. + # + # ==== Examples + # + # Without try + # @person && @person.name + # or + # @person ? @person.name : nil + # + # With try + # @person.try(:name) + # + # Try also accepts arguments/blocks for the method it is trying + # Person.try(:find, 1) + # @people.try(:collect) {|p| p.name} + #-- + # This method def is for rdoc only. The alias_method below overrides it as an optimization. + def try(method, *args, &block) + send(method, *args, &block) + end + alias_method :try, :__send__ +end + +class NilClass + def try(*args) + nil + end +end -- cgit v1.2.3 From 085991891e610ed0ab616ce434eabf42a9437039 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 18 Jan 2009 05:28:21 +0000 Subject: Bump up the year in MIT license files --- activesupport/MIT-LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activesupport') diff --git a/activesupport/MIT-LICENSE b/activesupport/MIT-LICENSE index 2ba4e17035..d6fdf21596 100644 --- a/activesupport/MIT-LICENSE +++ b/activesupport/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2005-2008 David Heinemeier Hansson +Copyright (c) 2005-2009 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the -- cgit v1.2.3