【问题标题】:How can I reverse ruby's include function如何反转 ruby​​ 的包含函数
【发布时间】:2011-01-25 01:46:39
【问题描述】:

我将在代码中解释我在寻找什么,因为那可能是最简洁的:

module Mixin
  def method
    puts "Foo"
  end
end

class Whatever
  include Mixin
end

w = Whatever.new
w.method
=> "Foo"

# some magic here
w2 = Whatever.new
w.method
=> NoMethodError

我曾尝试使用 remove_const 取消定义 Mixin 模块,但这似乎对无论什么都没有任何影响。我曾假设 #include 只是将对该模块的引用添加到类的方法解析链中 - 但这种行为与此不符。

谁能告诉我 include 在幕后实际上做了什么,以及如何扭转这种情况?

【问题讨论】:

  • 想要这样做的原因是因为我们为 merb 编写的 gem 将替换现有的表单助手,我们需要以某种方式删除它们。因此我不能真正改变第一个模块的包含方式,只能改变之后可以做的事情。

标签: ruby class metaprogramming


【解决方案1】:

看起来您可能希望在实例而不是整个类上完成这些,所以我会稍微更改 klochner 的代码以只处理一个实例而不是类的所有实例。

module ModuleRemover

    def remove_module(mod, options = {})
      metaclass = class << self; self end
      mod.instance_methods.each {|method_name| metaclass.class_eval { undef_method(method_name.to_sym) }}
    end

end

正如 Mladen 指出的那样,避免删除在宿主类上被覆盖的方法会很酷,因此此方法的 [only, exclude] 选项将是理想的。

>> c1 = C.new
>> c1.foo
=> fooing
>> c1.extend(ModuleRemover)
>> c1.remove_module(Mod)
>> c1.foo
=> NoMethodError: undefined method `foo' for #< C:0x11b0d90>
>> c2 = C.new
>> c2.foo
=> fooing

【讨论】:

  • 有没有一种方法可以做到这一点,而无需从继承链中删除同名调用的方法?将 undef_method 更改为 remove_method 对我不起作用
【解决方案2】:
module Mod
  def foo
    puts "fooing"
  end
end

class C
  include Mod
  def self.remove_module(m)
    m.instance_methods.each{|m| undef_method(m)}
  end
end

>> c = C.new
>> c.foo
fooing
>> C.remove_module(Mod)
=> ["foo"]
>> c.foo
NoMethodError: undefined method `foo' for #&lt C:0x11b0d90>

【讨论】:

  • 如果在包含模块Mod之前类C中已经存在同名的方法会不会有问题?
  • 当然。假设 Mod 使用 alias_method 覆盖以前的方法,您可以恢复覆盖的方法,但我认为这个问题的详细程度是多余的。
  • 经过进一步测试,undef_method 或 remove_method 都没有真正满足我的需求。
  • 我投反对票。这是一种糟糕的技术,不应该使用。您甚至不会从 #included_modules 数组中删除模块,并且存在丢失方法的风险。这个问题可能没有好的答案。
  • @farnoy 你认为这个问题的更好解决方案是什么?
【解决方案3】:

我不确定您要完成什么,但也许不是使用include 添加实例方法,而是使用extend 将方法添加到类的特定实例,那么你就不需要删除它们了。

More information on the difference between include and extend

【讨论】:

    【解决方案4】:

    几年前,我将 gem evil 用于不包含模块等,但显然它已不再维护。所以我只是尝试了un(仅在我的旧 ruby​​ 1.8.7 上)。像宣传的那样工作得很好:

    描述:

    un 提供 unextend 和 uninclude 以提供更好的面向原型的编程体验。

    如果您将“# some magic here”(安装 un 后)替换为

    require 'un'
    Whatever.uninclude Mixin
    

    你得到了你所描述的行为 - 几乎。 Object 已经有一个名为 method 的方法,因此您会得到“参数数量错误”的错误。

    如果有人在 ruby​​ 1.9 或 jruby 上尝试并报告结果(我为此创建了答案社区 wiki),那就太好了。

    【讨论】:

      猜你喜欢
      • 2011-01-25
      • 2015-05-10
      • 2012-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多