Обман оператора case в Ruby, ===, с помощью прокси-объектов

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

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

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#=== просто творит какую-то магию, которой нельзя избежать?


person bhuga    schedule 20.08.2010    source источник
comment
Предупреждение: если вы можете достичь своей цели, не маскируясь под Fixnum, делегируя is_a?, instance_of?, kind_of?, class и т. д., сделайте это! Изменение этих методов может привести вас (или кого-то, кто использует/поддерживает ваш код) прямо в ад отладки.   -  person molf    schedule 20.08.2010


Ответы (4)


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

person mkarasek    schedule 20.08.2010
comment
Спасибо. Какую тоску это приносит. - person bhuga; 20.08.2010

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

person grddev    schedule 20.08.2010

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

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

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

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

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

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

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

person guns    schedule 20.08.2010
comment
Техническое поведение является правильным, только если вы предполагаете, что существует форма интроспекции объекта, недоступная для программиста, которая, кажется, есть - в C. К сожалению, этот прокси должен отслеживать любой тип объекта, а не только Fixnums, поэтому расширение действительно не помогает (и по разным причинам расширение Object также бесполезно). - person bhuga; 20.08.2010
comment
Что ж, я понимаю вашу точку зрения. Вы надеетесь, что :=== реализовано send :instance_of?, MyClass в вашем прокси-объекте, но на самом деле это не так. Тем не менее, я по-прежнему утверждаю, что если вы пытаетесь маскироваться под объект, но вам нужна какая-то мета-функциональность, лучше создать класс Tracking, который украшает и отслеживает отдельные объекты. Неодноэлементные объекты могут быть расширены, а одноэлементные экземпляры (например, Fixnum и Symbol) могут быть обернуты и расширены или обработаны особым образом. - person guns; 21.08.2010

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

person Joc    schedule 21.08.2010