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