Perl: исправление атрибута Moose и проблем с приведением типов

Недавно я обновил Moose до версии 1.15 и обнаружил, что набор модулей, которые я использую, больше не работает. Ошибка, которую я получаю:

You cannot coerce an attribute (source) unless its type (GOBO::Node) has a coercion at
/opt/local/lib/perl5/site_perl/5.12.0/darwin-multi-2level/Moose/Meta/Role/Application/ToClass.pm line 142

Я вижу несколько возможных источников ошибки и был бы признателен за совет, как исправить проблему.

Первый фрагмент кода для GOBO::Node выглядит так:

package GOBO::Node;
[...]
extends 'GOBO::Base';
with 'GOBO::Labeled';
with 'GOBO::Attributed';

coerce 'GOBO::Node'
  => from 'Str'
  => via { new GOBO::Node(id=>$_) };

has 'source' => (is => 'rw', isa => 'GOBO::Node');

Роли, используемые этим пакетом, также имеют атрибуты GOBO::Nodes, и атрибут «источник», упомянутый в сообщении об ошибке, является одним из них.

  • отчасти причина наличия принуждения в GOBO::Node, по-видимому, заключается в сокращении при создании нового узла. Было бы лучше использовать BUILDARGS, а не принуждать?

  • где я должен поместить принуждение, если я хочу, чтобы несколько пакетов могли его использовать? Если я добавлю приведение к (например) GOBO::Attributed, я получу предупреждение о том, что оно уже существует. Однако без принуждения я получаю предупреждение выше о невозможности принуждения.

  • есть отдельный пакет подтипов; было бы лучше создать подтип GOBO::Node, например. GOBO::Node::ProtoNode -- и приведение, и использование этого атрибута должно быть GOBO::Nodes?

Спасибо за любую помощь или совет по этой проблеме!


person i alarmed alien    schedule 15.10.2010    source источник


Ответы (2)


Код примера, который вы вставили, на самом деле не вызывает ошибку. Атрибут source, как написано там, не будет пытаться что-либо принуждать. Однако я предполагаю, что одна из упомянутых вами ролей имеет атрибут с определенным coerce => 1.

В Moose типы и, следовательно, приведения являются глобальными. В сочетании с тем фактом, что Moose динамически строит класс, вы получаете странное поведение, которое вы видите здесь. Вам потребуется переместить определение приведения куда-нибудь до первого использования типа GOBO::Node. Обычно это делается путем создания пакета подтипов (который, как вы заметили, у вас уже есть) и включения его как можно раньше (через use).

Простое перемещение определения приведения GOBO::Node в этот пакет подтипов и обеспечение его использования везде, где требуется приведение, должно решить вашу проблему.

Чтобы ответить на другие ваши вопросы:

  • В общем, я бы рекомендовал использовать принуждение вместо BUILDARGS, потому что это гораздо более тонкий инструмент. Использование, которое вы здесь показываете, является примером правильного использования принуждения из учебника, поэтому нет реальной причины его менять.

  • Как указано выше, типичным ответом является создание пакета библиотеки в отдельном пространстве имен (MyApp::TypeLibrary), а затем включение этого пакета в верхнюю часть классов, для которых вы хотите, чтобы ваши типы были доступны. Perl не будет перекомпилировать пакет, который он уже скомпилировал, а это означает, что ошибка, которую вы получили об уже существующем приведении, в этом случае не будет вызвана.

  • Основываясь на приведенных вами примерах, нет необходимости создавать новый подтип, GOBO::Node уже должен работать, и без нового подтипа это фактически тот же ответ, что и последний. Да, используйте библиотеку подтипов.

Надеюсь, это поможет.

person perigrin    schedule 15.10.2010
comment
Это здорово, спасибо - и спасибо за ответы на другие вопросы. :) - person i alarmed alien; 19.10.2010

Обычное решение состоит в том, чтобы иметь отдельный файл только для объявления ограничений типов и их приведения. Если вам нужно убедиться, что определенный класс загружен, например. чтобы принуждать к этому - требуйте его внутри ваших функций принуждения.

Итак, что-то вроде:

package GOBO::Types;
use Moose::Util::TypeConstraints;
class_type 'GOBO::Node';
coerce 'GOBO::Node',
    from 'Str',
    via { require GOBO::Node; GOBO::Node->new(id => $_) };

Вы можете использовать эту библиотеку типов где угодно, не беспокоясь о порядке загрузки, и вы можете быть уверены, что все ваши типы уже будут определены — особенно важно, если у вас есть типы, которые не являются class_type, из-за того, как Moose пытается разрешить имена ограничений типов. что он еще не видел определенного.

Использование приведения здесь вместо BUILDARGS определенно правильно; это гораздо более многоразового использования.

person hdp    schedule 15.10.2010
comment
Отлично, спасибо! Теперь я исправил код проблемы, и все работает гладко. :D - person i alarmed alien; 19.10.2010