Fixed a couple doc typos
[gitmo/MooseX-Types-Structured.git] / lib / MooseX / Types / Structured.pm
CommitLineData
d24da8ec 1package MooseX::Types::Structured;
2
6c2f284c 3use Moose;
4use Moose::Util::TypeConstraints;
a30fa891 5use MooseX::Meta::TypeConstraint::Structured;
6use MooseX::Types -declare => [qw(Dict Tuple)];
011bacc6 7
6c2f284c 8
d24da8ec 9our $VERSION = '0.01';
10our $AUTHORITY = 'cpan:JJNAPIORK';
11
12=head1 NAME
13
14MooseX::Types::Structured; Structured Type Constraints for Moose
15
16=head1 SYNOPSIS
17
6c2f284c 18The following is example usage for this module. You can define a class that has
19an attribute with a structured type like so:
20
21 package MyApp::MyClass;
22
23 use Moose;
24 use MooseX::Types::Moose qw(Str Int);
25 use MooseX::Types::Structured qw(Dict Tuple);
26
27 has name => (isa=>Dict[first_name=>Str, last_name=>Str]);
28
29Then you can instantiate this class with something like:
30
31 my $instance = MyApp::MyClass->new(
32 name=>{first_name=>'John', last_name=>'Napiorkowski'},
33 );
d24da8ec 34
6c2f284c 35But all of these would cause an error:
36
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});
40
41Please see the test cases for more examples.
d24da8ec 42
43=head1 DESCRIPTION
44
6c2f284c 45This type library enables structured type constraints. Basically, this is very
46similar to parameterized constraints that are built into the core Moose types,
47except that you are allowed to define the container's entire structure. For
48example, you could define a parameterized constraint like so:
49
50 subtype HashOfInts, as Hashref[Int];
51
52which would constraint a value to something like [1,2,3,...] and so one. A
53structured constraint like so:
54
55 subtype StringFollowedByInt, as Tuple[Str,Int];
56
57would constrain it's value to something like ['hello', 111];
58
59These structures can be as simple or elaborate as you wish. You can even
60combine various structured, parameterized and simple constraints all together:
61
62 subtype crazy, as Tuple[Int, Dict[name=>Str, age=>Int], ArrayRef[Int]];
63
64Which would match "[1, {name=>'John', age=>25},[10,11,12]]".
65
66You should exercise some care as to whether or not your complex structured
67constraints would be better off contained by a real object as in the following
68example:
69
70 {
71 package MyApp::MyStruct;
72 use Moose;
73
74 has $_ for qw(name age);
75
76 package MyApp::MyClass;
77 use Moose;
78
79 has person => (isa=>'MyApp::MyStruct');
80 }
81
82 my $instance = MyApp::MyClass
83 ->new( person=>MyApp::MyStruct->new(name=>'John', age=>39) );
84
85This method may take some additional time to setup but will give you more
86flexibility. However, structured constraints are highly compatible with this
87method, granting some interesting possibilities for coercion. Try:
88
89 subtype 'MyStruct',
90 as 'MyApp::MyStruct';
91
92 coerce 'MyStruct',
93 from (Dict[name=>Str, age=>Int]),
94 via {
95 MyApp::MyStruct->new(%$_);
96 },
97 from (Dict[last_name=>Str, first_name=>Str, dob=>DateTime]),
98 via {
f9468aac 99 my $name = $_->{first_name} .' '. $_->{last_name};
100 my $age = DateTime->now - $_->{dob};
6c2f284c 101 MyApp::MyStruct->new(
102 name=>$name,
103 age=>$age->years );
104 };
a4a88fef 105
f9468aac 106You also need to exercise some care when you try to subtype a structured type
a4a88fef 107as in this example:
d24da8ec 108
a4a88fef 109 subtype Person,
110 as Dict[name=>Str, age=>iIt];
111
112 subtype FriendlyPerson,
113 as Person[name=>Str, age=>Int, totalFriends=>Int];
114
115This will actually work BUT you have to take care the the subtype has a
116structure that does not contradict the structure of it's parent. For now the
117above works, but I will probably clarify how this works at a future point, so
118it's recommended to avoid (should not realy be needed so much anyway). For
119now this is supported in an EXPERIMENTAL way.
d24da8ec 120
a30fa891 121=cut
122
67a8bc04 123Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
124 MooseX::Meta::TypeConstraint::Structured->new(
125 name => "MooseX::Types::Structured::Tuple" ,
126 parent => find_type_constraint('ArrayRef'),
127 constraint_generator=> sub {
128 ## Get the constraints and values to check
129 my @type_constraints = @{shift @_};
130 my @values = @{shift @_};
131 ## Perform the checking
132 while(@type_constraints) {
133 my $type_constraint = shift @type_constraints;
a30fa891 134 if(@values) {
67a8bc04 135 my $value = shift @values;
136 unless($type_constraint->check($value)) {
137 return;
138 }
139 } else {
a30fa891 140 return;
a30fa891 141 }
142 }
67a8bc04 143 ## Make sure there are no leftovers.
144 if(@values) {
145 return;
146 } elsif(@type_constraints) {
147 return;
148 }else {
149 return 1;
150 }
151 }
152 )
153);
154
155Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
156 MooseX::Meta::TypeConstraint::Structured->new(
157 name => "MooseX::Types::Structured::Dict",
158 parent => find_type_constraint('HashRef'),
159 constraint_generator=> sub {
160 ## Get the constraints and values to check
161 my %type_constraints = @{shift @_};
162 my %values = %{shift @_};
163 ## Perform the checking
164 while(%type_constraints) {
165 my($key, $type_constraint) = each %type_constraints;
166 delete $type_constraints{$key};
167 if(exists $values{$key}) {
168 my $value = $values{$key};
169 delete $values{$key};
170 unless($type_constraint->check($value)) {
a30fa891 171 return;
172 }
67a8bc04 173 } else {
a30fa891 174 return;
a30fa891 175 }
67a8bc04 176 }
177 ## Make sure there are no leftovers.
178 if(%values) {
179 return;
180 } elsif(%type_constraints) {
181 return;
182 }else {
183 return 1;
184 }
185 },
186 )
187);
d24da8ec 188
189=head1 SEE ALSO
190
191The following modules or resources may be of interest.
192
a30fa891 193L<Moose>, L<MooseX::TypeLibrary>, L<Moose::Meta::TypeConstraint>,
194L<MooseX::Meta::TypeConstraint::Structured>
d24da8ec 195
196=head1 AUTHOR
197
198John Napiorkowski, C<< <jjnapiork@cpan.org> >>
199
200=head1 COPYRIGHT & LICENSE
201
202This program is free software; you can redistribute it and/or modify
203it under the same terms as Perl itself.
204
205=cut
67a8bc04 206
2071;