Передача динамических параметров в параметризованную роль Moose - PullRequest
0 голосов
/ 02 мая 2018

Я хочу использовать параметризованную роль MooseX::Storage, но я также хочу разрешить предоставление параметров через конструктор. Вот «статическая» версия, которая работает как положено:

package Note;
use Moose;
use namespace::autoclean;
use MooseX::StrictConstructor;
use MooseX::Storage;

has title   => (is => 'ro', isa => 'Str');
has body    => (is => 'rw', isa => 'Str');

with Storage(format => 'JSON', io => 'File');

__PACKAGE__->meta->make_immutable;

package main;
use warnings;
use strict;
use feature 'say';

## make a new note and store it
my $note = Note->new(title => 'Note 1');
$note->body("Here is the note");
$note->store($note->title . ".json");

## load the stored note
undef $note;
$note = Note->load("Note 1.json");
say $note->body;

Но я хочу сделать параметры хранилища динамическими. Я думал об использовании черты, как описано в MooseX :: Traits , что-то вроде этого:

package Note;
use Moose;
use namespace::autoclean;
use MooseX::StrictConstructor;

has title   => (is => 'ro', isa => 'Str');
has body    => (is => 'rw', isa => 'Str');

with 'MooseX::Traits';

__PACKAGE__->meta->make_immutable;

package main;
use warnings;
use strict;
use feature 'say';
use MooseX::Storage;

## make a new note; set the storage format dynamically
my $note = Note->with_traits(Storage => {format => 'JSON', io => 'File'})->new(title => 'Note 1');
$note->body("Here is the note");
$note->store($note->title . ".json");

## load the stored note
undef $note;
$note = Note->load("Note 1.json");
say $note->body;

Но я получаю ошибку:

Can't locate Storage.pm in @INC

Я пытался use MooseX::Storage в обоих классах и т. Д. Я смотрел на meta->apply и apply_all_roles, но ни один из них, похоже, не подходит для параметризованных ролей.

Есть кое-что, чего я не понимаю в параметризованных ролях. Мне интересно, должен ли я обернуть параметризованную роль в непараметрическую роль (например, StorageWithJSONFile) и использовать эту черту. Это был бы хороший подход?

Как правильно установить параметры этой роли при строительстве объекта? Или есть лучший способ придать объекту параметризованную роль?

Редактировать: Я узнал, что функция Storage(), которая MooseX::Storage экспортирует , возвращает список ролей, когда она вызывается. Это кое-что прояснило для меня, поэтому я попробую это:

package Note;
use Moose;
use namespace::autoclean;
use MooseX::StrictConstructor;
use MooseX::Storage;
use Moose::Util 'apply_all_roles';

has title   => (is => 'ro', isa => 'Str');
has body    => (is => 'rw', isa => 'Str');
has storage => (is => 'ro', isa => 'HashRef[Str]');

sub BUILD {
    my ($self) = @_;
    apply_all_roles($self, Storage(%{$self->storage}));
}

__PACKAGE__->meta->make_immutable;

package main;
use warnings;
use strict;
use feature 'say';

## make a new note and store it: this works!
my $note = Note->new(title => 'Note 1', storage => {format => 'JSON', io => 'File'});
$note->body("Here is the note");
$note->store($note->title . ".json");

## load the stored note: this does not work: Can't locate object method "load" via package "Note" at test4.pl line 32.
undef $note;
$note = Note->load("Note 1.json");  ## where are the roles?
say $note->body;

Содержимое Note 1.json:

{"__CLASS__":"Moose::Meta::Class::__ANON__::SERIAL::2","body":"Here is the note","storage":{"format":"JSON","io":"File"},"title":"Note 1"}

Кажется, у меня проблема с куриным яйцом: load() применяется к классу после запуска BUILD; BUILD не будет работать, пока не будет вызван load(). 1040 *

Я думаю, что мне нужен новый класс с составленной ролью. Возможно, я слишком обдумываю это.

1 Ответ

0 голосов
/ 03 мая 2018

Вы можете создать новую роль (JSONFiler), которая составляет поведение хранилища, а затем динамически применить эту роль, используя with_traits:

package Note;
use Moose;
use namespace::autoclean;
use MooseX::StrictConstructor;

has title   => (is => 'ro', isa => 'Str');
has body    => (is => 'rw', isa => 'Str');

with 'MooseX::Traits';

__PACKAGE__->meta->make_immutable;

package JSONFiler;
use Moose::Role;
use MooseX::Storage;

with Storage(format => 'JSON', io => 'File');

package main;
use warnings;
use strict;
use feature 'say';

## create a new Note class with the Storage traits
my $note_class = Note->with_traits('JSONFiler');

## make a new note and store it
my $note = $note_class->new(title => 'Note 1');
$note->body("Here is the note");
$note->store($note->title . ".json");

## load the stored note
undef $note;
$note = $note_class->load("Note 1.json");
say $note->body;
...