Inheritance in Ruby: The Ancestor Chain

source JACLOU-DL, via pixabay

In the same way Eurasier puppies inherit attributes and behaviors from their parents, or heirs receive assets from the deceased, Ruby classes can inherit methods and behaviors from their superclasses. This functionality is fundamental to object-oriented programming, allowing for the creation of increasingly specific subclasses by leveraging method reusability. A child (subclass) inherits all the methods of its parent (superclass), and can increase in specificity depending on the programmer’s needs. These capabilities are illustrated in the following example:

Example 1: Simple Inheritance in Ruby

On line 7 in the example (above), the subclass Bulldog inherits the methods from its superclass Dog using the < key. The Bulldog class inherits the #speak method and becomes increasingly specific by introducing a new #sleep method unique to itself. When the Bulldog instance of baby is created, it’s clear that she has the ability to both #speak and #sleep.

Now imagine a scenario where a new subclass of Dog is required, but this specific type of dog’s speaking would be better described as “Yip” instead of “Woof”.

Example 2: Superclass Method Overridden by Its Subclass

In Example 2 (above), both the Schitzu subclass and the Dog superclass contain a method with the same name. When an instance of Schitzu is created, and #speak is called on it, the behavior inherited from the Dog class is overridden. If for some reason the programmer desires the #speak behavior from the superclass, and doesn’t want it to be overridden by Schitzu, the super keyword can be utilized.

Example 3: Inheritance in Ruby Using Super

The super keyword is used to call a method of the same name in the superclass. matilda is now expressing the #speak behavior of its superclass Dog, even though it has a method of the same name.

The Ancestors Chain

In the next section, different methods of inheriting behavior from classes and modules will be explored. .ancestors is a class method in Ruby that returns an array of classes and modules commonly known as the ancestors chain. The order of the elements in this array are listed in a hierarchy of increasing parent rank.

puts String.ancestors# => [String, Comparable, Object, Kernel, BasicObject]

When the .ancestors method is called on the String class, its respective ancestors chain is returned. From the top down BasicObject is the parent class of all Ruby classes, Kernel is its child module, Object is the child class of Kernel, Comparable is the child module of Object and the superclass of String.

When a method is called, a lot happens under the hood. Through the process of method lookup, the chain is referenced, and the method is executed using the instructions defined in the first class or module that appears in the ancestors chain containing that method name.

Example 4: Ancestors Chain Demonstrating Include Keyword

The above example utilizes the include keyword. This keyword is another way to express inheritance in Ruby. The included object is placed directly after the object that includes it in the ancestors chain. The ancestor chain in the above example is expressed as:

[AstroPhysics, Physics, Science, Object, Kernel, BasicObject]

AstroPhysics, the object that .ancestors was called on is the first in the chain, followed by its parent Physics, followed by its parent Science, etc. Typically, the object that .ancestors was called on, is first in the chain. However, there is one keyword that makes an exception: prepend.

Example 5: Ancestors Chain Demonstrating Prepend Keyword

Note that in the above example, Harvard.ancestors was called, but the first module in the chain was LawSchool. The prepend keyword places LawSchool in front of Harvard in the ancestors chain.

puts Harvard.ancestors# => [LawSchool, Harvard, University]

Imagine a situation where all of the class/module objects contain a method named scale. The ancestors chain can be a useful tool to use when trying to figure out what instructions for scale to follow. When viewing the ancestors chain, the object hierarchy in Ruby can be clearly visualized. Example 6 illustrates this point.

Example 6: Tying It All Together

The Drum class includes the Instrument module containing the scale method. However, because Drum also includes a scale method, the ancestors chain tells the programmer that the scale method in the Drum class will take priority over all of the other objects in the chain.

Resources:

  1. https://www.joromir.eu/blog/2018/01/16/the-ancestors-chain-in-ruby/
  2. https://www.rubyguides.com/2018/09/ruby-super-keyword/
  3. https://www.rubycademy.com/screencasts/12

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store