Ruby And The Method Lookup Path
The method lookup path is a concept I initially struggled with when I first started using Ruby. The method lookup path is the path an object takes to invoke a method with the same name as the message that was sent to it.
Wait What? Messages To Objects?
That sentence might be slightly confusing if you’re new to Ruby or programming in general.
In Ruby we say that messages(methods) are sent to receivers(objects) and those receivers respond to the messages. It is those objects responsibility to invoke a method with the same name as the message.
Heres a quick example:
zero_to_ten = (0..10)
zero_to_ten.to_a
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
zero_to_ten is initialized to reference an object that is an instance of Range. Then the to_a message is sent to the range object that the zero_to_ten variable points at. This range object responds by invoking the instance method.
If you take a look at the documentation for the Range class you will notice that there is no to_a instance method defined.
So why does the code in the above example still work?
The Method Lookup Path Saves The Day
It’s true that to_a is not defined in Range. However take a look at the Included Modules in the documentation, and you will see Enumerable.
If you open up the Enumerable documentation you will find that to_a is indeed defined here. This is the to_a method that will get invoked by the range object from the above example.
When an object receives a message it first looks in to its own class to see if a method is defined with the same name as the message, and if so invokes it. If the method does not exist the object will continue searching the method lookup path which itself contains other classes and modules for that method definition.
The first time the object encounters the method in the path that method will be invoked. If it never encounters a method with the name of the message it received a NoMethodError exception is raised.
The Path An Object Takes
All this talk about the path an object takes to invoke a method leaves one big question.
How am I supposed to know what classes and modules my object looks at in the method lookup path?
Well fellow programmer Ruby has you covered with the ancestors method.
The ancestors method will return an array of classes, and modules that instances of the caller will traverse to invoke a method whenever it receives a message.
In this case a Range object will first look in to its own class, then Enumerable, Object, Kernel, and finally ending at BasicObject.
The order of elements in the array returned by ancestors is very important.
Why?
If you defined an instance method in Enumerable that you want all Range objects to respond to, but forget about it, and define a method with the same name in Range then the method in Range is the one that will be invoked. The Range object will look in its own class first.
Understanding the method lookup path is an important concept to grasp once you start writing your own classes. If you’re ever in doubt just remember Ruby has you covered with ancestors.