lib/active_support/cache.rb in activesupport-2.3.18 vs lib/active_support/cache.rb in activesupport-3.0.0.beta

- old
+ new

@@ -1,14 +1,19 @@ require 'benchmark' +require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/benchmark' +require 'active_support/core_ext/exception' +require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/object/to_param' +require 'active_support/core_ext/string/inflections' module ActiveSupport # See ActiveSupport::Cache::Store for documentation. module Cache autoload :FileStore, 'active_support/cache/file_store' autoload :MemoryStore, 'active_support/cache/memory_store' autoload :SynchronizedMemoryStore, 'active_support/cache/synchronized_memory_store' - autoload :DRbStore, 'active_support/cache/drb_store' 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' @@ -23,14 +28,14 @@ # store class under the ActiveSupport::Cache namespace will be created. # For example: # # ActiveSupport::Cache.lookup_store(:memory_store) # # => returns a new ActiveSupport::Cache::MemoryStore object - # - # ActiveSupport::Cache.lookup_store(:drb_store) - # # => returns a new ActiveSupport::Cache::DRbStore object # + # ActiveSupport::Cache.lookup_store(:mem_cache_store) + # # => returns a new ActiveSupport::Cache::MemCacheStore object + # # Any additional arguments will be passed to the corresponding cache store # class's constructor: # # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache") # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache") @@ -38,37 +43,45 @@ # If the first argument is not a Symbol, then it will simply be returned: # # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new) # # => returns MyOwnCacheStore.new def self.lookup_store(*store_option) - store, *parameters = *([ store_option ].flatten) + store, *parameters = *Array.wrap(store_option).flatten case store when Symbol - store_class_name = (store == :drb_store ? "DRbStore" : store.to_s.camelize) + store_class_name = store.to_s.camelize store_class = ActiveSupport::Cache.const_get(store_class_name) store_class.new(*parameters) when nil ActiveSupport::Cache::MemoryStore.new else store end end + RAILS_CACHE_ID = ENV["RAILS_CACHE_ID"] + RAILS_APP_VERION = ENV["RAILS_APP_VERION"] + EXPANDED_CACHE = RAILS_CACHE_ID || RAILS_APP_VERION + def self.expand_cache_key(key, namespace = nil) expanded_cache_key = namespace ? "#{namespace}/" : "" - if ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] - expanded_cache_key << "#{ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]}/" + if EXPANDED_CACHE + expanded_cache_key << "#{RAILS_CACHE_ID || RAILS_APP_VERION}/" end - expanded_cache_key << case - when key.respond_to?(:cache_key) + expanded_cache_key << + if key.respond_to?(:cache_key) key.cache_key - when key.is_a?(Array) - key.collect { |element| expand_cache_key(element) }.to_param - when key + elsif key.is_a?(Array) + if key.size > 1 + key.collect { |element| expand_cache_key(element) }.to_param + else + key.first.to_param + end + elsif key key.to_param end.to_s expanded_cache_key end @@ -82,34 +95,41 @@ # ActiveSupport::Cache::Store is meant for caching strings. Some cache # store implementations, like MemoryStore, are able to cache arbitrary # Ruby objects, but don't count on every cache store to be able to do that. # # cache = ActiveSupport::Cache::MemoryStore.new - # + # # cache.read("city") # => nil # cache.write("city", "Duckburgh") # cache.read("city") # => "Duckburgh" class Store - cattr_accessor :logger + cattr_accessor :logger, :instance_writter => false - attr_reader :silence, :logger_off + attr_reader :silence + alias :silence? :silence def silence! @silence = true self end - alias silence? silence - alias logger_off? logger_off - def mute previous_silence, @silence = defined?(@silence) && @silence, true yield ensure @silence = previous_silence end + # Set to true if cache stores should be instrumented. By default is false. + def self.instrument=(boolean) + Thread.current[:instrument_cache_store] = boolean + end + + def self.instrument + Thread.current[:instrument_cache_store] || false + end + # Fetches data from the cache, using the given key. If there is data in # the cache with the given key, then that data is returned. # # If there is no such data in the cache (a cache miss occurred), then # then nil will be returned. However, if a block has been passed, then @@ -117,11 +137,11 @@ # of the block will be written to the cache under the given cache key, # and that return value will be returned. # # cache.write("today", "Monday") # cache.fetch("today") # => "Monday" - # + # # cache.fetch("city") # => nil # cache.fetch("city") do # "Duckburgh" # end # cache.fetch("city") # => "Duckburgh" @@ -136,113 +156,113 @@ # Internally, #fetch calls #read, and calls #write on a cache miss. # +options+ will be passed to the #read and #write calls. # # For example, MemCacheStore's #write method supports the +:expires_in+ # option, which tells the memcached server to automatically expire the - # cache item after a certain period. We can use this option with #fetch - # too: + # cache item after a certain period. This options is also supported by + # FileStore's #read method. We can use this option with #fetch too: # # cache = ActiveSupport::Cache::MemCacheStore.new # cache.fetch("foo", :force => true, :expires_in => 5.seconds) do # "bar" # end # cache.fetch("foo") # => "bar" # sleep(6) # cache.fetch("foo") # => nil - def fetch(key, options = {}) - @logger_off = true + def fetch(key, options = {}, &block) if !options[:force] && value = read(key, options) - @logger_off = false - log("hit", key, options) value elsif block_given? - @logger_off = false - log("miss", key, options) - - value = nil - ms = Benchmark.ms { value = yield } - - @logger_off = true - write(key, value, options) - @logger_off = false - - log('write (will save %.2fms)' % ms, key, nil) - - value + result = instrument(:generate, key, options, &block) + write(key, result, options) + result end end # Fetches data from the cache, using the given key. If there is data in # the cache with the given key, then that data is returned. Otherwise, # nil is returned. # # You may also specify additional options via the +options+ argument. # The specific cache store implementation will decide what to do with # +options+. - def read(key, options = nil) - log("read", key, options) + # + # For example, FileStore supports the +:expires_in+ option, which + # makes the method return nil for cache items older than the specified + # period. + def read(key, options = nil, &block) + instrument(:read, key, options, &block) end # Writes the given value to the cache, with the given key. # # You may also specify additional options via the +options+ argument. # The specific cache store implementation will decide what to do with # +options+. - # + # # For example, MemCacheStore supports the +:expires_in+ option, which # tells the memcached server to automatically expire the cache item after # a certain period: # # cache = ActiveSupport::Cache::MemCacheStore.new # cache.write("foo", "bar", :expires_in => 5.seconds) # cache.read("foo") # => "bar" # sleep(6) # cache.read("foo") # => nil - def write(key, value, options = nil) - log("write", key, options) + def write(key, value, options = nil, &block) + instrument(:write, key, options, &block) end - def delete(key, options = nil) - log("delete", key, options) + def delete(key, options = nil, &block) + instrument(:delete, key, options, &block) end - def delete_matched(matcher, options = nil) - log("delete matched", matcher.inspect, options) + def delete_matched(matcher, options = nil, &block) + instrument(:delete_matched, matcher.inspect, options, &block) end - def exist?(key, options = nil) - log("exist?", key, options) + def exist?(key, options = nil, &block) + instrument(:exist?, key, options, &block) end def increment(key, amount = 1) - log("incrementing", key, amount) if num = read(key) write(key, num + amount) else nil end end def decrement(key, amount = 1) - log("decrementing", key, amount) if num = read(key) write(key, num - amount) else nil end end private def expires_in(options) expires_in = options && options[:expires_in] - raise ":expires_in must be a number" if expires_in && !expires_in.is_a?(Numeric) - expires_in || 0 end + def instrument(operation, key, options) + log(operation, key, options) + + if self.class.instrument + payload = { :key => key } + payload.merge!(options) if options.is_a?(Hash) + ActiveSupport::Notifications.instrument("active_support.cache_#{operation}", payload){ yield } + else + yield + end + end + def log(operation, key, options) - logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !silence? && !logger_off? + return unless logger && !silence? + logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") end end end end