Angelos Orfanakos

DIY method memoization in Ruby

I recently needed to have methods memoized in a Ruby class. The standard way to do this is to use an instance variable and the ||= operator:

class Foo
  def bar
    @bar ||= some_expensive_call
  end
end

But I didn’t want to pollute my class like this, so I ended up rolling my own module with some metaprogramming to provide a nice DSL instead:

module Memoizable
  def memoize(method_name)
    original_method_name = "__original_#{method_name}"

    alias_method(original_method_name, method_name)

    instance_variable_name = "@#{method_name}"

    define_method method_name do
      if !instance_variable_defined?(instance_variable_name)
        instance_variable_set(
          instance_variable_name,
          send(original_method_name)
        )
      end

      instance_variable_get(instance_variable_name)
    end
  end
end

What the method does:

  • Line 5: Back up the original method before overwriting it with the memoized version
  • Line 9: Overwrite method with the memoized version
  • Line 10: If it’s never been called before
  • Lines 11-14: Call it and memoize the result
  • Line 17: Return the memoized result

Using it is very easy:

class Foo
  extend Memoizable

  def bar
    some_expensive_call
  end
  memoize :bar
end

And since def bar returns :bar, you can even do this:

class Foo
  extend Memoizable

  memoize def bar
    some_expensive_call
  end
end

That’s it!