如何根据方法属性对方法应用Moose方法修饰符?

4

我希望在我的类中应用Moose“before”方法修饰符到多个方法中。我想在一个角色中提供修饰方法。可以像这样实现:

package MyApp::Role;

use Moose::Role

before [qw(foo bar)] => sub {
    ...
};

package MyApp;

use Moose;
with (MyApp::Role);

sub foo { ... }

sub bar { ... }

sub baz { ... } # this method is unaffected

然而,必须在角色中维护相关方法列表,这使其与使用类相关联,这似乎是错误的。我希望以更智能的方式来做,例如使用方法属性:

package MyApp;

use Moose;
with (MyApp::Role);

sub foo :SomeFlag { ... }

sub bar :SomeFlag { ... }

sub baz { ... } # this method is unaffected

我不熟悉如何识别方法属性,也不知道如何动态地应用方法修饰符。

或者,也许有更好的方法吗?


你知道如何实现方法属性吗?(请参见attributes)。上次我看的时候,它真的很麻烦。但我必须承认,它可以创建出漂亮的接口。 - amon
1个回答

5
让我们使用Attribute::Handlers来实现这个,这是一种相当合理的使用属性的方式。我们必须在一个基类中定义一个函数,该函数本身具有属性:ATTR(CODE)。它需要传入以下几个参数:
  1. 子例程(或其他变量)所在的包。
  2. globref或字符串ANON
  3. 值的引用(这里是coderef)。
  4. 属性名称。
  5. 属性的可选数据。
  6. 调用属性的编译阶段。
  7. 声明子例程的文件名。
  8. 声明子例程的行号。
因此,我们可以编写一个处理程序应用before
use strict; use warnings; use feature 'say';

BEGIN {
    package MyRole;
    use Moose::Role;
    use Attribute::Handlers;

    sub SomeFlag :ATTR(CODE) {
        my ($package, $globref, $code, $attr, $data, $phase, $filename, $line) = @_;

        ref($globref) eq 'GLOB'
            or die "Only global subroutines can be decorated with :SomeFlag"
                    . " at $filename line $line.\n";

        # use the MOP to install the method modifier
        $package->meta->add_before_method_modifier(
            *$globref{NAME} => sub {
                warn "Just about to call a flagged sub!";
            },
        );
    }
}

BEGIN {
    package MyApp;
    use Moose;
    # important: SomeFlag must be available before the attrs are handled (CHECK phase)
    BEGIN { with 'MyRole' };

    sub foo :SomeFlag { say "Hi from foo sub!" }
    sub bar :SomeFlag { say "Hi from bar sub!" }
    sub baz           { say "Hi from baz sub!" }
}

package main;

my $o = MyApp->new;
$o->$_ for qw/foo bar baz/;

我将所有内容都塞到了一个文件中,但显然这是不必要的(只需添加所需的use)。
输出:
Just about to call a flagged sub! at so.pl line 16.
Hi from foo sub!
Just about to call a flagged sub! at so.pl line 16.
Hi from bar sub!
Hi from baz sub!

你可以在输出代码块上方放置 <!-- language: lang-none --> 来防止其被语法高亮。 - Brad Gilbert
谢谢!我已经拼了两天,试图让我的子类(在Moo中)能识别使用Attribute ::Handlers定义的属性。将“extends”调用放在BEGIN块内解决了我的问题。 - Jeff Ober

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接