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.

Tags:

Ruby