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 R.
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