Not A Programming Joke

Ruby Inheritance Revisted

After graduating from Flatiron School I’ve been going on job interviews and it seems everyone really wants to know how much I know about inheritance. So I told them “I used to work for a living and my grandmother left me nothing.”

After the uncomfortable crickets faded, I told them something like what’s below. I selfishly wrote down some thoughts to solidify my own review of the topic.

Ruby does not have multiple inheritance like some other languages, but it does support this feature through the use of Mixins and you do get to inherit from one super class for free. Since Mixin sounds like we’re cooking something up, let’s define a two classes and a module like this:

1
2
3
4
5
6
7
8
9
10
class Vegetable
  def self.cook
    puts "urghggh!"
  end

  def cook_yo_self
    puts "Parent Class: I might give in"
  end

end

Class Vegetable includes a class method cook, and an instance method cook_yo_self.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Potato < Vegetable
  include Veg
  extend Veg

  def self.eat
    puts "eating mySELF!"
  end

  def grow
    puts "growing to maxium vegetablosity of #{Veg::VEGNES}"
  end

  def cook_yo_self
    super
    puts "Instance method: would you like fries with that?"
  end

end

Class Potato inherits from Vegetable, gaining its methods. So now

1
Potato.cook and Potato.new.cook_yo_self

are possible. Note that the instance method cook_yo_self from the Vegetable class is inherited, but also overridden by the local definition of the same. So the Potato class definition is the one that outputs, not the Vegetable one. I promise I did that for a reason as we’ll see in a second.

Let’s also look at the module Veg:

1
2
3
4
5
6
7
8
9
10
11
12
13
module Veg
  VEGNES=9000

  def cry
    puts "much sadness"
  end

  def cook_yo_self
    super
    puts "Included Module: I will never give in"
  end

end

Class Potato includes and is extended by module Veg. Including it means that the method cry is available to all instances of Potato.

1
Potato.new.cry ## much sadness

Extending with module Veg means that the class Potato also gains that method

1
Potato.cry ## much sadness

An easy way to remember it that INcluding modules in classes allows INstances access to methods, and Extending doesn’t have an ‘I’ at the beginning. Just remember the first one.

So That’s Super

Defining a new instance of Potato called Russet, and asking it to cook itself results in interesting output:

1
2
3
4
5
6
russet = Potato.new
russet.cook_yo_self

##Parent Class: I might give in
##Included Module: I will never give in
##Instance method: would you like fries with that?

The Potato class’s cook_yo_self method should only output “Instance method: would you like fries with that?” But because of the ‘super’ statements, it instead includes the output of each cook_yo_self method from the class, the module and the parent class.

Most interesting is the order in which methods were invoked: first the object’s class, then the included module, and then the parent class. Pretty sweet right? No of course not, they’re potatos. But read here to learn more about the ruby method lookup chain. The order of modules included is in reverse inclusion order, and it also includes methods defined on the singleton.

quick side bar

What is a singleton? The singleton is an object itself. Maybe you want to add functionality to a particular object for testing or some other reason - the look up chain starts at the singleton level, because it is actually possible to do something like this:

1
2
3
4
yukon_gold = Potato.new
def yukon_gold.singing_singleton
  puts 'LA LA LA'
end

I just defined a method on the INSTANCE of Potato called yukon_gold, not the class. And you can do this as well:

1
2
3
4
5
6
module Yukon_Only
  def canada_translator
    puts "eh, ay, AY?"
  end
end
yukon_gold.extend(Yukon_Only)
1
2
yukon_gold.singing_singleton ## LA LA LA
yukon_gold.canada_translator ## eh, ay, AY?

These methods are only available on yukon_gold.

side bar over

Notice how Potato’s grow method accesses the constant defined in the Veg module, the double colon allows us to call the constant in the namespace of Veg. That’s the double colon’s job - name spacing.

So how do you get a module into a class?

The whole point of putting things in modules is so that you can modularize and reuse code, so the module is probably defined in a different file than the one you’re working on. The answer is with a require statement. If you’ve saved your modules in ‘modules.rb’, just add in

1
require "path/to/modules.rb"

to your file.