请参阅以下基于Moose的Perl代码片段:
$BusinessClass->meta->add_attribute($Key => { is => $rorw,
isa => $MooseType,
lazy => 0,
required => 0,
reader => sub { $_[0]->ORM->{$Key} },
writer => sub { $_[0]->ORM->newVal($Key, $_[1]) },
predicate => "has_$Key",
});
我收到错误:
bad accessor/reader/writer/predicate/clearer format, must be a HASH ref at /usr/local/lib/perl5/site_perl/mach/5.20/Class/MOP/Class.pm line 899
错误的原因很明显:读者和编写者必须是函数的字符串名称。
但在这个具体案例中该怎么做?我不想为每一百个ORM字段创建一个新函数(这里的ORM属性是一个绑定的哈希)。所以我不能在这里传递一个字符串,我需要一个闭包。
因此,我的编码需求导致了矛盾。我不知道该怎么做。
以上是真实代码的片段。现在我提出一个最小的例子:
#!/usr/bin/perl
my @Fields = qw( af sdaf gdsg ewwq fsf ); # pretend that we have 100 fields
# Imagine that this is a tied hash with 100 fields
my %Data = map { $_ => rand } @Fields;
package Test;
use Moose;
foreach my $Key (@Fields) {
__PACKAGE__->meta->add_attribute($Key => { is => 'rw',
isa => 'Str',
lazy => 0,
required => 0,
reader => sub { $Data{$Key} },
writer => sub { $Data{$Key} = $_[1] },
});
}
运行它会导致:
$ ./test.pl
bad accessor/reader/writer/predicate/clearer format, must be a HASH ref at /usr/lib/i386-linux-gnu/perl5/5.22/Class/MOP/Class.pm line 899
Class::MOP::Class::try {...} at /usr/share/perl5/Try/Tiny.pm line 92
eval {...} at /usr/share/perl5/Try/Tiny.pm line 83
Try::Tiny::try('CODE(0x9dc6cec)', 'Try::Tiny::Catch=REF(0x9ea0c60)') called at /usr/lib/i386-linux-gnu/perl5/5.22/Class/MOP/Class.pm line 904
Class::MOP::Class::_post_add_attribute('Moose::Meta::Class=HASH(0x9dc13f4)', 'Moose::Meta::Attribute=HASH(0x9dc6b5c)') called at /usr/lib/i386-linux-gnu/perl5/5.22/Class/MOP/Mixin/HasAttributes.pm line 39
Class::MOP::Mixin::HasAttributes::add_attribute('Moose::Meta::Class=HASH(0x9dc13f4)', 'Moose::Meta::Attribute=HASH(0x9dc6b5c)') called at /usr/lib/i386-linux-gnu/perl5/5.22/Moose/Meta/Class.pm line 572
Moose::Meta::Class::add_attribute('Moose::Meta::Class=HASH(0x9dc13f4)', 'af', 'HASH(0x9ea13a4)') called at test.pl line 18
我不知道该怎么做(如何创建"动态"(类似闭包)访问器,而不为每个100个字段编写单独的函数?)
答案 0 :(得分:3)
我认为改变这样的读者和作者方法需要不健康的精神错乱。如果您愿意,请查看the source code of Class::MOP::Method::Accessor,它在引擎盖下用于创建访问者。
相反,我建议使用around
方法修饰符覆盖(或附加)Moose生成的读者的功能。要使其与子类一起使用,您可以使用Class::Method::Modifiers代替Moose around
。
package Foo::Subclass;
use Moose;
extends 'Foo';
package Foo;
use Moose;
package main;
require Class::Method::Modifiers; # no import because it would overwrite Moose
my @Fields = qw( af sdaf gdsg ewwq fsf ); # pretend that we have 100 fields
# Imagine that this is a tied hash with 100 fields
my %Data = map { $_ => rand } @Fields;
my $class = 'Foo::Subclass';
foreach my $Key (@Fields) {
$class->meta->add_attribute(
$Key => {
is => 'rw',
isa => 'Str',
lazy => 0,
required => 0,
}
);
Class::Method::Modifiers::around( "${class}::$Key", sub {
my $orig = shift;
my $self = shift;
$self->$orig(@_); # just so Moose is up to speed
# writer
$Data{$Key} = $_[0] if @_;
return $Data{$Key};
});
}
然后进行测试。
package main;
use Data::Printer;
use v5.10;
my $foo = Test->new;
say $foo->sdaf;
$foo->sdaf('foobar');
say $foo->sdaf;
p %Data;
p $foo;
这是我机器上的STDOUT / STDERR。
{
af 0.972962507120432,
ewwq 0.959195914302605,
fsf 0.719139421719849,
gdsg 0.140205658312095,
sdaf "foobar"
}
Foo::Subclass {
Parents Foo
Linear @ISA Foo::Subclass, Foo, Moose::Object
public methods (6) : af, ewwq, fsf, gdsg, meta, sdaf
private methods (0)
internals: {
sdaf "foobar"
}
}
0.885114977459551
foobar
正如您所看到的,Moose并不真正了解哈希中的值,但如果您使用访问器,它将读取和写入它们。使用编写器时,Moose对象将慢慢填满新值,否则Moose对象内部的值并不重要。