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

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

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来链接到旧的=)

点赞