如何反转ruby的include函数

时间:2010-03-08 17:01:09

标签: ruby class metaprogramming

我将在代码中解释我正在寻找的内容,因为这可能是最简洁的:

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模块,但这似乎对Whatever没有任何影响。我假设#include只是在模块的方法解析链中添加了对模块的引用 - 但这种行为与此不一致。

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

4 个答案:

答案 0 :(得分:6)

因为看起来你可能想要在实例而不是整个类上完成这些,所以我会稍微更改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

答案 1 :(得分:4)

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 #< C:0x11b0d90>

答案 2 :(得分:3)

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

More information on the difference between include and extend

答案 3 :(得分:0)

几年前,我使用gem evil来取消包含模块等,但显然它已不再维护了。所以我只是尝试了un(仅限于我的旧红宝石1.8.7)。如宣传的那样工作得很好:

描述:

un提供了无意义和不包含的内容,以允许更好的面向原型的编程体验。

如果您通过

替换“#some magic here”(安装un之后)
require 'un'
Whatever.uninclude Mixin

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

如果有人在ruby 1.9或jruby上尝试它并报告结果(我为此制作答案社区维基),那将是很好的。