Ruby Method Lookup Demystified: Inheritance, Mixins, and Super
Published: 2023-05-22 11:01:42

---
title: Ruby Method Lookup Demystified: Inheritance, Mixins, and Super
published: true
date: 2023-05-15 08:37:10 UTC
tags: 
canonical_url: https://blog.unathichonco.com/ruby-method-lookup-demystified-inheritance-mixins-and-super
---

If youve worked on Ruby projects for a while, you would have encountered times when you were digging to find out how and why a certain method behaved the way it did, because it didnt seem to use the code in the method definition youre looking at. And what happens when there are multiple methods with the same name?

This can seem confusing, but Ruby does have a specific way or path that is used to determine what methods to actually call. This process is called method lookup. In this guide we will delve into the details of method lookup, covering inheritance, mixins using `include`, `prepend`, and `extend`, as well as the `super` method. With clear examples and explanations, this article will help you build a strong foundation and understanding that enables you to write more maintainable code.

## Inheritance and Method Lookup in Ruby

In Ruby, inheritance allows a class to inherit the methods and attributes of another class. The class that inherits is called a subclass, and the class that is inherited from is called a superclass. Method lookup in Ruby begins by searching the instance methods of the object's class, and if not found, it moves up the class hierarchy until it reaches the superclass. Let's consider an example:

```ruby
class Animal
  def speak
    "I'm an animal!"
  end
end

class Dog < Animal
end

dog = Dog.new
puts dog.speak # Output: I'm an animal!

```

In the example above, the `Dog` class inherits from the `Animal` class. When we call the `speak` method on a `Dog` object, Ruby first looks for the method in the `Dog` class. Since it doesn't find it there, it searches the superclass `Animal`, finds the `speak` method, and executes it.

## Mixins and Method Lookup

Ruby uses modules as a way to implement multiple inheritance through mixins. Mixins are used to share methods among different classes, making your code more modular and easier to maintain. There are three primary ways to include a module in your class: `include`, `prepend`, and `extend`.

### Include

`include` adds the module's methods as instance methods in the class. When a method is called on an object, Ruby first searches the object's class, then the included module, and finally the superclass.

```ruby
module Speak
  def speak
    "I'm a speaking mixin!"
  end
end

class Dog < Animal
  include Speak
end

dog = Dog.new
puts dog.speak # Output: I'm a speaking mixin!

```

In this example, we've included the `Speak` module in the `Dog` class. When we call the `speak` method on a `Dog` object, Ruby first looks for the method in the `Dog` class, then in the included `Speak` module, and finally in the superclass `Animal`. Since it finds the `speak` method in the `Speak` module, it executes that method.

### Prepend

`prepend` is similar to `include`, but it inserts the module's methods _before_ the class in the method lookup chain.

```ruby
class Dog2 < Animal
  prepend Speak
end

dog = Dog2.new
puts dog.speak # Output: I'm a speaking mixin!

```

In this case, when we call the `speak` method on a `Dog` object, Ruby first looks in the prepended `Speak` module, then in the `Dog` class, and finally in the superclass `Animal`. Since it finds the `speak` method in the `Speak` module, it executes that method.

### Extend

`extend` adds the module's methods as class methods, rather than instance methods. This means that the methods can be called directly on the class itself.

```ruby
class Dog < Animal
  extend Speak
end

puts Dog.speak # Output: I'm a speaking mixin

```

In this example, by using `extend` instead of `include` or `prepend`, we've added the `speak` method as a class method of `Dog`. This allows us to call the `speak` method directly on the `Dog` class.

## The `super` Method

The `super` method in Ruby allows a subclass to call a method from its superclass. This can be useful when you want to extend the behaviour of a superclass method in a subclass without completely overriding it. Let's see an example:

```ruby
class Animal
  def speak
    "I'm an animal!"
  end
end

class Dog < Animal
  def speak
    "#{super} and I'm a dog!"
  end
end

dog = Dog.new
puts dog.speak # Output: I'm an animal! and I'm a dog!

```

In the `Dog` class, we've defined a `speak` method that calls the `speak` method from its superclass `Animal` using the `super` keyword. This allows us to extend the behaviour of the `Animal#speak` method in the `Dog` class.

### The `super` Method with Mixins

The `super` methods can still be used when mixins are involved, and for the purposes of this post is a great showcase of the method lookup flow.

In this example, we'll demonstrate the use of `super` in an object that uses both `prepend` and `include` to mixin the same method. We'll have three modules with a `greet` method, and a `Person` class that will include and prepend those modules.

```ruby
module GreetInEnglish
  def greet
    "Hello! #{super}"
  end
end

module GreetInSpanish
  def greet
    "Hola! #{super}"
  end
end

module GreetInFrench
  def greet
    "Bonjour! #{super}"
  end
end

class LivingBeing
  prepend GreetInEnglish

  def greet
    "I'm a living being."
  end
end

class Person < LivingBeing
  include GreetInFrench
  prepend GreetInSpanish

  def greet
    "I'm a person. #{super}"
  end
end

person = Person.new
puts person.greet

```

In this example, we've added a `LivingBeing` class that includes `GreetInEnglish` and has its own `greet` method. The `Person` class now inherits from `LivingBeing`, includes `GreetInFrench`, and prepends `GreetInSpanish`.

When we call `person.greet`, the method lookup order is as follows:

1. `GreetInSpanish#greet` (prepended in `Person`)

2. `Person#greet` (defined in the class)

3. `GreetInFrench#greet` (included in `Person`)

4. `GreetInEnglish#greet` (prepended in `LivingBeing`)

5. `LivingBeing#greet` (inherited from the superclass)

The output will be:

```
Hola! I'm a person. Bonjour! Hello! I'm a living being.

```

Now, the method lookup chain correctly includes all mixins and the inherited method from `LivingBeing`. The `super` keyword is used in each of the mixin methods and the `Person#greet` method to call the next method in the lookup chain.

## Conclusion

Understanding Ruby's method lookup process helps you write more maintainable code and tackle complex Ruby projects and libraries. Use inheritance, mixins, and the `super` method to override and extend capabilities in Ruby when needed. Be mindful of code complexity and avoid hiding implementation details in too many places. Happy coding!

🧑🏾🚀🚀