使用一组方法创建一个Mixin,而不是可以调用另一个类方法

时间:2011-11-10 08:34:35

标签: ruby metaprogramming

我想定义一组可以使用Mixin添加到类(示例中为C)的方法。这些方法可以由从另一个类继承的任何类(示例中的A)定义,并且应该能够调用接收器实例(C实例)中的方法。

我有这段代码:

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>'

我做错了什么?或者这没有意义?

由于

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分: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>'