From: Marcin Raczkowski Date: 2008-06-16T20:24:40+09:00 Subject: Simple memoization module If you ewer wanted to easilly cache memoizable functions (ones that output depend ONLY on parameters given)- here's a way! Implementation contains sweeping support for classes/modules and methods inside those classes, ActiveRecord logger is used by memcache-client library so I provide it becouse i'm to lazy to remove that dependency in 3rd party pugin. If anyone shows interest i'll pack it as gem, and mayby add some stuff, all requests are welcome Marcin Raczkowski www.softwarelab.eu ========================================================================= module Memoization # store keys for sweeping def Memoization.add_key(key) @keys ||= [] @keys |= [key] end # remove keys that belongs to given class/method def Memoization.sweep(klass, method=nil) reg = method ? Regexp.new("^#{klass}:#{method}") : Regexp.new("^#{klass.to_s}") @keys.select{|k| reg.match(k) }.each{|k| Cache.delete(k)} end # sugar that allows SomeClass.sweep def sweep(method = nil) Memoization.sweep(self, method) end # example usage in test case, takes names of methods to memoize def memoize(*names) return unless defined?(Cache) names.each do |name| name = name.to_s new_name = "nc_"+name alias_method(new_name, name) define_method(name) do |*args| key = "#{self.class.name}:#{name}(#{args.hash})" Memoization.add_key(key) Cache.get(key, 60) { self.send(new_name.to_sym, *args) } end end end end # doing inclusion into module / class class Class include Memoization end class Module include Memoization end # workaround for non-rails environment unless defined?(ActiveRecord::Base) module ActiveRecord; end class ActiveRecord::Base; def self.logger; Logger.new(STDOUT); end; end end if __FILE__ == $0 # testcase require memcached server running on default port require 'test/unit' require 'rubygems' require 'memcache' CACHE = MemCache.new 'localhost:11211', :namespace => 'moization' require 'memcache_util' require 'logger' class A def normal(a); a; end def random(); rand; end memoize :normal, :random end class TC_MyTest < Test::Unit::TestCase def test_behaviour assert(A.new.normal(:a) == A.new.normal(:a)) assert(A.new.normal(:a) != A.new.normal(:b)) rnd = A.new.random assert(A.new.random == A.new.random) A.sweep assert(A.new.random != rnd) assert(A.new.random == A.new.random) rnd = A.new.random A.sweep(:random) assert(A.new.random != rnd) assert(A.new.random == A.new.random) end end end