Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't redefine a class's instance method by including a module?

Tags:

ruby

I'm having a herp-derpy moment. I could've sworn this worked:

module StubbedGreeting
  def sayit
    puts "StubbedGreeting"
  end
end

module RegularGreeting
  def sayit
    puts "RegularGreeting"
  end
end

class Greeting
  def sayit
    raise "Gotta catch me!"
  end
end

class GreetingIncludes
  include RegularGreeting
end

begin
  Greeting.send(:include, StubbedGreeting)
  Greeting.new.sayit
rescue Exception
  puts "Exception raised"
end

GreetingIncludes.send(:include, StubbedGreeting)
GreetingIncludes.new.sayit

What happens here is Greeting.new.sayit results in in the rescue block being called, ignoring the attempted overwrite by StubbedGreeting.

However, GreetingIncludes.new.sayit results in "StubbedGreeting", not an exception.

So a module can overwrite another module's methods, but not methods already defined directly in the class?

I know how to finagle my way around this, I just found it weird.

like image 526
Robert Speicher Avatar asked May 21 '26 11:05

Robert Speicher


1 Answers

When including a module, it's methods are put between the methods of the class and it's parent class in the method resolution order. So when resolving which actual method is to be called, ruby first searches for the method in the following order. If a matching method is found, the search is aborted and this method is used.

  • the singleton class of the object
  • modules included into the singleton class from last included to first included
  • the class of the object
  • modules included into the class

This then continues for each parent class until the Class class is reached.

As you can see, the modules can indeed not override methods defined on a class itself as modules come behind the actual class in the method resolution order. If you really need to override such a method, you can use alias_method or alias_method_chain to "rename" methods.

In the upcoming Ruby 2.0 there will be a prepend mechanism which will include modules before the class which will achieve what you want. But it's not released yet.

like image 176
Holger Just Avatar answered May 24 '26 02:05

Holger Just



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!