1MooseX::Types::StructurUesde(r3)Contributed Perl DocumenMtoaotsieoXn::Types::Structured(3)
2
3
4
6 MooseX::Types::Structured - MooseX::Types::Structured - Structured Type
7 Constraints for Moose
8
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
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
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
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
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
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
691 The following modules or resources may be of interest.
692
693 Moose, MooseX::Types, Moose::Meta::TypeConstraint,
694 MooseX::Meta::TypeConstraint::Structured
695
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)