Transparent caching in Ruby

Found the following code in the ruby-talk archives. It allows to cache what a method returns.

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

if __FILE__ == $0
    class Klass
        def foo; end
        def bar; end
        cached_method :foo, :bar
    end
end

Leave a Reply

CAPTCHA Image