X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMooseX%2FTypes%2FStructured.pm;h=e336cd3aa7f0e2d3a164ff80bc4a0c89f9fbbfba;hb=aa4718fef8106533dcfb8cf141091e824f4896b1;hp=ddf09ef80fb5d5dbd6c776acad52b78670a56290;hpb=ff801143e71409963bb8b0600118409247c1995b;p=gitmo%2FMooseX-Types-Structured.git diff --git a/lib/MooseX/Types/Structured.pm b/lib/MooseX/Types/Structured.pm index ddf09ef..e336cd3 100644 --- a/lib/MooseX/Types/Structured.pm +++ b/lib/MooseX/Types/Structured.pm @@ -1,11 +1,16 @@ package MooseX::Types::Structured; use 5.008; + use Moose::Util::TypeConstraints; use MooseX::Meta::TypeConstraint::Structured; +use MooseX::Types::Structured::OverflowHandler; use MooseX::Types -declare => [qw(Dict Tuple Optional)]; +use Sub::Exporter -setup => { exports => [ qw(Dict Tuple Optional slurpy) ] }; +use Devel::PartialDump; +use Scalar::Util qw(blessed); -our $VERSION = '0.07'; +our $VERSION = '0.13'; our $AUTHORITY = 'cpan:JJNAPIORK'; =head1 NAME @@ -479,7 +484,80 @@ following are valid: {first=>'John', middle=>'James', last=>'Napiorkowski'} {first=>'Vanessa', last=>'Li'} + +=head1 EXPORTABLE SUBROUTINES + +This type library makes available for export the following subroutines + +=head2 slurpy + +Structured type constraints by their nature are closed; that is validation will +depend on an exact match between your structure definition and the arguments to +be checked. Sometimes you might wish for a slightly looser amount of validation. +For example, you may wish to validate the first 3 elements of an array reference +and allow for an arbitrary number of additional elements. At first thought you +might think you could do it this way: + + # I want to validate stuff like: [1,"hello", $obj, 2,3,4,5,6,...] + subtype AllowTailingArgs, + as Tuple[ + Int, + Str, + Object, + ArrayRef[Int], + ]; + +However what this will actually validate are structures like this: + + [10,"Hello", $obj, [11,12,13,...] ]; # Notice element 4 is an ArrayRef + +In order to allow structured validation of, "and then some", arguments, you can +use the method against a type constraint. For example: + + use MooseX::Types::Structured qw(Tuple slurpy); + subtype AllowTailingArgs, + as Tuple[ + Int, + Str, + Object, + slurpy ArrayRef[Int], + ]; + +This will now work as expected, validating ArrayRef structures such as: + + [1,"hello", $obj, 2,3,4,5,6,...] + +A few caveats apply. First, the slurpy type constraint must be the last one in +the list of type constraint parameters. Second, the parent type of the slurpy +type constraint must match that of the containing type constraint. That means +that a Tuple can allow a slurpy ArrayRef (or children of ArrayRefs, including +another Tuple) and a Dict can allow a slurpy HashRef (or children/subtypes of +HashRef, also including other Dict constraints). + +Please note the the technical way this works 'under the hood' is that the +slurpy keywork transforms the target type constraint into a coderef. Please do +not try to create your own custom coderefs; always use the slurpy method. The +underlying technology may change in the future but the slurpy keyword will be +supported. + +=head1 ERROR MESSAGES + +Error reporting has been improved to return more useful debugging messages. Now +I will stringify the incoming check value with L so that you +can see the actual structure that is tripping up validation. Also, I report the +'internal' validation error, so that if a particular element inside the +Structured Type is failing validation, you will see that. There's a limit to +how deep this internal reporting goes, but you shouldn't see any of the "failed +with ARRAY(XXXXXX)" that we got with earlier versions of this module. + +This support is continuing to expand, so it's best to use these messages for +debugging purposes and not for creating messages that 'escape into the wild' +such as error messages sent to the user. + +Please see the test '12-error.t' for a more lengthy example. Your thoughts and +preferable tests or code patches very welcome! + =head1 EXAMPLES Here are some additional example usage for structured types. All examples can @@ -537,26 +615,32 @@ other MooseX::Types libraries. And now you can instantiate with all the following: __PACKAGE__->new( - name=>'John Napiorkowski', - age=>39, + person=>{ + name=>'John Napiorkowski', + age=>39, + }, ); __PACKAGE__->new( - first=>'John', - last=>'Napiorkowski', - years=>39, + person=>{ + first=>'John', + last=>'Napiorkowski', + years=>39, + }, ); __PACKAGE__->new( - fullname => { - first=>'John', - last=>'Napiorkowski' + person=>{ + fullname => { + first=>'John', + last=>'Napiorkowski' + }, + dob => 'DateTime'->new( + year=>1969, + month=>2, + day=>13 + ), }, - dob => 'DateTime'->new( - year=>1969, - month=>2, - day=>13 - ), ); This technique is a way to support various ways to instantiate your class in a @@ -575,7 +659,8 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint( @$type_constraints : (); my $overflow_handler; - if(ref $type_constraints[-1] eq 'CODE') { + if($type_constraints[-1] && blessed $type_constraints[-1] + && $type_constraints[-1]->isa('MooseX::Types::Structured::OverflowHandler')) { $overflow_handler = pop @type_constraints; } @@ -586,11 +671,15 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint( if(@values) { my $value = shift @values; unless($type_constraint->check($value)) { + $_[2]->{message} = $type_constraint->get_message($value) + if ref $_[2]; return; } } else { ## Test if the TC supports null values unless($type_constraint->check()) { + $_[2]->{message} = $type_constraint->get_message('NULL') + if ref $_[2]; return; } } @@ -598,12 +687,16 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint( ## Make sure there are no leftovers. if(@values) { if($overflow_handler) { - return $overflow_handler->(@values); + return $overflow_handler->check([@values], $_[2]); } else { + $_[2]->{message} = "More values than Type Constraints!" + if ref $_[2]; return; } } elsif(@type_constraints) { - warn "I failed due to left over TC"; + $_[2]->{message} = + "Not enough values for all defined type constraints. Remaining: ". join(', ',@type_constraints) + if ref $_[2]; return; } else { return 1; @@ -623,7 +716,8 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint( @$type_constraints : (); my $overflow_handler; - if(ref $type_constraints[-1] eq 'CODE') { + if($type_constraints[-1] && blessed $type_constraints[-1] + && $type_constraints[-1]->isa('MooseX::Types::Structured::OverflowHandler')) { $overflow_handler = pop @type_constraints; } my (%type_constraints) = @type_constraints; @@ -636,11 +730,15 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint( my $value = $values{$key}; delete $values{$key}; unless($type_constraint->check($value)) { + $_[2]->{message} = $type_constraint->get_message($value) + if ref $_[2]; return; } } else { ## Test to see if the TC supports null values unless($type_constraint->check()) { + $_[2]->{message} = $type_constraint->get_message('NULL') + if ref $_[2]; return; } } @@ -648,11 +746,16 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint( ## Make sure there are no leftovers. if(%values) { if($overflow_handler) { - return $overflow_handler->(%values); + return $overflow_handler->check(+{%values}); } else { + $_[2]->{message} = "More values than Type Constraints!" + if ref $_[2]; return; } } elsif(%type_constraints) { + $_[2]->{message} = + "Not enough values for all defined type constraints. Remaining: ". join(', ',values %values) + if ref $_[2]; return; } else { return 1; @@ -689,6 +792,12 @@ OPTIONAL: { Moose::Util::TypeConstraints::add_parameterizable_type($Optional); } +sub slurpy ($) { + my ($tc) = @_; + return MooseX::Types::Structured::OverflowHandler->new( + type_constraint => $tc, + ); +} =head1 SEE ALSO