快速生成Perl Moose访问器 [英] Perl Moose accessors generated on the fly

查看:96
本文介绍了快速生成Perl Moose访问器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请参阅以下基于Moose的Perl代码片段:

See the following fragment of Perl code which is based on Moose:

$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",
                                            });

我收到错误:

错误的访问器/读取器/写入器/谓词/清除器格式,必须为/usr/local/lib/perl5/site_perl/mach/5.20/Class/MOP/Class.pm第899行的HASH引用

错误原因很明确:读写器必须是函数的字符串名称.

The reason of the error is clear: reader and writer must be string names of functions.

但是在这种特定情况下该怎么办?我不想为一百个ORM字段中的每个字段创建一个新函数(这里的ORM属性是一个绑定哈希).所以我不能在这里传递字符串,我需要一个闭包.

But what to do it in this specific case? I do not want to create a new function for each of a hundred ORM fields (ORM attribute here is a tied hash). So I can't pass a string here, I need a closure.

因此,我的编码需求导致了矛盾.我不知道该怎么办.

Thus my coding needs resulted in a contradiction. I don't know what to do.

以上是真实代码的一部分.现在,我给出一个最小的示例:

The above was a fragment of real code. Now I present a minimal example:

#!/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个字段中的每个字段编写单独的函数,如何创建动态"(类似封闭的)访问器?)

I don't know what to do (how to create "dynamic" (closure-like) accessors, without writing an individual function for each of the 100 fields?)

推荐答案

我认为,更改此类读写器方法需要不健康的精神错乱程度.如果需要,请查看源Class :: MOP :: Method :: Accessor 的代码,用于在内部创建访问器.

I think changing the reader and writer methods like that requires an unhealthy level of insanity. If you want to, take a look at the source code of Class::MOP::Method::Accessor, which is used under the hood to create the accessors.

相反,我建议使用 around 方法修饰符将功能覆盖(或附加)到Moose生成的阅读器上.要使其与子类一起使用,可以使用 Class :: Method :: Modifiers 而不是Moose around .

Instead, I suggest to just overwrite (or attach) the functionality to the Moose-generated readers using an around method modifier. To get that to work with sub-classes, you can use Class::Method::Modifiers instead of the 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.

Here's the STDOUT/STDERR from my machine.

{
    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对象内部的值并不重要.

As you can see, Moose doesn't really know about the values inside of the hash, but if you use the accessors, it will read and write them. The Moose object will slowly fill up with new values when you use the writer, but otherwise the values inside of the Moose object do not really matter.

这篇关于快速生成Perl Moose访问器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆