2011-11-10 33 views
2

我想定义一组方法,可以使用Mixin将其添加到一个类(示例中的C)中。这些方法可以由任何继承自另一个类(本例中为A)的类定义,并且应该能够在接收者实例(C实例)中调用方法。使用一组方法创建一个Mixin,而不是调用另一个类的方法

我有这样的代码片段:使用块作品

M = Module.new 

class A 
    class << self 
    def init(method) 
     if block_given? 
     M.send(:define_method, method) do 
      instance_exec &Proc.new 
     end 
     else 
     block = self.method(method).to_proc 
     M.send(:define_method, method) do 
      yield block.call 
     end 
     end 
    end 
    end 
end 

class B < A 
    init(:foo) do 
    "foo+".concat(c_method) 
    end 

    def self.bar 
    "bar+".concat(c_method) 
    end 

    init(:bar) 
end 

C = Class.new do 
    def c_method 
    "c_method" 
    end 
end 

c = C.new 

c.extend(M) 

puts c.foo 

puts c.bar 

添加方法,但最后一行失败:(

foo+c_method 
test.rb:28:in `bar': undefined local variable or method `c_method' for B:Class (NameError) 
from test.rb:15:in `call' 
from test.rb:15:in `block in init' 
from test.rb:46:in `<main>' 

什么我做错了还是这是没有意义的?

感谢

胡安

回答

1

当您在if声明中准备instance_exec &Proc.new时,此声明在C类的实例中作为上下文执行。您可以通过在init(:foo)的内部块中添加puts self来验证此情况。 另一方面,当你调用yield block.call时,你可以将线程执行放到B类对象的上下文中(当然不是这个类的实例)。你的代码的这个地方不知道C :: c_method的任何内容,这是错误的原因。

+0

是的,这是正确的。我试图解除B的方法并绑定到C,它不起作用。我试图回答我的问题,但我没有特权,我需要等待几个小时:) [此链接解释了这个问题](http://ruby-metaprogramming.rubylearning.com/yugui_blog_translation_1.html) – juandebravo

0

看来,我想要做的是解除绑定方法:从B的酒吧和绑定到C,什么是不允许的。您可以在this great post中找到更多信息

M = Module.new 

class A 
    class << self 
    def init(method) 
     if block_given? 
     M.send(:define_method, method) do 
      instance_exec &Proc.new 
     end 
     else 
     block = self.method(method).unbind 
     M.send(:define_method, method) do 
      m = block.bind(self) 
      puts m 
     end 
     end 
    end 
    end 
end 

class B < A 
    init(:foo) do 
    "foo+".concat(c_method) 
    end 

    def self.bar 
    "bar+".concat(c_method) 
    end 

    init(:bar) 
end 

C = Class.new do 
    def c_method 
    "c_method" 
    end 
end 

c = C.new 

c.extend(M) 

puts c.foo 

puts c.bar 

foo+c_method 
test.rb:16:in `bind': singleton method called for a different object (TypeError) 
from test.rb:16:in `block in init' 
from test.rb:48:in `<main>'