doc formatting and spelling fixes
[gitmo/MooseX-Types-Structured.git] / lib / MooseX / Types / Structured.pm
1 package MooseX::Types::Structured;
2 # ABSTRACT: 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.13;
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 C<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 C<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 C<ArrayRef> or C<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 C<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 C<HashRef> and C<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 its value to things like C<< ['hello', 111] >>  but C<< ['hello', 'world'] >>
141 would fail, as well as C<< ['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 C<Optional> type constraint, which is a variation of the
163 core Moose type constraint C<Maybe>.  The main difference is that C<Optional> type
164 constraints are required to validate if they exist, while C<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 C<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 C<HashRef> with the C<Dict> type constraint as in this example:
192
193     subtype FirstNameLastName,
194      as Dict[
195         firstname => Str,
196         lastname => Str,
197      ];
198
199 This would constrain a C<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 C<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 it's 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 set up 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 =for stopwords Subtyping
340
341 =head2 Subtyping a Structured type constraint
342
343 You need to exercise some care when you try to subtype a structured type as in
344 this example:
345
346     subtype Person,
347      as Dict[name => Str];
348
349     subtype FriendlyPerson,
350      as Person[
351         name => Str,
352         total_friends => Int,
353      ];
354
355 This will actually work BUT you have to take care that the subtype has a
356 structure that does not contradict the structure of it's parent.  For now the
357 above works, but I will clarify the syntax for this at a future point, so
358 it's recommended to avoid (should not really be needed so much anyway).  For
359 now this is supported in an EXPERIMENTAL way.  Your thoughts, test cases and
360 patches are welcomed for discussion.  If you find a good use for this, please
361 let me know.
362
363 =head2 Coercions
364
365 Coercions currently work for 'one level' deep.  That is you can do:
366
367     subtype Person,
368      as Dict[
369         name => Str,
370         age => Int
371     ];
372
373     subtype Fullname,
374      as Dict[
375         first => Str,
376         last => Str
377      ];
378
379     coerce Person,
380      ## Coerce an object of a particular class
381      from BlessedPersonObject, via {
382         +{
383             name=>$_->name,
384             age=>$_->age,
385         };
386      },
387
388      ## Coerce from [$name, $age]
389      from ArrayRef, via {
390         +{
391             name=>$_->[0],
392             age=>$_->[1],
393         },
394      },
395      ## Coerce from {fullname=>{first=>...,last=>...}, dob=>$DateTimeObject}
396      from Dict[fullname=>Fullname, dob=>DateTime], via {
397         my $age = $_->dob - DateTime->now;
398         my $firstn = $_->{fullname}->{first};
399         my $lastn = $_->{fullname}->{last}
400         +{
401             name => $_->{fullname}->{first} .' '. ,
402             age =>$age->years
403         }
404      };
405
406 And that should just work as expected.  However, if there are any 'inner'
407 coercions, such as a coercion on C<Fullname> or on C<DateTime>, that coercion
408 won't currently get activated.
409
410 Please see the test F<07-coerce.t> for a more detailed example.  Discussion on
411 extending coercions to support this welcome on the Moose development channel or
412 mailing list.
413
414 =head2 Recursion
415
416 Newer versions of L<MooseX::Types> support recursive type constraints.  That is
417 you can include a type constraint as a contained type constraint of itself.  For
418 example:
419
420     subtype Person,
421      as Dict[
422          name=>Str,
423          friends=>Optional[
424              ArrayRef[Person]
425          ],
426      ];
427
428 This would declare a C<Person> subtype that contains a name and an optional
429 C<ArrayRef> of C<Person>s who are friends as in:
430
431     {
432         name => 'Mike',
433         friends => [
434             { name => 'John' },
435             { name => 'Vincent' },
436             {
437                 name => 'Tracey',
438                 friends => [
439                     { name => 'Stephenie' },
440                     { name => 'Ilya' },
441                 ],
442             },
443         ],
444     };
445
446 Please take care to make sure the recursion node is either C<Optional>, or declare
447 a union with an non-recursive option such as:
448
449     subtype Value
450      as Tuple[
451          Str,
452          Str|Tuple,
453      ];
454
455 Which validates:
456
457     [
458         'Hello', [
459             'World', [
460                 'Is', [
461                     'Getting',
462                     'Old',
463                 ],
464             ],
465         ],
466     ];
467
468 Otherwise you will define a subtype that is impossible to validate since it is
469 infinitely recursive.  For more information about defining recursive types,
470 please see the documentation in L<MooseX::Types> and the test cases.
471
472 =head1 TYPE CONSTRAINTS
473
474 This type library defines the following constraints.
475
476 =head2 Tuple[@constraints]
477
478 This defines an ArrayRef based constraint which allows you to validate a specific
479 list of contained constraints.  For example:
480
481     Tuple[Int,Str]; ## Validates [1,'hello']
482     Tuple[Str|Object, Int]; ## Validates ['hello', 1] or [$object, 2]
483
484 The Values of @constraints should ideally be L<MooseX::Types> declared type
485 constraints.  We do support 'old style' L<Moose> string based constraints to a
486 limited degree but these string type constraints are considered deprecated.
487 There will be limited support for bugs resulting from mixing string and
488 L<MooseX::Types> in your structures.  If you encounter such a bug and really
489 need it fixed, we will required a detailed test case at the minimum.
490
491 =head2 Dict[%constraints]
492
493 This defines a HashRef based constraint which allowed you to validate a specific
494 hashref.  For example:
495
496     Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
497
498 The keys in C<%constraints> follow the same rules as C<@constraints> in the above
499 section.
500
501 =head2 Map[ $key_constraint, $value_constraint ]
502
503 This defines a C<HashRef>-based constraint in which both the keys and values are
504 required to meet certain constraints.  For example, to map hostnames to IP
505 addresses, you might say:
506
507   Map[ HostName, IPAddress ]
508
509 The type constraint would only be met if every key was a valid C<HostName> and
510 every value was a valid C<IPAddress>.
511
512 =head2 Optional[$constraint]
513
514 This is primarily a helper constraint for C<Dict> and C<Tuple> type constraints.  What
515 this allows is for you to assert that a given type constraint is allowed to be
516 null (but NOT undefined).  If the value is null, then the type constraint passes
517 but if the value is defined it must validate against the type constraint.  This
518 makes it easy to make a Dict where one or more of the keys doesn't have to exist
519 or a tuple where some of the values are not required.  For example:
520
521     subtype Name() => as Dict[
522         first=>Str,
523         last=>Str,
524         middle=>Optional[Str],
525     ];
526
527 ...creates a constraint that validates against a hashref with the keys 'first' and
528 'last' being strings and required while an optional key 'middle' is must be a
529 string if it appears but doesn't have to appear.  So in this case both the
530 following are valid:
531
532     {first=>'John', middle=>'James', last=>'Napiorkowski'}
533     {first=>'Vanessa', last=>'Li'}
534
535 If you use the C<Maybe> type constraint instead, your values will also validate
536 against C<undef>, which may be incorrect for you.
537
538 =head1 EXPORTABLE SUBROUTINES
539
540 This type library makes available for export the following subroutines
541
542 =for stopwords slurpy
543
544 =head2 slurpy
545
546 Structured type constraints by their nature are closed; that is validation will
547 depend on an exact match between your structure definition and the arguments to
548 be checked.  Sometimes you might wish for a slightly looser amount of validation.
549 For example, you may wish to validate the first 3 elements of an array reference
550 and allow for an arbitrary number of additional elements.  At first thought you
551 might think you could do it this way:
552
553     #  I want to validate stuff like: [1,"hello", $obj, 2,3,4,5,6,...]
554     subtype AllowTailingArgs,
555      as Tuple[
556        Int,
557        Str,
558        Object,
559        ArrayRef[Int],
560      ];
561
562 However what this will actually validate are structures like this:
563
564     [10,"Hello", $obj, [11,12,13,...] ]; # Notice element 4 is an ArrayRef
565
566 In order to allow structured validation of, "and then some", arguments, you can
567 use the L</slurpy> method against a type constraint.  For example:
568
569     use MooseX::Types::Structured qw(Tuple slurpy);
570
571     subtype AllowTailingArgs,
572      as Tuple[
573        Int,
574        Str,
575        Object,
576        slurpy ArrayRef[Int],
577      ];
578
579 This will now work as expected, validating ArrayRef structures such as:
580
581     [1,"hello", $obj, 2,3,4,5,6,...]
582
583 A few caveats apply.  First, the slurpy type constraint must be the last one in
584 the list of type constraint parameters.  Second, the parent type of the slurpy
585 type constraint must match that of the containing type constraint.  That means
586 that a C<Tuple> can allow a slurpy C<ArrayRef> (or children of C<ArrayRef>s, including
587 another C<Tuple>) and a C<Dict> can allow a slurpy C<HashRef> (or children/subtypes of
588 HashRef, also including other C<Dict> constraints).
589
590 Please note the technical way this works 'under the hood' is that the
591 slurpy keyword transforms the target type constraint into a coderef.  Please do
592 not try to create your own custom coderefs; always use the slurpy method.  The
593 underlying technology may change in the future but the slurpy keyword will be
594 supported.
595
596 =head1 ERROR MESSAGES
597
598 Error reporting has been improved to return more useful debugging messages. Now
599 I will stringify the incoming check value with L<Devel::PartialDump> so that you
600 can see the actual structure that is tripping up validation.  Also, I report the
601 'internal' validation error, so that if a particular element inside the
602 Structured Type is failing validation, you will see that.  There's a limit to
603 how deep this internal reporting goes, but you shouldn't see any of the "failed
604 with ARRAY(XXXXXX)" that we got with earlier versions of this module.
605
606 This support is continuing to expand, so it's best to use these messages for
607 debugging purposes and not for creating messages that 'escape into the wild'
608 such as error messages sent to the user.
609
610 Please see the test '12-error.t' for a more lengthy example.  Your thoughts and
611 preferable tests or code patches very welcome!
612
613 =head1 EXAMPLES
614
615 Here are some additional example usage for structured types.  All examples can
616 be found also in the 't/examples.t' test.  Your contributions are also welcomed.
617
618 =head2 Normalize a HashRef
619
620 You need a hashref to conform to a canonical structure but are required accept a
621 bunch of different incoming structures.  You can normalize using the C<Dict> type
622 constraint and coercions.  This example also shows structured types mixed which
623 other L<MooseX::Types> libraries.
624
625     package Test::MooseX::Meta::TypeConstraint::Structured::Examples::Normalize;
626
627     use Moose;
628     use DateTime;
629
630     use MooseX::Types::Structured qw(Dict Tuple);
631     use MooseX::Types::DateTime qw(DateTime);
632     use MooseX::Types::Moose qw(Int Str Object);
633     use MooseX::Types -declare => [qw(Name Age Person)];
634
635     subtype Person,
636      as Dict[
637          name=>Str,
638          age=>Int,
639      ];
640
641     coerce Person,
642      from Dict[
643          first=>Str,
644          last=>Str,
645          years=>Int,
646      ], via { +{
647         name => "$_->{first} $_->{last}",
648         age => $_->{years},
649      }},
650      from Dict[
651          fullname=>Dict[
652              last=>Str,
653              first=>Str,
654          ],
655          dob=>DateTime,
656      ],
657      ## DateTime needs to be inside of single quotes here to disambiguate the
658      ## class package from the DataTime type constraint imported via the
659      ## line "use MooseX::Types::DateTime qw(DateTime);"
660      via { +{
661         name => "$_->{fullname}{first} $_->{fullname}{last}",
662         age => ($_->{dob} - 'DateTime'->now)->years,
663      }};
664
665     has person => (is=>'rw', isa=>Person, coerce=>1);
666
667 And now you can instantiate with all the following:
668
669     __PACKAGE__->new(
670         person=>{
671             name=>'John Napiorkowski',
672             age=>39,
673         },
674     );
675
676     __PACKAGE__->new(
677         person=>{
678             first=>'John',
679             last=>'Napiorkowski',
680             years=>39,
681         },
682     );
683
684     __PACKAGE__->new(
685         person=>{
686             fullname => {
687                 first=>'John',
688                 last=>'Napiorkowski'
689             },
690             dob => 'DateTime'->new(
691                 year=>1969,
692                 month=>2,
693                 day=>13
694             ),
695         },
696     );
697
698 This technique is a way to support various ways to instantiate your class in a
699 clean and declarative way.
700
701 =cut
702
703 my $Optional = MooseX::Meta::TypeConstraint::Structured::Optional->new(
704     name => 'MooseX::Types::Structured::Optional',
705     package_defined_in => __PACKAGE__,
706     parent => find_type_constraint('Item'),
707     constraint => sub { 1 },
708     constraint_generator => sub {
709         my ($type_parameter, @args) = @_;
710         my $check = $type_parameter->_compiled_type_constraint();
711         return sub {
712             my (@args) = @_;
713             ## Does the arg exist?  Something exists if it's a 'real' value
714             ## or if it is set to undef.
715             if(exists($args[0])) {
716                 ## If it exists, we need to validate it
717                 $check->($args[0]);
718             } else {
719                 ## But it's is okay if the value doesn't exists
720                 return 1;
721             }
722         }
723     }
724 );
725
726 my $IsType = sub {
727     my ($obj, $type) = @_;
728
729     return $obj->can('equals')
730         ? $obj->equals($type)
731         : undef;
732 };
733
734 my $CompiledTC = sub {
735     my ($obj) = @_;
736
737     my $method = '_compiled_type_constraint';
738     return(
739           $obj->$IsType('Any')  ? undef
740         : $obj->can($method)    ? $obj->$method
741         :                         sub { $obj->check(shift) },
742     );
743 };
744
745 Moose::Util::TypeConstraints::register_type_constraint($Optional);
746 Moose::Util::TypeConstraints::add_parameterizable_type($Optional);
747
748 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
749     MooseX::Meta::TypeConstraint::Structured->new(
750         name => "MooseX::Types::Structured::Tuple" ,
751         parent => find_type_constraint('ArrayRef'),
752         constraint_generator=> sub {
753             ## Get the constraints and values to check
754             my ($self, $type_constraints) = @_;
755             $type_constraints ||= $self->type_constraints;
756             my @type_constraints = defined $type_constraints ?
757              @$type_constraints : ();
758
759             my $overflow_handler;
760             if($type_constraints[-1] && blessed $type_constraints[-1]
761               && $type_constraints[-1]->isa('MooseX::Types::Structured::OverflowHandler')) {
762                 $overflow_handler = pop @type_constraints;
763             }
764
765             my $length = $#type_constraints;
766             foreach my $idx (0..$length) {
767                 unless(blessed $type_constraints[$idx]) {
768                     ($type_constraints[$idx] = find_type_constraint($type_constraints[$idx]))
769                       || die "$type_constraints[$idx] is not a registered type";
770                 }
771             }
772
773             my (@checks, @optional, $o_check, $is_compiled);
774             return sub {
775                 my ($values, $err) = @_;
776                 my @values = defined $values ? @$values : ();
777
778                 ## initialise on first time run
779                 unless ($is_compiled) {
780                     @checks   = map { $_->$CompiledTC } @type_constraints;
781                     @optional = map { $_->is_subtype_of($Optional) } @type_constraints;
782                     $o_check  = $overflow_handler->$CompiledTC
783                         if $overflow_handler;
784                     $is_compiled++;
785                 }
786
787                 ## Perform the checking
788               VALUE:
789                 for my $type_index (0 .. $#checks) {
790
791                     my $type_constraint = $checks[ $type_index ];
792
793                     if(@values) {
794                         my $value = shift @values;
795
796                         next VALUE
797                             unless $type_constraint;
798
799                         unless($type_constraint->($value)) {
800                             if($err) {
801                                my $message = $type_constraints[ $type_index ]->validate($value,$err);
802                                $err->add_message({message=>$message,level=>$err->level});
803                             }
804                             return;
805                         }
806                     } else {
807                         ## Test if the TC supports null values
808                         unless ($optional[ $type_index ]) {
809                             if($err) {
810                                my $message = $type_constraints[ $type_index ]->get_message('NULL',$err);
811                                $err->add_message({message=>$message,level=>$err->level});
812                             }
813                             return;
814                         }
815                     }
816                 }
817
818                 ## Make sure there are no leftovers.
819                 if(@values) {
820                     if($overflow_handler) {
821                         return $o_check->([@values], $err);
822                     } else {
823                         if($err) {
824                             my $message = "More values than Type Constraints!";
825                             $err->add_message({message=>$message,level=>$err->level});
826                         }
827                         return;
828                     }
829                 } else {
830                     return 1;
831                 }
832             };
833         }
834     )
835 );
836
837 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
838     MooseX::Meta::TypeConstraint::Structured->new(
839         name => "MooseX::Types::Structured::Dict",
840         parent => find_type_constraint('HashRef'),
841         constraint_generator => sub {
842             ## Get the constraints and values to check
843             my ($self, $type_constraints) = @_;
844             $type_constraints = $self->type_constraints;
845             my @type_constraints = defined $type_constraints ?
846              @$type_constraints : ();
847
848             my $overflow_handler;
849             if($type_constraints[-1] && blessed $type_constraints[-1]
850               && $type_constraints[-1]->isa('MooseX::Types::Structured::OverflowHandler')) {
851                 $overflow_handler = pop @type_constraints;
852             }
853             my %type_constraints = @type_constraints;
854             foreach my $key (keys %type_constraints) {
855                 unless(blessed $type_constraints{$key}) {
856                     ($type_constraints{$key} = find_type_constraint($type_constraints{$key}))
857                       || die "$type_constraints{$key} is not a registered type";
858                 }
859             }
860
861             my (%check, %optional, $o_check, $is_compiled);
862             return sub {
863                 my ($values, $err) = @_;
864                 my %values = defined $values ? %$values: ();
865
866                 unless ($is_compiled) {
867                     %check    = map { ($_ => $type_constraints{ $_ }->$CompiledTC) } keys %type_constraints;
868                     %optional = map { ($_ => $type_constraints{ $_ }->is_subtype_of($Optional)) } keys %type_constraints;
869                     $o_check  = $overflow_handler->$CompiledTC
870                         if $overflow_handler;
871                     $is_compiled++;
872                 }
873
874                 ## Perform the checking
875               KEY:
876                 for my $key (keys %check) {
877                     my $type_constraint = $check{ $key };
878
879                     if(exists $values{$key}) {
880                         my $value = $values{$key};
881                         delete $values{$key};
882
883                         next KEY
884                             unless $type_constraint;
885
886                         unless($type_constraint->($value)) {
887                             if($err) {
888                                 my $message = $type_constraints{ $key }->validate($value,$err);
889                                 $err->add_message({message=>$message,level=>$err->level});
890                             }
891                             return;
892                         }
893                     } else {
894                         ## Test to see if the TC supports null values
895                         unless ($optional{ $key }) {
896                             if($err) {
897                                my $message = $type_constraints{ $key }->get_message('NULL',$err);
898                                $err->add_message({message=>$message,level=>$err->level});
899                             }
900                             return;
901                         }
902                     }
903                 }
904
905                 ## Make sure there are no leftovers.
906                 if(%values) {
907                     if($overflow_handler) {
908                         return $o_check->(+{%values});
909                     } else {
910                         if($err) {
911                             my $message = "More values than Type Constraints!";
912                             $err->add_message({message=>$message,level=>$err->level});
913                         }
914                         return;
915                     }
916                 } else {
917                     return 1;
918                 }
919             }
920         },
921     )
922 );
923
924 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
925   MooseX::Meta::TypeConstraint::Structured->new(
926     name => "MooseX::Types::Structured::Map",
927     parent => find_type_constraint('HashRef'),
928     constraint_generator=> sub {
929       ## Get the constraints and values to check
930       my ($self, $type_constraints) = @_;
931       $type_constraints = $self->type_constraints;
932       my @constraints = defined $type_constraints ? @$type_constraints : ();
933
934       Carp::confess( "too many args for Map type" ) if @constraints > 2;
935
936       my ($key_type, $value_type) = @constraints == 2 ? @constraints
937                                   : @constraints == 1 ? (undef, @constraints)
938                                   :                     ();
939
940       my ($key_check, $value_check, $is_compiled);
941       return sub {
942           my ($values, $err) = @_;
943           my %values = defined $values ? %$values: ();
944
945           unless ($is_compiled) {
946               ($key_check, $value_check)
947                 = map { $_ ? $_->$CompiledTC : undef }
948                       $key_type, $value_type;
949               $is_compiled++;
950           }
951
952           ## Perform the checking
953           if ($value_check) {
954             for my $value (values %$values) {
955               unless ($value_check->($value)) {
956                 if($err) {
957                   my $message = $value_type->validate($value,$err);
958                   $err->add_message({message=>$message,level=>$err->level});
959                 }
960                 return;
961               }
962             }
963           }
964           if ($key_check) {
965             for my $key (keys %$values) {
966               unless ($key_check->($key)) {
967                 if($err) {
968                   my $message = $key_type->validate($key,$err);
969                   $err->add_message({message=>$message,level=>$err->level});
970                 }
971                 return;
972               }
973             }
974           }
975
976           return 1;
977       };
978     },
979   )
980 );
981
982 sub slurpy ($) {
983     my ($tc) = @_;
984     return MooseX::Types::Structured::OverflowHandler->new(
985         type_constraint => $tc,
986     );
987 }
988
989 =head1 SEE ALSO
990
991 The following modules or resources may be of interest.
992
993 L<Moose>, L<MooseX::Types>, L<Moose::Meta::TypeConstraint>,
994 L<MooseX::Meta::TypeConstraint::Structured>
995
996 =cut
997
998 1;