include
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 include
s 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
.