4

Possible Duplicate:
Overriding method by another defined in module

Here's some code:

class Foo
  def bar
    puts "Original bar"
  end
end

module M
  def bar
    puts "Called M::bar"    
  end
end

Foo.send(:include,M)
Foo.new.bar
# => Original bar

Does ruby prevent overriding a previously defined method when a method of the same name is "included"?

Community
  • 1
  • 1
farhadf
  • 1,918
  • 3
  • 19
  • 27

3 Answers3

8

I don't quite understand your question. What, exactly, do you think is "prevented" here, and by whom?

This is precisely how it is supposed to work. Module#include mixes in the module as the direct superclass of whatever class it is being mixed into. M is a superclass of Foo, so Foo#bar overrides M#bar, because that's how inheritance works: subclasses override superclasses, not the other way around. Nothing is being "prevented" here, of course you can still override Foo#bar in a subclass of Foo.

You can clearly see the ancestry:

class FooS; end
module M; end
class Foo < FooS; include M end

Foo.ancestors # => [Foo, M, FooS, Object, Kernel, BasicObject]
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • Ahh, I didn't know that Module#include mixes in the module as a direct superclass of what it's being mixed into, rather than the class itself. I actually still don't get it. So if I have class FooS and have Foo be a subclass of FooS, are you saying bar will now be defined in FooS? Doesn't seem to be and doesn't make sense to me either. I must be misunderstanding what you're saying. – farhadf Jun 03 '11 at 00:19
  • @LeftHem: Huh? Why would `bar` magically be defined somewhere it isn't? If you add `FooS` as a superclass of `Foo` into the mix, then there are *still* only two definitions of `bar`, one in `Foo` and one in `M`. Why would one of those suddenly jump around? `M` *still* gets inserted as a direct superclass of `Foo` (and thus, obviously, as a subclass of `FooS`) and the definition of `Foo#bar` *still* overrides the definition of `M#bar`, because `Foo` is *still* a subclass of `M`. Inserting a superclass above `Foo` changes nothing, after all, there already was a superclass before: `Object`. – Jörg W Mittag Jun 03 '11 at 00:27
  • My bad - "as a direct superclass", not "in a direct superclass". Now I get it. – farhadf Jun 03 '11 at 00:45
  • "that's how inheritance works" is not really fair given that Ruby completely throws away traditional inheritance... – Dan Jul 29 '15 at 02:39
  • And what if you include more than one module? Which one is now a direct superclass? Given that Ruby class can only inherit from one base class? – zwiebl Nov 29 '16 at 07:59
  • @zwiebl: The module you include (or more precisely, the include class of the module) becomes the direct superclass of the class you include it into, and whatever was the superclass before becomes the direct superclass of the module you are including (or more precisely, the corresponding include class of the module). – Jörg W Mittag Nov 29 '16 at 10:14
  • And what if a module doesnt have a specified class, but is instead just a module with defined methods? – zwiebl Nov 29 '16 at 11:26
5

Rephrasing @Jorg's answer:

If you do

Foo.send(:include,M)
Foo.ancestors

You get back

[Foo, M, Object, Kernel, BasicObject]

That means that when bar gets called, it looks to see if there's Foo#bar first, and only if that didn't exist would it try looking at M#bar (and then Object, Kernel, BasicObject and then calls method_missing).

Personally speaking, I wouldn't mind the ability for M to be looked at first, before looking at Foo. I've heard there's talk of adding to Ruby 2.0 the ability to do this, but that's not going to help you right now.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
  • 2
    I think `Module#prepend` (which is what you're talking about) is actually already implemented in YARV trunk. And unlike some other stuff (Refinements, in particular), it is completely uncontroversial and very easy to implement, so it's pretty much guaranteed to actually end up in the Ruby 2.0 specification. In fact, being backwards-compatible, I wouldn't be too surprised to see it in 1.9.3 (if there is even going to be one). – Jörg W Mittag Jun 03 '11 at 01:53
  • @Jorg: Why wouldn't there be a 1.9.3? Are they thinking of going straight to 2.0? – Andrew Grimm Jun 03 '11 at 02:28
  • `x = Foo.new; x.extend M; x.bar` would override the method, and `super` in the module's method would call the instance method. Details: http://subinterest.com/rubies-in-the-rough/15-learn-to-love-mix-ins – Henrik N May 22 '12 at 17:33
-1

Your include call doesn't do what you think... try Foo.send(:extend,M)

DGM
  • 26,629
  • 7
  • 58
  • 79
  • My understanding is that extend adds the methods as class methods. No? Replace the last two lines with puts Foo.methods.grep( /bar/).inspect Foo.send(:extend,M) Foo.new.bar puts Foo.methods.grep( /bar/).inspect and you see that it was added to the class. Don't know why this comment system doesn't allow carriage returns – farhadf Jun 03 '11 at 00:05
  • Looking at that, there's still something I don't understand either, that I think is at the heart of the problem; this should work if you put include inside the class... – DGM Jun 03 '11 at 00:05