Skip to content

Instantly share code, notes, and snippets.

@potapuff
Created February 11, 2011 10:21
Show Gist options
  • Save potapuff/822173 to your computer and use it in GitHub Desktop.
Save potapuff/822173 to your computer and use it in GitHub Desktop.
## Inspired by Rails memoizable, but use Rails.cache for storing informations
# core object must have id, if memoizible method not static (designed for ActiveRecord)
# each params must have method 'to_s' or be ActiveRecord
# params lenght.to_s might be less then 120 symbols - hasing is big black hole!
module CacheMemoizable
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def cache_memoize(*symbols)
symbols.each do |symbol|
dynamic = static = false
if (dynamic = method_defined?(symbol))
class_eval(aliases_body(symbol,'defined'))
end
if (static = method_exists?(symbol))
instance_eval(aliases_body(symbol,'exists'))
end
raise("Can't find method #{symbol} at class #{self.to_s}") unless dynamic || static
end
end
private
def method_exists? symbol
methods.include? symbol.to_s
end
def aliases_body symbol, context
class_name = (context== 'defined') ? self.class.to_s : self.to_s
original_method = :"_cache_#{class_name}_#{symbol}"
<<-CODE
if method_#{context}?(:#{original_method})
raise "Method #{symbol} already cached with CacheMemoized"
end
alias :#{original_method} :#{symbol}
def #{symbol}(*args)
reload = args.pop if args.last == true || args.last == :reload
arg_key = args.map {|x| (x.kind_of?(ActiveRecord)) ? x.class.to_s+'#'+x.id : (x.nil? ? 'nil' : x.to_s)}.join('_')
key = create_cahe_key_for_method '#{symbol}',*args
if reload || (value = Rails.cache.read(key)).nil?
value = self.#{original_method}(*args)
end
Rails.cache.write(key, value.nil? ? :empty_value : value)
(value == :empty_value) ? nil : value
end
unless method_#{context}?(:uncache_#{symbol})
def uncache_#{symbol}(*args)
key = create_cahe_key_for_method '#{symbol}',*args
Rails.cache.write(key, nil)
end
end
unless method_#{context}?(:create_cahe_key_for_method)
def create_cahe_key_for_method method_name, *args
arg_key = args.map do |x|
if (x.kind_of?(ActiveRecord))
x.class.to_s+'#'+x.id
else
(x.nil? ? 'nil' : x.to_s)
end
end.join('_')
key = method_name.to_s
key << id.to_s if ('#{context}' == 'defined')
key << ((arg_key.size+key.size<120) ? arg_key : arg_key.hash.to_s)
end
end
CODE
end
end
end
=begin
require 'cache_memoizable'
class Foo
include CacheMemoizable
def foo() rand end
def self.foo() rand end
cache_memoize :foo
end
Foo.foo # 1
Foo.foo # 1
foo = Foo.new
foo1= Foo.new
foo.foo # 2
foo.foo # 2
foo1.foo # 3
foo.clear_foo #nil
foo.foo # 4
foo.foo # 4
Foo.foo # 1
foo1.foo # 3
=end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment