Extend vs Include

This is a follow up to my July 27th post. I’m going to try to make this a short, concise blog post. The closer this is to a Stack Overflow answer, the more likely I am to re-read this post as a reference.

include: mixin a module’s methods into a class’s instances. If a module contains a class method called “included”, it is called after inclusion. Example:

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
module AModule
  def self.included(base)
    puts "included #{self} into #{base}"
  end

  def speak
    puts "I am #{self}"
  end
end
# => nil

class AClass
  include AModule
end
# included AModule into AClass
#  => AClass

AClass.speak
# NoMethodError: undefined method `speak' for AClass:Class

an_instance = AClass.new
# => #<AClass:0x007fcdd487b8e8>

an_instance.speak
# I am #<AClass:0x007fcdd487b8e8>
#  => nil

extend: mixin a module’s methods into a class’s, making the module methods class-methods. A similar callback capability is built in by defining a class method on the module extended. If defined, it’ll be called after the

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module BModule
  def self.extended(base)
    puts "extended #{self} into #{base}"
  end

  def speak
    puts "I am #{self}"
  end
end

class BClass
  extend BModule
end
# extended BModule into BClass
#  => BClass

BClass.speak
# I am BClass
#  => nil

I was careful to write “mixin” for my two definitions above, as monkeypatches to AModule and BModule will be seen in classes that included or extended their behavior.

Example: (continuing from the code block above)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# yarr we're monkey patching
module AModule
  def speak
    puts "there be monkeys"
  end
end

an_instance.speak
# there be monkies
# => nil

# Let's see how monkey patching affects extend
module BModule
  def speak
    puts "there B monkeys"
  end
end
#  => nil

BClass.speak
# there B monkeys
#  => nil

I spent the extra time to explore whether code from modules is copied into their respective classes because I said this during the interview I described in my July 27th blog post. One of my interviewers corrected me, and he seems to indeed be right. At least I’m learning from looking like an ass.

Additional references:

StackOverflow post

Yehuda Katz post on include and exclude idioms, calling for a style change

Last note about extend: extend can be used on singleton objects, but I don’t see a use case for this. I suppose it’s something to keep an eye out for.