Обманывающий оператор case Руби, ===, с прокси-объектами - PullRequest
5 голосов
/ 20 августа 2010

Я пытаюсь создать прокси-объект, который передает почти все вызовы методов дочернему объекту, по сути, шаблону делегатора.По большей части я просто использую BasicObject и передаю каждый вызов method_missing дочернему объекту.Пока все хорошо.

Хитрость в том, что, как ни старайся, я не могу обмануть оператор case Руби, поэтому я не могу сделать:

x = Proxy.new(15)
Fixnum === x #=> false, no matter what I do

Это, конечно, делаетлюбые операции case x не выполняются, что означает, что прокси нельзя безопасно передавать другим библиотекам.

Я не могу понять, что использует ===.Прокси отлично работает для всех интроспекций, основанных на классах, о которых я знаю, и которые все правильно передаются дочернему объекту:

x.is_a?(Fixnum) #=> true
x.instance_of?(Fixnum) #=> true
x.kind_of?(Fixnum) #=> true
x.class #=> Fixnum

Module#=== просто совершает какое-то волшебство, которое невозможноизбегать?

Ответы [ 4 ]

1 голос
/ 20 августа 2010

Да, это так.Module#=== реализован в C, непосредственно исследуя иерархию классов объекта.Не похоже, что есть способ обмануть его.

0 голосов
/ 21 августа 2010

вам, вероятно, следует выполнить поиск класса BlankSlate.Этот класс удаляет большинство методов из обычного объекта, и на веб-сайте есть пример простого класса Proxy, который распечатывает все вызываемые методы.Это плечо даст вам лучшее представление о том, что происходит.Извините, я не могу дать вам более полный ответ, но я разговариваю по телефону.Надеюсь, это поможет.

0 голосов
/ 20 августа 2010

Проблема в том, что он делает Fixnum === x, что означает, что метод === вызывается на Fixnum, а не на x. Вы могли бы заменить все существующие === методы (и также следить за появлением новых === методов), но это было бы много работы и довольно хрупкое.

0 голосов
/ 20 августа 2010

Я думаю, вы ищете класс Delegator.

Ваш прокси-класс должен создать подкласс класса Delegator, а затем определить __getobj__ и __setobj__ чтобы получить и установить целевой объект.

Забудьте, я попробовал это сам, и это не работает.

РЕДАКТИРОВАТЬ:

Как упоминает grddev, техническая проблема заключается в том, что Fixnum отправляетсяметод: ===.Думая об этом дальше, я думаю, что текущее поведение Руби правильно.Поскольку делегат должен быть абстрактным интерфейсом для скрытия деталей реализации, экземпляры Proxy правильно не идентифицируются как kind_of?Fixnum.

Если вы действительно хотите, чтобы класс Proxy был своего рода Fixnum, но хотите украсить его с помощью методов, то логично будет либо создать подкласс Fixnum, либо создать модуль ProxyMethods и расширить отдельные экземплярыFixnum.

Конечно, поскольку вы действительно не можете сделать Fixnum.new, вам придется создать подкласс Fixnum для расширения одного экземпляра, но общее правило остается в силе.

...