我有一个提供方法修饰符的角色,如下所示:
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'
IS 调用,但仅在随后调用 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 是我刚刚编写的用于应用角色的子程序,......我还没有弄清楚那里会发生什么,但你可能需要输入一些防止自调用子死亡循环发生的条件。
但问题的要点基本上归结为这样一个事实,即您不能用新的子程序替换正在执行的子程序本身,并期望它自动以菊花链方式连接,因为被替换的子程序本身并不知道它被替换,因为“方法修饰符”相当于用链接到旧的新的替换子 =)
关于perl - 在运行时从方法修饰符应用角色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19529996/