Archive for November, 2007

Automatic method forwarding in Ruby

Wednesday, November 21st, 2007

Warning for googlers: this post is probably not useful. I wasn’t that good at Ruby metaprogramming when I wrote this post. (The obvious followup question is why I wrote it in the first place. I have no idea.)


I’m implementing a Tree in Ruby for an Euler problem (I don’t think I’m quite good enough to attempt it in Haskell yet). Yes I know there’s probably several in the standard libraries, but that’s cheating.

Anyway, a lot of the methods, like size, each, and so on are easiest implemented as recursive methods of the nodes. Meaning they’re all just calling say @root.size, and nothing else. This is not on.

One of the best things about Ruby is that you can just define new methods for any class, at any time, and moreover you can do this automatically using instance_eval and friends. So this was just dying for a forward_to method which takes a list of names, and forwards those methods to some member or other. The first form is thus1:

module ForwardTo
    def forward_to mem, *meths
        meths.each do |meth|
            instance_eval "def #{meth}; @#{mem}.send :#{meth}; end"
        end
    end
end

Obviously this doesn’t allow arguments or blocks to be passed, making all kinds of things impossible, but notably each, which takes both (it has an argument specifying the order of traversal), and was the whole point. Luckily this is easily solved, by adding *args and &block. Also for some reason I forgot about %<> quoting2 the first time around, so this iteration is a lot more readable.

module ForwardTo
    def forward_to mem, meths
        meths.each do |meth|
            instance_eval %<
                def #{meth}(args, &block)
                    @#{mem}.send :#{meth}, *args, &block
                end
            >
        end
    end
end

end end end end end end end end end… Anyway, you probably already know that send doesn’t honour method visibility, so we could be accidentally calling private methods. So I changed it to —

— And then, just a second ago when writing this, I came to my senses and realised that I’m in an eval so I don’t have to mess around with this sort of thing: I can just have @#{mem}.#{meth} in the string instead. Also, I put the method in Object instead because it’s small and unlikely to mess things up, and it saves a call to include.

class Object
    def forward_to mem, meths
        meths.each do |meth|
            instance_eval %<
                def #{meth}(args, &block)
                    @#{mem}.#{meth}(*args, &block)
                end
            >
        end
    end
end

There is still a big problem with this, though. You will probably want to put it in the class body, along with things like attr_accessor:

class Foo
    @mem = []
    # ...
    forward_to :mem, :each, :size
end

… which will not work. The ‘instance’ that instance_eval sees is the instance of Class; in other words, this will add class methods each and size. Which will forward to instance variables. Useful, I know. The workaround I’m using at the moment is to put forward_to calls into initialize. I’ll fix it properly when I find the name of the method that does what I mean.

Update: There’s a not-very-well-hidden method called class_eval, which… does exactly what you’d expect. So you can use that instead of instance_eval and then forward_to can be used in exactly the same way as the attr_* methods. (There’s also a module_eval, incidentally.)


  1. Actually the first iteration was some godawful mess with method_missing, but let’s not go there. 

  2. Which the syntax highlighter can’t cope with, so I’ll not highlight the snippets which use it.