How can I flexibly add data to Moose objects?

2019-01-24 11:36发布

I'm writing a module for a moose object. I would like to allow a user using this object (or myself...) add some fields on the fly as he/she desires. I can't define these fields a priori since I simply don't know what they will be.

I currently simply added a single field called extra of type hashref which is is set to rw, so users can simply put stuff in that hash:

# $obj is a ref to my Moose object    
$obj->extra()->{new_thingie}="abc123"; # adds some arbitrary stuff to the object
say $obj->extra()->{new_thingie};

This works. But... is this a common practice? Any other (possibly more elegant) ideas?

Note I do not wish to create another module the extends this one, this really just for on-the-fly stuff I'd like to add.

标签: perl moose
4条回答
走好不送
2楼-- · 2019-01-24 12:23

If you haven't made the class immutable (there is a performance penalty for not doing that, in addition to my concerns about changing class definitions on the fly), you should be able to do that by getting the meta class for the object (using $meta = $object->meta) and using the add_attribute method in Class::MOP::Class.

#!/usr/bin/perl

package My::Class;

use Moose;
use namespace::autoclean;

package main;

my $x = My::Class->new;
my $meta = $x->meta;
$meta->add_attribute(
    foo => (
        accessor => 'foo',
    )
);

$x->foo(42);

print $x->foo, "\n";

my $y = My::Class->new({ foo => 5 });
print $y->foo, "\n";

Output:

42
5
查看更多
成全新的幸福
3楼-- · 2019-01-24 12:26

Just in case you want to add a method to an object and not to the whole class then have a look at something like MooseX::SingletonMethod.

E.g.

use 5.012;
use warnings;

{
    package Foo;
    use MooseX::SingletonMethod;
    sub bar { 'bar' }     # method available to all objects
}

my $foo = Foo->new;

$foo->add_singleton_method( baz => sub { 'baz!' } );

$foo->baz;     # => baz!

So in above the method baz is only added to the object $foo and not to class Foo.

Hmmm... I wonder if I could implement a MooseX::SingletonAttribute?


Some previous SO answer using MooseX::SingletonMethod:

And also this blog post maybe of use and/or interest: Easy Anonymous Objects

/I3az/

查看更多
啃猪蹄的小仙女
4楼-- · 2019-01-24 12:30

Even if it's not a good pratice to modify a class at runtime, you can simply make the meta-class mutable, add the attribute(s) and make class immutable again:

 $ref->meta->make_mutable ;
 $ref->meta->add_attribute($attr_name,%cfg) ;
 $ref->meta->make_immmutable ;
查看更多
我欲成王,谁敢阻挡
5楼-- · 2019-01-24 12:40

I would probably do this via native traits:

has custom_fields => (
    traits     => [qw( Hash )],
    isa        => 'HashRef',
    builder    => '_build_custom_fields',
    handles    => {
        custom_field         => 'accessor',
        has_custom_field     => 'exists',
        custom_fields        => 'keys',
        has_custom_fields    => 'count',
        delete_custom_field  => 'delete',
    },
);

sub _build_custom_fields { {} }

On an object you'd use this like the following:

my $val = $obj->custom_field('foo');           # get field value
$obj->custom_field('foo', 23);                 # set field to value

$obj->has_custom_field('foo');                 # does a specific field exist?
$obj->has_custom_fields;                       # are there any fields?

my @names = $obj->custom_fields;               # what fields are there?
my $value = $obj->delete_custom_field('foo');  # remove field value

A common use-case for stuff like this is adding optional introspectable data to exception and message classes.

查看更多
登录 后发表回答