#---Inheritance---

Inheritance allows a class to inherit properties and methods from another class. This allows the objects of the inheriting class to gain the functionalities of the parent class, while also having its own unique props and methods.

A dog, a cat and a mouse are all just some animals with 4 legs and a heart, and can eat, sleep and make babies just fine.
A bigger dog is still a dog that you can pet. But a bigger cat is a tiger that will definitely kill you, and a bigger mouse is a rodent that you will likely kill.
See the unique features as well as the commonalities? Inheritance allows you to model this real-world concept.

Syntax

Use < to define inheritance between a child class and a parent class.

class Animal
  def initialize(name)
    @name = name
  end
  
  def speak
    "#{@name}: <generic animal sound>"
  end
  def greet(other_name)
    "Welcome #{other_name}. I'm #{@name}."
  end
end

The Dog and Cat classes inherit from Animal. Their objects will get the speak method from the parent class for free:

class Dog < Animal
end
class Cat < Animal
end
dog = Dog.new("Pongo")
cat = Cat.new("Jerry")
p dog.speak
p cat.speak

But a dog also wags its tail and a cat purrs. We can add to them their own specific methods:

class Dog < Animal
  def wag
    "<<wags tail>>"
  end
end
class Cat < Animal
  def purr
    "<<purrs>>"
  end
end

(Note tha we’re opening the already-defined child classes and adding methods to them.)

p dog.wag # "<<wags tail>>"
p cat.purr # "<<purrs>>"
p [:speak,:wag].all? { Dog.instance_methods.include?(it) }
## => true

Overriding methods

But a generic sounding dog and cat isn’t interesting. We can make them speak their own tongue by re-defining the speak method in the child classes. This overrides the speak method in the parent class:

class Dog < Animal
  def speak
    "#{@name}: Woof!"
  end
end
class Cat < Animal
  def speak
    "#{@name}: Meow!"
  end
end
p dog.speak # "Pongo: Woof!"
p cat.speak # "Jerry: Meow!"

The Super Keyword

super is how from a child class you’ll call a method of the same name from the parent class.
We’ll use it in 2 places in our example.

A cat is low-maintenance, but a dog is not. So we might have to take that into account when creating a dog:

class Dog < Animal
  def initialize(name, cost)
    super(name) # calls Animal's initialize with name
    @cost = cost
  end
end
## Creating a dog without a cost will now fail.
## Dog.new("Pongo") will fail
p Dog.new("Pongo", 100).speak

The cat has a bigger ego though. So it wants its own greet method:

class Cat < Animal
  def greet(other_name)
    ## calls Animal's greet with other_name
    ## and interpolates the result
    "#{super} Jk, I don't care about you."
  end
end
p cat.greet("Pongo")
## "Welcome Pongo. I'm Jerry. Jk, I don't care about you."

In both these examples, we used the super keyword to call the parent class’s respective methods (initialize and greet).

Note that super, when called without any arguments, calls the parent method with the same arguments that was passed to the child method. You don’t have to explicitly list them all.
But if you do want to call the parent method without passing any arguments, you must call it like this: super(). (Only time using () while calling a method isn’t optional.)

Instrospecting Inheritance

A cat is a cat. But it’s also an animal. (deep)
How do you check that it still has the animalistic quality in it? You ask it nicely via is_a? or kind_of?:

p cat.class # => Cat
p cat.is_a?(Cat) # => true
p cat.is_a?(Dog) # => false
p cat.is_a?(Animal) # => true
p cat.kind_of?(Animal) # => true

Class Inheritance Hierarchy

You can just ask a class for its ancestors and it will list out its whole ancestral tree in order:

p Cat.ancestors
## => [Cat, Animal, Object, Kernel, BasicObject]

See Object and BasicObject in the list?
That’s why they say everything is an object in Ruby.