ruby_cool_kid.rb — Meta Programming series: Ghost Methods

Pablo Adell
6 min readOct 10, 2021
Photo by Joshua Fuller on Unsplash

Following my ruby series, here is the next chapter, where I will be diving into Ruby’s methodmethod_missing and its pros and cons.

The other day, I was thinking about writing this story when one of my colleagues heard me talking about Paolo Perrotta. He sent me one of his conferences from 2012 (reference). On it, Perrotta talked about the advantages of using method_missing to avoid code duplication on a Ruby app. However, with every great power, comes great responsibility (deep hero voice 🕸)

In a previous story, we talked about dynamic dispatching and the fact that Ruby, as a language, is interpreted and not compiled, allowing us to do things out of normal. For example, defining a method on runtime when needed. However, that is not the only way of getting rid of duplicated code.

In this story, following Perrotta’s talk, we are going to talk about Ghost methods and the use of method_missing . Let’s take the initial code we had on a previous example

Base code case

First, in order to avoid code duplication, we can try and extract the person to be congratulated to another class, let’s say BirthdayPerson. Additionally, let’s try to up the notch a bit and suppose we have a file containing all the people we can congratulate for their birthday, BirthdayDatabase.

Let’s define BirthdayPerson:

BirthdayPerson class

Here we have a couple of interesting points to note. First, take a look at the initialisation method.

allowed_people returns all the different people we have in our “database” that we could want to congratulate, each one of them being returned as a symbol.

Then by calling create_birthday we will define each one of the available methods to congratulate the people in our “database”.

As the reader can observe, the way of defining the methods is quite different from the method we used in a previous story. The reason for that change lays in the fact that we are defining methods at the instance level, rather than the singleton level, so that all instances of the class share the same methods.

A singleton method is a method only available for a “single” instance of the object

Thus, define_method (reference) will define the methods resulting from the people in our database.

We have now an interface to map the people in our database to methods that can be called or “sent” 😉

It is time then to refactor our base code and remove code duplication.

But first, a small note on Ruby runtime.

In Ruby, there is no compiler that enforces method calls due to its dynamic nature. This means that you could call a method that doesn’t exist

But, if that is the case, what is happening when a non-existent method is called? What is returning NoMethodError: undefined method 'method'? Where do all those send methods go, spam folder? (Sorry for the bad joke 😆) .Too many questions, very few answers.

Well, what starts in Ruby ends in Ruby. Let’s bring method_missing and ancestors into the stage.

When Ruby does not find a method in a Class or any of its ancestors, it calls method_missing . However, it is just another method that, as we saw in our ancestors story, is called on each element of the ancestors' chain until responded. It is like that time you want to go for a run with your friends, you send the message hoping for it to be answered.

In this case, method_missing will always be answered, if not by the closer ancestors, by Object and then BasicObject , returning that sweet NoMethodError message. However, knowing what we know about Ruby, where method names are sent on the ancestors' chain and the method named is sent from bottom to top, how could we use this to our advantage and duplicate code?

Ghost methods

Knowing what we know on method_missing and our knowledge on Ruby ancestors we could try to play a little with it.

Let’s imagine the first piece of code I showed you in this article:

And refactor it to use our abstraction to BirthdayPerson:

Looks way better and simpler, right? We are just delegating the method to BirthdayPerson by sending it the method name.

However, we still have to define the congratulate method. So, would it be possible to refactor the code in any way that no new method is defined? 🤔

What about using method_missing and Ruby’s ancestors? That’s it!

Using method missing to call method on BirthdayPerson

Boom!! If you take a look, you’d see that congratulate has been replaced by method_missing, which will be the one sending the method name to our BirthdayPerson object. Thus, saving us from defining a new method by taking advantage of Ruby’s ancestors' chain.

But, how is this happening under the hood?

A drawing may help, right?

Schema on how method_missing worked for the piece of code

The drawing above tries to explain the path the Interpreter of Ruby took to find the method sent. First, it tried to find it on the current object it was evaluating, an instance of BirthdayCongratulations. As it did not find it there, it went up on the ancestors' chain performing method_lookup until the method was found.

When it got to the top of the chain without any match, the Interpreter called method_missing on the first element on the chain, where the original call to the method was sent. Finding a match for method_missing at that level, it proceeds to execute the code inside.

In this case, the code inside was a call on an instance of BirthdayPerson that will execute the desired code.

Such scenario and availability for a method that is not within the object it gets called on, nor any of its ancestors, is called a Ghost method. It is there for the interpreter, but it is not really there. If we called respond_to? on birthday_congratulations the output would be:

So 🤔 The method call works on the object, but the object does not respond_to the method? Looks like something that makes sound but is not there. A ghost!!

The concept of Ghost method is really interesting and can be useful when developing things like gems or frameworks, but it still can cause some problems for implementations that expect objects to respond_to the method called upon. This is not a huge deal normally but is better to be aware of. Also, of course, there is a solution 😉

Respond_to_missing?

As the reader may have noticed by now, in Ruby nothing is what it seems.

So, why would be respond_to?? If we inspect the method (reference), we would see that it calls respond_to_missing? if it can’t find the method on the ancestors' chain of the class. Notice a behaviour similar to the one happening when the Interpreter can’t find the method on the object.

Bearing this in mind, implementing respond_to_missing? is key when applying the Ghost method pattern. If not implemented, again, it is not a big deal, but can cause problems if the object is used in another programs or in ways we did not anticipate. Better safe than sorry 😉

Implementing respond_to_missing?

As of right now, you are almost a gurú on Ghost methods but there are still some nuances worth mentioning. But, let’s leave it for another story as I think this one is dense enough.

I hope you enjoyed todays’ story. Don’t be scared of ghosts 👻 😉

See you around!!

Pablo

--

--

Pablo Adell

Hello there!! My name is Pablo, I am a Full-Stack Software Engineer at Affirm, focused mainly in Ruby on Rails and React. I hope you enjoy my content!