我有一个提供方法修饰符的角色:
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角色列表不可用,所以我不得不等待。
任何替代解决方案都将受到赞赏!
编辑有趣的是,after 'BuildMenu'
被调用,但仅限于后续调用BuildMenu。因此,方法的修饰符不能在其自己的修饰符中改变。这是预期的行为吗?有没有办法在运行时添加到修饰符的“列表”?
答案 0 :(得分:2)
这是其实施方式的副作用。
使用方法修饰符时,它们基本上用新的方法替换现有方法,而新方法包含对旧方法的引用。
所以当你看到
before foo => sub {
}
角色组成时间会发生什么:
my $orig = *package::foo;
*package::foo = sub {
$beforetrigger->( @args );
return $orig->( @args );
}
或多或少。
所以想象一下,你有两个潜艇,“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本身替换正在执行的子sub,并期望它自动菊花链,因为被替换的子是没有天生意识到它被替换,并且因为“方法修饰符”相当于用新链接替换subs而不是旧的=)