Reputation: 23770
I want to override a method from a module A from another module B that will monkey-patch A.
http://codepad.org/LPMCuszt
module A
def foo; puts 'A' end
end
module B
def foo; puts 'B'; super; end
end
A.module_eval { include B } # why no override ???
class C
include A
end
# must print 'A B', but only prints 'A' :(
C.new.foo
Upvotes: 15
Views: 21081
Reputation: 24637
module A
def foo
puts 'A'
end
end
module B
def foo
puts 'B'
super
end
end
include A # you need to include module A before you can override method
A.module_eval { include B }
class C
include A
end
C.new.foo # => B A
Upvotes: 8
Reputation: 1647
This is also one solution to your question. I am trying to achieve with module hooks included
. When you include the module A into class C. included callbacks defined in module A is called and executed. We included the module B on-fly. So our module A method foo is overridden by Module B foo to print the superclass module method just called super.
module A
def self.included klass
klass.send(:include, B)
end
def foo
puts 'A'
end
end
module B
def foo
super
puts 'B'
end
end
class C
include A
end
C.new.foo #out put A,B
Upvotes: 1
Reputation: 1162
Another way to accomplish this is to include module B when module A is included.
module A
def foo
puts "this should never be called!"
"a"
end
end
module B
def foo
"b"
end
end
module A
def self.included(base)
base.class_eval do
include B
end
end
end
class C
include A
end
C.new.foo # "b"
Upvotes: 0
Reputation: 3263
Including a module places it above the module/class that is including it in the class hierarchy. In other words, A#foo is not super of B#foo but rather the other way round.
If you think of including a module as a way of doing multiple inheritance this makes sense, include SomeModule
is a way of saying, "Treat SomeModule like it is a parent class for me".
To get the output you wanted you need to reverse the inclusion so that B includes A:
module A
def foo; puts 'A' end
end
module B
def foo; puts 'B'; super; end
end
B.module_eval { include A } # Reversing the inclusion
class C
include B # not include A
end
puts C.new.foo
Edit in response to comment:
Then either include both A and B in C with B included after A:
# A and B as before without including B in A.
class C
include A
include B
end
or patch A in C itself and don't bother with B.
# A as before, no B.
class C
include A
def foo; puts 'B'; super; end
end
The only way for this to work is if the method lookup on C is C -> B -> A and there is no way to do this without including B into C.
Upvotes: 3