Do YOU know Ruby’s ‘Chainsaw’ method?

by Paolo Perrotta on October 7, 2010

Do YOU know Ruby’s ‘Chainsaw’ method?

This guest post is contributed by Paolo Perrotta, a freelance geek, currently coaching agile teams for a large phone company. He also wrote the Metaprogramming Ruby book for the Prags. He lives in Northern Italy with his girlfriend and a cat. He loves Ruby.

The method_missing() method is a wonderful tool for every Ruby programmer. I love it. There, I said it!

method_missing
method_missing()

Some Rubyists are surprised when I declare my love for method_missing(). They do have a point. As far as tools go, method_missing() is a chainsaw: it’s powerful, but it’s also potentially dangerous.

Paolo Perrotta In case you don’t know about method_missing(), here is how it works. Imagine that you’re calling a method on a Ruby object. For example, you call duck.sing("Quacking in the Rain"). Usually, at this point one of two things happens: if duck has a sing() method that takes one argument, then Ruby executes the method; if duck doesn’t have that method, then Ruby raises an error. But wait: there is a third possibility. If duckdoesn’t have a method named sing(), but it does have a method named method_missing(), then Ruby executes method_missing() instead. Ruby also tells to method_missing() which method you originally called, with arguments and all. Here is an example:

You can say that duck.sing() and duck.dance() are Ghost Methods: they aren’t defined anywhere, but you can call them anyway.

That’s what method_missing() does. The difficult part is deciding when and how to use it. Let’s see an example of Ghost Methods in action.

Imagine that you have an InformationDesk object with many complex methods that provide some kind of tourist information or service:

To give a break to people working at the InformationDesk, you can wrap the InformationDesk in a DoNotDisturb object:

DoNotDisturb forwards method calls to the wrapped InformationDesk, unless it’s lunchtime. During lunch breaks, DoNotDisturb responds to calls by raising an exception – unless you’re calling emergency(), that works at any hour. (If you think that two hours are too much for a lunch, then you’ve probably never lived in Italy!)

The problem with the above code is that DoNotDisturb contains a lot of look-alike methods. This is a form of duplication, and duplication sucks. If a programmer adds methods to InformationDesk, she also needs to remember to add matching methods to DoNotDisturb, most likely by copy-pasting the existing look-alike methods and then modifying them. One misstep in this procedure, and you have a brand new bug.

When I work with Java or C#, I accept this kind of code duplication as a fact of life. In Ruby, I can be more aggressive and replace all the calls with a single method_missing():

DoNotDisturb is now a Dynamic Proxy: it forwards each method call to its inner InformationDesk, and it also wraps additional logic around each call. If you add methods to InformationDesk, you needn’t worry about DoNotDisturb: it will work for the new methods without any change.

This kind of trickery is useful, but it does have a dark side. If you abuse method_missing(), you can end up with code that’s difficult to read and maintain. I’m still intimidated by some of the wildest examples of method_missing() that abound the Ruby ecosystem. Even if you take care to keep your method_missing()small and maintainable, Ghost Methods are still inherently less explicit than regular methods. You can easily discover the regular methods in a class by looking at the auto-generated documentation, but you have to look at the source code (or the author’s documentation) to spot Ghost Methods.

Also, if you’re not careful around method_missing(), sneaky bugs can creep into your code. For example, an overly tolerant method_missing() might accept a mistyped method call without flinching. Chainsaws are meant to be handled with care!

So, what kind of tree should you use the method_missing() chainsaw on? Here are my simple rules of thumb:

  1. I use method_missing()to remove duplication. A well placed method_missing() allows me to remove duplicated code at a level that wouldn’t otherwise be possible.
  2. On the other hand, I usually think twice about using method_missing() for cosmetic reasons, like getting cool method names such as find_by_name_and_address().
  3. I always try to evaluate whether method_missing() is worth the extra reading effort. I dislike duplicated code because it’s hard to read and modify. If I replace that code with a method_missing() that’s even harder to read and modify, then I’ve defeated method_missing()‘s purpose.

It’s a balancing act: with some experience you can strike the sweet spot between shortness and clarity in your Ruby code. To do that, you’ll likely use the method_missing() chainsaw to cut out unnecessary branches from your forest of methods.

There are many more neat tricks, caveats and interesting details around method_missing(). In Metaprogramming Ruby, I devoted most of the Methods chapter to it (you can check the first few pages from that chapter here). You can also find a lot of information on method_missing() on the Web, including some pretty crazy bug reports. With this information, and some experience, you’ll be able to make up your own mind about the pros and cons of method_missing().

Me, I already made up my mind. Yes, I use method_missing() sparingly. Yes, I keep it as short as I can. Yeah, I triple-check it for bugs… But when the duplication begins to sting, I put on my evil grin and grab my faithful chainsaw.

Have you used method_missing() before? Why don’t you share your experiences with us? Let us know in the comments section of this post. Thanks!

Post supported by Tupalo.com: Tupalo.com is a privately-held internet startup located in Vienna, Austria. Their social yellow-pages app is developed using Ruby on Rails and also uses many of the other exciting tools like Cucumber, RSpec, Capistrano and Puppet, Ruby developers came to appreciate.

Do read these awesome Guest Posts:

Technorati Tags: , , , , ,

Posted by Paolo Perrotta

{ 21 comments… read them below or add one }

Sebastian von Conrad October 7, 2010 at 6:53 am

Good write up. However, in the spirit of removing duplication (and code), your final gist bothered me a bit. See my fork for how I’d do it: http://gist.github.com/614393

Reply

Paolo Perrotta October 7, 2010 at 2:57 pm

Sebastian: Your gist is definitely better, so I edited my example to match your fork. Thank you.

Reply

Terry Smith October 7, 2010 at 7:52 am

Bad methodology. What you do is code the software in such a way as the method will always be defined before you call it.

Reply

Paolo Perrotta October 7, 2010 at 3:38 pm

Terry: out of context, I disagree. Dynamic Method definitions are an alternate option, but not necessarily better, or even viable in some cases (I talk about this in my answer to Steve’s comment). I think you should know both techniques, and pick either one or the other depending on the situation.

In general, with pre-defined methods, the proxy’s interface is more explicit: if you instrospect the proxy, you see the methods. On the other hand, sometimes I find that a lazier method_missing()-based approach ends up being more readable when browsing through the code.

For the particular simple example I used, define_method() might be better than method_missing(), but I didn’t want to make the example too complicated.

Reply

Andrew October 7, 2010 at 7:53 am

Instead of doing the method missing always, you might want to use define_method within method missing. That should speed things up a bit, and have the bonus of showing up in the object methods when you drop into irb.

Reply

sep332 October 7, 2010 at 8:02 am

Maybe I missed something, but what’s the advantage to this over just having DoNotDisturb inherit from InformationDesk and overriding the single function you want to change?

Reply

Skade October 7, 2010 at 1:47 pm

Because that would mean that i have to decide whether I want to construct a non-disturbable Helpdesk for everyone or not. This pattern allows for a really nice delegate.

Also, when there are 4 different kinds of HelpDesks I can inherit from and all of them could “potentially” have Non-Disturb-Hours, which one would I inherit from?

Inheritance for code-sharing only is bad.

Reply

Giles Bowkett October 7, 2010 at 8:39 am

Chainsaws are for juggling. :-)

Reply

Steve Schwartz October 7, 2010 at 9:19 am

I posted this over on Hacker News (http://news.ycombinator.com/item?id=1766662), but thought it might benefit readers here as well:

I prefer to use define_method over method_missing when “metaprogramming”. It allows you to explicitly define your “metamethods” up-front.

Using the example of the DoNotDisturb wrapper around InformationDesk, you could easily do something like:

  [:emergency, :flights, :local_transports, :hotels].each do |name|
    define_method(name) do
      check_lunch_break unless name == :emergency
      @desk.send(name)
    end
  end

Now you don’t get the side effect of screwing up your exception handling and having to walk on eggshells with the rest of your coding in that class.

The other thing I like about this method is that it gets evaluated once, and then those methods exist. With method_missing, every single time you call one of those methods, it has to go all the way up the chain to find that method until it finally hits method_missing.

And if nothing else, you can at least put the define_method inside the method_missing (I had a project that required method_missing once, so this is what I did). That way, it only has to crawl all the way up the inheritance chain once, then that method gets defined on your class, so subsequent calls to that method don’t have to go all the way up the chain again.

Might not be much performance gain in Ruby, but when programming a rails app (where you’re likely working on a class that inherits from ActiveRecord and 10 other classes), it helps.

Another option would be to pull the common methods out into a module that you can include in each class. I like this option, because then it’s trivial to go create another class called OutForVacation, in which you could include this module, and now you have another wrapper with all the same functionality.

Reply

Paolo Perrotta October 7, 2010 at 3:28 pm

Steve (and all the other guys who suggested define_method() over method_missing()): That’s appropriate sometimes, but not necesarily in every situation. One counterexample is a situation where you don’t know which methods are available on the proxied target as you write your proxy – maybe because the target is changing independently from your code and is difficult to introspect, as in the case of an online service.

In general, method_missing() is more dynamic than define_method(), because you don’t have to set up your methods eagerly before you use your proxy. If you use Dynamic Method definitions in both the proxy and the target, then you have to worry about the order of definitions. In some cases, you might even want to use both techniques together, as ActiveRecord::Base does.

In the book, I compare the two techniques. In this post, I focus on one of them. I also tried to make it clear that there is more to learn about method_missing() than I covered in the post – for example, I don’t talk about redefining respond_to?() or using a Blank Slate as your proxy (http://gist.github.com/534987).

Thank you for the alternate example, Steve.

Reply

Glenn Gillen October 7, 2010 at 1:06 pm

Or you could just do something like the following: http://gist.github.com/614718

The problem with method_missing is everything starts to look like a nail, or in this case some lumber that needs to be savaged. It’s non-explicit, and as a result more difficult to debug than it needs to be. You also incur a bit of a performance hit.

At the very least if you are going to use method_missing you need to ensure you call super if you don’t take any action (to retain the default behaviour) and implement the corresponding support into responds_to? so that your class can correctly assert that it will take action on a given method.

Reply

Botanicus October 7, 2010 at 5:01 pm

It’s worthy to mention that most of the stuff folks are misusing (or even abusing?) method_mission in situations which should actually be solved by define_method. Advantages of define_method:

1) You can inspect it via obj.methods (Do you have find_by_oh_hell_how_was_the_syntax??? moments? I certainly do … ).
2) It’s faster (unless it’ll generate really loads of methods).

In my opinion your example should be actually an example of define_method usage, check the following code: http://gist.github.com/614980

Reply

Paolo Perrotta October 7, 2010 at 5:29 pm

See the other comments for discussions of define_method(). We agree that it’s a good or better alternative to method_missing() in many situations.

To nitpick, I wouldn’t say that define_method() is faster than method_missing() in general. It just shifts the performance hit from the method call to the method definition. If you define lots of methods and you never call most of them, then method_missing() might offer better performance (assuming that this kind of performance is noticeable in your system, which it rarely is). Depending on your situation, you might even want to use both techniques like Rails does: the first call goes to method_missing(), and method_missing() defines the method for subsequent calls.

Reply

Glenn Gillen October 7, 2010 at 5:41 pm

Paolo,

In most traditional OO languages this represents an almost textbook case for using inheritance. Ruby offers the alternative, and probably preferable, option of mixing in a module.

I think the problem here is that regardless of the justifications almost everyone agrees with Jakub that in the this particular (admittedly contrived) example method_missing is the least preferred way of implementing the feature. I don’t think it’s unreasonable to hold rubylearning.com to a higher level of standards than most other blogs, you have a duty of care to be teaching developers how to do it right. We’d really prefer if we weren’t teaching new developers bad habits, as we will likely inherit their code at some point in the future.

Reply

Paolo Perrotta October 7, 2010 at 6:37 pm

Inheritance is orthogonal to method_missing(). You could use either, or both. In a statically typed language, you’d need inheritance to be able to use a DoNotDisturb in place of an InformationDesk (bad choice of names – sorry). Whatever the type policy, using inheritance in this particular example would buy you nothing when it comes to removing duplication.

Ruby Mixins do offer better alternatives. In particular, your module-based solution above is good, as long as I’m willing to pollute the wrapped class’ domain with proxy-related concerns (check_lunch_break()). If I don’t want to, or I can’t edit the wrapped class, then method_missing() or define_method() are both better options.

I should have been clearer that this was a post about method_missing() and one legitimate use for it. It’s not a post about exploring alternatives that can sometimes be better. I hate to explain intentions (because it means I wasn’t able to get them through on first try), but my intention for this post was to discourage beginners from prematurely using method_missing(), and convincing them to wait until the code calls for it, for example until you have lots of duplication in your method definitions. I also pretended to ignore the topic of internal DSLs, where method_missing() is often very useful.

On a side note, I’m surprised that so many people objected so strongly to method_missing() in general. You’d be hard pressed to find a popular gem that doesn’t use it, and most of these gems could have done with alternatives.

Reply

Botanicus October 7, 2010 at 6:52 pm

I have to agree with Glen, it’s really job for inheritance (or mixins). I had the intention to point it out originally, but decided to rather shush and don’t be too critical. To sum it up, I agree that method_missing is a good thing to have in language, but it should be used just where you really have to. Take it easy though, it’s nothing horrible, I’d just expect better example for method_missing.

Erik October 7, 2010 at 7:56 pm

I don’t think people object to method_missing in general. In the ActiveRecord example you link to (but don’t describe) it serves a very useful purpose… defining a large space of complex methods that really need to be their own methods (find_by_*) and would be expensive to generate ahead of time with define_method (as you allude to in your above comment).

But you didn’t highlight any of that in your post… you just used trivial examples that would be better implemented with define_method… Really, some of your examples could just be plain old methods (DoNotDisturb.new.alert(“emergency”) instead of .new.emergecy, for example)

So I don’t think it’s that no one thinks there are important uses of method_missing…. you just haven’t really highlighted them yet.

Steve Schwartz October 7, 2010 at 8:04 pm

“To nitpick, I wouldn’t say that define_method() is faster than method_missing() in general. It just shifts the performance hit from the method call to the method definition. If you define lots of methods and you never call most of them, then method_missing() might offer better performance (assuming that this kind of performance is noticeable in your system, which it rarely is). ”

You can put define_method inside your method_missing to avoid this up-front performance hit of define_method for methods you may never call.

Reply

Mukund October 8, 2010 at 9:25 am

Have you considered the performance overhead of using method_missing? In a large project, the runtime is going to check all the way up to Object for the method and then re-traverse the hierarchy looking for method_missing. Projects like Rails uses work arounds to avoid this overhead.

Reply

Steve Schwartz October 10, 2010 at 12:50 am

OK, this post got me thinking about what situations really call for using method_missing.

The only scenario I could think of that wouldn’t be better served by some other approach, is when the number of methods you need to make available is far more than the number of methods you will actually end up calling. Case in point: ActiveRecord’s find_by_ methods. If you have 10 columns, you would have to dynamically define 10-factorial methods to cover every possible combination.

Anyway, I wrote a post that fully explains it: http://www.alfajango.com/blog/a-rubyists-beautiful-mistress-method_missing/

I also outlined 4 guidelines you should always follow when using method_missing.

Reply

Pieter November 19, 2011 at 1:56 am

well explained, thanks!

Reply

Leave a Comment

{ 78 trackbacks }

Previous post:

Next post: