Androidgrl's Blog About 3D Printing & Tech, by Jamie Kawahara

The difference between using include and extend when including modules in a Class

We’ve seen the use of include for including methods from a module into a Class. The following shows three modules included into Class A:

module B
  def b
    :b
  end
end

module C
  def c
    :c
  end
end

module D
  def d
    :d
  end
end

class A
  include B
  include C
  include D
end

Include turns the methods inside the module into instance methods for that class:

puts A.new.b >> b
puts A.new.c >> c
puts A.new.d >> d

However you can also change the methods into class methods by using extend:

module B
  def b
    :b
  end
end

module C
  def c
    :c
  end
end

module D
  def d
    :d
  end
end

class A
  extend B
  extend C
  extend D
end

puts A.b >> b
puts A.c >> c
puts A.d >> d

Notice how we didn’t have to change the method definitions inside the module to self.method. This is because modules have no sense of Class, they’re just buckets for methods. They are called ‘mixins’ because they mix methods into wherever they are included or extended into.

Our team architect Adam Zaninovich gives the analogy of modules being ‘viruses’ and classes being ‘hosts’. Modules ‘infect’ classes with their ‘methods’ (in a good way in this case), in the sense that viruses inject their dna into host cells.

I wrote include in class A three times which is repetitive. If you find that you are including modules in a class many times you can avoid this by using the self.included module method.

module B
  def self.included(klass)
    klass.include C
    klass.include D
  end

  def b
    :b
  end
end

module C
  def c
    :c
  end
end

module D
  def d
    :d
  end
end

class A
  include B
end

puts A.new.b >> b
puts A.new.c >> c
puts A.new.d >> d

Now there is only ‘include B’ inside class A. Whenever B is included in a Class, it calls self.included which passes in the class A as an argument. “self.included” then included modules C and D into the klass passed as the argument which in our case is class A.

Also, the self.included method comes in handy when you want your module to have BOTH instance and class methods. The following module C demonstrates the conventional way to specify both instance and class methods inside a module.

module C
  def self.included(base)
    base.extend(ClassMethods)
  end

  def c
    :c
  end

  module ClassMethods
    def b
      :b
    end
  end
end

class A
  include C
end

puts A.new.c >> c
puts A.b >> b