Use the new TypeError class to return structured error objects
[gitmo/MooseX-Types-Structured.git] / lib / MooseX / Types / Structured.pm
1 package MooseX::Types::Structured;
2
3 use 5.008;
4
5 use Moose::Util::TypeConstraints;
6 use MooseX::Meta::TypeConstraint::Structured;
7 use MooseX::Meta::TypeConstraint::Structured::Optional;
8 use MooseX::Types::Structured::OverflowHandler;
9 use MooseX::Types::Structured::TypeError;
10 use MooseX::Types -declare => [qw(Dict Map Tuple Optional)];
11 use Sub::Exporter -setup => [ qw(Dict Map Tuple Optional slurpy) ];
12 use Devel::PartialDump;
13 use Scalar::Util qw(blessed);
14
15 our $VERSION = '0.20';
16 our $AUTHORITY = 'cpan:JJNAPIORK';
17
18 =head1 NAME
19
20 MooseX::Types::Structured - Structured Type Constraints for Moose
21
22 =head1 SYNOPSIS
23
24 The following is example usage for this module.
25
26     package Person;
27
28     use Moose;
29     use MooseX::Types::Moose qw(Str Int HashRef);
30     use MooseX::Types::Structured qw(Dict Tuple Optional);
31
32     ## A name has a first and last part, but middle names are not required
33     has name => (
34         isa=>Dict[
35             first => Str,
36             last => Str,
37             middle => Optional[Str],
38         ],
39     );
40
41     ## description is a string field followed by a HashRef of tagged data.
42     has description => (
43       isa=>Tuple[
44         Str,
45         Optional[HashRef],
46      ],
47     );
48
49         ## Remainder of your class attributes and methods
50
51 Then you can instantiate this class with something like:
52
53     my $john = Person->new(
54         name => {
55             first => 'John',
56             middle => 'James'
57             last => 'Napiorkowski',
58         },
59         description => [
60             'A cool guy who loves Perl and Moose.', {
61                 married_to => 'Vanessa Li',
62                 born_in => 'USA',
63             };
64         ]
65     );
66
67 Or with:
68
69     my $vanessa = Person->new(
70         name => {
71             first => 'Vanessa',
72             last => 'Li'
73         },
74         description => ['A great student!'],
75     );
76
77 But all of these would cause a constraint error for the 'name' attribute:
78
79     ## Value for 'name' not a HashRef
80     Person->new( name => 'John' );
81
82     ## Value for 'name' has incorrect hash key and missing required keys
83     Person->new( name => {
84         first_name => 'John'
85     });
86
87     ## Also incorrect keys
88     Person->new( name => {
89         first_name => 'John',
90         age => 39,
91     });
92
93     ## key 'middle' incorrect type, should be a Str not a ArrayRef
94     Person->new( name => {
95         first => 'Vanessa',
96         middle => [1,2],
97         last => 'Li',
98     });
99
100 And these would cause a constraint error for the 'description' attribute:
101
102     ## Should be an ArrayRef
103     Person->new( description => 'Hello I am a String' );
104
105     ## First element must be a string not a HashRef.
106     Person->new (description => [{
107         tag1 => 'value1',
108         tag2 => 'value2'
109     }]);
110
111 Please see the test cases for more examples.
112
113 =head1 DESCRIPTION
114
115 A structured type constraint is a standard container L<Moose> type constraint,
116 such as an ArrayRef or HashRef, which has been enhanced to allow you to
117 explicitly name all the allowed type constraints inside the structure.  The
118 generalized form is:
119
120     TypeConstraint[@TypeParameters or %TypeParameters]
121
122 Where 'TypeParameters' is an array reference or hash references of
123 L<Moose::Meta::TypeConstraint> objects.
124
125 This type library enables structured type constraints. It is built on top of the
126 L<MooseX::Types> library system, so you should review the documentation for that
127 if you are not familiar with it.
128
129 =head2 Comparing Parameterized types to Structured types
130
131 Parameterized constraints are built into core Moose and you are probably already
132 familiar with the type constraints 'HashRef' and 'ArrayRef'.  Structured types
133 have similar functionality, so their syntax is likewise similar. For example,
134 you could define a parameterized constraint like:
135
136     subtype ArrayOfInts,
137      as ArrayRef[Int];
138
139 which would constrain a value to something like [1,2,3,...] and so on.  On the
140 other hand, a structured type constraint explicitly names all it's allowed
141 'internal' type parameter constraints.  For the example:
142
143     subtype StringFollowedByInt,
144      as Tuple[Str,Int];
145
146 would constrain it's value to things like ['hello', 111] but ['hello', 'world']
147 would fail, as well as ['hello', 111, 'world'] and so on.  Here's another
148 example:
149
150         package MyApp::Types;
151
152     use MooseX::Types -declare [qw(StringIntOptionalHashRef)];
153     use MooseX::Types::Moose qw(Str Int);
154         use MooseX::Types::Structured qw(Tuple Optional);
155
156     subtype StringIntOptionalHashRef,
157      as Tuple[
158         Str, Int,
159         Optional[HashRef]
160      ];
161
162 This defines a type constraint that validates values like:
163
164     ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
165     ['World', 200];
166
167 Notice that the last type constraint in the structure is optional.  This is
168 enabled via the helper Optional type constraint, which is a variation of the
169 core Moose type constraint 'Maybe'.  The main difference is that Optional type
170 constraints are required to validate if they exist, while 'Maybe' permits
171 undefined values.  So the following example would not validate:
172
173     StringIntOptionalHashRef->validate(['Hello Undefined', 1000, undef]);
174
175 Please note the subtle difference between undefined and null.  If you wish to
176 allow both null and undefined, you should use the core Moose 'Maybe' type
177 constraint instead:
178
179         package MyApp::Types;
180
181     use MooseX::Types -declare [qw(StringIntMaybeHashRef)];
182     use MooseX::Types::Moose qw(Str Int Maybe);
183     use MooseX::Types::Structured qw(Tuple);
184
185     subtype StringIntMaybeHashRef,
186      as Tuple[
187         Str, Int, Maybe[HashRef]
188      ];
189
190 This would validate the following:
191
192     ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
193     ['World', 200, undef];
194     ['World', 200];
195
196 Structured constraints are not limited to arrays.  You can define a structure
197 against a HashRef with the 'Dict' type constaint as in this example:
198
199     subtype FirstNameLastName,
200      as Dict[
201         firstname => Str,
202         lastname => Str,
203      ];
204
205 This would constrain a HashRef that validates something like:
206
207     {firstname => 'Christopher', lastname => 'Parsons'};
208
209 but all the following would fail validation:
210
211     ## Incorrect keys
212     {first => 'Christopher', last => 'Parsons'};
213
214     ## Too many keys
215     {firstname => 'Christopher', lastname => 'Parsons', middlename => 'Allen'};
216
217     ## Not a HashRef
218     ['Christopher', 'Parsons'];
219
220 These structures can be as simple or elaborate as you wish.  You can even
221 combine various structured, parameterized and simple constraints all together:
222
223     subtype Crazy,
224      as Tuple[
225         Int,
226         Dict[name=>Str, age=>Int],
227         ArrayRef[Int]
228      ];
229
230 Which would match:
231
232         [1, {name=>'John', age=>25},[10,11,12]];
233
234 Please notice how the type parameters can be visually arranged to your liking
235 and to improve the clarity of your meaning.  You don't need to run then
236 altogether onto a single line.  Additionally, since the 'Dict' type constraint
237 defines a hash constraint, the key order is not meaningful.  For example:
238
239         subtype AnyKeyOrder,
240          as Dict[
241                 key1=>Int,
242                 key2=>Str,
243                 key3=>Int,
244          ];
245
246 Would validate both:
247
248         {key1 => 1, key2 => "Hi!", key3 => 2};
249         {key2 => "Hi!", key1 => 100, key3 => 300};
250
251 As you would expect, since underneath its just a plain old Perl hash at work.
252
253 =head2 Alternatives
254
255 You should exercise some care as to whether or not your complex structured
256 constraints would be better off contained by a real object as in the following
257 example:
258
259     package MyApp::MyStruct;
260     use Moose;
261
262     ## lazy way to make a bunch of attributes
263     has $_ for qw(full_name age_in_years);
264
265     package MyApp::MyClass;
266     use Moose;
267
268     has person => (isa => 'MyApp::MyStruct');
269
270     my $instance = MyApp::MyClass->new(
271         person=>MyApp::MyStruct->new(
272             full_name => 'John',
273             age_in_years => 39,
274         ),
275     );
276
277 This method may take some additional time to setup but will give you more
278 flexibility.  However, structured constraints are highly compatible with this
279 method, granting some interesting possibilities for coercion.  Try:
280
281     package MyApp::MyClass;
282
283     use Moose;
284     use MyApp::MyStruct;
285
286     ## It's recommended your type declarations live in a separate class in order
287     ## to promote reusability and clarity.  Inlined here for brevity.
288
289     use MooseX::Types::DateTime qw(DateTime);
290     use MooseX::Types -declare [qw(MyStruct)];
291     use MooseX::Types::Moose qw(Str Int);
292     use MooseX::Types::Structured qw(Dict);
293
294     ## Use class_type to create an ISA type constraint if your object doesn't
295     ## inherit from Moose::Object.
296     class_type 'MyApp::MyStruct';
297
298     ## Just a shorter version really.
299     subtype MyStruct,
300      as 'MyApp::MyStruct';
301
302     ## Add the coercions.
303     coerce MyStruct,
304      from Dict[
305         full_name=>Str,
306         age_in_years=>Int
307      ], via {
308         MyApp::MyStruct->new(%$_);
309      },
310      from Dict[
311         lastname=>Str,
312         firstname=>Str,
313         dob=>DateTime
314      ], via {
315         my $name = $_->{firstname} .' '. $_->{lastname};
316         my $age = DateTime->now - $_->{dob};
317
318         MyApp::MyStruct->new(
319             full_name=>$name,
320             age_in_years=>$age->years,
321         );
322      };
323
324     has person => (isa=>MyStruct);
325
326 This would allow you to instantiate with something like:
327
328     my $obj = MyApp::MyClass->new( person => {
329         full_name=>'John Napiorkowski',
330         age_in_years=>39,
331     });
332
333 Or even:
334
335     my $obj = MyApp::MyClass->new( person => {
336         lastname=>'John',
337         firstname=>'Napiorkowski',
338         dob=>DateTime->new(year=>1969),
339     });
340
341 If you are not familiar with how coercions work, check out the L<Moose> cookbook
342 entry L<Moose::Cookbook::Recipe5> for an explanation.  The section L</Coercions>
343 has additional examples and discussion.
344
345 =head2 Subtyping a Structured type constraint
346
347 You need to exercise some care when you try to subtype a structured type as in
348 this example:
349
350     subtype Person,
351      as Dict[name => Str];
352
353     subtype FriendlyPerson,
354      as Person[
355         name => Str,
356         total_friends => Int,
357      ];
358
359 This will actually work BUT you have to take care that the subtype has a
360 structure that does not contradict the structure of it's parent.  For now the
361 above works, but I will clarify the syntax for this at a future point, so
362 it's recommended to avoid (should not really be needed so much anyway).  For
363 now this is supported in an EXPERIMENTAL way.  Your thoughts, test cases and
364 patches are welcomed for discussion.  If you find a good use for this, please
365 let me know.
366
367 =head2 Coercions
368
369 Coercions currently work for 'one level' deep.  That is you can do:
370
371     subtype Person,
372      as Dict[
373         name => Str,
374         age => Int
375     ];
376
377     subtype Fullname,
378      as Dict[
379         first => Str,
380         last => Str
381      ];
382
383     coerce Person,
384      ## Coerce an object of a particular class
385      from BlessedPersonObject, via {
386         +{
387             name=>$_->name,
388             age=>$_->age,
389         };
390      },
391
392      ## Coerce from [$name, $age]
393      from ArrayRef, via {
394         +{
395             name=>$_->[0],
396             age=>$_->[1],
397         },
398      },
399      ## Coerce from {fullname=>{first=>...,last=>...}, dob=>$DateTimeObject}
400      from Dict[fullname=>Fullname, dob=>DateTime], via {
401         my $age = $_->dob - DateTime->now;
402         my $firstn = $_->{fullname}->{first};
403         my $lastn = $_->{fullname}->{last}
404         +{
405             name => $_->{fullname}->{first} .' '. ,
406             age =>$age->years
407         }
408      };
409
410 And that should just work as expected.  However, if there are any 'inner'
411 coercions, such as a coercion on 'Fullname' or on 'DateTime', that coercion
412 won't currently get activated.
413
414 Please see the test '07-coerce.t' for a more detailed example.  Discussion on
415 extending coercions to support this welcome on the Moose development channel or
416 mailing list.
417
418 =head2 Recursion
419
420 Newer versions of L<MooseX::Types> support recursive type constraints.  That is
421 you can include a type constraint as a contained type constraint of itself.  For
422 example:
423
424         subtype Person,
425          as Dict[
426                 name=>Str,
427                 friends=>Optional[
428                         ArrayRef[Person]
429                 ],
430          ];
431
432 This would declare a Person subtype that contains a name and an optional
433 ArrayRef of Persons who are friends as in:
434
435         {
436                 name => 'Mike',
437                 friends => [
438                         { name => 'John' },
439                         { name => 'Vincent' },
440                         {
441                                 name => 'Tracey',
442                                 friends => [
443                                         { name => 'Stephenie' },
444                                         { name => 'Ilya' },
445                                 ],
446                         },
447                 ],
448         };
449
450 Please take care to make sure the recursion node is either Optional, or declare
451 a Union with an non recursive option such as:
452
453         subtype Value
454          as Tuple[
455                 Str,
456                 Str|Tuple,
457          ];
458
459 Which validates:
460
461         [
462                 'Hello', [
463                         'World', [
464                                 'Is', [
465                                         'Getting',
466                                         'Old',
467                                 ],
468                         ],
469                 ],
470         ];
471
472 Otherwise you will define a subtype thatis impossible to validate since it is
473 infinitely recursive.  For more information about defining recursive types,
474 please see the documentation in L<MooseX::Types> and the test cases.
475
476 =head1 TYPE CONSTRAINTS
477
478 This type library defines the following constraints.
479
480 =head2 Tuple[@constraints]
481
482 This defines an ArrayRef based constraint which allows you to validate a specific
483 list of contained constraints.  For example:
484
485     Tuple[Int,Str]; ## Validates [1,'hello']
486     Tuple[Str|Object, Int]; ## Validates ['hello', 1] or [$object, 2]
487
488 The Values of @constraints should ideally be L<MooseX::Types> declared type
489 constraints.  We do support 'old style' L<Moose> string based constraints to a
490 limited degree but these string type constraints are considered deprecated.
491 There will be limited support for bugs resulting from mixing string and
492 L<MooseX::Types> in your structures.  If you encounter such a bug and really
493 need it fixed, we will required a detailed test case at the minimum.
494
495 =head2 Dict[%constraints]
496
497 This defines a HashRef based constraint which allowed you to validate a specific
498 hashref.  For example:
499
500     Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
501
502 The keys in %constraints follow the same rules as @constraints in the above
503 section.
504
505 =head2 Map[ $key_constraint, $value_constraint ]
506
507 This defines a HashRef based constraint in which both the keys and values are
508 required to meet certain constraints.  For example, to map hostnames to IP
509 addresses, you might say:
510
511   Map[ HostName, IPAddress ]
512
513 The type constraint would only be met if every key was a valid HostName and
514 every value was a valid IPAddress.
515
516 =head2 Optional[$constraint]
517
518 This is primarily a helper constraint for Dict and Tuple type constraints.  What
519 this allows is for you to assert that a given type constraint is allowed to be
520 null (but NOT undefined).  If the value is null, then the type constraint passes
521 but if the value is defined it must validate against the type constraint.  This
522 makes it easy to make a Dict where one or more of the keys doesn't have to exist
523 or a tuple where some of the values are not required.  For example:
524
525     subtype Name() => as Dict[
526         first=>Str,
527         last=>Str,
528         middle=>Optional[Str],
529     ];
530
531 Creates a constraint that validates against a hashref with the keys 'first' and
532 'last' being strings and required while an optional key 'middle' is must be a
533 string if it appears but doesn't have to appear.  So in this case both the
534 following are valid:
535
536     {first=>'John', middle=>'James', last=>'Napiorkowski'}
537     {first=>'Vanessa', last=>'Li'}
538
539 If you use the 'Maybe' type constraint instead, your values will also validate
540 against 'undef', which may be incorrect for you.
541
542 =head1 EXPORTABLE SUBROUTINES
543
544 This type library makes available for export the following subroutines
545
546 =head2 slurpy
547
548 Structured type constraints by their nature are closed; that is validation will
549 depend on an exact match between your structure definition and the arguments to
550 be checked.  Sometimes you might wish for a slightly looser amount of validation.
551 For example, you may wish to validate the first 3 elements of an array reference
552 and allow for an arbitrary number of additional elements.  At first thought you
553 might think you could do it this way:
554
555     #  I want to validate stuff like: [1,"hello", $obj, 2,3,4,5,6,...]
556     subtype AllowTailingArgs,
557      as Tuple[
558        Int,
559        Str,
560        Object,
561        ArrayRef[Int],
562      ];
563
564 However what this will actually validate are structures like this:
565
566     [10,"Hello", $obj, [11,12,13,...] ]; # Notice element 4 is an ArrayRef
567
568 In order to allow structured validation of, "and then some", arguments, you can
569 use the L</slurpy> method against a type constraint.  For example:
570
571     use MooseX::Types::Structured qw(Tuple slurpy);
572
573     subtype AllowTailingArgs,
574      as Tuple[
575        Int,
576        Str,
577        Object,
578        slurpy ArrayRef[Int],
579      ];
580
581 This will now work as expected, validating ArrayRef structures such as:
582
583     [1,"hello", $obj, 2,3,4,5,6,...]
584
585 A few caveats apply.  First, the slurpy type constraint must be the last one in
586 the list of type constraint parameters.  Second, the parent type of the slurpy
587 type constraint must match that of the containing type constraint.  That means
588 that a Tuple can allow a slurpy ArrayRef (or children of ArrayRefs, including
589 another Tuple) and a Dict can allow a slurpy HashRef (or children/subtypes of
590 HashRef, also including other Dict constraints).
591
592 Please note the the technical way this works 'under the hood' is that the
593 slurpy keyword transforms the target type constraint into a coderef.  Please do
594 not try to create your own custom coderefs; always use the slurpy method.  The
595 underlying technology may change in the future but the slurpy keyword will be
596 supported.
597
598 =head1 ERROR MESSAGES
599
600 Error reporting has been improved to return more useful debugging messages. Now
601 I will stringify the incoming check value with L<Devel::PartialDump> so that you
602 can see the actual structure that is tripping up validation.  Also, I report the
603 'internal' validation error, so that if a particular element inside the
604 Structured Type is failing validation, you will see that.  There's a limit to
605 how deep this internal reporting goes, but you shouldn't see any of the "failed
606 with ARRAY(XXXXXX)" that we got with earlier versions of this module.
607
608 This support is continuing to expand, so it's best to use these messages for
609 debugging purposes and not for creating messages that 'escape into the wild'
610 such as error messages sent to the user.
611
612 Please see the test '12-error.t' for a more lengthy example.  Your thoughts and
613 preferable tests or code patches very welcome!
614
615 =head1 EXAMPLES
616
617 Here are some additional example usage for structured types.  All examples can
618 be found also in the 't/examples.t' test.  Your contributions are also welcomed.
619
620 =head2 Normalize a HashRef
621
622 You need a hashref to conform to a canonical structure but are required accept a
623 bunch of different incoming structures.  You can normalize using the Dict type
624 constraint and coercions.  This example also shows structured types mixed which
625 other MooseX::Types libraries.
626
627     package Test::MooseX::Meta::TypeConstraint::Structured::Examples::Normalize;
628
629     use Moose;
630     use DateTime;
631
632     use MooseX::Types::Structured qw(Dict Tuple);
633     use MooseX::Types::DateTime qw(DateTime);
634     use MooseX::Types::Moose qw(Int Str Object);
635     use MooseX::Types -declare => [qw(Name Age Person)];
636
637     subtype Person,
638      as Dict[
639         name=>Str,
640         age=>Int,
641      ];
642
643     coerce Person,
644      from Dict[
645         first=>Str,
646         last=>Str,
647         years=>Int,
648      ], via { +{
649         name => "$_->{first} $_->{last}",
650         age => $_->{years},
651      }},
652      from Dict[
653         fullname=>Dict[
654                 last=>Str,
655                 first=>Str,
656         ],
657         dob=>DateTime,
658      ],
659      ## DateTime needs to be inside of single quotes here to disambiguate the
660      ## class package from the DataTime type constraint imported via the
661      ## line "use MooseX::Types::DateTime qw(DateTime);"
662      via { +{
663         name => "$_->{fullname}{first} $_->{fullname}{last}",
664         age => ($_->{dob} - 'DateTime'->now)->years,
665      }};
666
667     has person => (is=>'rw', isa=>Person, coerce=>1);
668
669 And now you can instantiate with all the following:
670
671     __PACKAGE__->new(
672         person=>{
673             name=>'John Napiorkowski',
674             age=>39,
675         },
676     );
677
678     __PACKAGE__->new(
679         person=>{
680             first=>'John',
681             last=>'Napiorkowski',
682             years=>39,
683         },
684     );
685
686     __PACKAGE__->new(
687         person=>{
688             fullname => {
689                 first=>'John',
690                 last=>'Napiorkowski'
691             },
692             dob => 'DateTime'->new(
693                 year=>1969,
694                 month=>2,
695                 day=>13
696             ),
697         },
698     );
699
700 This technique is a way to support various ways to instantiate your class in a
701 clean and declarative way.
702
703 =cut
704
705 my $Optional = MooseX::Meta::TypeConstraint::Structured::Optional->new(
706     name => 'MooseX::Types::Structured::Optional',
707     package_defined_in => __PACKAGE__,
708     parent => find_type_constraint('Item'),
709     constraint => sub { 1 },
710     constraint_generator => sub {
711         my ($type_parameter, @args) = @_;
712         my $check = $type_parameter->_compiled_type_constraint();
713         return sub {
714             my (@args) = @_;
715             ## Does the arg exist?  Something exists if it's a 'real' value
716             ## or if it is set to undef.
717             if(exists($args[0])) {
718                 ## If it exists, we need to validate it
719                 $check->($args[0]);
720             } else {
721                 ## But it's is okay if the value doesn't exists
722                 return 1;
723             }
724         }
725     }
726 );
727
728 Moose::Util::TypeConstraints::register_type_constraint($Optional);
729 Moose::Util::TypeConstraints::add_parameterizable_type($Optional);
730
731 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
732         MooseX::Meta::TypeConstraint::Structured->new(
733                 name => "MooseX::Types::Structured::Tuple" ,
734                 parent => find_type_constraint('ArrayRef'),
735                 constraint_generator=> sub {
736                         ## Get the constraints and values to check
737             my ($type_constraints, $values) = @_;
738                         my @type_constraints = defined $type_constraints ?
739              @$type_constraints : ();
740
741             my $overflow_handler;
742             if($type_constraints[-1] && blessed $type_constraints[-1]
743               && $type_constraints[-1]->isa('MooseX::Types::Structured::OverflowHandler')) {
744                 $overflow_handler = pop @type_constraints;
745             }
746
747                         my @values = defined $values ? @$values: ();
748                         ## Perform the checking
749                         my $slot = 0;
750                         while(@type_constraints) {
751                                 my $type_constraint = shift @type_constraints;
752                                 if(@values) {
753                                         my $value = shift @values;
754                                         unless($type_constraint->check($value)) {
755                         $_[2]->{message} = MooseX::Types::Structured::TypeError->new(
756                                 constraint => $type_constraint,
757                                 value => $value,
758                                 index => $slot,
759                                )
760                          if ref $_[2];
761                                                 return;
762                                         }
763                                 } else {
764                     ## Test if the TC supports null values
765                     unless ($type_constraint->is_subtype_of($Optional)) {
766                         $_[2]->{message} = MooseX::Types::Structured::TypeError->new(
767                                 constraint => $type_constraint,
768                                 index => $slot,
769                                )
770                          if ref $_[2];
771                                                 return;
772                                         }
773                                 }
774                                 ++$slot;
775                         }
776                         ## Make sure there are no leftovers.
777                         if(@values) {
778                 if($overflow_handler) {
779                     return $overflow_handler->check([@values], $_[2]);
780                 } else {
781                     $_[2]->{message} = "More values than Type Constraints!"
782                      if ref $_[2];
783                     return;
784                 }
785                         } elsif(@type_constraints) {
786                 $_[2]->{message} =
787                  "Not enough values for all defined type constraints.  Remaining: ". join(', ',@type_constraints)
788                  if ref $_[2];
789                                 return;
790                         } else {
791                                 return 1;
792                         }
793                 }
794         )
795 );
796
797 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
798         MooseX::Meta::TypeConstraint::Structured->new(
799                 name => "MooseX::Types::Structured::Dict",
800                 parent => find_type_constraint('HashRef'),
801                 constraint_generator=> sub {
802                         ## Get the constraints and values to check
803             my ($type_constraints, $values) = @_;
804                         my @type_constraints = defined $type_constraints ?
805              @$type_constraints : ();
806
807             my $overflow_handler;
808             if($type_constraints[-1] && blessed $type_constraints[-1]
809               && $type_constraints[-1]->isa('MooseX::Types::Structured::OverflowHandler')) {
810                 $overflow_handler = pop @type_constraints;
811             }
812             my (%type_constraints) = @type_constraints;
813                         my %values = defined $values ? %$values: ();
814                         ## Perform the checking
815                         while(%type_constraints) {
816                                 my($key, $type_constraint) = each %type_constraints;
817                                 delete $type_constraints{$key};
818                                 if(exists $values{$key}) {
819                                         my $value = $values{$key};
820                                         delete $values{$key};
821                                         unless($type_constraint->check($value)) {
822                          $_[2]->{message} = MooseX::Types::Structured::TypeError->new(
823                                 constraint => $type_constraint,
824                                 index => $key,
825                                 value => $value,
826                                )
827                          if ref $_[2];
828                                                 return;
829                                         }
830                                 } else {
831                     ## Test to see if the TC supports null values
832                     unless ($type_constraint->is_subtype_of($Optional)) {
833                          $_[2]->{message} = MooseX::Types::Structured::TypeError->new(
834                                 constraint => $type_constraint,
835                                 index => $key,
836                                )
837                          if ref $_[2];
838                                                 return;
839                                         }
840                                 }
841                         }
842                         ## Make sure there are no leftovers.
843                         if(%values) {
844                 if($overflow_handler) {
845                     return $overflow_handler->check(+{%values});
846                 } else {
847                     $_[2]->{message} = "More values than Type Constraints!"
848                      if ref $_[2];
849                     return;
850                 }
851                         } elsif(%type_constraints) {
852                 $_[2]->{message} =
853                  "Not enough values for all defined type constraints.  Remaining: ". join(', ',values %values)
854                  if ref $_[2];
855                                 return;
856                         } else {
857                                 return 1;
858                         }
859                 },
860         )
861 );
862
863 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
864   MooseX::Meta::TypeConstraint::Structured->new(
865     name => "MooseX::Types::Structured::Map",
866     parent => find_type_constraint('HashRef'),
867     constraint_generator=> sub {
868       ## Get the constraints and values to check
869       my ($type_constraints, $values) = @_;
870       my @constraints = defined $type_constraints ? @$type_constraints : ();
871
872       Carp::confess( "too many args for Map type" ) if @constraints > 2;
873
874       my ($key_type, $value_type) = @constraints == 2 ? @constraints
875                                   : @constraints == 1 ? (undef, @constraints)
876                                   :                     ();
877
878       my %values = defined $values ? %$values: ();
879       ## Perform the checking
880       if ($value_type) {
881         for my $value (values %$values) {
882           unless ($value_type->check($value)) {
883             $_[2]->{message} = $value_type->get_message($value) if ref $_[2];
884             return;
885           }
886         }
887       }
888
889       if ($key_type) {
890         for my $key (keys %$values) {
891           unless ($key_type->check($key)) {
892             $_[2]->{message} = $key_type->get_message($key) if ref $_[2];
893             return;
894           }
895         }
896       }
897
898       return 1;
899     },
900   )
901 );
902
903 sub slurpy ($) {
904         my ($tc) = @_;
905         return MooseX::Types::Structured::OverflowHandler->new(
906         type_constraint => $tc,
907     );
908 }
909
910 =head1 SEE ALSO
911
912 The following modules or resources may be of interest.
913
914 L<Moose>, L<MooseX::Types>, L<Moose::Meta::TypeConstraint>,
915 L<MooseX::Meta::TypeConstraint::Structured>
916
917 =head1 TODO
918
919 Here's a list of stuff I would be happy to get volunteers helping with:
920
921         * All POD examples need test cases in t/documentation/*.t
922         * Want to break out the examples section to a separate cookbook style POD.
923         * Want more examples and best practice / usage guidance for authors
924         * Need to clarify deep coercions,
925
926 =head1 AUTHOR
927
928 John Napiorkowski <jjnapiork@cpan.org>
929
930 =head1 CONTRIBUTORS
931
932 The following people have contributed to this module and agree with the listed
933 Copyright & license information included below:
934
935     Florian Ragwitz, <rafl@debian.org>
936     Yuval Kogman, <nothingmuch@woobling.org>
937     Tomas Doran, <bobtfish@bobtfish.net>
938
939 =head1 COPYRIGHT & LICENSE
940
941 Copyright 2008-2009, John Napiorkowski <jjnapiork@cpan.org>
942
943 This program is free software; you can redistribute it and/or modify it under
944 the same terms as Perl itself.
945
946 =cut
947
948 1;