File Coverage

blib/lib/MooseX/Types/Structured.pm
Criterion Covered Total %
statement 37 37 100.0
branch n/a
condition n/a
subroutine 13 13 100.0
pod 1 1 100.0
total 51 51 100.0


line stmt bran cond sub pod time code
1             package MooseX::Types::Structured; # git description: v0.35-8-gc2cf3da
2             # ABSTRACT: Structured Type Constraints for Moose
3              
4             our $VERSION = '0.36';
5              
6 19     19   6367571 use 5.008;
  19         72  
7 19     19   2360 use Moose::Util::TypeConstraints 1.06 'find_type_constraint';
  19         991582  
  19         139  
8 19     19   15385 use MooseX::Meta::TypeConstraint::Structured;
  19         12202  
  19         718  
9 19     19   8699 use MooseX::Meta::TypeConstraint::Structured::Optional;
  19         77  
  19         641  
10 19     19   6859 use MooseX::Types::Structured::OverflowHandler;
  19         72  
  19         641  
11 19     19   6665 use MooseX::Types::Structured::MessageStack;
  19         77  
  19         748  
12 19     19   157 use Devel::PartialDump 0.13;
  19         522  
  19         153  
13 19     19   4403 use Scalar::Util qw(blessed);
  19         44  
  19         1212  
14 19     19   119 use namespace::clean 0.19;
  19         306  
  19         150  
15 19     19   12132 use MooseX::Types 0.22 -declare => [qw(Dict Map Tuple Optional)];
  19         466344  
  19         156  
16 19         149 use Sub::Exporter 0.982 -setup => {
17             exports => [ qw(Dict Map Tuple Optional slurpy) ],
18 19     19   107282 };
  19         292  
19 19         302 use if MooseX::Types->VERSION >= 0.42,
20 19     19   8246 'namespace::autoclean' => -except => 'import'; # TODO: https://github.com/rjbs/Sub-Exporter/issues/8
  19         47  
21              
22             #pod =head1 SYNOPSIS
23             #pod
24             #pod The following is example usage for this module.
25             #pod
26             #pod package Person;
27             #pod
28             #pod use Moose;
29             #pod use MooseX::Types::Moose qw(Str Int HashRef);
30             #pod use MooseX::Types::Structured qw(Dict Tuple Optional);
31             #pod
32             #pod ## A name has a first and last part, but middle names are not required
33             #pod has name => (
34             #pod isa=>Dict[
35             #pod first => Str,
36             #pod last => Str,
37             #pod middle => Optional[Str],
38             #pod ],
39             #pod );
40             #pod
41             #pod ## description is a string field followed by a HashRef of tagged data.
42             #pod has description => (
43             #pod isa=>Tuple[
44             #pod Str,
45             #pod Optional[HashRef],
46             #pod ],
47             #pod );
48             #pod
49             #pod ## Remainder of your class attributes and methods
50             #pod
51             #pod Then you can instantiate this class with something like:
52             #pod
53             #pod my $john = Person->new(
54             #pod name => {
55             #pod first => 'John',
56             #pod middle => 'James'
57             #pod last => 'Napiorkowski',
58             #pod },
59             #pod description => [
60             #pod 'A cool guy who loves Perl and Moose.', {
61             #pod married_to => 'Vanessa Li',
62             #pod born_in => 'USA',
63             #pod };
64             #pod ]
65             #pod );
66             #pod
67             #pod Or with:
68             #pod
69             #pod my $vanessa = Person->new(
70             #pod name => {
71             #pod first => 'Vanessa',
72             #pod last => 'Li'
73             #pod },
74             #pod description => ['A great student!'],
75             #pod );
76             #pod
77             #pod But all of these would cause a constraint error for the C<name> attribute:
78             #pod
79             #pod ## Value for 'name' not a HashRef
80             #pod Person->new( name => 'John' );
81             #pod
82             #pod ## Value for 'name' has incorrect hash key and missing required keys
83             #pod Person->new( name => {
84             #pod first_name => 'John'
85             #pod });
86             #pod
87             #pod ## Also incorrect keys
88             #pod Person->new( name => {
89             #pod first_name => 'John',
90             #pod age => 39,
91             #pod });
92             #pod
93             #pod ## key 'middle' incorrect type, should be a Str not a ArrayRef
94             #pod Person->new( name => {
95             #pod first => 'Vanessa',
96             #pod middle => [1,2],
97             #pod last => 'Li',
98             #pod });
99             #pod
100             #pod And these would cause a constraint error for the C<description> attribute:
101             #pod
102             #pod ## Should be an ArrayRef
103             #pod Person->new( description => 'Hello I am a String' );
104             #pod
105             #pod ## First element must be a string not a HashRef.
106             #pod Person->new (description => [{
107             #pod tag1 => 'value1',
108             #pod tag2 => 'value2'
109             #pod }]);
110             #pod
111             #pod Please see the test cases for more examples.
112             #pod
113             #pod =head1 DESCRIPTION
114             #pod
115             #pod A structured type constraint is a standard container L<Moose> type constraint,
116             #pod such as an C<ArrayRef> or C<HashRef>, which has been enhanced to allow you to
117             #pod explicitly name all the allowed type constraints inside the structure. The
118             #pod generalized form is:
119             #pod
120             #pod TypeConstraint[@TypeParameters or %TypeParameters]
121             #pod
122             #pod Where C<TypeParameters> is an array reference or hash references of
123             #pod L<Moose::Meta::TypeConstraint> objects.
124             #pod
125             #pod This type library enables structured type constraints. It is built on top of the
126             #pod L<MooseX::Types> library system, so you should review the documentation for that
127             #pod if you are not familiar with it.
128             #pod
129             #pod =head2 Comparing Parameterized types to Structured types
130             #pod
131             #pod Parameterized constraints are built into core Moose and you are probably already
132             #pod familiar with the type constraints C<HashRef> and C<ArrayRef>. Structured types
133             #pod have similar functionality, so their syntax is likewise similar. For example,
134             #pod you could define a parameterized constraint like:
135             #pod
136             #pod subtype ArrayOfInts,
137             #pod as ArrayRef[Int];
138             #pod
139             #pod which would constrain a value to something like [1,2,3,...] and so on. On the
140             #pod other hand, a structured type constraint explicitly names all it's allowed
141             #pod 'internal' type parameter constraints. For the example:
142             #pod
143             #pod subtype StringFollowedByInt,
144             #pod as Tuple[Str,Int];
145             #pod
146             #pod would constrain its value to things like C<< ['hello', 111] >> but C<< ['hello', 'world'] >>
147             #pod would fail, as well as C<< ['hello', 111, 'world'] >> and so on. Here's another
148             #pod example:
149             #pod
150             #pod package MyApp::Types;
151             #pod
152             #pod use MooseX::Types -declare [qw(StringIntOptionalHashRef)];
153             #pod use MooseX::Types::Moose qw(Str Int);
154             #pod use MooseX::Types::Structured qw(Tuple Optional);
155             #pod
156             #pod subtype StringIntOptionalHashRef,
157             #pod as Tuple[
158             #pod Str, Int,
159             #pod Optional[HashRef]
160             #pod ];
161             #pod
162             #pod This defines a type constraint that validates values like:
163             #pod
164             #pod ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
165             #pod ['World', 200];
166             #pod
167             #pod Notice that the last type constraint in the structure is optional. This is
168             #pod enabled via the helper C<Optional> type constraint, which is a variation of the
169             #pod core Moose type constraint C<Maybe>. The main difference is that C<Optional> type
170             #pod constraints are required to validate if they exist, while C<Maybe> permits
171             #pod undefined values. So the following example would not validate:
172             #pod
173             #pod StringIntOptionalHashRef->validate(['Hello Undefined', 1000, undef]);
174             #pod
175             #pod Please note the subtle difference between undefined and null. If you wish to
176             #pod allow both null and undefined, you should use the core Moose C<Maybe> type
177             #pod constraint instead:
178             #pod
179             #pod package MyApp::Types;
180             #pod
181             #pod use MooseX::Types -declare [qw(StringIntMaybeHashRef)];
182             #pod use MooseX::Types::Moose qw(Str Int Maybe);
183             #pod use MooseX::Types::Structured qw(Tuple);
184             #pod
185             #pod subtype StringIntMaybeHashRef,
186             #pod as Tuple[
187             #pod Str, Int, Maybe[HashRef]
188             #pod ];
189             #pod
190             #pod This would validate the following:
191             #pod
192             #pod ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
193             #pod ['World', 200, undef];
194             #pod ['World', 200];
195             #pod
196             #pod Structured constraints are not limited to arrays. You can define a structure
197             #pod against a C<HashRef> with the C<Dict> type constraint as in this example:
198             #pod
199             #pod subtype FirstNameLastName,
200             #pod as Dict[
201             #pod firstname => Str,
202             #pod lastname => Str,
203             #pod ];
204             #pod
205             #pod This would constrain a C<HashRef> that validates something like:
206             #pod
207             #pod {firstname => 'Christopher', lastname => 'Parsons'};
208             #pod
209             #pod but all the following would fail validation:
210             #pod
211             #pod ## Incorrect keys
212             #pod {first => 'Christopher', last => 'Parsons'};
213             #pod
214             #pod ## Too many keys
215             #pod {firstname => 'Christopher', lastname => 'Parsons', middlename => 'Allen'};
216             #pod
217             #pod ## Not a HashRef
218             #pod ['Christopher', 'Parsons'];
219             #pod
220             #pod These structures can be as simple or elaborate as you wish. You can even
221             #pod combine various structured, parameterized and simple constraints all together:
222             #pod
223             #pod subtype Crazy,
224             #pod as Tuple[
225             #pod Int,
226             #pod Dict[name=>Str, age=>Int],
227             #pod ArrayRef[Int]
228             #pod ];
229             #pod
230             #pod Which would match:
231             #pod
232             #pod [1, {name=>'John', age=>25},[10,11,12]];
233             #pod
234             #pod Please notice how the type parameters can be visually arranged to your liking
235             #pod and to improve the clarity of your meaning. You don't need to run then
236             #pod altogether onto a single line. Additionally, since the C<Dict> type constraint
237             #pod defines a hash constraint, the key order is not meaningful. For example:
238             #pod
239             #pod subtype AnyKeyOrder,
240             #pod as Dict[
241             #pod key1=>Int,
242             #pod key2=>Str,
243             #pod key3=>Int,
244             #pod ];
245             #pod
246             #pod Would validate both:
247             #pod
248             #pod {key1 => 1, key2 => "Hi!", key3 => 2};
249             #pod {key2 => "Hi!", key1 => 100, key3 => 300};
250             #pod
251             #pod As you would expect, since underneath it's just a plain old Perl hash at work.
252             #pod
253             #pod =head2 Alternatives
254             #pod
255             #pod You should exercise some care as to whether or not your complex structured
256             #pod constraints would be better off contained by a real object as in the following
257             #pod example:
258             #pod
259             #pod package MyApp::MyStruct;
260             #pod use Moose;
261             #pod
262             #pod ## lazy way to make a bunch of attributes
263             #pod has $_ for qw(full_name age_in_years);
264             #pod
265             #pod package MyApp::MyClass;
266             #pod use Moose;
267             #pod
268             #pod has person => (isa => 'MyApp::MyStruct');
269             #pod
270             #pod my $instance = MyApp::MyClass->new(
271             #pod person=>MyApp::MyStruct->new(
272             #pod full_name => 'John',
273             #pod age_in_years => 39,
274             #pod ),
275             #pod );
276             #pod
277             #pod This method may take some additional time to set up but will give you more
278             #pod flexibility. However, structured constraints are highly compatible with this
279             #pod method, granting some interesting possibilities for coercion. Try:
280             #pod
281             #pod package MyApp::MyClass;
282             #pod
283             #pod use Moose;
284             #pod use MyApp::MyStruct;
285             #pod
286             #pod ## It's recommended your type declarations live in a separate class in order
287             #pod ## to promote reusability and clarity. Inlined here for brevity.
288             #pod
289             #pod use MooseX::Types::DateTime qw(DateTime);
290             #pod use MooseX::Types -declare [qw(MyStruct)];
291             #pod use MooseX::Types::Moose qw(Str Int);
292             #pod use MooseX::Types::Structured qw(Dict);
293             #pod
294             #pod ## Use class_type to create an ISA type constraint if your object doesn't
295             #pod ## inherit from Moose::Object.
296             #pod class_type 'MyApp::MyStruct';
297             #pod
298             #pod ## Just a shorter version really.
299             #pod subtype MyStruct,
300             #pod as 'MyApp::MyStruct';
301             #pod
302             #pod ## Add the coercions.
303             #pod coerce MyStruct,
304             #pod from Dict[
305             #pod full_name=>Str,
306             #pod age_in_years=>Int
307             #pod ], via {
308             #pod MyApp::MyStruct->new(%$_);
309             #pod },
310             #pod from Dict[
311             #pod lastname=>Str,
312             #pod firstname=>Str,
313             #pod dob=>DateTime
314             #pod ], via {
315             #pod my $name = $_->{firstname} .' '. $_->{lastname};
316             #pod my $age = DateTime->now - $_->{dob};
317             #pod
318             #pod MyApp::MyStruct->new(
319             #pod full_name=>$name,
320             #pod age_in_years=>$age->years,
321             #pod );
322             #pod };
323             #pod
324             #pod has person => (isa=>MyStruct);
325             #pod
326             #pod This would allow you to instantiate with something like:
327             #pod
328             #pod my $obj = MyApp::MyClass->new( person => {
329             #pod full_name=>'John Napiorkowski',
330             #pod age_in_years=>39,
331             #pod });
332             #pod
333             #pod Or even:
334             #pod
335             #pod my $obj = MyApp::MyClass->new( person => {
336             #pod lastname=>'John',
337             #pod firstname=>'Napiorkowski',
338             #pod dob=>DateTime->new(year=>1969),
339             #pod });
340             #pod
341             #pod If you are not familiar with how coercions work, check out the L<Moose> cookbook
342             #pod entry L<Moose::Cookbook::Recipe5> for an explanation. The section L</Coercions>
343             #pod has additional examples and discussion.
344             #pod
345             #pod =for stopwords Subtyping
346             #pod
347             #pod =head2 Subtyping a Structured type constraint
348             #pod
349             #pod You need to exercise some care when you try to subtype a structured type as in
350             #pod this example:
351             #pod
352             #pod subtype Person,
353             #pod as Dict[name => Str];
354             #pod
355             #pod subtype FriendlyPerson,
356             #pod as Person[
357             #pod name => Str,
358             #pod total_friends => Int,
359             #pod ];
360             #pod
361             #pod This will actually work BUT you have to take care that the subtype has a
362             #pod structure that does not contradict the structure of it's parent. For now the
363             #pod above works, but I will clarify the syntax for this at a future point, so
364             #pod it's recommended to avoid (should not really be needed so much anyway). For
365             #pod now this is supported in an EXPERIMENTAL way. Your thoughts, test cases and
366             #pod patches are welcomed for discussion. If you find a good use for this, please
367             #pod let me know.
368             #pod
369             #pod =head2 Coercions
370             #pod
371             #pod Coercions currently work for 'one level' deep. That is you can do:
372             #pod
373             #pod subtype Person,
374             #pod as Dict[
375             #pod name => Str,
376             #pod age => Int
377             #pod ];
378             #pod
379             #pod subtype Fullname,
380             #pod as Dict[
381             #pod first => Str,
382             #pod last => Str
383             #pod ];
384             #pod
385             #pod coerce Person,
386             #pod ## Coerce an object of a particular class
387             #pod from BlessedPersonObject, via {
388             #pod +{
389             #pod name=>$_->name,
390             #pod age=>$_->age,
391             #pod };
392             #pod },
393             #pod
394             #pod ## Coerce from [$name, $age]
395             #pod from ArrayRef, via {
396             #pod +{
397             #pod name=>$_->[0],
398             #pod age=>$_->[1],
399             #pod },
400             #pod },
401             #pod ## Coerce from {fullname=>{first=>...,last=>...}, dob=>$DateTimeObject}
402             #pod from Dict[fullname=>Fullname, dob=>DateTime], via {
403             #pod my $age = $_->dob - DateTime->now;
404             #pod my $firstn = $_->{fullname}->{first};
405             #pod my $lastn = $_->{fullname}->{last}
406             #pod +{
407             #pod name => $_->{fullname}->{first} .' '. ,
408             #pod age =>$age->years
409             #pod }
410             #pod };
411             #pod
412             #pod And that should just work as expected. However, if there are any 'inner'
413             #pod coercions, such as a coercion on C<Fullname> or on C<DateTime>, that coercion
414             #pod won't currently get activated.
415             #pod
416             #pod Please see the test F<07-coerce.t> for a more detailed example. Discussion on
417             #pod extending coercions to support this welcome on the Moose development channel or
418             #pod mailing list.
419             #pod
420             #pod =head2 Recursion
421             #pod
422             #pod Newer versions of L<MooseX::Types> support recursive type constraints. That is
423             #pod you can include a type constraint as a contained type constraint of itself. For
424             #pod example:
425             #pod
426             #pod subtype Person,
427             #pod as Dict[
428             #pod name=>Str,
429             #pod friends=>Optional[
430             #pod ArrayRef[Person]
431             #pod ],
432             #pod ];
433             #pod
434             #pod This would declare a C<Person> subtype that contains a name and an optional
435             #pod C<ArrayRef> of C<Person>s who are friends as in:
436             #pod
437             #pod {
438             #pod name => 'Mike',
439             #pod friends => [
440             #pod { name => 'John' },
441             #pod { name => 'Vincent' },
442             #pod {
443             #pod name => 'Tracey',
444             #pod friends => [
445             #pod { name => 'Stephenie' },
446             #pod { name => 'Ilya' },
447             #pod ],
448             #pod },
449             #pod ],
450             #pod };
451             #pod
452             #pod Please take care to make sure the recursion node is either C<Optional>, or declare
453             #pod a union with an non-recursive option such as:
454             #pod
455             #pod subtype Value
456             #pod as Tuple[
457             #pod Str,
458             #pod Str|Tuple,
459             #pod ];
460             #pod
461             #pod Which validates:
462             #pod
463             #pod [
464             #pod 'Hello', [
465             #pod 'World', [
466             #pod 'Is', [
467             #pod 'Getting',
468             #pod 'Old',
469             #pod ],
470             #pod ],
471             #pod ],
472             #pod ];
473             #pod
474             #pod Otherwise you will define a subtype that is impossible to validate since it is
475             #pod infinitely recursive. For more information about defining recursive types,
476             #pod please see the documentation in L<MooseX::Types> and the test cases.
477             #pod
478             #pod =head1 TYPE CONSTRAINTS
479             #pod
480             #pod This type library defines the following constraints.
481             #pod
482             #pod =head2 Tuple[@constraints]
483             #pod
484             #pod This defines an ArrayRef based constraint which allows you to validate a specific
485             #pod list of contained constraints. For example:
486             #pod
487             #pod Tuple[Int,Str]; ## Validates [1,'hello']
488             #pod Tuple[Str|Object, Int]; ## Validates ['hello', 1] or [$object, 2]
489             #pod
490             #pod The Values of @constraints should ideally be L<MooseX::Types> declared type
491             #pod constraints. We do support 'old style' L<Moose> string based constraints to a
492             #pod limited degree but these string type constraints are considered deprecated.
493             #pod There will be limited support for bugs resulting from mixing string and
494             #pod L<MooseX::Types> in your structures. If you encounter such a bug and really
495             #pod need it fixed, we will required a detailed test case at the minimum.
496             #pod
497             #pod =head2 Dict[%constraints]
498             #pod
499             #pod This defines a HashRef based constraint which allowed you to validate a specific
500             #pod hashref. For example:
501             #pod
502             #pod Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
503             #pod
504             #pod The keys in C<%constraints> follow the same rules as C<@constraints> in the above
505             #pod section.
506             #pod
507             #pod =head2 Map[ $key_constraint, $value_constraint ]
508             #pod
509             #pod This defines a C<HashRef>-based constraint in which both the keys and values are
510             #pod required to meet certain constraints. For example, to map hostnames to IP
511             #pod addresses, you might say:
512             #pod
513             #pod Map[ HostName, IPAddress ]
514             #pod
515             #pod The type constraint would only be met if every key was a valid C<HostName> and
516             #pod every value was a valid C<IPAddress>.
517             #pod
518             #pod =head2 Optional[$constraint]
519             #pod
520             #pod This is primarily a helper constraint for C<Dict> and C<Tuple> type constraints. What
521             #pod this allows is for you to assert that a given type constraint is allowed to be
522             #pod null (but NOT undefined). If the value is null, then the type constraint passes
523             #pod but if the value is defined it must validate against the type constraint. This
524             #pod makes it easy to make a Dict where one or more of the keys doesn't have to exist
525             #pod or a tuple where some of the values are not required. For example:
526             #pod
527             #pod subtype Name() => as Dict[
528             #pod first=>Str,
529             #pod last=>Str,
530             #pod middle=>Optional[Str],
531             #pod ];
532             #pod
533             #pod ...creates a constraint that validates against a hashref with the keys 'first' and
534             #pod 'last' being strings and required while an optional key 'middle' is must be a
535             #pod string if it appears but doesn't have to appear. So in this case both the
536             #pod following are valid:
537             #pod
538             #pod {first=>'John', middle=>'James', last=>'Napiorkowski'}
539             #pod {first=>'Vanessa', last=>'Li'}
540             #pod
541             #pod If you use the C<Maybe> type constraint instead, your values will also validate
542             #pod against C<undef>, which may be incorrect for you.
543             #pod
544             #pod =head1 EXPORTABLE SUBROUTINES
545             #pod
546             #pod This type library makes available for export the following subroutines
547             #pod
548             #pod =for stopwords slurpy
549             #pod
550             #pod =head2 slurpy
551             #pod
552             #pod Structured type constraints by their nature are closed; that is validation will
553             #pod depend on an exact match between your structure definition and the arguments to
554             #pod be checked. Sometimes you might wish for a slightly looser amount of validation.
555             #pod For example, you may wish to validate the first 3 elements of an array reference
556             #pod and allow for an arbitrary number of additional elements. At first thought you
557             #pod might think you could do it this way:
558             #pod
559             #pod # I want to validate stuff like: [1,"hello", $obj, 2,3,4,5,6,...]
560             #pod subtype AllowTailingArgs,
561             #pod as Tuple[
562             #pod Int,
563             #pod Str,
564             #pod Object,
565             #pod ArrayRef[Int],
566             #pod ];
567             #pod
568             #pod However what this will actually validate are structures like this:
569             #pod
570             #pod [10,"Hello", $obj, [11,12,13,...] ]; # Notice element 4 is an ArrayRef
571             #pod
572             #pod In order to allow structured validation of, "and then some", arguments, you can
573             #pod use the L</slurpy> method against a type constraint. For example:
574             #pod
575             #pod use MooseX::Types::Structured qw(Tuple slurpy);
576             #pod
577             #pod subtype AllowTailingArgs,
578             #pod as Tuple[
579             #pod Int,
580             #pod Str,
581             #pod Object,
582             #pod slurpy ArrayRef[Int],
583             #pod ];
584             #pod
585             #pod This will now work as expected, validating ArrayRef structures such as:
586             #pod
587             #pod [1,"hello", $obj, 2,3,4,5,6,...]
588             #pod
589             #pod A few caveats apply. First, the slurpy type constraint must be the last one in
590             #pod the list of type constraint parameters. Second, the parent type of the slurpy
591             #pod type constraint must match that of the containing type constraint. That means
592             #pod that a C<Tuple> can allow a slurpy C<ArrayRef> (or children of C<ArrayRef>s, including
593             #pod another C<Tuple>) and a C<Dict> can allow a slurpy C<HashRef> (or children/subtypes of
594             #pod HashRef, also including other C<Dict> constraints).
595             #pod
596             #pod Please note the technical way this works 'under the hood' is that the
597             #pod slurpy keyword transforms the target type constraint into a coderef. Please do
598             #pod not try to create your own custom coderefs; always use the slurpy method. The
599             #pod underlying technology may change in the future but the slurpy keyword will be
600             #pod supported.
601             #pod
602             #pod =head1 ERROR MESSAGES
603             #pod
604             #pod Error reporting has been improved to return more useful debugging messages. Now
605             #pod I will stringify the incoming check value with L<Devel::PartialDump> so that you
606             #pod can see the actual structure that is tripping up validation. Also, I report the
607             #pod 'internal' validation error, so that if a particular element inside the
608             #pod Structured Type is failing validation, you will see that. There's a limit to
609             #pod how deep this internal reporting goes, but you shouldn't see any of the "failed
610             #pod with ARRAY(XXXXXX)" that we got with earlier versions of this module.
611             #pod
612             #pod This support is continuing to expand, so it's best to use these messages for
613             #pod debugging purposes and not for creating messages that 'escape into the wild'
614             #pod such as error messages sent to the user.
615             #pod
616             #pod Please see the test '12-error.t' for a more lengthy example. Your thoughts and
617             #pod preferable tests or code patches very welcome!
618             #pod
619             #pod =head1 EXAMPLES
620             #pod
621             #pod Here are some additional example usage for structured types. All examples can
622             #pod be found also in the 't/examples.t' test. Your contributions are also welcomed.
623             #pod
624             #pod =head2 Normalize a HashRef
625             #pod
626             #pod You need a hashref to conform to a canonical structure but are required accept a
627             #pod bunch of different incoming structures. You can normalize using the C<Dict> type
628             #pod constraint and coercions. This example also shows structured types mixed which
629             #pod other L<MooseX::Types> libraries.
630             #pod
631             #pod package Test::MooseX::Meta::TypeConstraint::Structured::Examples::Normalize;
632             #pod
633             #pod use Moose;
634             #pod use DateTime;
635             #pod
636             #pod use MooseX::Types::Structured qw(Dict Tuple);
637             #pod use MooseX::Types::DateTime qw(DateTime);
638             #pod use MooseX::Types::Moose qw(Int Str Object);
639             #pod use MooseX::Types -declare => [qw(Name Age Person)];
640             #pod
641             #pod subtype Person,
642             #pod as Dict[
643             #pod name=>Str,
644             #pod age=>Int,
645             #pod ];
646             #pod
647             #pod coerce Person,
648             #pod from Dict[
649             #pod first=>Str,
650             #pod last=>Str,
651             #pod years=>Int,
652             #pod ], via { +{
653             #pod name => "$_->{first} $_->{last}",
654             #pod age => $_->{years},
655             #pod }},
656             #pod from Dict[
657             #pod fullname=>Dict[
658             #pod last=>Str,
659             #pod first=>Str,
660             #pod ],
661             #pod dob=>DateTime,
662             #pod ],
663             #pod ## DateTime needs to be inside of single quotes here to disambiguate the
664             #pod ## class package from the DataTime type constraint imported via the
665             #pod ## line "use MooseX::Types::DateTime qw(DateTime);"
666             #pod via { +{
667             #pod name => "$_->{fullname}{first} $_->{fullname}{last}",
668             #pod age => ($_->{dob} - 'DateTime'->now)->years,
669             #pod }};
670             #pod
671             #pod has person => (is=>'rw', isa=>Person, coerce=>1);
672             #pod
673             #pod And now you can instantiate with all the following:
674             #pod
675             #pod __PACKAGE__->new(
676             #pod person=>{
677             #pod name=>'John Napiorkowski',
678             #pod age=>39,
679             #pod },
680             #pod );
681             #pod
682             #pod __PACKAGE__->new(
683             #pod person=>{
684             #pod first=>'John',
685             #pod last=>'Napiorkowski',
686             #pod years=>39,
687             #pod },
688             #pod );
689             #pod
690             #pod __PACKAGE__->new(
691             #pod person=>{
692             #pod fullname => {
693             #pod first=>'John',
694             #pod last=>'Napiorkowski'
695             #pod },
696             #pod dob => 'DateTime'->new(
697             #pod year=>1969,
698             #pod month=>2,
699             #pod day=>13
700             #pod ),
701             #pod },
702             #pod );
703             #pod
704             #pod This technique is a way to support various ways to instantiate your class in a
705             #pod clean and declarative way.
706             #pod
707             #pod =cut
708              
709             my $Optional = MooseX::Meta::TypeConstraint::Structured::Optional->new(
710             name => 'MooseX::Types::Structured::Optional',
711             package_defined_in => __PACKAGE__,
712             parent => find_type_constraint('Item'),
713             constraint => sub { 1 },
714             constraint_generator => sub {
715             my ($type_parameter, @args) = @_;
716             my $check = $type_parameter->_compiled_type_constraint();
717             return sub {
718             my (@args) = @_;
719             ## Does the arg exist? Something exists if it's a 'real' value
720             ## or if it is set to undef.
721             if(exists($args[0])) {
722             ## If it exists, we need to validate it
723             $check->($args[0]);
724             } else {
725             ## But it's is okay if the value doesn't exists
726             return 1;
727             }
728             }
729             }
730             );
731              
732             my $IsType = sub {
733             my ($obj, $type) = @_;
734              
735             return $obj->can('equals')
736             ? $obj->equals($type)
737             : undef;
738             };
739              
740             my $CompiledTC = sub {
741             my ($obj) = @_;
742              
743             my $method = '_compiled_type_constraint';
744             return(
745             $obj->$IsType('Any') ? undef
746             : $obj->can($method) ? $obj->$method
747             : sub { $obj->check(shift) },
748             );
749             };
750              
751             Moose::Util::TypeConstraints::register_type_constraint($Optional);
752             Moose::Util::TypeConstraints::add_parameterizable_type($Optional);
753              
754             Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
755             MooseX::Meta::TypeConstraint::Structured->new(
756             name => "MooseX::Types::Structured::Tuple" ,
757             parent => find_type_constraint('ArrayRef'),
758             constraint_generator=> sub {
759             ## Get the constraints and values to check
760             my ($self, $type_constraints) = @_;
761             $type_constraints ||= $self->type_constraints;
762             my @type_constraints = defined $type_constraints ?
763             @$type_constraints : ();
764              
765             my $overflow_handler;
766             if($type_constraints[-1] && blessed $type_constraints[-1]
767             && $type_constraints[-1]->isa('MooseX::Types::Structured::OverflowHandler')) {
768             $overflow_handler = pop @type_constraints;
769             }
770              
771             my $length = $#type_constraints;
772             foreach my $idx (0..$length) {
773             unless(blessed $type_constraints[$idx]) {
774             ($type_constraints[$idx] = find_type_constraint($type_constraints[$idx]))
775             || die "$type_constraints[$idx] is not a registered type";
776             }
777             }
778              
779             my (@checks, @optional, $o_check, $is_compiled);
780             return sub {
781             my ($values, $err) = @_;
782             my @values = defined $values ? @$values : ();
783              
784             ## initialise on first time run
785             unless ($is_compiled) {
786             @checks = map { $_->$CompiledTC } @type_constraints;
787             @optional = map { $_->is_subtype_of($Optional) } @type_constraints;
788             $o_check = $overflow_handler->$CompiledTC
789             if $overflow_handler;
790             $is_compiled++;
791             }
792              
793             ## Perform the checking
794             VALUE:
795             for my $type_index (0 .. $#checks) {
796              
797             my $type_constraint = $checks[ $type_index ];
798              
799             if(@values) {
800             my $value = shift @values;
801              
802             next VALUE
803             unless $type_constraint;
804              
805             unless($type_constraint->($value)) {
806             if($err) {
807             my $message = $type_constraints[ $type_index ]->validate($value,$err);
808             $err->add_message({message=>$message,level=>$err->level});
809             }
810             return;
811             }
812             } else {
813             ## Test if the TC supports null values
814             unless ($optional[ $type_index ]) {
815             if($err) {
816             my $message = $type_constraints[ $type_index ]->get_message('NULL',$err);
817             $err->add_message({message=>$message,level=>$err->level});
818             }
819             return;
820             }
821             }
822             }
823              
824             ## Make sure there are no leftovers.
825             if(@values) {
826             if($overflow_handler) {
827             return $o_check->([@values], $err);
828             } else {
829             if($err) {
830             my $message = "More values than Type Constraints!";
831             $err->add_message({message=>$message,level=>$err->level});
832             }
833             return;
834             }
835             } else {
836             return 1;
837             }
838             };
839             }
840             )
841             );
842              
843             Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
844             MooseX::Meta::TypeConstraint::Structured->new(
845             name => "MooseX::Types::Structured::Dict",
846             parent => find_type_constraint('HashRef'),
847             constraint_generator => sub {
848             ## Get the constraints and values to check
849             my ($self, $type_constraints) = @_;
850             $type_constraints = $self->type_constraints;
851             my @type_constraints = defined $type_constraints ?
852             @$type_constraints : ();
853              
854             my $overflow_handler;
855             if($type_constraints[-1] && blessed $type_constraints[-1]
856             && $type_constraints[-1]->isa('MooseX::Types::Structured::OverflowHandler')) {
857             $overflow_handler = pop @type_constraints;
858             }
859             my %type_constraints = @type_constraints;
860             foreach my $key (keys %type_constraints) {
861             unless(blessed $type_constraints{$key}) {
862             ($type_constraints{$key} = find_type_constraint($type_constraints{$key}))
863             || die "$type_constraints{$key} is not a registered type";
864             }
865             }
866              
867             my (%check, %optional, $o_check, $is_compiled);
868             return sub {
869             my ($values, $err) = @_;
870             my %values = defined $values ? %$values: ();
871              
872             unless ($is_compiled) {
873             %check = map { ($_ => $type_constraints{ $_ }->$CompiledTC) } keys %type_constraints;
874             %optional = map { ($_ => $type_constraints{ $_ }->is_subtype_of($Optional)) } keys %type_constraints;
875             $o_check = $overflow_handler->$CompiledTC
876             if $overflow_handler;
877             $is_compiled++;
878             }
879              
880             ## Perform the checking
881             KEY:
882             for my $key (keys %check) {
883             my $type_constraint = $check{ $key };
884              
885             if(exists $values{$key}) {
886             my $value = $values{$key};
887             delete $values{$key};
888              
889             next KEY
890             unless $type_constraint;
891              
892             unless($type_constraint->($value)) {
893             if($err) {
894             my $message = $type_constraints{ $key }->validate($value,$err);
895             $err->add_message({message=>$message,level=>$err->level});
896             }
897             return;
898             }
899             } else {
900             ## Test to see if the TC supports null values
901             unless ($optional{ $key }) {
902             if($err) {
903             my $message = $type_constraints{ $key }->get_message('NULL',$err);
904             $err->add_message({message=>$message,level=>$err->level});
905             }
906             return;
907             }
908             }
909             }
910              
911             ## Make sure there are no leftovers.
912             if(%values) {
913             if($overflow_handler) {
914             return $o_check->(+{%values});
915             } else {
916             if($err) {
917             my $message = "More values than Type Constraints!";
918             $err->add_message({message=>$message,level=>$err->level});
919             }
920             return;
921             }
922             } else {
923             return 1;
924             }
925             }
926             },
927             )
928             );
929              
930             Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
931             MooseX::Meta::TypeConstraint::Structured->new(
932             name => "MooseX::Types::Structured::Map",
933             parent => find_type_constraint('HashRef'),
934             constraint_generator=> sub {
935             ## Get the constraints and values to check
936             my ($self, $type_constraints) = @_;
937             $type_constraints = $self->type_constraints;
938             my @constraints = defined $type_constraints ? @$type_constraints : ();
939              
940             Carp::confess( "too many args for Map type" ) if @constraints > 2;
941              
942             my ($key_type, $value_type) = @constraints == 2 ? @constraints
943             : @constraints == 1 ? (undef, @constraints)
944             : ();
945              
946             my ($key_check, $value_check, $is_compiled);
947             return sub {
948             my ($values, $err) = @_;
949             my %values = defined $values ? %$values: ();
950              
951             unless ($is_compiled) {
952             ($key_check, $value_check)
953             = map { $_ ? $_->$CompiledTC : undef }
954             $key_type, $value_type;
955             $is_compiled++;
956             }
957              
958             ## Perform the checking
959             if ($value_check) {
960             for my $value (values %$values) {
961             unless ($value_check->($value)) {
962             if($err) {
963             my $message = $value_type->validate($value,$err);
964             $err->add_message({message=>$message,level=>$err->level});
965             }
966             return;
967             }
968             }
969             }
970             if ($key_check) {
971             for my $key (keys %$values) {
972             unless ($key_check->($key)) {
973             if($err) {
974             my $message = $key_type->validate($key,$err);
975             $err->add_message({message=>$message,level=>$err->level});
976             }
977             return;
978             }
979             }
980             }
981              
982             return 1;
983             };
984             },
985             )
986             );
987              
988             sub slurpy ($) {
989 2     2 1 16223 my ($tc) = @_;
990 2         73 return MooseX::Types::Structured::OverflowHandler->new(
991             type_constraint => $tc,
992             );
993             }
994              
995             #pod =head1 SEE ALSO
996             #pod
997             #pod The following modules or resources may be of interest.
998             #pod
999             #pod L<Moose>, L<MooseX::Types>, L<Moose::Meta::TypeConstraint>,
1000             #pod L<MooseX::Meta::TypeConstraint::Structured>
1001             #pod
1002             #pod =cut
1003              
1004             1;
1005              
1006             __END__
1007              
1008             =pod
1009              
1010             =encoding UTF-8
1011              
1012             =head1 NAME
1013              
1014             MooseX::Types::Structured - Structured Type Constraints for Moose
1015              
1016             =head1 VERSION
1017              
1018             version 0.36
1019              
1020             =head1 SYNOPSIS
1021              
1022             The following is example usage for this module.
1023              
1024             package Person;
1025              
1026             use Moose;
1027             use MooseX::Types::Moose qw(Str Int HashRef);
1028             use MooseX::Types::Structured qw(Dict Tuple Optional);
1029              
1030             ## A name has a first and last part, but middle names are not required
1031             has name => (
1032             isa=>Dict[
1033             first => Str,
1034             last => Str,
1035             middle => Optional[Str],
1036             ],
1037             );
1038              
1039             ## description is a string field followed by a HashRef of tagged data.
1040             has description => (
1041             isa=>Tuple[
1042             Str,
1043             Optional[HashRef],
1044             ],
1045             );
1046              
1047             ## Remainder of your class attributes and methods
1048              
1049             Then you can instantiate this class with something like:
1050              
1051             my $john = Person->new(
1052             name => {
1053             first => 'John',
1054             middle => 'James'
1055             last => 'Napiorkowski',
1056             },
1057             description => [
1058             'A cool guy who loves Perl and Moose.', {
1059             married_to => 'Vanessa Li',
1060             born_in => 'USA',
1061             };
1062             ]
1063             );
1064              
1065             Or with:
1066              
1067             my $vanessa = Person->new(
1068             name => {
1069             first => 'Vanessa',
1070             last => 'Li'
1071             },
1072             description => ['A great student!'],
1073             );
1074              
1075             But all of these would cause a constraint error for the C<name> attribute:
1076              
1077             ## Value for 'name' not a HashRef
1078             Person->new( name => 'John' );
1079              
1080             ## Value for 'name' has incorrect hash key and missing required keys
1081             Person->new( name => {
1082             first_name => 'John'
1083             });
1084              
1085             ## Also incorrect keys
1086             Person->new( name => {
1087             first_name => 'John',
1088             age => 39,
1089             });
1090              
1091             ## key 'middle' incorrect type, should be a Str not a ArrayRef
1092             Person->new( name => {
1093             first => 'Vanessa',
1094             middle => [1,2],
1095             last => 'Li',
1096             });
1097              
1098             And these would cause a constraint error for the C<description> attribute:
1099              
1100             ## Should be an ArrayRef
1101             Person->new( description => 'Hello I am a String' );
1102              
1103             ## First element must be a string not a HashRef.
1104             Person->new (description => [{
1105             tag1 => 'value1',
1106             tag2 => 'value2'
1107             }]);
1108              
1109             Please see the test cases for more examples.
1110              
1111             =head1 DESCRIPTION
1112              
1113             A structured type constraint is a standard container L<Moose> type constraint,
1114             such as an C<ArrayRef> or C<HashRef>, which has been enhanced to allow you to
1115             explicitly name all the allowed type constraints inside the structure. The
1116             generalized form is:
1117              
1118             TypeConstraint[@TypeParameters or %TypeParameters]
1119              
1120             Where C<TypeParameters> is an array reference or hash references of
1121             L<Moose::Meta::TypeConstraint> objects.
1122              
1123             This type library enables structured type constraints. It is built on top of the
1124             L<MooseX::Types> library system, so you should review the documentation for that
1125             if you are not familiar with it.
1126              
1127             =head2 Comparing Parameterized types to Structured types
1128              
1129             Parameterized constraints are built into core Moose and you are probably already
1130             familiar with the type constraints C<HashRef> and C<ArrayRef>. Structured types
1131             have similar functionality, so their syntax is likewise similar. For example,
1132             you could define a parameterized constraint like:
1133              
1134             subtype ArrayOfInts,
1135             as ArrayRef[Int];
1136              
1137             which would constrain a value to something like [1,2,3,...] and so on. On the
1138             other hand, a structured type constraint explicitly names all it's allowed
1139             'internal' type parameter constraints. For the example:
1140              
1141             subtype StringFollowedByInt,
1142             as Tuple[Str,Int];
1143              
1144             would constrain its value to things like C<< ['hello', 111] >> but C<< ['hello', 'world'] >>
1145             would fail, as well as C<< ['hello', 111, 'world'] >> and so on. Here's another
1146             example:
1147              
1148             package MyApp::Types;
1149              
1150             use MooseX::Types -declare [qw(StringIntOptionalHashRef)];
1151             use MooseX::Types::Moose qw(Str Int);
1152             use MooseX::Types::Structured qw(Tuple Optional);
1153              
1154             subtype StringIntOptionalHashRef,
1155             as Tuple[
1156             Str, Int,
1157             Optional[HashRef]
1158             ];
1159              
1160             This defines a type constraint that validates values like:
1161              
1162             ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
1163             ['World', 200];
1164              
1165             Notice that the last type constraint in the structure is optional. This is
1166             enabled via the helper C<Optional> type constraint, which is a variation of the
1167             core Moose type constraint C<Maybe>. The main difference is that C<Optional> type
1168             constraints are required to validate if they exist, while C<Maybe> permits
1169             undefined values. So the following example would not validate:
1170              
1171             StringIntOptionalHashRef->validate(['Hello Undefined', 1000, undef]);
1172              
1173             Please note the subtle difference between undefined and null. If you wish to
1174             allow both null and undefined, you should use the core Moose C<Maybe> type
1175             constraint instead:
1176              
1177             package MyApp::Types;
1178              
1179             use MooseX::Types -declare [qw(StringIntMaybeHashRef)];
1180             use MooseX::Types::Moose qw(Str Int Maybe);
1181             use MooseX::Types::Structured qw(Tuple);
1182              
1183             subtype StringIntMaybeHashRef,
1184             as Tuple[
1185             Str, Int, Maybe[HashRef]
1186             ];
1187              
1188             This would validate the following:
1189              
1190             ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
1191             ['World', 200, undef];
1192             ['World', 200];
1193              
1194             Structured constraints are not limited to arrays. You can define a structure
1195             against a C<HashRef> with the C<Dict> type constraint as in this example:
1196              
1197             subtype FirstNameLastName,
1198             as Dict[
1199             firstname => Str,
1200             lastname => Str,
1201             ];
1202              
1203             This would constrain a C<HashRef> that validates something like:
1204              
1205             {firstname => 'Christopher', lastname => 'Parsons'};
1206              
1207             but all the following would fail validation:
1208              
1209             ## Incorrect keys
1210             {first => 'Christopher', last => 'Parsons'};
1211              
1212             ## Too many keys
1213             {firstname => 'Christopher', lastname => 'Parsons', middlename => 'Allen'};
1214              
1215             ## Not a HashRef
1216             ['Christopher', 'Parsons'];
1217              
1218             These structures can be as simple or elaborate as you wish. You can even
1219             combine various structured, parameterized and simple constraints all together:
1220              
1221             subtype Crazy,
1222             as Tuple[
1223             Int,
1224             Dict[name=>Str, age=>Int],
1225             ArrayRef[Int]
1226             ];
1227              
1228             Which would match:
1229              
1230             [1, {name=>'John', age=>25},[10,11,12]];
1231              
1232             Please notice how the type parameters can be visually arranged to your liking
1233             and to improve the clarity of your meaning. You don't need to run then
1234             altogether onto a single line. Additionally, since the C<Dict> type constraint
1235             defines a hash constraint, the key order is not meaningful. For example:
1236              
1237             subtype AnyKeyOrder,
1238             as Dict[
1239             key1=>Int,
1240             key2=>Str,
1241             key3=>Int,
1242             ];
1243              
1244             Would validate both:
1245              
1246             {key1 => 1, key2 => "Hi!", key3 => 2};
1247             {key2 => "Hi!", key1 => 100, key3 => 300};
1248              
1249             As you would expect, since underneath it's just a plain old Perl hash at work.
1250              
1251             =head2 Alternatives
1252              
1253             You should exercise some care as to whether or not your complex structured
1254             constraints would be better off contained by a real object as in the following
1255             example:
1256              
1257             package MyApp::MyStruct;
1258             use Moose;
1259              
1260             ## lazy way to make a bunch of attributes
1261             has $_ for qw(full_name age_in_years);
1262              
1263             package MyApp::MyClass;
1264             use Moose;
1265              
1266             has person => (isa => 'MyApp::MyStruct');
1267              
1268             my $instance = MyApp::MyClass->new(
1269             person=>MyApp::MyStruct->new(
1270             full_name => 'John',
1271             age_in_years => 39,
1272             ),
1273             );
1274              
1275             This method may take some additional time to set up but will give you more
1276             flexibility. However, structured constraints are highly compatible with this
1277             method, granting some interesting possibilities for coercion. Try:
1278              
1279             package MyApp::MyClass;
1280              
1281             use Moose;
1282             use MyApp::MyStruct;
1283              
1284             ## It's recommended your type declarations live in a separate class in order
1285             ## to promote reusability and clarity. Inlined here for brevity.
1286              
1287             use MooseX::Types::DateTime qw(DateTime);
1288             use MooseX::Types -declare [qw(MyStruct)];
1289             use MooseX::Types::Moose qw(Str Int);
1290             use MooseX::Types::Structured qw(Dict);
1291              
1292             ## Use class_type to create an ISA type constraint if your object doesn't
1293             ## inherit from Moose::Object.
1294             class_type 'MyApp::MyStruct';
1295              
1296             ## Just a shorter version really.
1297             subtype MyStruct,
1298             as 'MyApp::MyStruct';
1299              
1300             ## Add the coercions.
1301             coerce MyStruct,
1302             from Dict[
1303             full_name=>Str,
1304             age_in_years=>Int
1305             ], via {
1306             MyApp::MyStruct->new(%$_);
1307             },
1308             from Dict[
1309             lastname=>Str,
1310             firstname=>Str,
1311             dob=>DateTime
1312             ], via {
1313             my $name = $_->{firstname} .' '. $_->{lastname};
1314             my $age = DateTime->now - $_->{dob};
1315              
1316             MyApp::MyStruct->new(
1317             full_name=>$name,
1318             age_in_years=>$age->years,
1319             );
1320             };
1321              
1322             has person => (isa=>MyStruct);
1323              
1324             This would allow you to instantiate with something like:
1325              
1326             my $obj = MyApp::MyClass->new( person => {
1327             full_name=>'John Napiorkowski',
1328             age_in_years=>39,
1329             });
1330              
1331             Or even:
1332              
1333             my $obj = MyApp::MyClass->new( person => {
1334             lastname=>'John',
1335             firstname=>'Napiorkowski',
1336             dob=>DateTime->new(year=>1969),
1337             });
1338              
1339             If you are not familiar with how coercions work, check out the L<Moose> cookbook
1340             entry L<Moose::Cookbook::Recipe5> for an explanation. The section L</Coercions>
1341             has additional examples and discussion.
1342              
1343             =for stopwords Subtyping
1344              
1345             =head2 Subtyping a Structured type constraint
1346              
1347             You need to exercise some care when you try to subtype a structured type as in
1348             this example:
1349              
1350             subtype Person,
1351             as Dict[name => Str];
1352              
1353             subtype FriendlyPerson,
1354             as Person[
1355             name => Str,
1356             total_friends => Int,
1357             ];
1358              
1359             This will actually work BUT you have to take care that the subtype has a
1360             structure that does not contradict the structure of it's parent. For now the
1361             above works, but I will clarify the syntax for this at a future point, so
1362             it's recommended to avoid (should not really be needed so much anyway). For
1363             now this is supported in an EXPERIMENTAL way. Your thoughts, test cases and
1364             patches are welcomed for discussion. If you find a good use for this, please
1365             let me know.
1366              
1367             =head2 Coercions
1368              
1369             Coercions currently work for 'one level' deep. That is you can do:
1370              
1371             subtype Person,
1372             as Dict[
1373             name => Str,
1374             age => Int
1375             ];
1376              
1377             subtype Fullname,
1378             as Dict[
1379             first => Str,
1380             last => Str
1381             ];
1382              
1383             coerce Person,
1384             ## Coerce an object of a particular class
1385             from BlessedPersonObject, via {
1386             +{
1387             name=>$_->name,
1388             age=>$_->age,
1389             };
1390             },
1391              
1392             ## Coerce from [$name, $age]
1393             from ArrayRef, via {
1394             +{
1395             name=>$_->[0],
1396             age=>$_->[1],
1397             },
1398             },
1399             ## Coerce from {fullname=>{first=>...,last=>...}, dob=>$DateTimeObject}
1400             from Dict[fullname=>Fullname, dob=>DateTime], via {
1401             my $age = $_->dob - DateTime->now;
1402             my $firstn = $_->{fullname}->{first};
1403             my $lastn = $_->{fullname}->{last}
1404             +{
1405             name => $_->{fullname}->{first} .' '. ,
1406             age =>$age->years
1407             }
1408             };
1409              
1410             And that should just work as expected. However, if there are any 'inner'
1411             coercions, such as a coercion on C<Fullname> or on C<DateTime>, that coercion
1412             won't currently get activated.
1413              
1414             Please see the test F<07-coerce.t> for a more detailed example. Discussion on
1415             extending coercions to support this welcome on the Moose development channel or
1416             mailing list.
1417              
1418             =head2 Recursion
1419              
1420             Newer versions of L<MooseX::Types> support recursive type constraints. That is
1421             you can include a type constraint as a contained type constraint of itself. For
1422             example:
1423              
1424             subtype Person,
1425             as Dict[
1426             name=>Str,
1427             friends=>Optional[
1428             ArrayRef[Person]
1429             ],
1430             ];
1431              
1432             This would declare a C<Person> subtype that contains a name and an optional
1433             C<ArrayRef> of C<Person>s who are friends as in:
1434              
1435             {
1436             name => 'Mike',
1437             friends => [
1438             { name => 'John' },
1439             { name => 'Vincent' },
1440             {
1441             name => 'Tracey',
1442             friends => [
1443             { name => 'Stephenie' },
1444             { name => 'Ilya' },
1445             ],
1446             },
1447             ],
1448             };
1449              
1450             Please take care to make sure the recursion node is either C<Optional>, or declare
1451             a union with an non-recursive option such as:
1452              
1453             subtype Value
1454             as Tuple[
1455             Str,
1456             Str|Tuple,
1457             ];
1458              
1459             Which validates:
1460              
1461             [
1462             'Hello', [
1463             'World', [
1464             'Is', [
1465             'Getting',
1466             'Old',
1467             ],
1468             ],
1469             ],
1470             ];
1471              
1472             Otherwise you will define a subtype that is impossible to validate since it is
1473             infinitely recursive. For more information about defining recursive types,
1474             please see the documentation in L<MooseX::Types> and the test cases.
1475              
1476             =head1 TYPE CONSTRAINTS
1477              
1478             This type library defines the following constraints.
1479              
1480             =head2 Tuple[@constraints]
1481              
1482             This defines an ArrayRef based constraint which allows you to validate a specific
1483             list of contained constraints. For example:
1484              
1485             Tuple[Int,Str]; ## Validates [1,'hello']
1486             Tuple[Str|Object, Int]; ## Validates ['hello', 1] or [$object, 2]
1487              
1488             The Values of @constraints should ideally be L<MooseX::Types> declared type
1489             constraints. We do support 'old style' L<Moose> string based constraints to a
1490             limited degree but these string type constraints are considered deprecated.
1491             There will be limited support for bugs resulting from mixing string and
1492             L<MooseX::Types> in your structures. If you encounter such a bug and really
1493             need it fixed, we will required a detailed test case at the minimum.
1494              
1495             =head2 Dict[%constraints]
1496              
1497             This defines a HashRef based constraint which allowed you to validate a specific
1498             hashref. For example:
1499              
1500             Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
1501              
1502             The keys in C<%constraints> follow the same rules as C<@constraints> in the above
1503             section.
1504              
1505             =head2 Map[ $key_constraint, $value_constraint ]
1506              
1507             This defines a C<HashRef>-based constraint in which both the keys and values are
1508             required to meet certain constraints. For example, to map hostnames to IP
1509             addresses, you might say:
1510              
1511             Map[ HostName, IPAddress ]
1512              
1513             The type constraint would only be met if every key was a valid C<HostName> and
1514             every value was a valid C<IPAddress>.
1515              
1516             =head2 Optional[$constraint]
1517              
1518             This is primarily a helper constraint for C<Dict> and C<Tuple> type constraints. What
1519             this allows is for you to assert that a given type constraint is allowed to be
1520             null (but NOT undefined). If the value is null, then the type constraint passes
1521             but if the value is defined it must validate against the type constraint. This
1522             makes it easy to make a Dict where one or more of the keys doesn't have to exist
1523             or a tuple where some of the values are not required. For example:
1524              
1525             subtype Name() => as Dict[
1526             first=>Str,
1527             last=>Str,
1528             middle=>Optional[Str],
1529             ];
1530              
1531             ...creates a constraint that validates against a hashref with the keys 'first' and
1532             'last' being strings and required while an optional key 'middle' is must be a
1533             string if it appears but doesn't have to appear. So in this case both the
1534             following are valid:
1535              
1536             {first=>'John', middle=>'James', last=>'Napiorkowski'}
1537             {first=>'Vanessa', last=>'Li'}
1538              
1539             If you use the C<Maybe> type constraint instead, your values will also validate
1540             against C<undef>, which may be incorrect for you.
1541              
1542             =head1 EXPORTABLE SUBROUTINES
1543              
1544             This type library makes available for export the following subroutines
1545              
1546             =for stopwords slurpy
1547              
1548             =head2 slurpy
1549              
1550             Structured type constraints by their nature are closed; that is validation will
1551             depend on an exact match between your structure definition and the arguments to
1552             be checked. Sometimes you might wish for a slightly looser amount of validation.
1553             For example, you may wish to validate the first 3 elements of an array reference
1554             and allow for an arbitrary number of additional elements. At first thought you
1555             might think you could do it this way:
1556              
1557             # I want to validate stuff like: [1,"hello", $obj, 2,3,4,5,6,...]
1558             subtype AllowTailingArgs,
1559             as Tuple[
1560             Int,
1561             Str,
1562             Object,
1563             ArrayRef[Int],
1564             ];
1565              
1566             However what this will actually validate are structures like this:
1567              
1568             [10,"Hello", $obj, [11,12,13,...] ]; # Notice element 4 is an ArrayRef
1569              
1570             In order to allow structured validation of, "and then some", arguments, you can
1571             use the L</slurpy> method against a type constraint. For example:
1572              
1573             use MooseX::Types::Structured qw(Tuple slurpy);
1574              
1575             subtype AllowTailingArgs,
1576             as Tuple[
1577             Int,
1578             Str,
1579             Object,
1580             slurpy ArrayRef[Int],
1581             ];
1582              
1583             This will now work as expected, validating ArrayRef structures such as:
1584              
1585             [1,"hello", $obj, 2,3,4,5,6,...]
1586              
1587             A few caveats apply. First, the slurpy type constraint must be the last one in
1588             the list of type constraint parameters. Second, the parent type of the slurpy
1589             type constraint must match that of the containing type constraint. That means
1590             that a C<Tuple> can allow a slurpy C<ArrayRef> (or children of C<ArrayRef>s, including
1591             another C<Tuple>) and a C<Dict> can allow a slurpy C<HashRef> (or children/subtypes of
1592             HashRef, also including other C<Dict> constraints).
1593              
1594             Please note the technical way this works 'under the hood' is that the
1595             slurpy keyword transforms the target type constraint into a coderef. Please do
1596             not try to create your own custom coderefs; always use the slurpy method. The
1597             underlying technology may change in the future but the slurpy keyword will be
1598             supported.
1599              
1600             =head1 ERROR MESSAGES
1601              
1602             Error reporting has been improved to return more useful debugging messages. Now
1603             I will stringify the incoming check value with L<Devel::PartialDump> so that you
1604             can see the actual structure that is tripping up validation. Also, I report the
1605             'internal' validation error, so that if a particular element inside the
1606             Structured Type is failing validation, you will see that. There's a limit to
1607             how deep this internal reporting goes, but you shouldn't see any of the "failed
1608             with ARRAY(XXXXXX)" that we got with earlier versions of this module.
1609              
1610             This support is continuing to expand, so it's best to use these messages for
1611             debugging purposes and not for creating messages that 'escape into the wild'
1612             such as error messages sent to the user.
1613              
1614             Please see the test '12-error.t' for a more lengthy example. Your thoughts and
1615             preferable tests or code patches very welcome!
1616              
1617             =head1 EXAMPLES
1618              
1619             Here are some additional example usage for structured types. All examples can
1620             be found also in the 't/examples.t' test. Your contributions are also welcomed.
1621              
1622             =head2 Normalize a HashRef
1623              
1624             You need a hashref to conform to a canonical structure but are required accept a
1625             bunch of different incoming structures. You can normalize using the C<Dict> type
1626             constraint and coercions. This example also shows structured types mixed which
1627             other L<MooseX::Types> libraries.
1628              
1629             package Test::MooseX::Meta::TypeConstraint::Structured::Examples::Normalize;
1630              
1631             use Moose;
1632             use DateTime;
1633              
1634             use MooseX::Types::Structured qw(Dict Tuple);
1635             use MooseX::Types::DateTime qw(DateTime);
1636             use MooseX::Types::Moose qw(Int Str Object);
1637             use MooseX::Types -declare => [qw(Name Age Person)];
1638              
1639             subtype Person,
1640             as Dict[
1641             name=>Str,
1642             age=>Int,
1643             ];
1644              
1645             coerce Person,
1646             from Dict[
1647             first=>Str,
1648             last=>Str,
1649             years=>Int,
1650             ], via { +{
1651             name => "$_->{first} $_->{last}",
1652             age => $_->{years},
1653             }},
1654             from Dict[
1655             fullname=>Dict[
1656             last=>Str,
1657             first=>Str,
1658             ],
1659             dob=>DateTime,
1660             ],
1661             ## DateTime needs to be inside of single quotes here to disambiguate the
1662             ## class package from the DataTime type constraint imported via the
1663             ## line "use MooseX::Types::DateTime qw(DateTime);"
1664             via { +{
1665             name => "$_->{fullname}{first} $_->{fullname}{last}",
1666             age => ($_->{dob} - 'DateTime'->now)->years,
1667             }};
1668              
1669             has person => (is=>'rw', isa=>Person, coerce=>1);
1670              
1671             And now you can instantiate with all the following:
1672              
1673             __PACKAGE__->new(
1674             person=>{
1675             name=>'John Napiorkowski',
1676             age=>39,
1677             },
1678             );
1679              
1680             __PACKAGE__->new(
1681             person=>{
1682             first=>'John',
1683             last=>'Napiorkowski',
1684             years=>39,
1685             },
1686             );
1687              
1688             __PACKAGE__->new(
1689             person=>{
1690             fullname => {
1691             first=>'John',
1692             last=>'Napiorkowski'
1693             },
1694             dob => 'DateTime'->new(
1695             year=>1969,
1696             month=>2,
1697             day=>13
1698             ),
1699             },
1700             );
1701              
1702             This technique is a way to support various ways to instantiate your class in a
1703             clean and declarative way.
1704              
1705             =head1 SEE ALSO
1706              
1707             The following modules or resources may be of interest.
1708              
1709             L<Moose>, L<MooseX::Types>, L<Moose::Meta::TypeConstraint>,
1710             L<MooseX::Meta::TypeConstraint::Structured>
1711              
1712             =head1 SUPPORT
1713              
1714             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Types-Structured>
1715             (or L<bug-MooseX-Types-Structured@rt.cpan.org|mailto:bug-MooseX-Types-Structured@rt.cpan.org>).
1716              
1717             There is also a mailing list available for users of this distribution, at
1718             L<http://lists.perl.org/list/moose.html>.
1719              
1720             There is also an irc channel available for users of this distribution, at
1721             L<C<#moose> on C<irc.perl.org>|irc://irc.perl.org/#moose>.
1722              
1723             =head1 AUTHORS
1724              
1725             =over 4
1726              
1727             =item *
1728              
1729             John Napiorkowski <jjnapiork@cpan.org>
1730              
1731             =item *
1732              
1733             Florian Ragwitz <rafl@debian.org>
1734              
1735             =item *
1736              
1737             יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
1738              
1739             =item *
1740              
1741             Tomas (t0m) Doran <bobtfish@bobtfish.net>
1742              
1743             =item *
1744              
1745             Robert Sedlacek <rs@474.at>
1746              
1747             =back
1748              
1749             =head1 CONTRIBUTORS
1750              
1751             =for stopwords Karen Etheridge Ricardo Signes Dave Rolsky Ansgar Burchardt Stevan Little arcanez Jesse Luehrs D. Ilmari MannsÃ¥ker
1752              
1753             =over 4
1754              
1755             =item *
1756              
1757             Karen Etheridge <ether@cpan.org>
1758              
1759             =item *
1760              
1761             Ricardo Signes <rjbs@cpan.org>
1762              
1763             =item *
1764              
1765             Dave Rolsky <autarch@urth.org>
1766              
1767             =item *
1768              
1769             Ansgar Burchardt <ansgar@43-1.org>
1770              
1771             =item *
1772              
1773             Stevan Little <stevan.little@iinteractive.com>
1774              
1775             =item *
1776              
1777             arcanez <justin.d.hunter@gmail.com>
1778              
1779             =item *
1780              
1781             Jesse Luehrs <doy@tozt.net>
1782              
1783             =item *
1784              
1785             D. Ilmari MannsÃ¥ker <ilmari@cpan.org>
1786              
1787             =back
1788              
1789             =head1 COPYRIGHT AND LICENSE
1790              
1791             This software is copyright (c) 2008 by John Napiorkowski.
1792              
1793             This is free software; you can redistribute it and/or modify it under
1794             the same terms as the Perl 5 programming language system itself.
1795              
1796             =cut