Method Lookup in Ruby: How Ruby Finds Methods

Method Lookup in Ruby: How Ruby Finds Methods

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

  1. Introduction to Method Lookup
  2. Singleton Classes and Their Role in Method Lookup
  3. Class and Module Lookup
  4. The Role of method_missing
  5. 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:

  1. Singleton class (if the object has one)
  2. Class of the object
  3. Modules included in the class (in reverse order of inclusion)
  4. Superclass and its modules
  5. Object and Kernel modules
  6. 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:

  1. dog.speak: Ruby checks the singleton class first and finds speak.
  2. Dog.new.speak: Since there’s no singleton method, Ruby checks Dog, where it finds speak.
  3. dog.move: Ruby finds move in the Swimmable module, included last in Dog.

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.