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