2016-08-10 2 views
2

Смотрите следующий фрагмент кода Perl, который основан на лося:Perl 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", 
              }); 

Я получаю сообщение об ошибке:

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

Причиной ошибки является clear: читатель и писатель должны быть строковыми именами функций.

Но что делать в этом конкретном случае? Я не хочу создавать новую функцию для каждого из ста полей 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

Можете ли вы создать [mcve], чтобы мы могли запустить это? – simbabque

+0

@simbabque Done – porton

+1

Писатель должен писать в хеш непосредственно, а не в объект? – simbabque

ответ

3

Я думаю, что изменение методов чтения и сочинения, подобных этому, требует нездорового уровня безумия. Если вы хотите, взгляните на the source code of Class::MOP::Method::Accessor, который используется под капотом для создания аксессуаров.

Вместо этого я предлагаю просто перезаписать (или прикрепить) функции для созданных Moose-считывателей с использованием модификатора метода around. Чтобы получить это для работы с подклассами, вы можете использовать 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 не имеют большого значения.

+0

Отличное решение, но оно не работает для меня, потому что в реальном коде я вызываю '$ BusinessClass-> meta-> add_attribute (...);' в базовом классе, служащем для производного класса. Ваш предложенный 'around $ Key' вызывается в базовом классе и не выполняется с ошибкой, потому что' add_attribute() 'был вызван для другого (производного) класса, и поэтому в базовом классе нет атрибута $ Key. Что делать? – porton

+0

Это должно работать в дочерних классах. Я попробую, когда у меня будет компьютер. – simbabque

+0

@porton Я адаптировал ответ. 'Test' был переименован в' Foo', и теперь есть подкласс. Хитрость заключается не в том, чтобы использовать лося 'around'. – simbabque

Смежные вопросы