Руби делегирование / прокси - PullRequest
2 голосов
/ 26 декабря 2011

С учетом следующего кода:

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

Почему вызов 'foo' делегируется массиву?

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

Ответы [ 3 ]

6 голосов
/ 26 декабря 2011

Как отметил в комментарии 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.

Отсутствие send метода экземпляра в классе BasicObect приводит к следующей цепочке вызовов:

# 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   
1 голос
/ 26 декабря 2011

Возможно, было бы более разумно использовать стандартный библиотечный инструмент Ruby вместо собственного?

Делегатор класса .(Я указал на документы 1.9.3, но класс существует и в 1.8.x).

0 голосов
/ 26 декабря 2011

Подпись для method_missing является method_sym, *args, &block.

Я думаю, что он отправляется в массив, когда вы вызываете new в объявлении уровня класса method_missing (которое создает новый ArrayProxy) и вызывает send для возвращаемого значения. * Я немного запутался, почему вы устанавливаете @array равным возвращаемому значению @array.send(*args, &block) в объявлении уровня экземпляра method_missing.

Редактировать: это довольно странное поведение. Ожидается отправка :foo экземпляру ArrayProxy для печати foo, а не делегирование вызова его @array через method_missing.

...