ruby_cool_kid.rb — Meta Programming series: Ghost Methods, pulling the sheets

Pablo Adell
3 min readOct 17, 2021

--

Photo by Joshua Fuller on Unsplash

As of right now, you are almost a gurú on Ghost methods, if you followed our previous story , but there are still some nuances worth mentioning.

Let’s first talk about the Ghost within the Ghost

Ghost within the ghost

Implementing method_missing can be a good practice that saves time for a developer, but can also carry some unexpected errors with it.

Taking the code explained above, what would happen if we modified method_missing to covert the name of the person to lower case?

What?

Why does it throw an exception? Look closely to where the name is converted to lower case. Do you see it?

Of course, everything was going good but we had to make a typo 😓. But, if it was a typo, why doesn’t it just throw a NoMethodError exception?

Well, if you remember, we overwrote the method_missing method to match our desires, and we call the method that has the typo within it. So, Ruby falls into a loophole of not finding the method and going into method_missing , which has our definition for the method with the typo, going again into method_missing, until it throws the SystemStackError due to the stacked calls to method_missing. Interesting, right? 😅

Fake Ghosts

It can also happen, that a method we thought was going to be the perfect ghost, gets played by Ruby’s own methods for the objects in the ancestors' chain.

Let’s suppose that we define a method on BirthdayCongratulations called display that will return all the possible congratulations messages based on the records in our database. Of course, we would need to refactor our classes so that the default is not an interpolation with congratulate_ but those are just nuances for this scenario.

What would happen if display is called following the Ghost method pattern. It won’t be called on BirthdayPerson as you’d expect looking at our implementation of method_missing. It would be called on Object (reference).

If you remember what we said about method_missing is that, before triggering it, the Interpreter will look up on the ancestor's chain for a match and, if not found, trigger method_missing.

However, in this case, display is found and will stop the Interpreter from calling method_missing.

Blank slate

One way of solving this would be to undefine every instance method for the class so that the calls will not collide:

Buuuut, this may remove some essentials methods for Ruby. So, what can be the alternative?

Well, Ruby serves us with a class that has just the basic methods needed for an object to work in Ruby, BasicObject. Inheriting from BasicObject ensures that we just have the minimum methods for an object to work, thus reducing the probabilities of collision.

This pattern is also known as a blank slate.

That’s all for today folks, I hope you enjoyed the story and do not be scared of Ghosts 😉

See you around!!

Pablo

--

--

Pablo Adell
Pablo Adell

Written by 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!

No responses yet