1 package MooseX::Types::Structured;
4 use Moose::Util::TypeConstraints;
5 use MooseX::Meta::TypeConstraint::Structured;
6 use MooseX::Types -declare => [qw(Dict Tuple)];
9 our $AUTHORITY = 'cpan:JJNAPIORK';
13 MooseX::Types::Structured - Structured Type Constraints for Moose
17 The following is example usage for this module.
19 package MyApp::MyClass;
22 use MooseX::Types::Moose qw(Str Int);
23 use MooseX::Types::Structured qw(Dict Tuple);
25 has name => (isa=>Dict[first_name=>Str, last_name=>Str]);
27 Then you can instantiate this class with something like:
29 my $instance = MyApp::MyClass->new(
30 name=>{first_name=>'John', last_name=>'Napiorkowski'},
33 But all of these would cause an error:
35 my $instance = MyApp::MyClass->new(name=>'John');
36 my $instance = MyApp::MyClass->new(name=>{first_name=>'John'});
37 my $instance = MyApp::MyClass->new(name=>{first_name=>'John', age=>39});
39 Please see the test cases for more examples.
43 A structured type constraint is a standard container L</Moose> type constraint,
44 such as an arrayref or hashref, which has been enhanced to allow you to
45 explicitly name all the allow type constraints inside the structure. The
48 TypeConstraint[TypeParameters]
50 Where TypeParameters is a list of type constraints.
52 This type library enables structured type constraints. It is build on top of the
53 L<MooseX::Types> library system, so you should review the documentation for that
54 if you are not familiar with it.
56 =head Comparing Parameterized types to Structured types
58 Parameterized constraints are built into the core Moose types 'HashRef' and
59 'ArrayRef'. Structured types have similar functionality, so their syntax is
60 likewise similar. For example, you could define a parameterized constraint like:
65 which would constraint a value to something like [1,2,3,...] and so on. On the
66 other hand, a structured type constraint explicitly names all it's allowed type
67 parameter constraints. For the example:
69 subtype StringFollowedByInt,
72 would constrain it's value to something like ['hello', 111] but ['hello', 'world']
73 would fail, as well as ['hello', 111, 'world']
75 These structures can be as simple or elaborate as you wish. You can even
76 combine various structured, parameterized and simple constraints all together:
81 Dict[name=>Str, age=>Int],
85 Which would match "[1, {name=>'John', age=>25},[10,11,12]]". Please notice how
86 the type parameters can be visually arranged to your liking and to improve the
87 clarity of your meaning. You don't need to run then altogether onto a single
92 You should exercise some care as to whether or not your complex structured
93 constraints would be better off contained by a real object as in the following
96 package MyApp::MyStruct;
99 has $_ for qw(name age);
101 package MyApp::MyClass;
104 has person => (isa=>'MyApp::MyStruct');
106 my $instance = MyApp::MyClass->new(
107 person=>MyApp::MyStruct->new(name=>'John', age=>39),
110 This method may take some additional time to setup but will give you more
111 flexibility. However, structured constraints are highly compatible with this
112 method, granting some interesting possibilities for coercion. Try:
115 as 'MyApp::MyStruct';
118 from (Dict[name=>Str, age=>Int]),
119 via { MyApp::MyStruct->new(%$_) },
120 from (Dict[last_name=>Str, first_name=>Str, dob=>DateTime]),
122 my $name = $_->{first_name} .' '. $_->{last_name};
123 my $age = DateTime->now - $_->{dob};
124 MyApp::MyStruct->new( name=>$name, age=>$age->years );
127 =head2 Subtyping a structured subtype
129 You need to exercise some care when you try to subtype a structured type
133 as Dict[name=>Str, age=>iIt];
135 subtype FriendlyPerson,
136 as Person[name=>Str, age=>Int, totalFriends=>Int];
138 This will actually work BUT you have to take care that the subtype has a
139 structure that does not contradict the structure of it's parent. For now the
140 above works, but I will clarify the syntax for this at a future point, so
141 it's recommended to avoid (should not realy be needed so much anyway). For
142 now this is supported in an EXPERIMENTAL way. Your thoughts, test cases and
143 patches are welcomed for discussion.
147 Coercions currently work for 'one level' deep. That is you can do:
150 as Dict[name=>Str, age=>Int];
153 as Dict[first=>Str, last=>Str];
156 from BlessedPersonObject,
157 via { +{name=>$_->name, age=>$_->age} },
159 via { +{name=>$_->[0], age=>$_->[1] },
160 from Dict[fullname=>Fullname, dob=>DateTime],
162 my $age = $_->dob - DateTime->now;
164 name=> $_->{fullname}->{first} .' '. $_->{fullname}->{last},
169 And that should just work as expected. However, if there are any 'inner'
170 coercions, such as a coercion on 'Fullname' or on 'DateTime', that coercion
171 won't currently get activated.
173 Please see the test '07-coerce.t' for a more detailed example.
175 =head1 TYPE CONSTRAINTS
177 This type library defines the following constraints.
179 =head2 Tuple[@constraints]
181 This defines an arrayref based constraint which allows you to validate a specific
182 list of constraints. For example:
184 Tuple[Int,Str]; ## Validates [1,'hello']
185 Tuple[Str|Object, Int]; ##Validates ['hello', 1] or [$object, 2]
187 =head2 Dict [%constraints]
189 This defines a hashref based constraint which allowed you to validate a specific
190 hashref. For example:
192 Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
196 Here are some additional example usage for structured types. All examples can
197 be found also in the 't/examples.t' test. Your contributions are also welcomed.
199 =head2 Normalize a HashRef
201 You need a hashref to conform to a canonical structure but are required accept a
202 bunch of different incoming structures. You can normalize using the Dict type
203 constraint and coercions. This example also shows structured types mixed which
204 other MooseX::Types libraries.
206 package Test::MooseX::Meta::TypeConstraint::Structured::Examples::Normalize;
211 use MooseX::Types::Structured qw(Dict Tuple);
212 use MooseX::Types::DateTime qw(DateTime);
213 use MooseX::Types::Moose qw(Int Str Object);
214 use MooseX::Types -declare => [qw(Name Age Person)];
217 as Dict[name=>Str, age=>Int];
220 from Dict[first=>Str, last=>Str, years=>Int],
222 name => "$_->{first} $_->{last}",
225 from Dict[fullname=>Dict[last=>Str, first=>Str], dob=>DateTime],
227 name => "$_->{fullname}{first} $_->{fullname}{last}",
228 age => ($_->{dob} - 'DateTime'->now)->years,
231 has person => (is=>'rw', isa=>Person, coerce=>1);
235 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
236 MooseX::Meta::TypeConstraint::Structured->new(
237 name => "MooseX::Types::Structured::Tuple" ,
238 parent => find_type_constraint('ArrayRef'),
239 constraint_generator=> sub {
240 ## Get the constraints and values to check
241 my @type_constraints = @{shift @_};
242 my @values = @{shift @_};
243 ## Perform the checking
244 while(@type_constraints) {
245 my $type_constraint = shift @type_constraints;
247 my $value = shift @values;
248 unless($type_constraint->check($value)) {
255 ## Make sure there are no leftovers.
258 } elsif(@type_constraints) {
267 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
268 MooseX::Meta::TypeConstraint::Structured->new(
269 name => "MooseX::Types::Structured::Dict",
270 parent => find_type_constraint('HashRef'),
271 constraint_generator=> sub {
272 ## Get the constraints and values to check
273 my %type_constraints = @{shift @_};
274 my %values = %{shift @_};
275 ## Perform the checking
276 while(%type_constraints) {
277 my($key, $type_constraint) = each %type_constraints;
278 delete $type_constraints{$key};
279 if(exists $values{$key}) {
280 my $value = $values{$key};
281 delete $values{$key};
282 unless($type_constraint->check($value)) {
289 ## Make sure there are no leftovers.
292 } elsif(%type_constraints) {
303 The following modules or resources may be of interest.
305 L<Moose>, L<MooseX::TypeLibrary>, L<Moose::Meta::TypeConstraint>,
306 L<MooseX::Meta::TypeConstraint::Structured>
310 Need to clarify deep coercions, need to clarify subtypes of subtypes.
314 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
316 =head1 COPYRIGHT & LICENSE
318 This program is free software; you can redistribute it and/or modify
319 it under the same terms as Perl itself.