2016-05-01 81 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