2016-05-01 79 views
1

我想让一个Foo的实例执行一些方法或过程(不管),如果它或它的foo_instance知道它。 foo_instanceFoo的一个实例,它定义了一些方法。我想,那个时候的Foo另一个实例不响应一些方法,如果foo_instance确实,它将在它的实例执行PROC:method_missing结合instance_exec的奇怪行为(ruby)

class Foo 
attr_accessor :foo_instance # this is an instance of Foo 

    def get_proc(symbol) 
    # ... a method that gets a proc (it has nothing to do with to_proc, and it is not binded to any thing, it only gets the code (as I'm only allowing to define new methods or accessors not directly, it doesn't matter) 
    end 


    def method_missing(symbol, *args, &block) 
    throw NoMethodError unless self.foo_instance.respond_to? symbol 
    some_proc = self.foo_instance.get_proc symbol 
    self.instance_exec args, &some_proc 
    end 
end 

例如,假设foo_instance响应一些所谓foo_method方法,这需要一个参数,它应该是一个数字。并假设,即,当我们定义foo_method

self.define_singleton_method :foo_method, & proc {|a| a + 1} 

这就是我结识了PROC与get_proc,当我定义的方法,我把它保存在一个变量。反正:

another_instance = Foo.new 
another_instance.foo_method(1) # it goes to method_missing 

当它到达method_missing,通过调试我发现,args[1],并且它的作用:

self.instance_exec [1], proc { 'foo' } 

,当然还有:

TypeError: no implicit conversion of Fixnum into Array 

的问题是:我究竟做错了什么?由于instance_execmethod_missing应该收到1,而不是[1],而且我也没有发送数组!我认为这可能是因为*args,因为它可能是一个参数数组,但我不确定。我无法让它工作。

+0

你块试图返回字符串''foo''?我无法在这里重现问题。你能想出一个你想在这里做什么的最简单的例子吗? – tadman

回答

0

确实是因为method_missing签名中的*args。这将保存通过的任何参数(第一个参数,即方法名称除外),并将它们放入数组中(1 - >[1])。为了解决这个问题,呼吁instance_exec时只使用*作为一吐操作:

self.instance_exec *args, &some_proc 

How splatting works as explained in CRuby's repo

+0

谢谢!这是我错过的。 – medusa

0

我的理解是,你希望:

  • 创建Foo
  • 类的实例 foo
  • foo的单身人员类别上创建一个或多个方法
  • 时的foo的单例类的方法,m一个符号被发送到boo,的Foo不同实例,并且boo并不m响应(即,m既不是Foo实例方法也没有在一个方法boo的单身类),method_missing将用于发送mfoo

如果这是正确的,对于@foo_instance的访问必须在Foo的单例类,所以它是提供给的Foo所有实例。因此,我们开始如下。

class Foo 
    singleton_class.send(:attr_accessor, :foo_instance) 
end 

foo = Foo.new 
    #=> #<Foo:0x007ff90c00f278> 
Foo.foo_instance = foo 
Foo.foo_instance 
    #=> #<Foo:0x007ff90c00f278> 

现在让我们来定义上foo的单例类的方法:

foo.define_singleton_method :foo_method, & proc {|a| a + 1} 
foo.foo_method(3) 
    #=> 4 

,并创建Foo另一个实例:

boo = Foo.new 
    #=> #<Foo:0x007fcd63968858> 
boo.foo_method(1) 
    #=> NoMethodError: undefined method `foo_method' for #<Foo:0x007fcd63968858> 

当执行的最后一条语句,并boo做对foo_method没有反应,我们希望foo_method,与参数1,是发送到foo。因此,我们可以写method_missing如下:

class Foo 
    def method_missing(symbol, *args, &block) 
    instance = self.class.foo_instance 
    raise NoMethodError unless instance.respond_to? symbol 
    instance.send(symbol, *args, &block) 
    end 
end 

现在

boo.foo_method(1) 
    #=> 2