Perl: исправление проблем с атрибутами Moose и приведением типов - PullRequest
2 голосов
/ 15 октября 2010

Я недавно обновил Moose до v1.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?

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

Ответы [ 2 ]

9 голосов
/ 16 октября 2010

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

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

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

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

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

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

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

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

6 голосов
/ 16 октября 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 - определенно правильная вещь;это гораздо более многоразового использования.

...