如何将方法修饰符应用于 AUTOLOAD 生成的方法?
How can I apply a method modifier to a method generated by AUTOLOAD?
我有一个非常有趣的困境。我正在开发 CVS 存储库的 Perl 脚本接口,并创建了 Perl 对象来表示 Modules
、Paths
和 Files
。由于 Modules
、Paths
和 Files
都可以对它们发出 CVS 命令,我设置了 AUTOLOAD
例程以采用任何未识别的方法并将它们作为对象发出如果它们是 CVS 命令。
所有这些 CVS 命令的执行方式完全相同,但其中一些命令需要对输出进行特殊处理才能获得我想要的结果。
例如,我想获取 diff 命令的输出并在 return 之前重新格式化它。
我正在使用 Moose
,因此通常可以按如下方式完成此特殊处理:
after 'diff' => sub {
# Reformat output here
}
问题是...我从未明确创建 diff
方法,因为它是由 AUTOLOAD
生成的,Perl 不允许我为它创建方法修饰符,因为它在技术上不不存在!
Is there a way to get this to work how I want?
将 after
应用于您的 AUTOLOAD
方法。
after 'AUTOLOAD' => sub {
my $method = $The::Package::AUTOLOAD;
$method =~ s/.*:://;
if ($method eq 'diff') {
# do after diff stuff
} elsif ($method eq 'foo') {
# do after foo stuff
} else {
# never mind, don't want to do anything after this function
}
};
编辑:
我发现我可能想要更多地控制 diff
命令,所以我在您的回答中添加了更多细节。希望有人会发现此信息有用。
要获得更多控制,您可以使用 around
!
around 'AUTOLOAD' => sub {
my $orig = shift;
my $self = shift;
(my $command = $AUTOLOAD) =~ s{.+::}{};
# Special processing
if ($command eq 'diff') {
#
# Add "before" special processing here
#
my $output = $self->$orig(@_);
#
# Add "after" special processing here
#
}
else {
return $self->$orig(@_);
}
};
这允许您在调用函数之前和之后进行特殊处理。
有关详细信息,请参阅:Moose::Manual::MethodModifiers
根据 AUTOLOAD
-using class 的实施情况,您可能会发现它也尊重 can
方法,而只需调用 can
足以创建方法。
__PACKAGE__->can( "diff" );
after diff => sub { ... };
我建议您重新构建系统以使用特征,而不是依赖 AUTOLOAD 行为。如果您没有将行为分散到各处,可维护性和意图将更加明显。
例如,您可以使用以下内容来执行您想要的操作:
package Trait::CVSActions;
use Moose::Role;
sub commit { print 'in commit for ' . shift . "\n" }
sub diff { print 'diffing for ' . shift . "\n" }
package Module;
use Moose;
with 'Trait::CVSActions';
package Path;
use Moose;
with 'Trait::CVSActions';
after commit => sub { print "after commit on Path\n" };
package main;
my $module = new Module;
my $path = new Path;
$module->commit;
$path->commit;
如果您希望使用 AUTOLOAD 来分派未知命令,那么这很危险,因为可能有一些您必须对您不知道的特殊处理,因此您可能给自己带来未来的麻烦。
我有一个非常有趣的困境。我正在开发 CVS 存储库的 Perl 脚本接口,并创建了 Perl 对象来表示 Modules
、Paths
和 Files
。由于 Modules
、Paths
和 Files
都可以对它们发出 CVS 命令,我设置了 AUTOLOAD
例程以采用任何未识别的方法并将它们作为对象发出如果它们是 CVS 命令。
所有这些 CVS 命令的执行方式完全相同,但其中一些命令需要对输出进行特殊处理才能获得我想要的结果。
例如,我想获取 diff 命令的输出并在 return 之前重新格式化它。
我正在使用 Moose
,因此通常可以按如下方式完成此特殊处理:
after 'diff' => sub {
# Reformat output here
}
问题是...我从未明确创建 diff
方法,因为它是由 AUTOLOAD
生成的,Perl 不允许我为它创建方法修饰符,因为它在技术上不不存在!
Is there a way to get this to work how I want?
将 after
应用于您的 AUTOLOAD
方法。
after 'AUTOLOAD' => sub {
my $method = $The::Package::AUTOLOAD;
$method =~ s/.*:://;
if ($method eq 'diff') {
# do after diff stuff
} elsif ($method eq 'foo') {
# do after foo stuff
} else {
# never mind, don't want to do anything after this function
}
};
编辑:
我发现我可能想要更多地控制 diff
命令,所以我在您的回答中添加了更多细节。希望有人会发现此信息有用。
要获得更多控制,您可以使用 around
!
around 'AUTOLOAD' => sub {
my $orig = shift;
my $self = shift;
(my $command = $AUTOLOAD) =~ s{.+::}{};
# Special processing
if ($command eq 'diff') {
#
# Add "before" special processing here
#
my $output = $self->$orig(@_);
#
# Add "after" special processing here
#
}
else {
return $self->$orig(@_);
}
};
这允许您在调用函数之前和之后进行特殊处理。
有关详细信息,请参阅:Moose::Manual::MethodModifiers
根据 AUTOLOAD
-using class 的实施情况,您可能会发现它也尊重 can
方法,而只需调用 can
足以创建方法。
__PACKAGE__->can( "diff" );
after diff => sub { ... };
我建议您重新构建系统以使用特征,而不是依赖 AUTOLOAD 行为。如果您没有将行为分散到各处,可维护性和意图将更加明显。
例如,您可以使用以下内容来执行您想要的操作:
package Trait::CVSActions;
use Moose::Role;
sub commit { print 'in commit for ' . shift . "\n" }
sub diff { print 'diffing for ' . shift . "\n" }
package Module;
use Moose;
with 'Trait::CVSActions';
package Path;
use Moose;
with 'Trait::CVSActions';
after commit => sub { print "after commit on Path\n" };
package main;
my $module = new Module;
my $path = new Path;
$module->commit;
$path->commit;
如果您希望使用 AUTOLOAD 来分派未知命令,那么这很危险,因为可能有一些您必须对您不知道的特殊处理,因此您可能给自己带来未来的麻烦。