我一直支持在工作中使用Moose(和MooseX :: Declare)几个月。它鼓励的风格将真正有助于我们的代码库的可维护性,但不是没有学习新语法的初始成本,而尤其在学习如何解析类型验证错误。
我已经在网上看到了这个问题的讨论,并且认为我会向这个社区发布一个查询:
a)已知的解决方案
b)讨论验证错误消息应该是什么样的
c)提出实施一些想法的概念证明
我也会联系作者,但我也看到了这个论坛的一些很好的讨论,所以我想我会发布一些公开的内容。
#!/usr/bin/perl
use MooseX::Declare;
class Foo {
has 'x' => (isa => 'Int', is => 'ro');
method doit( Int $id, Str :$z, Str :$y ) {
print "doit called with id = " . $id . "\n";
print "z = " . $z . "\n";
print "y = " . $y . "\n";
}
method bar( ) {
$self->doit(); # 2, z => 'hello', y => 'there' );
}
}
my $foo = Foo->new( x => 4 );
$foo->bar();
注意使用方法签名调用Foo :: doit时不匹配。
结果的错误消息是:
Validation failed for 'MooseX::Types::Structured::Tuple[MooseX::Types::Structured::Tuple[Object,Int],MooseX::Types::Structured::Dict[z,MooseX::Types::Structured::Optional[Str],y,MooseX::Types::Structured::Optional[Str]]]' failed with value [ [ Foo=HASH(0x2e02dd0) ], { } ], Internal Validation Error is: Validation failed for 'MooseX::Types::Structured::Tuple[Object,Int]' failed with value [ Foo{ x: 4 } ] at /usr/local/share/perl/5.10.0/MooseX/Method/Signatures/Meta/Method.pm line 441
MooseX::Method::Signatures::Meta::Method::validate('MooseX::Method::Signatures::Meta::Method=HASH(0x2ed9dd0)', 'ARRAY(0x2eb8b28)') called at /usr/local/share/perl/5.10.0/MooseX/Method/Signatures/Meta/Method.pm line 145
Foo::doit('Foo=HASH(0x2e02dd0)') called at ./type_mismatch.pl line 15
Foo::bar('Foo=HASH(0x2e02dd0)') called at ./type_mismatch.pl line 20
我认为大多数人都认为这不是那么直接。我在我的MooseX :: Method :: Signatures :: Meta :: Method的本地副本中实现了一个hack,它为同一个程序产生了这个输出:
Validation failed for
'[[Object,Int],Dict[z,Optional[Str],y,Optional[Str]]]' failed with value [ [ Foo=HASH(0x1c97d48) ], { } ]
Internal Validation Error:
'[Object,Int]' failed with value [ Foo{ x: 4 } ]
Caller: ./type_mismatch.pl line 15 (package Foo, subroutine Foo::doit)
执行此操作的超级hacky代码是
if (defined (my $msg = $self->type_constraint->validate($args, \$coerced))) {
if( $msg =~ /MooseX::Types::Structured::/ ) {
$msg =~ s/MooseX::Types::Structured:://g;
$msg =~ s/,.Internal/\n\nInternal/;
$msg =~ s/failed.for./failed for\n\n /g;
$msg =~ s/Tuple//g;
$msg =~ s/ is: Validation failed for/:/;
}
my ($pkg, $filename, $lineno, $subroutine) = caller(1);
$msg .= "\n\nCaller: $filename line $lineno (package $pkg, subroutine $subroutine)\n";
die $msg;
}
[注意:再过几分钟爬行代码,看起来像MooseX :: Meta :: TypeConstraint :: Structured :: validate更接近应该更改的代码。在任何情况下,关于理想错误信息的问题,以及是否有人正在积极研究或考虑类似的变化都是有效的。]
完成了3件事:
1)不那么冗长,更多的空白(我辩论过包括s / Tuple //,但我现在还坚持使用它)
2)包括调用文件/行(使用调用者(1))
3)死而不是忏悔 - 因为我认为忏悔的主要优点是找到用户进入类型检查的入口点,我们可以用更简洁的方式实现当然我实际上并不想支持这个补丁。我的问题是:平衡这些错误消息的完整性和简洁性的最佳方法是什么?目前是否有计划将这样的内容放到适当位置?
答案 0 :(得分:10)
我很高兴你喜欢MooseX::Declare
。但是,方法签名验证
你所谈论的错误并非真的来自那里,而是来自
MooseX::Method::Signatures
,后者又使用MooseX::Types::Structured
它的验证需求。您目前看到的每个验证错误都未经修改
来自MooseX::Types::Structured
。
我也将忽略错误消息的堆栈跟踪部分。我碰巧 发现它们非常有用,其余的Moose cabal也是如此。我不去 默认删除它们。
如果你想要一种关闭它的方法,需要改变Moose以抛出异常 用于类型约束验证错误的对象而不是字符串 其他事情。那些可能总是捕获回溯,但决定 无论是否显示它,或者在显示时如何格式化它都可以 在其他地方制作,用户可以自由修改默认行为 - 全球,本地,词汇,等等。
我要解决的是构建实际的验证错误消息 方法签名。
正如所指出的那样,MooseX::Types::Structured
进行实际验证
工作。当某些内容无法验证时,它的工作就是引发异常。这个
异常当前恰好是一个字符串,所以它并不是那么有用
想要构建漂亮的错误,以便需要改变,类似于问题
上面有堆栈跟踪。
一旦MooseX :: Types :: Structured抛出结构化异常对象,可能会 看起来有点像
bless({
type => Tuple[Tuple[Object,Int],Dict[z,Optional[Str],y,Optional[Str]]],
err => [
0 => bless({
type => Tuple[Object,Int],
err => [
0 => undef,
1 => bless({
type => Int,
err => bless({}, 'ValidationError::MissingValue'),
}, 'ValidationError'),
],
}, 'ValidationError::Tuple'),
1 => undef,
],
}, 'ValidationError::Tuple')
我们会有足够的信息来实际关联个人
MooseX::Method::Signatures
中部分签名的内部验证错误。在上面的例子中,和
鉴于你的(Int $id, Str :$z, Str :$y)
签名,它很容易知道
那是第二个元素的内部Validation::MissingValue
位置参数的元组应该为$id
提供一个值,但是
不能。
鉴于此,生成诸如
之类的错误很容易http://files.perldition.org/err1.png
或
http://files.perldition.org/err2.png
这就是我想要的,而不仅仅是格式化可怕的 我们现在拥有的信息更加精彩。但是,如果有人想这样做,那就是 一旦我们有结构化验证异常而不是 简单的字符串。
这些都不是很难 - 它只是需要做。如果有人想要帮助
与此同时,请#moose
irc.perl.org
与我们联系。
答案 1 :(得分:1)
Method::Signatures::Modifiers
是一个希望解决MooseX::Method::Signatures
的一些问题的软件包。只需use
就可以超载。
use MooseX::Declare;
use Method::Signatures::Modifiers;
class Foo
{
method bar (Int $thing) {
# this method is declared with Method::Signatures instead of MooseX::Method::Signatures
}
}