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