When calling a method on an object in Ruby, the language searches for that method across a hierarchy of classes and modules. This lookup process ensures that Ruby finds the correct method to execute, even if it’s defined in a superclass, module, or as a singleton. In this blog post, we’ll dive deep into Ruby’s method lookup process, covering essential concepts with examples to help you understand how it works.
Table of Contents
- Introduction to Method Lookup
- Singleton Classes and Their Role in Method Lookup
- Class and Module Lookup
- The Role of
method_missing
- Putting It All Together with Detailed Examples
1. Introduction to Method Lookup in Ruby
In Ruby, method lookup is the process of finding the correct method to execute when you call a method on an object. Ruby’s lookup path is known as the method lookup chain. When you call object.method_name
, Ruby checks the following locations in this order:
- Singleton class (if the object has one)
- Class of the object
- Modules included in the class (in reverse order of inclusion)
- Superclass and its modules
- Object and Kernel modules
- BasicObject
If Ruby doesn’t find the method after exhausting the lookup chain, it calls method_missing
.
2. Singleton Classes and Their Role in Method Lookup
A singleton class (or eigenclass) allows defining methods on a single instance of a class. Ruby checks the singleton class first, which lets you override methods or define new ones for individual objects.
Example of Singleton Method Lookup
class Animal
def speak
"I am an animal"
end
end
dog = Animal.new
# Adding a singleton method to `dog`
def dog.speak
"I am a dog"
end
puts dog.speak # => "I am a dog"
puts Animal.new.speak # => "I am an animal"
When dog.speak
is called, Ruby checks dog
‘s singleton class and finds the speak
method there, so it doesn’t proceed to the Animal
class.
3. Class and Module Lookup
If no method is found in the singleton class, Ruby continues looking in the object’s class and any modules included in that class. Modules are included in reverse order of inclusion, so the last included module is checked first.
Example of Module Lookup
module Walkable
def move
"I can walk"
end
end
module Swimmable
def move
"I can swim"
end
end
class Animal
include Walkable
include Swimmable
end
dog = Animal.new
puts dog.move # => "I can swim"
In this case, the Swimmable
module is checked before Walkable
because it was included last. Thus, dog.move
calls the move
method from Swimmable
.
4. The Role of Superclass and Kernel
After the object’s class and its included modules, Ruby moves up the inheritance chain. Ruby keeps checking the superclass and any modules included in it until it reaches the Object
class, which includes the Kernel
module for commonly used methods.
Example of Superclass Lookup
class Animal
def eat
"Eating food"
end
end
class Dog < Animal
end
dog = Dog.new
puts dog.eat # => "Eating food"
Since eat
is defined in the superclass Animal
, dog.eat
finds and uses this method, even though it’s not defined in Dog
.
5. method_missing: Handling Unfound Methods
If the method isn’t found in any of the checked locations, Ruby calls the method_missing
method, which can be customized to handle unknown methods.
Example of method_missing
class Animal
def method_missing(method_name, *args)
"#{method_name} is not defined"
end
end
dog = Animal.new
puts dog.run # => "run is not defined"
Here, run
isn’t defined anywhere in Animal
or its ancestors, so method_missing
is called and handles the output.
6. Detailed Examples of Method Lookup in Action
Let’s see an extended example that combines all these elements to illustrate the complete lookup process.
Comprehensive Example
module Swimmable
def move
"I can swim"
end
end
module Walkable
def move
"I can walk"
end
end
class Animal
include Walkable
end
class Dog < Animal
include Swimmable
def speak
"Bark!"
end
end
dog = Dog.new
# Singleton method definition
def dog.speak
"I am a unique dog"
end
puts dog.speak # => "I am a unique dog" (singleton class)
puts Dog.new.speak # => "Bark!" (Dog class)
puts dog.move # => "I can swim" (Swimmable module)
In this example:
- dog.speak: Ruby checks the singleton class first and finds
speak
. - Dog.new.speak: Since there’s no singleton method, Ruby checks
Dog
, where it findsspeak
. - dog.move: Ruby finds
move
in theSwimmable
module, included last inDog
.
This order preserves modularity and lets developers create flexible, maintainable code by layering methods through modules and superclasses.
Conclusion
Understanding method lookup in Ruby is essential for writing effective, modular, and efficient code. This lookup chain allows Ruby to provide powerful features, such as singleton methods and module inclusion, which let you customize behavior at various levels in the inheritance hierarchy. Whether working on small applications or large codebases, knowing how Ruby finds methods can help avoid pitfalls and improve debugging efficiency.