2013-10-22 25 views
4

我提供了一個方法修飾符像這樣一個角色:由於需求在運行時從方法應用角色修改

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角色列表在此時不可用,所以我必須等待。

任何備用解決方案,將不勝感激!

EDIT有趣的是,after 'BuildMenu'被調用,但僅在隨後調用BuildMenu時調用。所以一個方法的修飾符不能從它自己的修飾符中改變。這是預期的行爲?有沒有辦法在運行時添加到修飾符的「列表」?

+0

只是一個猜測,但它大概檢查在進入子文件之前/之後的包裝。 –

回答

2

這是如何實施的副作用。

當您使用方法修飾符時,它們基本上用新的方法替換現有的方法,而新的方法包含對舊的方法的引用。

所以當你看到

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來替換正在執行的sub,並且期望它自動菊鏈,因爲sub被替換了並不是內在地意識到它被取代,並且因爲「方法修飾符」等於用舊鏈替換新鏈=)

+0

感謝您的好評!說得通! – Ryan