perl - 在运行时从方法修饰符应用角色

标签 perl moose

我有一个提供方法修饰符的角色,如下所示:

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/

相关文章:

perl - Perl Moose 类中的私有(private)变量

perl - 需要帮助安装 MooseX::Declare

linux - 如何用 shell 脚本替换文本文件中的特定行?

json - yml 文件中不允许重复标签

perl - 如何使用 Moose 从属性的元对象创建值实例?

perl - 我可以在构造时设置 Moose 对象属性的 'isa' 吗?

perl - 我应该如何在两个 KiokuDB 目录之间复制对象?

perl - 我如何说服 cpanminus 在 lib/而不是 lib/perl5 中安装模块?

perl - 仅使用低优先级短路运算符警告无效上下文

sql - 如何将属性值对列表转换为列为属性的平面表