This was the tip of the week in the August 5, 2021 Ruby Weekly Newsletter.


In last week’s tip, we learned about ancestors. This set us up to learn about three ways of using code from a module in a class. This week, we’ll discuss what is likely the most common of these approaches: include.

We can look at an example of using include to include a module’s methods in a class:

module ExampleModule
  def example_method
    "This is defined in the module we're including"
  end
end

class ExampleClass
  include ExampleModule
end

ExampleClass.new.example_method
=> "This is defined in the module we're including"

What is actually happening when we include ExampleModule, or include any module or class, is that this module or class will be inserted in the ancestors chain directly after the class which includes it:

ExampleClass.ancestors
=> [ExampleClass, ExampleModule, Object, Kernel, BasicObject]

As we learned last week, this means that Ruby will first look for a method defined on ExampleClass, and if it’s not defined there, it will continue to traverse the list of ancestors, looking next at ExampleModule.

This means using include gives us the helpful property of being able to easily override methods defined in an included module:

module ExampleModule
  def example_method
    "This is defined in the module we're including"
  end
end

class ExampleClass
  include ExampleModule

  def example_method
    "This is defined in ExampleClass itself"
  end
end

ExampleClass.new.example_method
=> "This is defined in ExampleClass itself"

If we’re used to using include, we might have an intuitive understanding of this overriding in action. Hopefully now with the context around ancestors, we can see why it’s happening. In the coming weeks, we’ll have tips around two other ways of using code from modules or classes: extend and prepend.