def things_n_stuff

Adventures in Code

Ruby Inheritance Quiz Answer

Answer Below

The Miniquiz question was, given:

1
2
3
4
5
6
7
8
9
10
11
class Thing < AbstractThing
  include SomeActions
  include OtherActions
  include MoreActions
  extend Existence

  def be
    puts "thing LIVES"
  end

end

If AbstractThing, SomeActions, OtherAction, MoreActions, and Existence all implement a method definition for explode, and we execute the following:

1
2
3
my_brain = Thing.new
my_brain.explode
#=> ...

Where does the method explode get called?

Answer: MoreActions

Demo below

Why? Basic version

When an object has a method called on it, ruby looks to see if that object’s class defines an implementation of that method, and if not looks in that class’s super class, and then that class’s super class, until it reaches Basic Object. (…at which point the process begins again, this time searching for method_missing.)

include-ing a module into a class makes the module’s methods available to all instances of that class, and extend-ing a module into a class makes the module’s available to just that class, as class methods.

In this case, my_brain is an instance of the Thing class, which makes explode an instance method. Therefore, the winning implementation doesn’t come from Existence

It’s possible to both include and extend objects using modules, both instances and classes. It’s really interesting and a bit weird, and seems quite useful to understand. Stay tuned.

Why? Slightly less basic version

The method will be executed as soon as it’s method definition is found. In terms of the class’s methods, included modules, and parent classes, the order of places to look, (the lookup path,) is:

  • first in the methods of the class to which the instance belongs
  • then in each the modules included in that class, in reverse order of inclusion
  • then in the class’ superclasses, their modules, that classes superclass, its modules etc…
  • (There’s more to it, but that’s as far as I need at the moment.)

In the question prompt, my_brain is an instance of the Thing class. And MoreActions is the last module included into the Thing class. Therefore, provided that Thing class doesn’t define an explode method, MoreActions is the next place ruby would search, and the if it finds it there it will execute MoreActions’s explode method irrespective of any implementation in the earlier included methods or the parent classes.

Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class AbstractThing
  def explode
    'BOOM from AbstractThing'
  end
end

module SomeActions
  def explode
    'BOOM from SomeActions'
  end
end

module OtherActions
  def explode
    'BOOM from OtherActions'
  end
end

module MoreActions
  def explode
    'BOOM from MoreActions'
  end
end

module Existence
  def explode
    'BOOM from Existence'
  end
end

class Thing < AbstractThing
  include SomeActions
  include OtherActions
  include MoreActions
  extend Existence

  def be
    puts "thing LIVES"
  end
end



> my_brain = Thing.new
#=> #<Thing:0x007faa029145c0>
> my_brain.explode
#=> "BOOM from MoreActions"

I’m going to turn on commenting soon so you wonderful people can tell me what I’ve made mistakes on. For everything that’s correct, massive credit due to Dave Thomas’ and Sandi Metz’s books.