将动态参数传递给参数化的Moose角色

时间:2018-05-02 20:02:40

标签: perl moose

我想使用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->applyapply_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运行后适用于该类; BUILDload()被调用之前不会运行。

我认为我需要的是一个具有角色的新课程。我可能会过度思考这个。

1 个答案:

答案 0 :(得分:1)

您可以创建组成存储行为的新角色(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;