我有一个角色,它提供了一个方法修饰符,如下所示:
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’之前应用角色,它可以工作,我在BUILD方法中应用它们.但不幸的是,那时我的menu_extension角色列表不可用,所以我必须等待.
任何替代解决方案将不胜感激!
编辑有趣的是,在’BuildMenu’被调用之后,但仅在后续调用BuildMenu时.因此,方法的修饰符不能在其自己的修饰符中改变.这是预期的行为吗?有没有办法在运行时添加到修饰符的“列表”?
最佳答案 这是它实施方式的副作用.
当您使用方法修饰符时,它们基本上用新的方法替换现有方法,而新方法包含对旧方法的引用.
所以当你看到
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,并期望它自动菊花链,因为被替换的子本身不是天生的意识它被替换,并且因为“方法修饰符”相当于用新链接替换subs来链接到旧的=)