In Progress
Unit 1, Lesson 1
In Progress

# Ancestral Behavior

Ruby’s inheritance chain is simple: the last module included wins. Sometimes this can cause problems: what if a module you include happens to have a method that collides with a method from the superclass? It’s not a common problem, but when it does happen it’s a real head-scratcher. In this episode, you’ll learn how to solve it.

Video transcript & code

Let's say we've got a `WashingMachine` class.

Given a `WashingMachine` object, we have access to some typical washing machine settings, like the current water level, the current wash cycle , and the temperature in the tub.

``````wm = WashingMachine.new
wm.water_level                  # => 0
wm.cycle                        # => :fill
wm.temperature                  # => 72
``````

Now let's say we develop a new washing machine, that builds on the old one.

``````class StainMaster5000 < WashingMachine
attr_accessor :clothes

def initialize
super
@clothes = ["red shirt", "blue sock", "black sock"]
end
end
``````

Let's make an instance. This amazing new appliance can actually tell us what clothes are currently inside it!

``````sm = StainMaster5000.new
sm.clothes                      # => ["red shirt", "blue sock", "black sock"]
``````

But why stop there? The StainMaster 5000 should have all the bells and whistles!

Let's include the Enumerable module, and then define the `each` method to iterate through the clothes.

``````class StainMaster5000
# ...
include Enumerable

def each(&block)
clothes.each(&block)
end
end
``````

Now we can do cool stuff like convert the machine directly to an array of clothes, sort the clothes , or even get a list of just the clothes' colors,, all directly from the washing machine object!

``````sm.to_a
# => ["red shirt", "blue sock", "black sock"]
sm.sort
# => ["black sock", "blue sock", "red shirt"]
sm.map{|g| g.split.first}
# => ["red", "blue", "black"]
``````

OK, I admit it. This is one of the more contrived examples I've come up with on this show.

But I did it to highlight a problem that you may well run into some day, under less contrived circumstances.

Here's the problem. Let's take a second look back at the original `WashingMachine` attributes.

``````sm.water_level                  # => 0
sm.cycle                        # => #<Enumerator: #<StainMaster5000:0x005610...
sm.temperature                  # => 72
``````

Hmmm… one of these things is not like the others. The `water_level` and `temperature` readers still look right, but the `cycle` indicator is now returning something very different than it used to.

The problem is, cycle is one of the methods that Enumerable defines. We can talk about what it does in another episode. The right now, it's just in the way.

What we'd like to do is to include all of the Enumerable methods, except any with names that conflict with base class methods. But that's not how Ruby module inheritance works. We can't be selective about which methods are brought in. It's all or nothing.

How do we say that this class should use the superclass definition of `cycle`, instead of the `Enumerable` definition? It's actually quite easy, but the solution is not at all obvious.

First off, we need to be able to acquire a handle on the original `WashingMachine` definition of `cycle`.

As we've seen in a few other episodes, we can do this by asking the `WashingMachine` class for the `instance_method` named `:cycle`.

``````WashingMachine.instance_method(:cycle)
# => #<UnboundMethod: WashingMachine#cycle>
``````

In return, we get an `UnboundMethod` object.

Knowing this, let's return to the `StainMaster5000` class definition.

After we include Enumerable, we send the `define_method` message to the class.

We tell it we're defining the method named `:cycle`.

Now typically, when we use `define_method`, we pass in a block which will make up the body of the method. But not this time.

In this case, we pass a second argument, which is the `UnboundMethod` we learned how to retrieve a moment ago.

``````class StainMaster5000
# ...
define_method(:cycle, WashingMachine.instance_method(:cycle))
end
``````

When we re-test the reader methods, all is well again. `cycle` once again returns `:fill`.

``````sm.water_level                  # => 0
sm.cycle                        # => :fill
sm.temperature                  # => 72
``````

This exploits an obscure capability of the `define_method` call:

Instead of a block, it can take an `UnboundMethod` object as a second argument. In effect, it acts almost like an aliasing mechanism: it gives us the ability to take method objects and plug them in under any name we want.

For instance, if we had wanted to leave the original `Enumerable` definition intact, but we could have renamed the method `:wash_cycle` instead.

``````class StainMaster5000
# ...
define_method(:wash_cycle, WashingMachine.instance_method(:cycle))
end
``````

Admittedly, this is some deep metaprogramming, and hopefully you'll never need to use it. But it gives some insight into how methods in Ruby can be detached and then re-bound in other contexts.

Happy hacking!