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