【问题标题】:Perl Moose Method Modifiers: Call 'around' before 'before' and 'after'Perl Moose 方法修饰符:在 'before' 和 'after' 之前调用 'around'
【发布时间】:2010-12-14 17:22:21
【问题描述】:

我正在使用 Moose,我需要在我的项目中包装方法调用。重要的是我的包装代码是最外层的修饰符。到目前为止,我所做的是将我的方法修饰符放在 Moose 角色中,然后在我的课程结束时应用该角色,如下所示:

use Moose::Util;
Moose::Util::apply_all_roles(__PACKAGE__->meta, ('App:Roles::CustomRole'));
__PACKAGE__->meta->make_immutable;

这使我能够合理地确定我的角色的修饰符是最后定义的,因此为我提供了“之前”和“之后”的正确行为。 (角色中的“之前”和“之后”被称为最前和最末。)

我原本以为这样就足够了,但现在我真的需要用“around”以类似的方式包装方法。 Moose 所基于的 Class::MOP 最先应用“around”修饰符,因此它们在“before”之后和“after”之前被调用。

更多细节,这里是我的修饰符的当前调用顺序:

CUSTOM ROLE before
    before 2
       before 1
           CUSTOM ROLE around
               around
                   method
               around
           CUSTOM ROLE around
       after 1
    after 2
 CUSTOM ROLE AFTER

我真的需要这样的东西:

CUSTOM ROLE before
    CUSTOM ROLE around
        before 2
           before 1
               around
                   method
               around
           after 1
        after 2
    CUSTOM ROLE around
 CUSTOM ROLE AFTER

关于如何在我想要的地方应用/调用我的“around”修饰符有什么想法吗?我知道我可以做一些符号表黑客(比如 Class::MOP 已经在做),但我真的不想这样做。

【问题讨论】:

  • 我同意下面 Ether 的问题,你为什么使用Moose::Util::apply_all_roles 而不是with
  • 与类中可能已经存在的其他修饰符相比,我希望我的角色中的“之前”和“之后”修饰符首先或最后运行。稍后手动应用角色定义修饰符,然后修饰符首先运行(对于之前)和最后(对于之后)。
  • @perigrin 我终于明白了你和以太的问题。我不需要像我想的那样用 apply_all_roles 应用角色。我对 Moose 还很陌生,并且对需要“手动”应用该角色的想法感到困惑。我真正需要做的就是在文件末尾(在其他修饰符之后)使用“with”而不是开头。

标签: perl methods moose


【解决方案1】:

最简单的解决方案是让 CUSTOM ROLE 定义一个调用 main 方法的方法,然后包装它。

role MyRole { 
    required 'wrapped_method';
    method custom_role_base_wrapper { $self->wrapped_method(@_) }

    around custom_role_base_wrapper { ... }
    before custom_role_base_wrapper { ... }
}

您遇到的问题是您试图让 CUSTOM ROLE 围绕方法包装其他的东西。这不是它的设计目的。除了像您建议的那样编写类似的符号表骇客(可能您可以争辩其中一个 Moose 人在 Class::MOP 中公开 API 以帮助实现目标),我能想到的唯一其他解决方案就是上面的那个。

如果您不希望custom_role_base_wrapper 会添加额外的调用堆栈帧,您应该查看Yuval 的Sub::Call::Tail 或使用goto 来操作调用堆栈。

【讨论】:

  • 我最终采用了自定义包装器的想法。它给了我所需的灵活性,谢谢。
【解决方案2】:

我对 Moose 还很陌生,但你为什么要这样做:

use Moose::Util;
Moose::Util::apply_all_roles(__PACKAGE__->meta, ('App:Roles::CustomRole'));

而不仅仅是这个?

with 'App:Roles::CustomRole';

关于您的问题,这有点像 hack,但是您能否将您的 around 方法拆分为 beforeafter 方法并在您的类定义末尾应用角色(因此它适用于您的想要的顺序)?如果绝对必要,您可以使用私有属性来保存两种方法之间的状态。

【讨论】:

  • 问题是beforeafter 都不允许你(干净地)像around 那样改变返回语义。如果这些语义很重要,那你就完蛋了。如果不是,您为什么要使用around 开头?
  • 我手动应用它的原因是修饰符是按照定义的顺序添加(然后运行)的。通常,您不关心运行的顺序修饰符。但是,在我的情况下,我希望我的角色修饰符是之前的第一个和之后的最后一个。如果您使用“with”语法来应用角色,则首先定义其修饰符,因此它们将作为最内层的“之前”和“之后”运行。通过在最后手动应用角色,它们是最后定义的,因此在我想要它们时运行。
  • @Matt:这似乎确实是 Moose 的一个缺点。也许after 语义应该允许像around 那样更改返回值,或者应该修改beforearoundafter 方法的顺序,以便它们彼此之间都是后进先出,而不是而不仅仅是他们自己(如果这有意义的话)。
  • beforeafter 的设计有意不包含这样的副作用。特别是beforeafter 应该用于附加到该方法的带外行为,而around 仅用于这种“覆盖”的方法。像这样改变三年前的语义确实是 Moose 团队不会接受的,我认为这也不是真正的答案。
猜你喜欢
  • 1970-01-01
  • 2013-02-02
  • 2018-02-10
  • 2012-10-30
  • 2012-02-22
  • 2011-03-30
  • 1970-01-01
  • 2012-09-04
  • 1970-01-01
相关资源
最近更新 更多