1MooseX::Types::StructurUesde(r3pCmo)ntributed Perl DocumMeonotsaetXi:o:nTypes::Structured(3pm)
2
3
4

NAME

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

VERSION

9       version 0.36
10

SYNOPSIS

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

DESCRIPTION

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

TYPE CONSTRAINTS

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

EXPORTABLE SUBROUTINES

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

ERROR MESSAGES

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

EXAMPLES

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

SEE ALSO

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

SUPPORT

699       Bugs may be submitted through the RT bug tracker
700       <https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Types-
701       Structured> (or bug-MooseX-Types-Structured@rt.cpan.org <mailto:bug-
702       MooseX-Types-Structured@rt.cpan.org>).
703
704       There is also a mailing list available for users of this distribution,
705       at <http://lists.perl.org/list/moose.html>.
706
707       There is also an irc channel available for users of this distribution,
708       at "#moose" on "irc.perl.org" <irc://irc.perl.org/#moose>.
709

AUTHORS

711       •   John Napiorkowski <jjnapiork@cpan.org>
712
713       •   Florian Ragwitz <rafl@debian.org>
714
715       •   יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
716
717       •   Tomas (t0m) Doran <bobtfish@bobtfish.net>
718
719       •   Robert Sedlacek <rs@474.at>
720

CONTRIBUTORS

722       •   Karen Etheridge <ether@cpan.org>
723
724       •   Ricardo Signes <rjbs@cpan.org>
725
726       •   Dave Rolsky <autarch@urth.org>
727
728       •   Ansgar Burchardt <ansgar@43-1.org>
729
730       •   Stevan Little <stevan.little@iinteractive.com>
731
732       •   arcanez <justin.d.hunter@gmail.com>
733
734       •   Jesse Luehrs <doy@tozt.net>
735
736       •   D. Ilmari Mannsåker <ilmari@cpan.org>
737
739       This software is copyright (c) 2008 by John Napiorkowski.
740
741       This is free software; you can redistribute it and/or modify it under
742       the same terms as the Perl 5 programming language system itself.
743
744
745
746perl v5.38.0                      2023-07-21    MooseX::Types::Structured(3pm)
Impressum