require 'memcached' class Memcached FATAL = [ ABadKeyWasProvidedOrCharactersOutOfRange, AKeyLengthOfZeroWasProvided, ConnectionBindFailure, ConnectionDataDoesNotExist, ConnectionFailure, ConnectionSocketCreateFailure, CouldNotOpenUnixSocket, NoServersDefined, TheHostTransportProtocolDoesNotMatchThatOfTheClient ] NONFATAL = EXCEPTIONS - FATAL (instance_methods - NilClass.instance_methods).each do |method_name| eval("alias :'#{method_name}_orig' :'#{method_name}'") end # A legacy compatibility wrapper for the Memcached class. It has basic compatibility with the memcache-client API and Rails 3.2. (Note that ActiveSupport::Duration objects are supported, but not recommended, as ttl parameters. Using Fixnum ttls, such as provided by time_constants.gem, is much faster.) class Rails < ::Memcached DEFAULTS = { :logger => nil, :string_return_types => false } attr_reader :logger alias :servers= :set_servers # See Memcached#new for details. def initialize(*args) opts = args.last.is_a?(Hash) ? args.pop : {} servers = Array( args.any? ? args.unshift : opts.delete(:servers) ).flatten.compact opts[:prefix_key] = opts[:namespace] if opts[:namespace] opts[:prefix_delimiter] = opts[:namespace_separator] if opts[:namespace_separator] @logger = opts[:logger] @string_return_types = opts[:string_return_types] logger.info { "memcached #{VERSION} #{servers.inspect}" } if logger super(servers, opts) end def logger=(logger) @logger = logger end # Check if there are any servers defined? def active? servers.any? end # Wraps Memcached#get so that it doesn't raise. This has the side-effect of preventing you from # storing nil values. def get(key, raw=false) super(key, !raw) rescue *NONFATAL end # Alternative to #get. Accepts a key and an optional options hash supporting the single option # :raw. def read(key, options = {}) get(key, options[:raw]) end # Returns whether the key exists, even if the value is nil. def exist?(key, options = {}) exist(key) true rescue *NONFATAL false end # Wraps Memcached#cas so that it doesn't raise. Doesn't set anything if no value is present. def cas(key, ttl=@default_ttl, raw=false, &block) super(key, ttl, !raw, &block) true rescue TypeError => e # Maybe we got an ActiveSupport::Duration ttl = ttl.value and retry rescue raise e rescue *NONFATAL false end alias :compare_and_swap :cas # Wraps Memcached#get. def get_multi(keys, raw=false) get_orig(keys, !raw) rescue *NONFATAL {} end # Wraps Memcached#set. def set(key, value, ttl=@default_ttl, raw=false) super(key, value, ttl, !raw) true rescue TypeError => e # Maybe we got an ActiveSupport::Duration ttl = ttl.value and retry rescue raise e rescue *NONFATAL false end # Alternative to #set. Accepts a key, value, and an optional options hash supporting the # options :raw and :ttl. def write(key, value, options = {}) set(key, value, options[:ttl] || options[:expires_in] || @default_ttl, options[:raw]) end def fetch(key, options={}) result = read(key, options) if result.nil? result = yield write(key, result, options) result else result end end # Wraps Memcached#add so that it doesn't raise. def add(key, value, ttl=@default_ttl, raw=false) super(key, value, ttl, !raw) @string_return_types ? "STORED\r\n" : true rescue TypeError => e # Maybe we got an ActiveSupport::Duration ttl = ttl.value and retry rescue raise e rescue *NONFATAL @string_return_types? "NOT STORED\r\n" : false end # Wraps Memcached#delete so that it doesn't raise. def delete(key, expiry=0) super(key) rescue *NONFATAL end # Wraps Memcached#incr so that it doesn't raise. def incr(*args) super rescue *NONFATAL end # Wraps Memcached#decr so that it doesn't raise. def decr(*args) super rescue *NONFATAL end # Wraps Memcached#append so that it doesn't raise. def append(*args) super rescue *NONFATAL end # Wraps Memcached#prepend so that it doesn't raise. def prepend(*args) super rescue *NONFATAL end alias :flush_all :flush alias :clear :flush alias :"[]" :get alias :"[]=" :set def read_multi(*keys) return {} if keys.empty? get_multi(keys) end # Return an array of server objects. def servers server_structs.each do |server| def server.alive? next_retry <= Time.now end end end # Wraps Memcached#set_servers to convert server objects to strings. def set_servers(servers) servers = Array(servers) servers.map! do |server| server.is_a?(String) ? server : inspect_server(server) end super end end end