重新包含模块

时间:2010-04-18 23:06:34

标签: ruby metaprogramming

我需要这样的一些:

module One
  def test; puts 'Test One'; end
end

module Two
  def test; puts 'Test Two'; end
end

class Foo
  include One
  include Two
  include One
end

在这种情况下,我需要“Test One”,但显然它会返回“Test Two”。我需要一个简洁的方法来重新包含我的模块。

有什么建议吗?

谢谢!

4 个答案:

答案 0 :(得分:2)

 class Module
     def include_again(mod)
         mod.instance_methods.each { |m|
             self.send(:define_method, m) { |*args|
                 mod.instance_method(m).bind(self).call(*args)
             }
         }
     end
 end

module One
    def test(a); puts "test one #{a}"; end
end

module Two
    def test; puts "test two"; end
end

class Foo
    include One
    include Two
end

Foo.new.test #=> "test two"

class Foo
    include_again One
end

Foo.new.test(1) #=> "test one 1"

答案 1 :(得分:0)

我不确定remove_method以外的解决方法,但为什么你所展示的内容不起作用的原因是Ruby执行方法查找的方式。< / p>

Foo创建的每个步骤中检查ancestors的{​​{1}}给了我们一个很大的暗示:

Foo

输出:

module One
  def test; puts 'Test One'; end
end

module Two
  def test; puts 'Test Two'; end
end

class Foo
  include One
  p ancestors
  include Two
  p ancestors
  include One
  p ancestors
end

如果一个模块已经在类的祖先中,Ruby不允许你再次重新包含它。因此,当<{em> [Foo, One, Object, Kernel] [Foo, Two, One, Object, Kernel] [Foo, Two, One, Object, Kernel] 之后包含<{1}}时,Two会在One的查找表中出现,Two才有机会,无论您重新包含多少次Foo

答案 2 :(得分:0)

您可以调整include的行为:

module One
  def test; puts 'Test One'; end
end

module Two
  def test; puts 'Test Two'; end
end

class Foo
  @mods = []
  def self.include(mod)
    @mods.delet mod
    @mods << mod
  end

  def self.new(*args)
    super.tap { |o| @mods.each { |m| o.extend m } } 
  end

  include One
  include Two
  include One
end

答案 3 :(得分:0)

@banister,非常感谢你的回答,因为我无法发表评论我会在这里添加代码,使其与块和参数一起使用:

class Module
  def include_again(mod)
    mod.instance_methods.each{ |m| 
      self.send(:define_method, m) { |*args, &block|
        mod.instance_method(m).bind(self).call(*args, &block)
      }
    }
  end
end
相关问题