在我看来,你差一点就要彻底改造Moose。考虑使用Moose而不是构建类似但不太有用的东西。
错误消息意味着您在代码需要代码引用的地方传入了一个字符串。获取堆栈跟踪以找出错误的来源。
您可以通过使用 Carp::Always、覆盖$SIG{__DIE__}
处理程序以生成堆栈跟踪或将 aCarp::confess
插入代码来执行此操作。
这是一个 sigdie 解决方案,将其粘贴在您的代码中,它将在您的模块初始化之前运行:
$SIG{__DIE__} = sub { Carp::confess(@_) };
你可能需要把它放在一个BEGIN
块中。
我真的很想劝阻您不要采用这种方法来构建对象。你很高兴地祝福任何作为你对象的一部分传入构造函数的随机垃圾!你愉快地进入你的对象内部。字段验证规则 *不属于构造函数——它们属于属性修改器。
如果您必须使用 DIY 对象,请清理您的做法:
# Here's a bunch of validators.
# I set them up so that each attribute supports:
# Multiple validators per attribute
# Distinct error message per attribute
my %VALIDATORS = (
some_attribute => [
[ sub { 'foo1' }, 'Foo 1 is bad thing' ],
[ sub { 'foo2' }, 'Foo 2 is bad thing' ],
[ sub { 'foo3' }, 'Foo 3 is bad thing' ],
],
other_attribute => [ [ sub { 'bar' }, 'Bar is bad thing' ] ],
);
sub new {
my $class = shift; # Get the invocant
my %args = @_; # Get named arguments
# Do NOT make this a clone method as well
my $self = {};
bless $class, $self;
# Initialize the object;
for my $arg ( keys %args ) {
# Make sure we have a sane error message on a bad argument.
croak "Bogus argument $arg not allowed in $class\n"
unless $class->can( $arg );
$self->$arg( $args{$arg} );
}
return $self;
}
# Here's an example getter/setter method combined in one.
# You may prefer to separate get and set behavior.
sub some_attribute {
my $self = shift;
if( @_ ){
my $val = shift;
# Do any validation for the field
$_->[0]->($val) or croak $_->[1]
for @{ $VALIDATORS{some_attribute} || [] };
$self->{some_attribute} = $val;
}
return $self->{some_attribute};
}
所有这些代码都非常好,但是您必须为每个属性重复您的属性代码。这意味着很多容易出错的样板代码。您可以通过学习使用闭包或字符串 eval 来动态创建方法来解决此问题,或者您可以使用 Perl 的许多类生成库之一,例如 Class::Accessor、Class::Struct、Accessor::Tiny 等等.
或者你可以学习 [Moose][3]。Moose 是已经接管 Perl OOP 实践的新的(ish)对象库。它提供了一组强大的功能,并大大减少了经典 Perl OOP 的样板:
use Moose;
type 'Foo'
=> as 'Int'
=> where {
$_ > 23 and $_ < 42
}
=> message 'Monkeys flew out my butt';
has 'some_attribute' => (
is => 'rw',
isa => 'Foo',
);