rubydecoratorsingleton-methods

On multiple Class#extend calls


Consider a code from this article:

class Coffee
  def cost
    2
  end
end

module Milk
  def cost
    super + 0.4
  end
end

module Sugar
  def cost
    super + 0.2
  end
end

coffee = Coffee.new
coffee.extend(Milk)
coffee.extend(Sugar)
coffee.cost   # 2.6, while I expected 2.2

The reason why it's 2.6 and not 2.2 is because every call of extend adds a module into the instance singleton class ancestors chain, as pointed out below.


Solution

  • extend adds to the list of ancestors, it doesn't replace any existing ancestors - if you examine coffee.singleton_class.ancestors after the second extend you can see that it includes both Milk and Sugar.

    If you are running 2.2, you can use the new super_method method to examine this:

    coffee.method(:cost) => #<Method: Coffee(Sugar)#cost>
    

    This shows that if you call 'cost' you first go to the 'Sugar' module's implementation

    coffee.method(:cost).super_method => #<Method: Coffee(Milk)#cost>
    

    this shows that in Sugar#cost you call super then you will jump in to the method provided by Milk

    and finally

    coffee.method(:cost).super_method.super_method => #<Method: Coffee#cost>
    

    when you call super from there you end up in the base class implementation