如何"替换"通过ruby include函数包含的模块

Gle*_*min 5 ruby metaprogramming class

作为一个跟进我如何反转ruby的包含函数,这个问题得到了很好的解答,但结果表明我对实际问题的简化并不意味着该解决方案不适用.

我现在面对这个(名称改为保护身份!):

module OldFormHelpers
  def foo
    puts "foo"
  end
  def bar
    puts "bar"
  end
end

module Helpers
  include OldFormHelpers
end
Run Code Online (Sandbox Code Playgroud)

这给了我:

Helpers.instance_methods
=> ["bar", "foo"]
Helpers.ancestors
=> [Helpers, OldFormHelpers]
Run Code Online (Sandbox Code Playgroud)

这是我无法真正有权修改的代码,而不需要分叉.

我想要做的是创建一个新模块;

module BetterFormHelpers
  def foo
    puts "better foo"
  end
end
Run Code Online (Sandbox Code Playgroud)

这需要从中删除行为OldFormHelpers,然后从中添加新内容BetterFormHelpers

以前的解决方案是这样使用undef_method:

Helpers.module_eval do
  OldFormHelpers.instance_methods do |m|
    undef_method(m)
  end
end
Run Code Online (Sandbox Code Playgroud)

但是,在包含之后BetterFormHelpers,Helpers.instance_methods不包含"foo".其原因在http://ruby-doc.org/core/classes/Module.src/M001652.html上解释.

使用remove_method告诉我Helpers没有"foo"方法,所以我想我需要一些方法从祖先链中删除第一个包含...

这有点长,所以我最后停止了这么多片段,但是我添加了一个irb会话,显示了undef/remove的行为,然后是一个include.

cri*_*spy 2

不能只取消定义不会被覆盖的方法吗?

Helpers.module_eval do
  (OldFormHelpers.instance_methods - BetterFormHelpers.instance_methods).each do |m|
    undef_method(m)
  end
end
Run Code Online (Sandbox Code Playgroud)

(将首先搜索后者包含的模块,以便如果 BetterFormHelpers 也定义了 OldFormHelpers 方法,则不会执行该方法。)

但是,如果您想动态覆盖 OldFormHelpers 模块的其他方法,问题仍然相同。