I want to merge two Ruby modules without breaking the lookup chain. Basically I want the behavior of BothAnB
to be exactly as if I concatenated the textual source code from A and B and the new foo replaces the old. The problem occurs when MRO linearizes an inheritance diamond.
module O
def foo; puts "O" end
end
module A
include O
def foo; puts "A"; super end
def aaa; puts "aaa" end
end
module B
include O
def foo; puts "B"; super end
def bbb; puts "bbb" end
end
module BothAnB
#insert magic here such that a class C that includes BothAnB:
# C.new.foo => B O
# C.new.aaa => aaa
# C.new.bbb => bbb
end
module JustA
#insert magic here such that a class C that includes JustA:
# C.new.foo => A O
# C.new.aaa => aaa
# C.new.bbb => FAIL
end
#and similarly JustB
A and B are fairly complex modules that can have deep inheritance chains (this is for a meta-programming framework that allows programmers to do just that).
Include B, A
doesn't work because instead of the lookup BothAnB->B->A->O, I need it to be BothAnB->B->O(and optionally ->A).
I've gotten close by:
- deep cloning entire inheritance tree of A (to remove diamond)
undef_method
on the A's clone to remove methods found in B- making a new method for
Module
to reflect this behavior
Is there a better solution than this? I would ideally want to keep at least some of the modules recognizable when calling BothAnB.ancestors
.
[Note: I completely changed the question after getting two answers based on Phrogz's feedback, so if they seem irrelevant they are]
Here's another possible trick:
Here we make
super
inB#foo
to point atO#foo
instead ofA#foo
.If
O
is complex and includes other stuff, it may require more of such magic: