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. You can define a class that has
19 an attribute with a structured type like so:
21 package MyApp::MyClass;
24 use MooseX::Types::Moose qw(Str Int);
25 use MooseX::Types::Structured qw(Dict Tuple);
27 has name => (isa=>Dict[first_name=>Str, last_name=>Str]);
29 Then you can instantiate this class with something like:
31 my $instance = MyApp::MyClass->new(
32 name=>{first_name=>'John', last_name=>'Napiorkowski'},
35 But all of these would cause an error:
37 my $instance = MyApp::MyClass->new(name=>'John');
38 my $instance = MyApp::MyClass->new(name=>{first_name=>'John'});
39 my $instance = MyApp::MyClass->new(name=>{first_name=>'John', age=>39});
41 Please see the test cases for more examples.
45 This type library enables structured type constraints. Basically, this is very
46 similar to parameterized constraints that are built into the core Moose types,
47 except that you are allowed to define the container's entire structure. For
48 example, you could define a parameterized constraint like so:
50 subtype HashOfInts, as Hashref[Int];
52 which would constraint a value to something like [1,2,3,...] and so one. A
53 structured constraint like so:
55 subtype StringFollowedByInt, as Tuple[Str,Int];
57 would constrain it's value to something like ['hello', 111];
59 These structures can be as simple or elaborate as you wish. You can even
60 combine various structured, parameterized and simple constraints all together:
62 subtype crazy, as Tuple[Int, Dict[name=>Str, age=>Int], ArrayRef[Int]];
64 Which would match "[1, {name=>'John', age=>25},[10,11,12]]".
66 You should exercise some care as to whether or not your complex structured
67 constraints would be better off contained by a real object as in the following
71 package MyApp::MyStruct;
74 has $_ for qw(name age);
76 package MyApp::MyClass;
79 has person => (isa=>'MyApp::MyStruct');
82 my $instance = MyApp::MyClass
83 ->new( person=>MyApp::MyStruct->new(name=>'John', age=>39) );
85 This method may take some additional time to setup but will give you more
86 flexibility. However, structured constraints are highly compatible with this
87 method, granting some interesting possibilities for coercion. Try:
93 from (Dict[name=>Str, age=>Int]),
95 MyApp::MyStruct->new(%$_);
97 from (Dict[last_name=>Str, first_name=>Str, dob=>DateTime]),
99 my $name = $_->{first_name} .' '. $_->{last_name};
100 my $age = DateTime->now - $_->{dob};
101 MyApp::MyStruct->new(
106 =head2 Subtyping a structured subtype
108 You need to exercise some care when you try to subtype a structured type
112 as Dict[name=>Str, age=>iIt];
114 subtype FriendlyPerson,
115 as Person[name=>Str, age=>Int, totalFriends=>Int];
117 This will actually work BUT you have to take care that the subtype has a
118 structure that does not contradict the structure of it's parent. For now the
119 above works, but I will probably clarify how this works at a future point, so
120 it's recommended to avoid (should not realy be needed so much anyway). For
121 now this is supported in an EXPERIMENTAL way. In the future we will probably
122 clarify how to augment existing structured types.
126 Coercions currently work for 'one level' deep. That is you can do:
129 as Dict[name=>Str, age=>Int];
132 as Dict[first=>Str, last=>Str];
135 from BlessedPersonObject,
136 via { +{name=>$_->name, age=>$_->age} },
138 via { +{name=>$_->[0], age=>$_->[1] },
139 from Dict[fullname=>Fullname, dob=>DateTime],
141 my $age = $_->dob - DateTime->now;
143 name=> $_->{fullname}->{first} .' '. $_->{fullname}->{last},
148 And that should just work as expected. However, if there are any 'inner'
149 coercions, such as a coercion on 'Fullname' or on 'DateTime', that coercion
150 won't currently get activated.
152 Please see the test '07-coerce.t' for a more detailed example.
154 =head1 TYPE CONSTRAINTS
156 This type library defines the following constraints.
158 =head2 Tuple[@constraints]
160 This defines an arrayref based constraint which allows you to validate a specific
161 list of constraints. For example:
163 Tuple[Int,Str]; ## Validates [1,'hello']
164 Tuple[Str|Object, Int]; ##Validates ['hello', 1] or [$object, 2]
166 =head2 Dict [%constraints]
168 This defines a hashref based constraint which allowed you to validate a specific
169 hashref. For example:
171 Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
175 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
176 MooseX::Meta::TypeConstraint::Structured->new(
177 name => "MooseX::Types::Structured::Tuple" ,
178 parent => find_type_constraint('ArrayRef'),
179 constraint_generator=> sub {
180 ## Get the constraints and values to check
181 my @type_constraints = @{shift @_};
182 my @values = @{shift @_};
183 ## Perform the checking
184 while(@type_constraints) {
185 my $type_constraint = shift @type_constraints;
187 my $value = shift @values;
188 unless($type_constraint->check($value)) {
195 ## Make sure there are no leftovers.
198 } elsif(@type_constraints) {
207 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
208 MooseX::Meta::TypeConstraint::Structured->new(
209 name => "MooseX::Types::Structured::Dict",
210 parent => find_type_constraint('HashRef'),
211 constraint_generator=> sub {
212 ## Get the constraints and values to check
213 my %type_constraints = @{shift @_};
214 my %values = %{shift @_};
215 ## Perform the checking
216 while(%type_constraints) {
217 my($key, $type_constraint) = each %type_constraints;
218 delete $type_constraints{$key};
219 if(exists $values{$key}) {
220 my $value = $values{$key};
221 delete $values{$key};
222 unless($type_constraint->check($value)) {
224 #if ($type_constraint->has_coercion) {
225 # my $temp = $type_constraint->coerce($value);
226 # use Data::Dump qw/dump/; warn dump $value, $temp;
227 # unless($type_constraint->check($temp)) {
238 ## Make sure there are no leftovers.
241 } elsif(%type_constraints) {
252 The following modules or resources may be of interest.
254 L<Moose>, L<MooseX::TypeLibrary>, L<Moose::Meta::TypeConstraint>,
255 L<MooseX::Meta::TypeConstraint::Structured>
259 Need to clarify deep coercions, need to clarify subtypes of subtypes.
263 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
265 =head1 COPYRIGHT & LICENSE
267 This program is free software; you can redistribute it and/or modify
268 it under the same terms as Perl itself.