method_missing中的奇怪行为与instance_exec(ruby)结合

时间:2016-05-01 04:32:44

标签: arrays ruby

我试图让Foo的实例执行某个方法或proc(无论如何),如果它或它的foo_instance知道它。 foo_instanceFoo的一个实例,它定义了一些方法。我想,当Foo的另一个实例没有响应某个方法时,如果foo_instance这样做,它将在其实例中执行proc:

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}

这就是我用get_proc了解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因为它可能是一个args数组,但我不确定。我无法使它发挥作用。

2 个答案:

答案 0 :(得分:0)

确实是因为*args签名中的method_missing。这将保存传递的所有参数(第一个参数除外,它是方法名称)并将它们放在一个数组中(1 - > [1])。要解决此问题,请在调用*时使用instance_exec作为spat运算符:

self.instance_exec *args, &some_proc

How splatting works as explained in CRuby's repo

答案 1 :(得分:0)

我的理解是你希望:

  • 创建类foo
  • 的实例Foo
  • foo的单身类
  • 上创建一个或多个方法
  • 当其中一个foo的单件类方法m的符号发送到boo时,Foo的另一个实例和{ {1}}不响应boo(即,m既不是m的实例方法,也不是Foo单例类中的方法, boo用于将method_missing发送至m

如果这是正确的,foo的访问者必须位于@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