Как я могу гибко добавлять данные в объекты Moose? - PullRequest
7 голосов
/ 22 октября 2010

Я пишу модуль для лося.Я хотел бы позволить пользователю, использующему этот объект (или мне самому), добавлять некоторые поля на лету, как он / она желает.Я не могу определить эти поля априори, поскольку я просто не знаю, какими они будут.

В настоящее время я просто добавил одно поле с именем extra типа hashref, для которого установлено значение rw, поэтому пользователиможно просто поместить вещи в этот хеш:

# $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};

Это работает.Но ... это обычная практика?Какие-нибудь другие (возможно, более изящные) идеи?

Примечание. Я не хочу создавать еще один модуль, который расширяет этот модуль, это действительно просто для вещей, которые я хотел бы добавить.

Ответы [ 4 ]

6 голосов
/ 22 октября 2010

Я бы, вероятно, сделал это через нативные черты:

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 { {} }

На объекте вы бы использовали это следующим образом:

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

Типичный пример использования для таких вещей, какэто добавление необязательных интроспективных данных к исключениям и классам сообщений.

5 голосов
/ 22 октября 2010

Если вы не сделали класс неизменным (за невыполнение этого правила штраф за производительность , в дополнение к моим опасениям по поводу изменения определений классов на лету), вы сможете это сделатьполучая мета-класс для объекта (используя $meta = $object->meta) и используя метод add_attribute в 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";

Вывод:

42
5
3 голосов
/ 22 октября 2010

На всякий случай, если вы хотите добавить метод к объекту, а не ко всему классу, взгляните на что-то вроде MooseX::SingletonMethod.

* 1006 Е.Г. *

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!

Таким образом, выше метод baz добавляется только к объекту $foo, а не к классу Foo.

Хммм ... Интересно, смогу ли я реализовать MooseX :: SingletonAttribute?


Некоторый предыдущий SO ответ, используя MooseX::SingletonMethod:

А также этот блог может быть полезен и / или интересен: Простые анонимные объекты

/ I3az /

0 голосов
/ 16 сентября 2018

Даже если модификация класса во время выполнения не является хорошей практикой, вы можете просто сделать изменяемый метакласс, добавить атрибуты и сделать класс неизменным:

 $ref->meta->make_mutable ;
 $ref->meta->add_attribute($attr_name,%cfg) ;
 $ref->meta->make_immmutable ;
...