2011-12-26 42 views
2

考慮下面的代碼:紅寶石代表團/代理

class ArrayProxy < BasicObject 
    def initialize 
    @array = [] 
    end 

    def foo 
    puts 'foo' 
    end 

    def method_missing(*args, &block) 
    @array = @array.send(*args, &block) 
    end 

    def self.method_missing(*args, &block) 
    new.send(*args, &block) 
    end 
end 

爲什麼調用「富」被委託給數組?

ruby-1.9.2-p290 :018 > ArrayProxy.new << 1 
=> [1] 
ruby-1.9.2-p290 :019 > ArrayProxy << 1 
=> [1] 
ruby-1.9.2-p290 :020 > ArrayProxy.new.foo 
foo 
=> nil 
ruby-1.9.2-p290 :021 > ArrayProxy.foo 
NoMethodError: undefined method `foo' for []:Array 
+0

在代碼中嘗試'__send__'。這可能會做到。 – Linuxios 2011-12-26 19:01:52

+0

這樣做,從文檔'您可以使用__send__如果名稱發送衝突與obj中的現有方法'。但'foo'不是Object中的方法嗎?有人可以澄清接受的答案嗎? – Chris 2011-12-26 19:06:03

+0

順便說一句,爲什麼你重新分配'@ array'內'method_missing'? – 2011-12-26 19:22:29

回答

6

由於Linux_iOS.rb.cpp.c.lisp.m.sh在註釋中提到,你應該在這種情況下使用__send__方法,如BasicObject沒有定義實例方法send

Object.instance_methods.grep /send/ 
# => [:send, :public_send, :__send__] 

BasicObject.instance_methods.grep /send/ 
# => [:__send__] 

,可以也可以通過BasicObject的文檔證明。

缺席在BasicObect類結果send實例方法的呼叫中的下列鏈:

# initial call 
ArrayProxy.foo 

# there's no class method 'foo', so we go to class 'method_missing' method 
ArrayProxy.method_missing :foo 

# inside class 'method_missing' we delegate call to new instance using 'send' 
ArrayProxy.new.send :foo 

# there is no instance method 'send' in ArrayProxy class (and its parent class 
# BasicObject) so instance 'method_missing' is called 
ArrayProxy.new.method_missing :send, :foo 

# instance 'method_missing' delegates call of 'send' method to @array 
@array.send :send, :foo 

# that is unfolded into regular call of 'send' on @array object 
@array.send :foo 

# and finally 'foo' is called for @array object 
@array.foo 
# => NoMethodError: undefined method `foo' for []:Array 
+0

偉大的穿越,謝謝=] – Chris 2011-12-26 22:22:37

0

的簽名method_missingmethod_sym, *args, &block

我認爲這是發送到陣列,因爲您正在呼叫new在類級method_missing聲明(它實例化一個新的ArrayProxy)並調用發送返回值。

我對於爲什麼要設置@array等於返回值@array.send(*args, &block)在實例級別聲明method_missing中存在一些困惑。

編輯:這是相當古怪的行爲。我們希望發送:fooArrayProxy的實例來打印foo,而不是將呼叫委託給其@arraymethod_missing

1

也許它將使使用Ruby的標準庫的工具,而不是滾動您自己更有意義?

Delegator class。 (我指的是1.9.3文檔,但該類也存在於1.8.x中)。

+0

不一定。如果你使用'Delegator'或者它的子類,'class'等方法將不會被委派。 'BasicObject'最小的一組方法可以讓你創建一個對象,在'Delegator'允許的情況下,可以在更多場景下對原始對象進行「替身」。 – Kelvin 2014-04-24 19:42:08