Function Decorators in Ruby

Function decorators1 are cool. Put simply, a decorator “wraps” a function in order to extend its behavior without modifying the original function. Python has them built-in:

def memoize(fn): """ Decorator to memoize the result of a given function """ cache = {} def memoizer(*args): if args not in cache: cache[args] = fn(*args) return cache[args] return memoizer @memoize def fib(n): """ Calculate the nth Fibonacci number """ if n == 0 or n == 1: return n return fib(n-1) + fib(n-2)

With a bit of metaprogramming, we can achieve a similar result in Ruby:

# Decorator to memoize the result of a given function def memoize(fn) cache = {} fxn = singleton_class.instance_method(fn) define_singleton_method fn do |*args| unless cache.include?(args) cache[args] = fxn.bind(self).call(*args) end cache[args] end end # Calculate the nth Fibonacci number def fib(n) return n if n == 0 || n == 1 return fib(n-1) + fib(n-2) end memoize :fib

In Ruby 2.1, def returns the name of the defined method2. We can make it even nicer:

memoize def fib(n) return n if n == 0 || n == 1 return fib(n-1) + fib(n-2) end

How cool is that?

1Intro to Python Decorators, by Bruce Eckel

2value of def-expr: Ruby feature #3753, implemented in Revision 42337