new tag for version 0.03
[gitmo/MooseX-Types-Structured.git] / lib / MooseX / Meta / TypeConstraint / Structured.pm
1 package ## Hide from PAUSE
2  MooseX::Meta::TypeConstraint::Structured;
3
4 use Moose;
5 use Moose::Util::TypeConstraints ();
6 use MooseX::Meta::TypeCoercion::Structured;
7 extends 'Moose::Meta::TypeConstraint';
8
9 =head1 NAME
10
11 MooseX::Meta::TypeConstraint::Structured - Structured type constraints.
12
13 =head1 DESCRIPTION
14
15 A structure is a set of L<Moose::Meta::TypeConstraint> that are 'aggregated' in
16 such a way as that they are all applied to an incoming list of arguments.  The
17 idea here is that a Type Constraint could be something like, "An Int followed by
18 an Int and then a Str" and that this could be done so with a declaration like:
19
20     Tuple[Int,Int,Str]; ## Example syntax
21     
22 So a structure is a list of Type constraints (the "Int,Int,Str" in the above
23 example) which are intended to function together.
24
25 =head1 ATTRIBUTES
26
27 This class defines the following attributes.
28
29 =head2 type_constraints
30
31 A list of L<Moose::Meta::TypeConstraint> objects.
32
33 =cut
34
35 has 'type_constraints' => (
36     is=>'ro',
37     isa=>'Ref',
38     predicate=>'has_type_constraints',
39 );
40
41 =head2 constraint_generator
42
43 A subref or closure that contains the way we validate incoming values against
44 a set of type constraints.
45
46 =cut
47
48 has 'constraint_generator' => (is=>'ro', isa=>'CodeRef');
49
50 =head1 METHODS
51
52 This class defines the following methods.
53
54 =head2 new
55
56 Initialization stuff.
57
58 =cut
59
60 around 'new' => sub {
61     my ($new, $class, @args)  = @_;
62     my $self = $class->$new(@args);
63     $self->coercion(MooseX::Meta::TypeCoercion::Structured->new(
64         type_constraint => $self,
65     ));
66     return $self;
67 };
68
69 =head2 generate_constraint_for ($type_constraints)
70
71 Given some type constraints, use them to generate validation rules for an ref
72 of values (to be passed at check time)
73
74 =cut
75
76 sub generate_constraint_for {
77     my ($self, $type_constraints) = @_;
78     return sub {
79         my $constraint_generator = $self->constraint_generator;
80         return $constraint_generator->($type_constraints, @_);
81     };
82 }
83
84 =head2 parameterize (@type_constraints)
85
86 Given a ref of type constraints, create a structured type.
87
88 =cut
89
90 sub parameterize {
91     my ($self, @type_constraints) = @_;
92     my $class = ref $self;
93     my $name = $self->name .'['. join(',', map {"$_"} @type_constraints) .']';
94
95     return $class->new(
96         name => $name,
97         parent => $self,
98         type_constraints => \@type_constraints,
99         constraint_generator => $self->constraint_generator || sub {
100             my $tc = shift @_;
101             my $merged_tc = [@$tc, @{$self->parent->type_constraints}];
102             $self->constraint->($merged_tc, @_);
103         },
104     );
105 }
106
107 =head2 compile_type_constraint
108
109 hook into compile_type_constraint so we can set the correct validation rules.
110
111 =cut
112
113 around 'compile_type_constraint' => sub {
114     my ($compile_type_constraint, $self, @args) = @_;
115     
116     if($self->has_type_constraints) {
117         my $type_constraints = $self->type_constraints;
118         my $constraint = $self->generate_constraint_for($type_constraints);
119         $self->_set_constraint($constraint);        
120     }
121
122     return $self->$compile_type_constraint(@args);
123 };
124
125 =head2 create_child_type
126
127 modifier to make sure we get the constraint_generator
128
129 =cut
130
131 around 'create_child_type' => sub {
132     my ($create_child_type, $self, %opts) = @_;
133     return $self->$create_child_type(
134         %opts,
135         constraint_generator => $self->constraint_generator,
136     );
137 };
138
139 =head2 is_a_type_of
140
141 =head2 is_subtype_of
142
143 =head2 equals
144
145 Override the base class behavior.
146
147 =cut
148
149 sub equals {
150     my ( $self, $type_or_name ) = @_;
151     my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
152
153     return unless $other->isa(__PACKAGE__);
154     
155     return (
156         $self->type_constraints_equals($other)
157             and
158         $self->parent->equals( $other->parent )
159     );
160 }
161
162 =head2 type_constraints_equals
163
164 Checks to see if the internal type contraints are equal.
165
166 =cut
167
168 sub type_constraints_equals {
169     my ($self, $other) = @_;
170     my @self_type_constraints = @{$self->type_constraints||[]};
171     my @other_type_constraints = @{$other->type_constraints||[]};
172     
173     ## Incoming ay be either arrayref or hashref, need top compare both
174     while(@self_type_constraints) {
175         my $self_type_constraint = shift @self_type_constraints;
176         my $other_type_constraint = shift @other_type_constraints
177          || return; ## $other needs the same number of children.
178         
179         if( ref $self_type_constraint) {
180             $self_type_constraint->equals($other_type_constraint)
181              || return; ## type constraints obviously need top be equal
182         } else {
183             $self_type_constraint eq $other_type_constraint
184              || return; ## strings should be equal
185         }
186
187     }
188     
189     return 1; ##If we get this far, everything is good.
190 }
191
192 =head2 get_message
193
194 May want to override this to set a more useful error message
195
196 =head1 SEE ALSO
197
198 The following modules or resources may be of interest.
199
200 L<Moose>, L<Moose::Meta::TypeConstraint>
201
202 =head1 AUTHOR
203
204 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
205
206 =head1 COPYRIGHT & LICENSE
207
208 This program is free software; you can redistribute it and/or modify
209 it under the same terms as Perl itself.
210
211 =cut
212
213 1;