Skip to content

Ruby Inheritance — Superclass < Operator super Keyword and Ancestors Chain

DodaTech Updated 2026-06-28 9 min read

In this tutorial, you will learn about Ruby Inheritance. We cover key concepts, practical examples, and best practices to help you master this topic.

Ruby inheritance uses the < operator to create class hierarchies, super to invoke parent class methods, and the ancestors method to inspect the complete method lookup chain.

What You'll Learn

  • Creating class hierarchies with the < operator
  • Using super to call parent methods
  • Overriding and extending inherited methods
  • The ancestor chain and method lookup algorithm

Why It Matters

Inheritance enables code reuse and establishes logical relationships. Durga Antivirus Pro uses inheritance for specialized scanners (FileScanner, NetworkScanner, EmailScanner) that share base functionality. Doda Browser uses inheritance for page types (WebPage, PDFPage, ImagePage). Understanding inheritance hierarchies helps you design clean, maintainable object models.

Real-World Use

Rails models inherit from ApplicationRecord, which inherits from ActiveRecord::Base. Controllers inherit from ApplicationController. This gives every model/controller access to shared functionality like CRUD operations, parameter handling, and callbacks.

flowchart LR
    A["Inheritance"] --> B["Base Class"]
    B --> C["Subclass"]
    C --> D["Method Override"]
    D --> E["super"]
    E --> F["Ancestors"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style F fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Basic Inheritance

class Animal
  def speak
    "Some sound"
  end

  def move
    "Moving..."
  end
end

class Dog < Animal
  def speak
    "Woof!"
  end
end

class Cat < Animal
  def speak
    "Meow!"
  end
end

dog = Dog.new
cat = Cat.new

puts dog.speak   # Woof!
puts cat.speak   # Meow!
puts dog.move    # Moving... (inherited from Animal)

Using Super

super calls the parent class's implementation of the same method:

class Animal
  def initialize(name)
    @name = name
  end

  def describe
    "I am an animal named #{@name}"
  end
end

class Dog < Animal
  def initialize(name, breed)
    super(name)  # Calls Animal's initialize
    @breed = breed
  end

  def describe
    "#{super} and I am a #{@breed} dog"
  end
end

dog = Dog.new("Rex", "German Shepherd")
puts dog.describe
# I am an animal named Rex and I am a German Shepherd dog

Super with No Arguments

Without explicit arguments, super forwards the current method's arguments:

class Parent
  def greet(name)
    "Parent says hello to #{name}"
  end
end

class Child < Parent
  def greet(name)
    super  # Automatically passes 'name'
  end
end

puts Child.new.greet("Alice")
# Parent says hello to Alice

Super with Empty Parentheses

super() calls the parent method with NO arguments:

class Parent
  def greet(name = "World")
    "Hello #{name}"
  end
end

class Child < Parent
  def greet(name)
    super()  # Calls Parent#greet with no arguments → uses default
  end
end

puts Child.new.greet("Alice")
# Hello World

Super with Specific Arguments

class Parent
  def calculate(a, b, c)
    a + b + c
  end
end

class Child < Parent
  def calculate(a, b, c)
    super(a, b, 0)  # Override third argument
  end
end

puts Child.new.calculate(1, 2, 3)  # 3 (1+2+0)

Method Overriding

class Vehicle
  def initialize(make, model)
    @make = make
    @model = model
  end

  def start
    "Engine starting"
  end

  def fuel_type
    "Unknown"
  end
end

class ElectricCar < Vehicle
  def start
    "Electric motor humming"
  end

  def fuel_type
    "Electricity"
  end

  def battery_range
    "300 miles"
  end
end

tesla = ElectricCar.new("Tesla", "Model 3")
puts tesla.start         # Electric motor humming
puts tesla.fuel_type     # Electricity
puts tesla.battery_range # 300 miles

Overriding and Extending

class Logger
  def log(message)
    puts "[#{Time.now}] #{message}"
  end
end

class FileLogger < Logger
  def initialize(filename)
    @file = File.open(filename, "a")
  end

  def log(message)
    super(message)  # Also prints to console
    @file.puts("[#{Time.now}] #{message}")
    @file.flush
  end

  def close
    @file.close
  end
end

logger = FileLogger.new("app.log")
logger.log("Application started")
logger.close

The Ancestors Chain

class Grandparent
end

class Parent < Grandparent
end

class Child < Parent
end

puts Child.ancestors.inspect
# [Child, Parent, Grandparent, Object, Kernel, BasicObject]

How Method Lookup Works

class A
  def who
    "A"
  end
end

module M
  def who
    "M"
  end
end

class B < A
  include M

  def who
    "B"
  end
end

obj = B.new
puts obj.who  # "B"

# Lookup: obj → B → M → A → Object → Kernel → BasicObject

Inheritance and Class Variables

Class variables are shared across the hierarchy:

class Base
  @@count = 0

  def self.count
    @@count
  end

  def increment
    @@count += 1
  end
end

class A < Base
end

class B < Base
end

A.new.increment
puts Base.count  # 1
puts A.count     # 1
puts B.count     # 1 (shared!)

This is often surprising. Use class instance variables instead:

class Base
  @count = 0

  class << self
    attr_accessor :count
  end
end

class A < Base
  @count = 10
end

class B < Base
  @count = 20
end

puts Base.count  # 0
puts A.count     # 10
puts B.count     # 20

Abstract Base Classes

Ruby doesn't have abstract classes, but you can simulate them:

class Shape
  def area
    raise NotImplementedError, "Subclass must implement area"
  end

  def perimeter
    raise NotImplementedError, "Subclass must implement perimeter"
  end
end

class Circle < Shape
  def initialize(radius)
    @radius = radius
  end

  def area
    Math::PI * @radius ** 2
  end

  def perimeter
    2 * Math::PI * @radius
  end
end

circle = Circle.new(5)
puts circle.area.round(2)       # 78.54
puts circle.perimeter.round(2)  # 31.42

Inheritance vs Composition

While Ruby supports inheritance, composition is often preferred:

# Inheritance
class Car < Vehicle
  # Car is a Vehicle
end

# Composition
class Car
  def initialize
    @engine = Engine.new  # Car has an Engine
    @wheels = [Wheel.new, Wheel.new, Wheel.new, Wheel.new]
  end
end

The is_a? and kind_of? Methods

class Animal; end
class Dog < Animal; end
class Cat < Animal; end

dog = Dog.new
cat = Cat.new

puts dog.is_a?(Dog)     # true
puts dog.is_a?(Animal)  # true
puts dog.is_a?(Object)  # true
puts cat.is_a?(Dog)     # false

puts dog.kind_of?(Animal)  # true (alias for is_a?)

Common Mistakes

1. Forgetting super in Overridden Initialize

class Animal
  def initialize(name)
    @name = name
  end
end

class Dog < Animal
  def initialize(name, breed)
    # Forgot super(name) — @name never set!
    @breed = breed
  end
end

2. Confusing super with super()

class Parent
  def greet(name = "World")
    "Hello #{name}"
  end
end

class A < Parent
  def greet(name)
    super  # Forwards name argument
    # "Hello Alice"
  end
end

class B < Parent
  def greet(name)
    super()  # Calls with NO arguments → uses default
    # "Hello World"
  end
end

3. Overusing Inheritance

# Deep hierarchies are hard to maintain
class A < B
end
class B < C
end
class C < D
end
# Prefer composition: class A; def initialize; @b = B.new; end; end

4. Class Variable Surprises

class Base
  @@values = []
end

class A < Base
  @@values << "A"
end

class B < Base
  @@values << "B"
end

puts Base.class_variable_get(:@@values).inspect
# ["A", "B"] — shared across hierarchy!

5. Not Using super in Hook Methods

module Trackable
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def inherited(subclass)
      puts "#{subclass} inherits from #{self}"
      super  # Don't forget super!
    end
  end
end

Practice Questions

1. How do you create a class that inherits from another class?

Use the < operator: class Dog < Animal. Dog is the subclass, Animal is the superclass.

2. What does super do?

super calls the parent class's method with the same name. Without arguments, it forwards the current method's arguments. super() calls with no arguments. super(a, b) calls with specific arguments.

3. What is the method lookup chain in Ruby?

When calling a method, Ruby searches: the object's class, prepended modules, the class, included modules, the superclass, the superclass's modules, and so on up to BasicObject. Class.ancestors shows the chain.

4. What's the difference between class variables (@@) and class instance variables (@)?

Class variables are shared across the entire hierarchy (parent and all subclasses). Class instance variables (@ in the class body) are private to that specific class and not shared with subclasses.

Challenge: Create a hierarchy: Mammal → Animal → Dog/Cat. Add a class-level species tracking using class instance variables (not class variables).

Solution
class Mammal
  @species = "Unknown"

  class << self
    attr_reader :species
  end

  def initialize(name)
    @name = name
  end

  def describe
    "#{@name} is a #{self.class.species}"
  end
end

class Dog < Mammal
  @species = "Canis familiaris"
end

class Cat < Mammal
  @species = "Felis catus"
end

dog = Dog.new("Rex")
cat = Cat.new("Whiskers")
puts dog.describe  # Rex is a Canis familiaris
puts cat.describe  # Whiskers is a Felis catus

FAQ

{{< faq question="Can a Ruby class inherit from multiple classes?" >}} No. Ruby supports single inheritance — a class can have only one direct parent. Multiple inheritance is achieved through modules (mixins) which don't have the diamond problem. {{< /faq >}}

{{< faq question="What happens if a method isn't found in the ancestor chain?" >}} Ruby calls method_missing on the object. If that's not defined, it raises a NoMethodError. The method_missing approach is used by ActiveRecord for dynamic finders. {{< /faq >}}

{{< faq question="Can I change which class a method is defined in at runtime?" >}} Yes. Ruby is dynamic. You can define methods on any class at any time. This is called open classes or monkey patching. Use prepend or refinements to do this safely. {{< /faq >}}

{{< faq question="What is the root class of all Ruby objects?" >}} BasicObject is the root. Object inherits from BasicObject, and all objects inherit from Object. Kernel is a module included by Object that provides methods like puts, print, and require. {{< /faq >}}

{{< faq question="Is inheritance or composition preferred in Ruby?" >}} Composition is generally preferred (has-a over is-a). Ruby makes composition easy with modules. Deep inheritance hierarchies (more than 2-3 levels) become hard to maintain. Use modules for shared behavior and composition for related objects. {{< /faq >}}

Try It Yourself

# inheritance_demo.rb

class Employee
  attr_reader :name, :salary

  def initialize(name, salary)
    @name = name
    @salary = salary
  end

  def work
    "Doing general work"
  end

  def bonus
    salary * 0.1
  end

  def details
    "#{name} earns $#{salary}"
  end
end

class Manager < Employee
  def initialize(name, salary, team_size)
    super(name, salary)
    @team_size = team_size
  end

  def work
    "Managing a team of #{@team_size}"
  end

  def bonus
    super + salary * 0.1  # Manager gets double bonus
  end

  def details
    "#{super}, manages #{@team_size} people"
  end
end

emp = Employee.new("Alice", 50000)
mgr = Manager.new("Bob", 80000, 5)

puts emp.work      # Doing general work
puts mgr.work      # Managing a team of 5
puts emp.details   # Alice earns $50000
puts mgr.details   # Bob earns $80000, manages 5 people
puts emp.bonus     # 5000.0
puts mgr.bonus     # 16000.0

Expected output:

Doing general work
Managing a team of 5
Alice earns $50000
Bob earns $80000, manages 5 people
5000.0
16000.0

What's Next

Now that you understand inheritance, learn about blocks and procs — Ruby's powerful closure mechanism for passing code as arguments.

Topic Description Link
Ruby Blocks & Procs Blocks, yield, Proc.new, call {{< ref "12-blocks-procs" >}}
Ruby Lambdas ->, lambda, arity, closure {{< ref "13-lambdas" >}}
Python Classes Compare Python class inheritance Python

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro