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

Ada*_*gel 10 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();
Run Code Online (Sandbox Code Playgroud)

注意使用方法的签名调用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
Run Code Online (Sandbox Code Playgroud)

我认为大多数人都认为这不是那么直接.我在我的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)
Run Code Online (Sandbox Code Playgroud)

执行此操作的超级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;
    }
Run Code Online (Sandbox Code Playgroud)

[注意:再过几分钟爬行代码,看起来像MooseX :: Meta :: TypeConstraint :: Structured :: validate更接近应该更改的代码.无论如何,关于理想错误信息的问题,以及是否有人正在积极研究或考虑类似的变化都是有效的.

这完成了3件事:

1)更简洁,更多的空白(我辩论包括s/Tuple //,但我现在坚持使用它)

2)包括调用文件/行(使用调用者(1))

3)死而不是忏悔 - 因为我认为忏悔的主要优点是找到用户进入类型检查的入口点,我们可以用更简洁的方式实现

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

raf*_*afl 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')
Run Code Online (Sandbox Code Playgroud)

我们将有足够的信息来实际将各个内部验证错误与部分签名相关联MooseX::Method::Signatures.在上面的例子中,并给出了你的(Int $id, Str :$z, Str :$y)签名,很容易知道Validation::MissingValue位置参数的元组的第二个元素的内部应该提供值$id,但不能.

鉴于此,生成错误很容易

http://files.perldition.org/err1.png

要么

http://files.perldition.org/err2.png

这就是我想要的,而不仅仅是格式化我们现在更好的消息.但是,如果有人想这样做,一旦我们有结构化验证异常而不是普通字符串,它仍然很容易.

这些都不是很难 - 它只是需要做.如果有人感觉就像帮助了这一点,再来说说我们#mooseirc.perl.org.