1 package MooseX::Types::Structured;
4 use Moose::Util::TypeConstraints;
5 use MooseX::Meta::TypeConstraint::Structured;
6 use MooseX::Types -declare => [qw(Dict Tuple)];
10 our $AUTHORITY = 'cpan:JJNAPIORK';
14 MooseX::Types::Structured - Structured Type Constraints for Moose
18 The following is example usage for this module.
20 package MyApp::MyClass;
23 use MooseX::Types::Moose qw(Str Int);
24 use MooseX::Types::Structured qw(Dict Tuple);
26 has name => (isa=>Dict[first_name=>Str, last_name=>Str]);
28 Then you can instantiate this class with something like:
30 my $instance = MyApp::MyClass->new(
31 name=>{first_name=>'John', last_name=>'Napiorkowski'},
34 But all of these would cause an error:
36 my $instance = MyApp::MyClass->new(name=>'John');
37 my $instance = MyApp::MyClass->new(name=>{first_name=>'John'});
38 my $instance = MyApp::MyClass->new(name=>{first_name=>'John', age=>39});
40 Please see the test cases for more examples.
44 A structured type constraint is a standard container L</Moose> type constraint,
45 such as an arrayref or hashref, which has been enhanced to allow you to
46 explicitely name all the allow type constraints inside the structure. The
49 TypeConstraint[TypeParameters]
51 Where TypeParameters is a list of type constraints.
53 This type library enables structured type constraints. These work in a similar
54 way to parameterized constraints that are built into the core Moose types,
55 except that you are allowed to define the container's entire structure. For
56 example, you could define a parameterized constraint like so:
61 which would constraint a value to something like [1,2,3,...] and so on. On the
62 other hand, a structured type constrain explicitly names all it's allowed type
63 parameter constraints. For the example:
65 subtype StringFollowedByInt,
68 would constrain it's value to something like ['hello', 111];
70 These structures can be as simple or elaborate as you wish. You can even
71 combine various structured, parameterized and simple constraints all together:
76 Dict[name=>Str, age=>Int],
80 Which would match "[1, {name=>'John', age=>25},[10,11,12]]". Please notice how
83 You should exercise some care as to whether or not your complex structured
84 constraints would be better off contained by a real object as in the following
87 package MyApp::MyStruct;
90 has $_ for qw(name age);
92 package MyApp::MyClass;
95 has person => (isa=>'MyApp::MyStruct');
97 my $instance = MyApp::MyClass->new(
98 person=>MyApp::MyStruct->new(name=>'John', age=>39),
101 This method may take some additional time to setup but will give you more
102 flexibility. However, structured constraints are highly compatible with this
103 method, granting some interesting possibilities for coercion. Try:
106 as 'MyApp::MyStruct';
109 from (Dict[name=>Str, age=>Int]),
111 MyApp::MyStruct->new(%$_);
113 from (Dict[last_name=>Str, first_name=>Str, dob=>DateTime]),
115 my $name = $_->{first_name} .' '. $_->{last_name};
116 my $age = DateTime->now - $_->{dob};
117 MyApp::MyStruct->new(
122 =head2 Subtyping a structured subtype
124 You need to exercise some care when you try to subtype a structured type
128 as Dict[name=>Str, age=>iIt];
130 subtype FriendlyPerson,
131 as Person[name=>Str, age=>Int, totalFriends=>Int];
133 This will actually work BUT you have to take care that the subtype has a
134 structure that does not contradict the structure of it's parent. For now the
135 above works, but I will probably clarify how this works at a future point, so
136 it's recommended to avoid (should not realy be needed so much anyway). For
137 now this is supported in an EXPERIMENTAL way. In the future we will probably
138 clarify how to augment existing structured types.
142 Coercions currently work for 'one level' deep. That is you can do:
145 as Dict[name=>Str, age=>Int];
148 as Dict[first=>Str, last=>Str];
151 from BlessedPersonObject,
152 via { +{name=>$_->name, age=>$_->age} },
154 via { +{name=>$_->[0], age=>$_->[1] },
155 from Dict[fullname=>Fullname, dob=>DateTime],
157 my $age = $_->dob - DateTime->now;
159 name=> $_->{fullname}->{first} .' '. $_->{fullname}->{last},
164 And that should just work as expected. However, if there are any 'inner'
165 coercions, such as a coercion on 'Fullname' or on 'DateTime', that coercion
166 won't currently get activated.
168 Please see the test '07-coerce.t' for a more detailed example.
170 =head1 TYPE CONSTRAINTS
172 This type library defines the following constraints.
174 =head2 Tuple[@constraints]
176 This defines an arrayref based constraint which allows you to validate a specific
177 list of constraints. For example:
179 Tuple[Int,Str]; ## Validates [1,'hello']
180 Tuple[Str|Object, Int]; ##Validates ['hello', 1] or [$object, 2]
182 =head2 Dict [%constraints]
184 This defines a hashref based constraint which allowed you to validate a specific
185 hashref. For example:
187 Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
191 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
192 MooseX::Meta::TypeConstraint::Structured->new(
193 name => "MooseX::Types::Structured::Tuple" ,
194 parent => find_type_constraint('ArrayRef'),
195 constraint_generator=> sub {
196 ## Get the constraints and values to check
197 my @type_constraints = @{shift @_};
198 my @values = @{shift @_};
199 ## Perform the checking
200 while(@type_constraints) {
201 my $type_constraint = shift @type_constraints;
203 my $value = shift @values;
204 unless($type_constraint->check($value)) {
211 ## Make sure there are no leftovers.
214 } elsif(@type_constraints) {
223 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
224 MooseX::Meta::TypeConstraint::Structured->new(
225 name => "MooseX::Types::Structured::Dict",
226 parent => find_type_constraint('HashRef'),
227 constraint_generator=> sub {
228 ## Get the constraints and values to check
229 my %type_constraints = @{shift @_};
230 my %values = %{shift @_};
231 ## Perform the checking
232 while(%type_constraints) {
233 my($key, $type_constraint) = each %type_constraints;
234 delete $type_constraints{$key};
235 if(exists $values{$key}) {
236 my $value = $values{$key};
237 delete $values{$key};
238 unless($type_constraint->check($value)) {
240 #if ($type_constraint->has_coercion) {
241 # my $temp = $type_constraint->coerce($value);
242 # use Data::Dump qw/dump/; warn dump $value, $temp;
243 # unless($type_constraint->check($temp)) {
254 ## Make sure there are no leftovers.
257 } elsif(%type_constraints) {
268 The following modules or resources may be of interest.
270 L<Moose>, L<MooseX::TypeLibrary>, L<Moose::Meta::TypeConstraint>,
271 L<MooseX::Meta::TypeConstraint::Structured>
275 Need to clarify deep coercions, need to clarify subtypes of subtypes.
279 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
281 =head1 COPYRIGHT & LICENSE
283 This program is free software; you can redistribute it and/or modify
284 it under the same terms as Perl itself.