class MethodCache::Proxy

Constants

NULL

Attributes

args[R]
method_name[R]
opts[R]
target[R]

Public Class Methods

new(method_name, opts) click to toggle source
# File lib/method_cache/proxy.rb, line 12
def initialize(method_name, opts)
  opts[:cache] ||= :counters if opts[:counter]
  @method_name = method_name
  @opts        = opts
end

Public Instance Methods

bind(target, args) click to toggle source
# File lib/method_cache/proxy.rb, line 18
def bind(target, args)
  self.clone.bind!(target, args)
end
bind!(target, args) click to toggle source
# File lib/method_cache/proxy.rb, line 22
def bind!(target, args)
  @target = target
  @args   = args
  @key    = nil
  self
end
cache() click to toggle source
# File lib/method_cache/proxy.rb, line 115
def cache
  if @cache.nil?
    @cache = opts[:cache] || MethodCache.default_cache
    @cache = Memcache.pool[@cache] if @cache.kind_of?(Symbol)
    if not @cache.respond_to?(:[]) and @cache.respond_to?(:get)
      @cache.metaclass.module_eval do
        define_method :[] do |key|
          get(key)
        end
      end
    end
  end
  @cache
end
cached?() click to toggle source
# File lib/method_cache/proxy.rb, line 46
def cached?
  not cache[key].nil?
end
cached_at() click to toggle source
# File lib/method_cache/proxy.rb, line 149
def cached_at
  cache.cached_at(key) if cache.respond_to?(:cached_at)
end
clone?() click to toggle source
# File lib/method_cache/proxy.rb, line 134
def clone?
  !!opts[:clone]
end
context() click to toggle source
# File lib/method_cache/proxy.rb, line 38
def context
  opts[:context]
end
counter_method(method_name) click to toggle source
# File lib/method_cache/proxy.rb, line 96
def counter_method(method_name)
  proxy = self # Need access to the proxy in the closure.

  lambda do |*args|
    if args.last.kind_of?(Hash) and args.last.keys == [:by]
      amount = args.last[:by]
      args.pop
    end
    proxy.bind(self, args).send(method_name, amount || 1)
  end
end
expires_at() click to toggle source
# File lib/method_cache/proxy.rb, line 153
def expires_at
  cache.expires_at(key) if cache.respond_to?(:expires_at)
end
invalidate() { |value| ... } click to toggle source
# File lib/method_cache/proxy.rb, line 29
def invalidate
  if block_given?
    # Only invalidate if the block returns true.
    value = cache[key]
    return if value and not yield(value)
  end
  cache.delete(key)
end
key() click to toggle source
# File lib/method_cache/proxy.rb, line 138
def key
  if @key.nil?
    arg_string = ([method_name, target] + args).collect do |arg|
      object_key(arg)
    end.join('|')
    @key = [version, arg_string].compact.join('|')
    @key = Digest::SHA1.hexdigest(@key) if @key.length > 250
  end
  "m#{MethodCache.version}|#{@key}"
end
local?() click to toggle source
# File lib/method_cache/proxy.rb, line 130
def local?
  cache.kind_of?(LocalCache) or cache.kind_of?(Hash)
end
method_name_without_caching() click to toggle source
# File lib/method_cache/proxy.rb, line 108
def method_name_without_caching
  @method_name_without_caching ||= begin
    base_name, punctuation = method_name.to_s.sub(/([?!=])$/, ''), $1
    "#{base_name}_without_caching#{punctuation}".to_sym
  end
end
method_with_caching() click to toggle source
# File lib/method_cache/proxy.rb, line 88
def method_with_caching
  proxy = self # Need access to the proxy in the closure.

  lambda do |*args|
    proxy.bind(self, args).value
  end
end
update() { |old_value| ... } click to toggle source
# File lib/method_cache/proxy.rb, line 50
def update
  if block_given?
    old_value = read_from_cache(key)
    return if old_value.nil?

    old_value = nil if old_value == NULL
    new_value = yield(old_value)
    return if old_value == new_value
  else
    new_value = target.send(method_name_without_caching, *args)
  end
  write_to_cache(key, new_value)
  new_value
end
value() click to toggle source
# File lib/method_cache/proxy.rb, line 65
def value
  value = read_from_cache(key)
  value = nil unless valid?(:load, value)

  if value.nil?
    value = target.send(method_name_without_caching, *args)
    raise "non-integer value returned by counter method" if opts[:counter] and not value.kind_of?(Fixnum)
    write_to_cache(key, value) if valid?(:save, value)
  end

  if opts[:counter]
    value = [value, opts[:max]].min if opts[:max]
    value = [value, opts[:min]].max if opts[:min]
  end

  value = nil if value == NULL
  if clone? and value
    value.clone
  else
    value
  end
end
version() click to toggle source
# File lib/method_cache/proxy.rb, line 42
def version
  dynamic_opt(:version)
end

Private Instance Methods

class_key(klass) click to toggle source
# File lib/method_cache/proxy.rb, line 252
def class_key(klass)
  klass.respond_to?(:version) ? "#{klass.name}_#{klass.version(context)}" : klass.name
end
decrement(amount) click to toggle source
# File lib/method_cache/proxy.rb, line 220
def decrement(amount)
  raise "cannot decrement non-counter method" unless opts[:counter]
  cache.decr(key, amount)
end
dynamic_opt(name, value = nil) click to toggle source
# File lib/method_cache/proxy.rb, line 176
def dynamic_opt(name, value = nil)
  if opts[name].kind_of?(Proc)
    proc = opts[name]
    case proc.arity
    when 0 then target.instance_exec(&proc)
    when 1 then target.instance_exec(value, &proc)
    else
      meta = {
        :args       => args,
        :cached_at  => cached_at,
        :expires_at => expires_at,
      }
      target.instance_exec(value, meta, &proc)
    end
  else
    opts[name]
  end
end
expiry(value) click to toggle source
# File lib/method_cache/proxy.rb, line 159
def expiry(value)
  value = dynamic_opt(:expiry, value).to_i
  if defined?(Memcache) and cache.kind_of?(Memcache)
    {:expiry => value}
  else
    value
  end
end
increment(amount) click to toggle source
# File lib/method_cache/proxy.rb, line 215
def increment(amount)
  raise "cannot increment non-counter method" unless opts[:counter]
  cache.incr(key, amount)
end
object_key(arg) click to toggle source
# File lib/method_cache/proxy.rb, line 225
def object_key(arg)
  return "#{class_key(arg.class)}-#{arg.string_hash}" if arg.respond_to?(:string_hash)

  case arg
  when NilClass      then 'nil'
  when TrueClass     then 'true'
  when FalseClass    then 'false'
  when Numeric       then arg.to_s
  when Symbol        then ":#{arg}"
  when String        then "'#{arg}'"
  when Class, Module then class_key(arg)
  when Hash
    '{' + arg.collect {|key, value| "#{object_key(key)}=#{object_key(value)}"}.sort.join(',') + '}'
  when Array
    '[' + arg.collect {|item| object_key(item)}.join(',') + ']'
  when defined?(ActiveRecord::Base) && ActiveRecord::Base
    "#{class_key(arg.class)}-#{arg.id}"
  else
    if arg.respond_to?(:method_cache_key)
      arg.method_cache_key
    else
      hash = local? ? arg.hash : Marshal.dump(arg).hash
      "#{class_key(arg.class)}-#{hash}"
    end
  end
end
read_from_cache(key) click to toggle source
# File lib/method_cache/proxy.rb, line 210
def read_from_cache(key)
  return if MethodCache.disabled?
  opts[:counter] ? cache.count(key) : cache[key]
end
valid?(type, value) click to toggle source
# File lib/method_cache/proxy.rb, line 168
def valid?(type, value)
  name = "#{type}_validation".to_sym
  return true unless opts[name]
  return unless value

  dynamic_opt(name, value)
end
write_to_cache(key, value) click to toggle source
# File lib/method_cache/proxy.rb, line 195
def write_to_cache(key, value)
  unless opts[:counter]
    value = value.nil? ? NULL : value
  end
  if cache.kind_of?(Hash)
    raise 'expiry not permitted when cache is a Hash'        if opts[:expiry]
    raise 'counter cache not permitted when cache is a Hash' if opts[:counter]
    cache[key] = value
  elsif opts[:counter]
    cache.write(key, value.to_s, expiry(value))
  else
    cache.set(key, value, expiry(value))
  end
end