将Moose类中的所有属性作为哈希的更好方法

时间:2018-01-17 08:04:40

标签: perl moose

我希望从类中获取所有属性作为哈希。 有没有比这更好的方法呢? 理想情况下(?)我希望能够说出类似的内容:

my $hash = \%{ Diag->new( {range =>1, code => 'AB'} ) };

但愿意接受:

my $d = Diag->new( {range =>1, code => 'AB'} );
my $hash = $d->hash;

package Diag;
use Moose;

my @attrs = qw/range code severity source message/;

has 'range'    => ( is => 'rw', isa => 'Int' );
has 'code'     => ( is => 'rw', isa => 'String' );
has 'severity' => ( is => 'rw', isa => 'Int' );
has 'source'   => ( is => 'rw', isa => 'String' );
has 'message'  => ( is => 'rw', isa => 'String' );

sub hash {
    my $self = shift;
    my $hash = {};
    for (@attrs) {
        $hash->{$_} = $self->$_;
    }
    return $hash;
}

no Moose;
1;

编辑包装/解包的字符串输出哈希:

# Combining this attribute and the record_format would be great.
# if $self->record->format worked that would be cool.
has 'record' => (
    is => 'ro',
    isa => 'HashRef',
    default => sub {
        {  
            foo => 'A5',
            foo2 => 'A16',
        }
);

sub record_format 
{
    my $self = shift;
    my @fields = qw( foo foo2 );

    return _build_format_string($self->record, \@fields);
}

sub _build_format_string {
    return join '', map { $_[1]->{$_} } @{ $_[2] };
}

EDIT2 我发现如果我创建了一个属性特征,我可以让它更好一些。这样,哈希顺序与属性一起使用,只需要一种格式方法。

package Order;
use Moose::Role;

  has order => (
      is        => 'ro',
      isa       => 'ArrayRef',
      predicate => 'has_order',
  );

Moose::Util::meta_attribute_alias('Order');
1;

package Record;
use Moose;

has 'record' => (
    traits  => [qw/Order/],
    is      => 'ro',
    isa     => 'HashRef',
    default => sub {
        {
            foo  => 'A5',
            foo2 => 'A16',
        },
        ;
    },
    order => [qw(foo foo2)]
);

sub format {
    my ( $self, $attr ) = @_;
    my $fields = $self->meta->get_attribute($attr)->order();
    return join '', map { $self->{$attr}{$_} } @$fields;
}

1;


my $r = Record->new();
print $r->format("record");
Outputs: A5A16

1 个答案:

答案 0 :(得分:3)

我宁愿把它打包成一种方法,但是你的理想"案件几乎就在那里

my $data = { %{ Diag->new( {range =>1, code => 'AB'} ) } };

%{...}会返回一个(键,值,... )列表,因此您希望{}从中创建一个hashref,而不是\ (好奇地把它变回一个物体)。

但实际上,这应该隐藏在方法中

my $data = Diag->new(...)->get_data;

package Diag;
...
sub get_data { return { %{$_[0]} } };
...
1;

出于纯粹的表现目的 - 打印出来 - 考虑使用模块,因此您不必担心(或知道)哪些属性具有作为值的参考。我使用Data::Dump来简化其输出

my $obj = Diag->new(...);

say $obj->stringify();                           # whole object serialized

say for $obj->stringify('attr1', 'attr1', ...);  # serialized values for each

package Diag;
...
use Data::Dump qw(pp);
...
sub stringify {
    my $self = shift;
    return map { pp $self->{$_} } @_  if @_;
    return { pp %$self } }
}

如果使用原生OO,Moo / Moose也不会"" say $obj;超载{/ 1>

use overload q("") => sub { return shift->stringify() }

MooMoose中,提供了""下的对象的字符串化(也隐含在版画中)。

通过进一步说明,下面的代码并没有解决实际问题。我会编辑,但我现在要离开,因为它被认为通常很有用。

在评论和问题编辑中提出,意图的一部分也是能够检索属性的值,并且已经打包。添加的代码执行此操作,但由于存在显式解除引用,因此应添加ref的检查,以便从arrayref,hashref或string / number中正确检索所有值。例如

sub record_format {
    my ($self, @attrs) = @_;
    @attrs = qw(attr1 attr2 ...) if not @attrs;  # default list
    my $packed;
    foreach my $attr (@attrs) {
        my $val = $self->{$attr};
        my $rv = ref $val;
        if    (not $rv)        { $packed .= $val }
        elsif ($rv eq 'HASH')  { $packed .= join '', values %$val }
        elsif ($rv eq 'ARRAY') { $packed .= join '', @$val }   
    }
    return $packed;
}

此打包传递的属性或列出的默认值的值。

由于$self->record->format没有返回对象而无法对其他方法调用进行字符串处理,因此所需的$self->record无法正常工作。你可以编写一个访问者,但是如果你让它在任何情况下都会返回一个对象,这可能是一个令人惊讶的行为,因此不是很好的设计。

相关问题