From: Daniel Schierbeck Date: 2005-12-10T03:57:36+09:00 Subject: Re: Transparent Caching Idiom beng wrote: > I've made good use of this idiom recently, and wanted to share it. > > Once this code is active, any method named with a "_c" suffix is > automatically cached. The method is assumed to follow a simple > contract: It performs no side-effects / mutations, and it's arguments > properly implement "Object.hash". > > This is specifically useful for expensive but functional calculations. > Enjoy! > > --Ben (b@gimpert.com) > > # > class Class > > alias old_new new > def new(*args, &block) > Class.defc_wrap(old_new(*args, &block)) > end > > def Class.defc_wrap(obj) > klass = obj.class > defc_methods = obj.methods.select { |n| n =~ /_c$/ } > defc_methods.each do |name| > meth = obj.method(name) > wrapped_name = Class.defc_next_name(klass) > cache = klass.class_eval("@#{wrapped_name} = {}") > wrapper = Proc.new do |*args_a| > arg_hash = args_a.hash > if cache.has_key?(arg_hash) > cache[arg_hash] > else > cache[arg_hash] = meth.call(*args_a) > end > end > klass.send(:alias_method, wrapped_name, name) > klass.send(:define_method, name, wrapper) > end > obj > end > > def Class.defc_next_name(obj) > klass = obj.class > i = 1 > i += 1 while obj.respond_to?("_defc_#{i}") || > obj.instance_variables.member?("@_defc_#{i}") > "_defc_#{i}" > end > > end > This is a touched-up version of your code. class Class alias_method :__new__, :new def cached_method(*methods) @cached_methods ||= [] @cached_methods += methods @cached_methods.uniq! end def new(*args, &block) obj = __new__(*args, &block) klass = obj.class @cached_methods ||= [] @cached_methods.each do |name| meth = obj.method(name) i = 1 while klass.instance_variables.member?("@_defc_#{i}") i += 1 end wrapped_name = "_defc_#{i}" cache = klass.class_eval("@#{wrapped_name} = {}") wrapper = Proc.new do |*args_a| arg_hash = args_a.hash if cache.has_key?(arg_hash) cache[arg_hash] else cache[arg_hash] = meth.call(*args_a) end end klass.send(:alias_method, wrapped_name, name) klass.send(:define_method, name, wrapper) end return obj end end class Klass def foo; end def bar; end cached_method :foo, :bar end