1

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.

Andrey Esperanza
  • 625
  • 1
  • 6
  • 11

2 Answers2

2

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

Stefan
  • 109,145
  • 14
  • 143
  • 218
Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174
  • Well, it turns out I've overlooked the fact that Milk and Sugar get into the ancestors chain. Why would I think they don't? Thanks a lot for pointing this out. – Andrey Esperanza Jan 23 '15 at 16:27
0

You always just have #cost which is modified by future arguments - it's not a matter of being overwritten:

coffee = Coffee.new
#cost is 2
coffee.extend(Milk)
#cost is 2.4
coffee.extend(Sugar)
#cost is 2.6

The article is saying that this implementation is the same as the other inheritance based examples that they give.

dax
  • 10,779
  • 8
  • 51
  • 86
  • I don't understand. My assumption is that when the `cost` method **is** replaced with the same-named method from the module. You're saying two `cost` methods from the mixed in modules co-exist together? But that's not true, because `coffee.singleton_methods` returns `[:cost]` even after the second `#extend`. – Andrey Esperanza Jan 23 '15 at 11:08
  • no, i'm saying that they do not co-exist - when you `extend(milk)`, the original `#cost` is now `cost + 0.4` - so further extensions use that as the base – dax Jan 23 '15 at 11:11
  • You mean the source code of the methods is concatenated? It seems unrealistic. – Andrey Esperanza Jan 23 '15 at 12:26