Применение ролей во время выполнения из модификатора метода
У меня есть роль, которая предоставляет модификатор метода так:
package MyApp::Role::MenuExtensionRed;
use Moose::Role;
requires 'BuildMenu';
after 'BuildMenu' => sub {...};
Из-за требований в другом месте мне нужно применить ряд ролей во время выполнения, например:
package MyApp::MainMenu
before 'BuildMenu' => sub {
my ( $self, $args ) = @_;
my $roles = $args->{menu_extensions};
apply_all_roles($self,@$roles);
};
sub BuildMenu {...}
Однако модификатор метода "after" никогда не вызывается. Очевидно, я нарушаю какое-то правило, но я действительно хотел бы понять, почему это не работает!
Это работает, если вместо применения ролей перед 'BuildMenu', я применяю их в методе сборки. Но, к сожалению, мой список ролей menu_extension не является в данный момент он свободен, так что мне придется подождать.
Любые альтернативные решения будут оценены!
EDIT интересно, что after 'BuildMenu'
вызывается, но только при последующих вызовах к BuildMenu. Таким образом, модификаторы метода не могут быть изменены изнутри другого из его собственных модификаторов. Является ли это ожидаемым поведением? Есть ли способ добавить в" список " модификаторов во время выполнения?
1 ответ:
Это побочный эффект того, как его реализовали.
Когда вы используете модификатор метода, они в основном заменяют существующий метод новым, и новый метод содержит ссылку на старый.
Итак, когда вы видите
before foo => sub { }
Что происходит во время составления роли:
my $orig = *package::foo; *package::foo = sub { $beforetrigger->( @args ); return $orig->( @args ); }
Более или менее.
Итак, представьте, что у вас есть 2 подмена," A", версия BuildMenu, которая вызывается перед применением роли, и" B", версия BuildMenu, которая вызывается после применения роли.
Итак, что происходит, ваш порядок вызова выглядит следующим образом:
First Call -> A -> before -> apply roles -> replace A with B <-- return from before A body -> <-- return from A Body Subsequent Call B -> something
И т. д., Поэтому я думаю, что вам нужно сделать, чтобы ваш код упаковки подтвердил это и передал контроль после применения.
around foo => sub { my ( $orig, $self , @args ) = @_; # apply role to $self here # this gets the sub that will be resolved *after* the role is applied my $wrapped_sub = $self->can('foo'); my $rval = $wrapped_sub->( $self, @args ); return $wrapped_sub->( $self, @args ); }
Обратите внимание, что есть потенциал для того, чтобы этот код имел забавную вещь, где $wrapped_sub-это sub, который я только что написал, чтобы применить роли, ... Я еще не выяснил, что именно там произойдет, но вам, возможно, придется поставить некоторые условия, чтобы предотвратить собственн-призвание-суб смерти петля от происходить.
Но суть проблемы по существу сводится к тому, что вы не можете заменить суб, который выполняется, на новый суб, из самого Суба, и ожидать, что он будет автоматически цепляться, потому что заменяемый суб не осознает, что он заменяется, и потому что "модификаторы метода" сводятся к замене субов новыми, которые цепляются к старым=)