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