Elegant way to prepend to a module which is already included?
TL;DR – you can't in general, but Base1.include Patch
may be good enough.
For your example code, the ancestors
of Base1
and Base2
are: (aligned for clarity)
Base1.ancestors #=> [Base1, Feature, Object, Kernel, BasicObject]
Base2.ancestors #=> [Base2, Patch, Feature, Object, Kernel, BasicObject]
Base2
has an additional ancestor Patch
before Feature
– the result of Feature.prepend Patch
.
Ruby doesn't allow us to freely modify a module's ancestors chain, so we can't just prepend Patch
to Feature
retroactively.
But fortunately, Patch
is the first module after the Base
class, so we can resort to include
to append Patch
to Base1
instead:
Base1.include Patch
Base1.ancestors #=> [Base1, Patch, Feature, Object, Kernel, BasicObject]
Obviously, this only works for very specific cases and not in general.
Here's a counter example:
module Feature
def action ; 'Feature' ; end
end
module Foo
def action ; "#{super} overridden" ; end
end
module Patch
def action ; 'Patch' ; end
end
class Base1
include Feature
include Foo
end
Feature.prepend(Patch)
class Base2
include Feature
include Foo
end
Base1.new.action #=> "Feature overridden"
Base2.new.action #=> "Patch overridden"
Base1.include Patch
Base1.new.action #=> "Patch"
Looking at the ancestors reveals the problem:
Base1.ancestors #=> [Base1, Foo, Feature, Object, Kernel, BasicObject]
Base2.ancestors #=> [Base2, Foo, Patch, Feature, Object, Kernel, BasicObject]
Base1.include Patch
Base1.ancestors #=> [Base1, Patch, Foo, Feature, Object, Kernel, BasicObject]
Patch
and Foo
are out of order.