module Cachetastic # :nodoc:
module Adapters
class << self
# This method will return the appropriate
# Cachetastic::Adapters::Base class that is defined
# for the Class passed in. If an adapter has not been
# defined for the Class than the default adapter is returned.
#
# Examples:
# configatron.cachetastic.defaults.adapter = Cachetastic::Adapters::LocalMemory
# configatron.cachetastic.user.adapter = Cachetastic::Adapters::Memcached
# Cachetastic::Adapters.build(User).class # => Cachetastic::Adapters::Memcached
# Cachetastic::Adapters.build(Comment).class # => Cachetastic::Adapters::LocalMemory
def build(klass)
adp = klass.to_configatron(:cachetastic).adapter
if adp.nil?
adp = configatron.cachetastic.defaults.adapter
end
adp.new(klass)
end
end # class << self
# This class should be extended to create new adapters for various
# backends. It is important that all subclasses call the initialize
# method in this base, otherwise things just will not work right.
#
# This base class provides common functionality and an API for all
# adapters to be used with Cachetastic.
#
# The default settings for all adapters are:
#
# configatron.cachetastic.defaults.marshal_method = :none
# configatron.cachetastic.defaults.expiry_swing = 0
# configatron.cachetastic.defaults.default_expiry = 86400
# configatron.cachetastic.defaults.debug = true
# configatron.cachetastic.defaults.adapter = Cachetastic::Adapters::LocalMemory
# logger = ::Logger.new(File.join(FileUtils.pwd, 'log', 'cachetastic.log'))
# logger.level = ::Logger::DEBUG
# configatron.cachetastic.defaults.logger = logger
#
# See the README for more information on what each of those settings mean,
# and what are values may be used for each one.
class Base
# The Class that this adapter is associated with. Note that it is a
# class reference and not an instance reference.
attr_accessor :klass
# Creates a new adapter. It takes a class reference to tie the
# instance of the adapter to a particular class. Note that it is a
# class reference and not an instance reference.
#
# Examples:
# Cachetastic::Adapters::Base.new(User)
#
# Adapters are configured using the Configatron gem.
#
# Examples:
# configatron.cachetastic.user.adapter = Cachetastic::Adapters::File
# configatron.cachetastic.user.expiry_time = 5.hours
# configatron.cachetastic.defaults.expiry_time = 24.hours
#
# Refered to each adapter for its specific configuration settings.
def initialize(klass)
self.klass = klass
configatron.cachetastic.defaults.configatron_keys.each do |key|
define_accessor(key)
self.send("#{key}=", configatron.cachetastic.defaults.send(key))
end
klass.to_configatron(:cachetastic).configatron_keys.each do |key|
define_accessor(key)
self.send("#{key}=", klass.to_configatron(:cachetastic).send(key))
end
end
# This method MUST be implemented by a subclass!
#
# The implementation of this method should take a key and return
# an associated object, if available, from the underlying persistence
# layer.
def get(key)
raise NoMethodError.new('get')
end # get
# This method MUST be implemented by a subclass!
#
# The implementation of this method should take a key, a value, and
# an expiry time and save it to the persistence store, where it should
# live until it is either deleted by the user of the expiry time has passed.
def set(key, value, expiry_time = configatron.cachetastic.defaults.default_expiry)
raise NoMethodError.new('set')
end # set
# This method MUST be implemented by a subclass!
#
# The implementation of this method should take a key and remove
# an object, if it exists, from an underlying persistence store.
def delete(key)
raise NoMethodError.new('delete')
end # delete
# This method MUST be implemented by a subclass!
#
# The implementation of this method is expected to delete all
# objects belonging to the associated cache from the underlying
# persistence store. It is NOT meant to delete ALL
# objects across ALL caches for the underlying persistence
# store. That would be very very bad!!
def expire_all
raise NoMethodError.new('expire_all')
end # expire_all
# Allows an adapter to transform the key
# to a safe representation for it's backend.
# For example, the key: '$*...123()%~q' is not a
# key for the file system, so the
# Cachetastic::Adapters::File class should override
# this to make it safe for the file system.
def transform_key(key)
key
end
# This method MUST be implemented by a subclass!
#
# The implementation of this method should return true
# if the adapter is in a valid state, and false if it is
# not.
def valid?
true
end
def debug? # :nodoc:
return self.debug if self.respond_to?(:debug)
return false
end
def marshal(value) # :nodoc:
return nil if value.nil?
case self.marshal_method.to_sym
when :yaml
return YAML.dump(value)
when :ruby
return Marshal.dump(value)
else
return value
end
end
def unmarshal(value) # :nodoc:
return nil if value.nil?
case self.marshal_method.to_sym
when :yaml
return YAML.load(value)
when :ruby
return Marshal.load(value)
else
return value
end
end
private
def define_accessor(key)
instance_eval(%{
def #{key}
@#{key}
end
def #{key}=(x)
@#{key} = x
end
})
end
end # Base
end # Adapters
end # Cachetastic