简洁的MooseX ::声明方法签名验证错误

时间:2010-12-02 23:33:23

标签: perl moose

我一直支持在工作中使用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)死而不是忏悔 - 因为我认为忏悔的主要优点是找到用户进入类型检查的入口点,我们可以用更简洁的方式实现

当然我实际上并不想支持这个补丁。我的问题是:平衡这些错误消息的完整性和简洁性的最佳方法是什么?目前是否有计划将这样的内容放到适当位置?

2 个答案:

答案 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
    }
}