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