1MooseX::Types::StructurUesde(r3)Contributed Perl DocumenMtoaotsieoXn::Types::Structured(3)
2
3
4

NAME

6       MooseX::Types::Structured - MooseX::Types::Structured - Structured Type
7       Constraints for Moose
8

SYNOPSIS

10       The following is example usage for this module.
11
12           package Person;
13
14           use Moose;
15           use MooseX::Types::Moose qw(Str Int HashRef);
16           use MooseX::Types::Structured qw(Dict Tuple Optional);
17
18           ## A name has a first and last part, but middle names are not required
19           has name => (
20               isa=>Dict[
21                   first => Str,
22                   last => Str,
23                   middle => Optional[Str],
24               ],
25           );
26
27           ## description is a string field followed by a HashRef of tagged data.
28           has description => (
29             isa=>Tuple[
30               Str,
31               Optional[HashRef],
32            ],
33           );
34
35           ## Remainder of your class attributes and methods
36
37       Then you can instantiate this class with something like:
38
39           my $john = Person->new(
40               name => {
41                   first => 'John',
42                   middle => 'James'
43                   last => 'Napiorkowski',
44               },
45               description => [
46                   'A cool guy who loves Perl and Moose.', {
47                       married_to => 'Vanessa Li',
48                       born_in => 'USA',
49                   };
50               ]
51           );
52
53       Or with:
54
55           my $vanessa = Person->new(
56               name => {
57                   first => 'Vanessa',
58                   last => 'Li'
59               },
60               description => ['A great student!'],
61           );
62
63       But all of these would cause a constraint error for the 'name'
64       attribute:
65
66           ## Value for 'name' not a HashRef
67           Person->new( name => 'John' );
68
69           ## Value for 'name' has incorrect hash key and missing required keys
70           Person->new( name => {
71               first_name => 'John'
72           });
73
74           ## Also incorrect keys
75           Person->new( name => {
76               first_name => 'John',
77               age => 39,
78           });
79
80           ## key 'middle' incorrect type, should be a Str not a ArrayRef
81           Person->new( name => {
82               first => 'Vanessa',
83               middle => [1,2],
84               last => 'Li',
85           });
86
87       And these would cause a constraint error for the 'description'
88       attribute:
89
90           ## Should be an ArrayRef
91           Person->new( description => 'Hello I am a String' );
92
93           ## First element must be a string not a HashRef.
94           Person->new (description => [{
95               tag1 => 'value1',
96               tag2 => 'value2'
97           }]);
98
99       Please see the test cases for more examples.
100

DESCRIPTION

102       A structured type constraint is a standard container Moose type
103       constraint, such as an ArrayRef or HashRef, which has been enhanced to
104       allow you to explicitly name all the allowed type constraints inside
105       the structure.  The generalized form is:
106
107           TypeConstraint[@TypeParameters or %TypeParameters]
108
109       Where 'TypeParameters' is an array reference or hash references of
110       Moose::Meta::TypeConstraint objects.
111
112       This type library enables structured type constraints. It is built on
113       top of the MooseX::Types library system, so you should review the
114       documentation for that if you are not familiar with it.
115
116   Comparing Parameterized types to Structured types
117       Parameterized constraints are built into core Moose and you are
118       probably already familiar with the type constraints 'HashRef' and
119       'ArrayRef'.  Structured types have similar functionality, so their
120       syntax is likewise similar. For example, you could define a
121       parameterized constraint like:
122
123           subtype ArrayOfInts,
124            as ArrayRef[Int];
125
126       which would constrain a value to something like [1,2,3,...] and so on.
127       On the other hand, a structured type constraint explicitly names all
128       it's allowed 'internal' type parameter constraints.  For the example:
129
130           subtype StringFollowedByInt,
131            as Tuple[Str,Int];
132
133       would constrain it's value to things like ['hello', 111] but ['hello',
134       'world'] would fail, as well as ['hello', 111, 'world'] and so on.
135       Here's another example:
136
137               package MyApp::Types;
138
139           use MooseX::Types -declare [qw(StringIntOptionalHashRef)];
140           use MooseX::Types::Moose qw(Str Int);
141           use MooseX::Types::Structured qw(Tuple Optional);
142
143           subtype StringIntOptionalHashRef,
144            as Tuple[
145               Str, Int,
146               Optional[HashRef]
147            ];
148
149       This defines a type constraint that validates values like:
150
151           ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
152           ['World', 200];
153
154       Notice that the last type constraint in the structure is optional.
155       This is enabled via the helper Optional type constraint, which is a
156       variation of the core Moose type constraint 'Maybe'.  The main
157       difference is that Optional type constraints are required to validate
158       if they exist, while 'Maybe' permits undefined values.  So the
159       following example would not validate:
160
161           StringIntOptionalHashRef->validate(['Hello Undefined', 1000, undef]);
162
163       Please note the subtle difference between undefined and null.  If you
164       wish to allow both null and undefined, you should use the core Moose
165       'Maybe' type constraint instead:
166
167           package MyApp::Types;
168
169           use MooseX::Types -declare [qw(StringIntMaybeHashRef)];
170           use MooseX::Types::Moose qw(Str Int Maybe);
171           use MooseX::Types::Structured qw(Tuple);
172
173           subtype StringIntMaybeHashRef,
174            as Tuple[
175               Str, Int, Maybe[HashRef]
176            ];
177
178       This would validate the following:
179
180           ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
181           ['World', 200, undef];
182           ['World', 200];
183
184       Structured constraints are not limited to arrays.  You can define a
185       structure against a HashRef with the 'Dict' type constaint as in this
186       example:
187
188           subtype FirstNameLastName,
189            as Dict[
190               firstname => Str,
191               lastname => Str,
192            ];
193
194       This would constrain a HashRef that validates something like:
195
196           {firstname => 'Christopher', lastname => 'Parsons'};
197
198       but all the following would fail validation:
199
200           ## Incorrect keys
201           {first => 'Christopher', last => 'Parsons'};
202
203           ## Too many keys
204           {firstname => 'Christopher', lastname => 'Parsons', middlename => 'Allen'};
205
206           ## Not a HashRef
207           ['Christopher', 'Parsons'];
208
209       These structures can be as simple or elaborate as you wish.  You can
210       even combine various structured, parameterized and simple constraints
211       all together:
212
213           subtype Crazy,
214            as Tuple[
215               Int,
216               Dict[name=>Str, age=>Int],
217               ArrayRef[Int]
218            ];
219
220       Which would match:
221
222           [1, {name=>'John', age=>25},[10,11,12]];
223
224       Please notice how the type parameters can be visually arranged to your
225       liking and to improve the clarity of your meaning.  You don't need to
226       run then altogether onto a single line.  Additionally, since the 'Dict'
227       type constraint defines a hash constraint, the key order is not
228       meaningful.  For example:
229
230           subtype AnyKeyOrder,
231             as Dict[
232               key1=>Int,
233               key2=>Str,
234               key3=>Int,
235            ];
236
237       Would validate both:
238
239           {key1 => 1, key2 => "Hi!", key3 => 2};
240           {key2 => "Hi!", key1 => 100, key3 => 300};
241
242       As you would expect, since underneath its just a plain old Perl hash at
243       work.
244
245   Alternatives
246       You should exercise some care as to whether or not your complex
247       structured constraints would be better off contained by a real object
248       as in the following example:
249
250           package MyApp::MyStruct;
251           use Moose;
252
253           ## lazy way to make a bunch of attributes
254           has $_ for qw(full_name age_in_years);
255
256           package MyApp::MyClass;
257           use Moose;
258
259           has person => (isa => 'MyApp::MyStruct');
260
261           my $instance = MyApp::MyClass->new(
262               person=>MyApp::MyStruct->new(
263                   full_name => 'John',
264                   age_in_years => 39,
265               ),
266           );
267
268       This method may take some additional time to setup but will give you
269       more flexibility.  However, structured constraints are highly
270       compatible with this method, granting some interesting possibilities
271       for coercion.  Try:
272
273           package MyApp::MyClass;
274
275           use Moose;
276           use MyApp::MyStruct;
277
278           ## It's recommended your type declarations live in a separate class in order
279           ## to promote reusability and clarity.  Inlined here for brevity.
280
281           use MooseX::Types::DateTime qw(DateTime);
282           use MooseX::Types -declare [qw(MyStruct)];
283           use MooseX::Types::Moose qw(Str Int);
284           use MooseX::Types::Structured qw(Dict);
285
286           ## Use class_type to create an ISA type constraint if your object doesn't
287           ## inherit from Moose::Object.
288           class_type 'MyApp::MyStruct';
289
290           ## Just a shorter version really.
291           subtype MyStruct,
292            as 'MyApp::MyStruct';
293
294           ## Add the coercions.
295           coerce MyStruct,
296            from Dict[
297               full_name=>Str,
298               age_in_years=>Int
299            ], via {
300               MyApp::MyStruct->new(%$_);
301            },
302            from Dict[
303               lastname=>Str,
304               firstname=>Str,
305               dob=>DateTime
306            ], via {
307               my $name = $_->{firstname} .' '. $_->{lastname};
308               my $age = DateTime->now - $_->{dob};
309
310               MyApp::MyStruct->new(
311                   full_name=>$name,
312                   age_in_years=>$age->years,
313               );
314            };
315
316           has person => (isa=>MyStruct);
317
318       This would allow you to instantiate with something like:
319
320           my $obj = MyApp::MyClass->new( person => {
321               full_name=>'John Napiorkowski',
322               age_in_years=>39,
323           });
324
325       Or even:
326
327           my $obj = MyApp::MyClass->new( person => {
328               lastname=>'John',
329               firstname=>'Napiorkowski',
330               dob=>DateTime->new(year=>1969),
331           });
332
333       If you are not familiar with how coercions work, check out the Moose
334       cookbook entry Moose::Cookbook::Recipe5 for an explanation.  The
335       section "Coercions" has additional examples and discussion.
336
337   Subtyping a Structured type constraint
338       You need to exercise some care when you try to subtype a structured
339       type as in this example:
340
341           subtype Person,
342            as Dict[name => Str];
343
344           subtype FriendlyPerson,
345            as Person[
346               name => Str,
347               total_friends => Int,
348            ];
349
350       This will actually work BUT you have to take care that the subtype has
351       a structure that does not contradict the structure of it's parent.  For
352       now the above works, but I will clarify the syntax for this at a future
353       point, so it's recommended to avoid (should not really be needed so
354       much anyway).  For now this is supported in an EXPERIMENTAL way.  Your
355       thoughts, test cases and patches are welcomed for discussion.  If you
356       find a good use for this, please let me know.
357
358   Coercions
359       Coercions currently work for 'one level' deep.  That is you can do:
360
361           subtype Person,
362            as Dict[
363               name => Str,
364               age => Int
365           ];
366
367           subtype Fullname,
368            as Dict[
369               first => Str,
370               last => Str
371            ];
372
373           coerce Person,
374            ## Coerce an object of a particular class
375            from BlessedPersonObject, via {
376               +{
377                   name=>$_->name,
378                   age=>$_->age,
379               };
380            },
381
382            ## Coerce from [$name, $age]
383            from ArrayRef, via {
384               +{
385                   name=>$_->[0],
386                   age=>$_->[1],
387               },
388            },
389            ## Coerce from {fullname=>{first=>...,last=>...}, dob=>$DateTimeObject}
390            from Dict[fullname=>Fullname, dob=>DateTime], via {
391               my $age = $_->dob - DateTime->now;
392               my $firstn = $_->{fullname}->{first};
393               my $lastn = $_->{fullname}->{last}
394               +{
395                   name => $_->{fullname}->{first} .' '. ,
396                   age =>$age->years
397               }
398            };
399
400       And that should just work as expected.  However, if there are any
401       'inner' coercions, such as a coercion on 'Fullname' or on 'DateTime',
402       that coercion won't currently get activated.
403
404       Please see the test '07-coerce.t' for a more detailed example.
405       Discussion on extending coercions to support this welcome on the Moose
406       development channel or mailing list.
407
408   Recursion
409       Newer versions of MooseX::Types support recursive type constraints.
410       That is you can include a type constraint as a contained type
411       constraint of itself.  For example:
412
413           subtype Person,
414            as Dict[
415                name=>Str,
416                friends=>Optional[
417                    ArrayRef[Person]
418                ],
419            ];
420
421       This would declare a Person subtype that contains a name and an
422       optional ArrayRef of Persons who are friends as in:
423
424           {
425               name => 'Mike',
426               friends => [
427                   { name => 'John' },
428                   { name => 'Vincent' },
429                   {
430                       name => 'Tracey',
431                       friends => [
432                           { name => 'Stephenie' },
433                           { name => 'Ilya' },
434                       ],
435                   },
436               ],
437           };
438
439       Please take care to make sure the recursion node is either Optional, or
440       declare a Union with an non recursive option such as:
441
442           subtype Value
443            as Tuple[
444                Str,
445                Str|Tuple,
446            ];
447
448       Which validates:
449
450           [
451               'Hello', [
452                   'World', [
453                       'Is', [
454                           'Getting',
455                           'Old',
456                       ],
457                   ],
458               ],
459           ];
460
461       Otherwise you will define a subtype thatis impossible to validate since
462       it is infinitely recursive.  For more information about defining
463       recursive types, please see the documentation in MooseX::Types and the
464       test cases.
465

TYPE CONSTRAINTS

467       This type library defines the following constraints.
468
469   Tuple[@constraints]
470       This defines an ArrayRef based constraint which allows you to validate
471       a specific list of contained constraints.  For example:
472
473           Tuple[Int,Str]; ## Validates [1,'hello']
474           Tuple[Str|Object, Int]; ## Validates ['hello', 1] or [$object, 2]
475
476       The Values of @constraints should ideally be MooseX::Types declared
477       type constraints.  We do support 'old style' Moose string based
478       constraints to a limited degree but these string type constraints are
479       considered deprecated.  There will be limited support for bugs
480       resulting from mixing string and MooseX::Types in your structures.  If
481       you encounter such a bug and really need it fixed, we will required a
482       detailed test case at the minimum.
483
484   Dict[%constraints]
485       This defines a HashRef based constraint which allowed you to validate a
486       specific hashref.  For example:
487
488           Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
489
490       The keys in %constraints follow the same rules as @constraints in the
491       above section.
492
493   Map[ $key_constraint, $value_constraint ]
494       This defines a HashRef based constraint in which both the keys and
495       values are required to meet certain constraints.  For example, to map
496       hostnames to IP addresses, you might say:
497
498         Map[ HostName, IPAddress ]
499
500       The type constraint would only be met if every key was a valid HostName
501       and every value was a valid IPAddress.
502
503   Optional[$constraint]
504       This is primarily a helper constraint for Dict and Tuple type
505       constraints.  What this allows is for you to assert that a given type
506       constraint is allowed to be null (but NOT undefined).  If the value is
507       null, then the type constraint passes but if the value is defined it
508       must validate against the type constraint.  This makes it easy to make
509       a Dict where one or more of the keys doesn't have to exist or a tuple
510       where some of the values are not required.  For example:
511
512           subtype Name() => as Dict[
513               first=>Str,
514               last=>Str,
515               middle=>Optional[Str],
516           ];
517
518       Creates a constraint that validates against a hashref with the keys
519       'first' and 'last' being strings and required while an optional key
520       'middle' is must be a string if it appears but doesn't have to appear.
521       So in this case both the following are valid:
522
523           {first=>'John', middle=>'James', last=>'Napiorkowski'}
524           {first=>'Vanessa', last=>'Li'}
525
526       If you use the 'Maybe' type constraint instead, your values will also
527       validate against 'undef', which may be incorrect for you.
528

EXPORTABLE SUBROUTINES

530       This type library makes available for export the following subroutines
531
532   slurpy
533       Structured type constraints by their nature are closed; that is
534       validation will depend on an exact match between your structure
535       definition and the arguments to be checked.  Sometimes you might wish
536       for a slightly looser amount of validation.  For example, you may wish
537       to validate the first 3 elements of an array reference and allow for an
538       arbitrary number of additional elements.  At first thought you might
539       think you could do it this way:
540
541           #  I want to validate stuff like: [1,"hello", $obj, 2,3,4,5,6,...]
542           subtype AllowTailingArgs,
543            as Tuple[
544              Int,
545              Str,
546              Object,
547              ArrayRef[Int],
548            ];
549
550       However what this will actually validate are structures like this:
551
552           [10,"Hello", $obj, [11,12,13,...] ]; # Notice element 4 is an ArrayRef
553
554       In order to allow structured validation of, "and then some", arguments,
555       you can use the "slurpy" method against a type constraint.  For
556       example:
557
558           use MooseX::Types::Structured qw(Tuple slurpy);
559
560           subtype AllowTailingArgs,
561            as Tuple[
562              Int,
563              Str,
564              Object,
565              slurpy ArrayRef[Int],
566            ];
567
568       This will now work as expected, validating ArrayRef structures such as:
569
570           [1,"hello", $obj, 2,3,4,5,6,...]
571
572       A few caveats apply.  First, the slurpy type constraint must be the
573       last one in the list of type constraint parameters.  Second, the parent
574       type of the slurpy type constraint must match that of the containing
575       type constraint.  That means that a Tuple can allow a slurpy ArrayRef
576       (or children of ArrayRefs, including another Tuple) and a Dict can
577       allow a slurpy HashRef (or children/subtypes of HashRef, also including
578       other Dict constraints).
579
580       Please note the the technical way this works 'under the hood' is that
581       the slurpy keyword transforms the target type constraint into a
582       coderef.  Please do not try to create your own custom coderefs; always
583       use the slurpy method.  The underlying technology may change in the
584       future but the slurpy keyword will be supported.
585

ERROR MESSAGES

587       Error reporting has been improved to return more useful debugging
588       messages. Now I will stringify the incoming check value with
589       Devel::PartialDump so that you can see the actual structure that is
590       tripping up validation.  Also, I report the 'internal' validation
591       error, so that if a particular element inside the Structured Type is
592       failing validation, you will see that.  There's a limit to how deep
593       this internal reporting goes, but you shouldn't see any of the "failed
594       with ARRAY(XXXXXX)" that we got with earlier versions of this module.
595
596       This support is continuing to expand, so it's best to use these
597       messages for debugging purposes and not for creating messages that
598       'escape into the wild' such as error messages sent to the user.
599
600       Please see the test '12-error.t' for a more lengthy example.  Your
601       thoughts and preferable tests or code patches very welcome!
602

EXAMPLES

604       Here are some additional example usage for structured types.  All
605       examples can be found also in the 't/examples.t' test.  Your
606       contributions are also welcomed.
607
608   Normalize a HashRef
609       You need a hashref to conform to a canonical structure but are required
610       accept a bunch of different incoming structures.  You can normalize
611       using the Dict type constraint and coercions.  This example also shows
612       structured types mixed which other MooseX::Types libraries.
613
614           package Test::MooseX::Meta::TypeConstraint::Structured::Examples::Normalize;
615
616           use Moose;
617           use DateTime;
618
619           use MooseX::Types::Structured qw(Dict Tuple);
620           use MooseX::Types::DateTime qw(DateTime);
621           use MooseX::Types::Moose qw(Int Str Object);
622           use MooseX::Types -declare => [qw(Name Age Person)];
623
624           subtype Person,
625            as Dict[
626                name=>Str,
627                age=>Int,
628            ];
629
630           coerce Person,
631            from Dict[
632                first=>Str,
633                last=>Str,
634                years=>Int,
635            ], via { +{
636               name => "$_->{first} $_->{last}",
637               age => $_->{years},
638            }},
639            from Dict[
640                fullname=>Dict[
641                    last=>Str,
642                    first=>Str,
643                ],
644                dob=>DateTime,
645            ],
646            ## DateTime needs to be inside of single quotes here to disambiguate the
647            ## class package from the DataTime type constraint imported via the
648            ## line "use MooseX::Types::DateTime qw(DateTime);"
649            via { +{
650               name => "$_->{fullname}{first} $_->{fullname}{last}",
651               age => ($_->{dob} - 'DateTime'->now)->years,
652            }};
653
654           has person => (is=>'rw', isa=>Person, coerce=>1);
655
656       And now you can instantiate with all the following:
657
658           __PACKAGE__->new(
659               person=>{
660                   name=>'John Napiorkowski',
661                   age=>39,
662               },
663           );
664
665           __PACKAGE__->new(
666               person=>{
667                   first=>'John',
668                   last=>'Napiorkowski',
669                   years=>39,
670               },
671           );
672
673           __PACKAGE__->new(
674               person=>{
675                   fullname => {
676                       first=>'John',
677                       last=>'Napiorkowski'
678                   },
679                   dob => 'DateTime'->new(
680                       year=>1969,
681                       month=>2,
682                       day=>13
683                   ),
684               },
685           );
686
687       This technique is a way to support various ways to instantiate your
688       class in a clean and declarative way.
689

SEE ALSO

691       The following modules or resources may be of interest.
692
693       Moose, MooseX::Types, Moose::Meta::TypeConstraint,
694       MooseX::Meta::TypeConstraint::Structured
695

AUTHORS

697       ·   John Napiorkowski <jjnapiork@cpan.org>
698
699       ·   Florian Ragwitz <rafl@debian.org>
700
701       ·   Yuval Kogman <nothingmuch@woobling.org>
702
703       ·   Tomas Doran <bobtfish@bobtfish.net>
704
705       ·   Robert Sedlacek <rs@474.at>
706
708       This software is copyright (c) 2011 by John Napiorkowski.
709
710       This is free software; you can redistribute it and/or modify it under
711       the same terms as the Perl 5 programming language system itself.
712
713
714
715perl v5.12.4                      2011-10-03      MooseX::Types::Structured(3)
Impressum