more refactoring and first go at getting the tests to work again
[gitmo/MooseX-Dependent.git] / lib / MooseX / Dependent / Meta / TypeConstraint / Dependent.pm
CommitLineData
3cfd35fd 1package ## Hide from PAUSE
a588ee00 2 MooseX::Dependent::Meta::TypeConstraint::Dependent;
3cfd35fd 3
4use Moose;
5use Moose::Util::TypeConstraints ();
3cfd35fd 6extends 'Moose::Meta::TypeConstraint';
7
8=head1 NAME
9
a588ee00 10MooseX::Dependent::Meta::TypeConstraint::Dependent - Metaclass for Dependent type constraints.
3cfd35fd 11
12=head1 DESCRIPTION
13
a588ee00 14see L<MooseX::Dependent> for examples and details of how to use dependent
3cfd35fd 15types. This class is a subclass of L<Moose::Meta::TypeConstraint> which
16provides the gut functionality to enable dependent type constraints.
17
18=head1 ATTRIBUTES
19
20This class defines the following attributes.
21
a588ee00 22=head2 parent_type_constraint
3cfd35fd 23
a588ee00 24The type constraint whose validity is being made dependent.
3cfd35fd 25
26=cut
27
a588ee00 28has 'parent_type_constraint' => (
3cfd35fd 29 is=>'ro',
3a5dab74 30 isa=>'Object',
a588ee00 31 predicate=>'has_parent_type_constraint',
32 default=> sub {
33 Moose::Util::TypeConstraints::find_type_constraint("Any");
3a5dab74 34 },
a588ee00 35 required=>1,
3cfd35fd 36);
37
a588ee00 38=head2 constraining_value_type_constraint
3cfd35fd 39
40This is a type constraint which defines what kind of value is allowed to be the
a588ee00 41constraining value of the dependent type.
3cfd35fd 42
43=cut
44
a588ee00 45has 'constraining_value_type_constraint' => (
3cfd35fd 46 is=>'ro',
3a5dab74 47 isa=>'Object',
a588ee00 48 predicate=>'has_constraining_value_type_constraint',
49 default=> sub {
50 Moose::Util::TypeConstraints::find_type_constraint("Any");
3a5dab74 51 },
a588ee00 52 required=>1,
3cfd35fd 53);
54
a588ee00 55=head2 constrainting_value
3cfd35fd 56
a588ee00 57This is the actual value that constraints the L</parent_type_constraint>
01a12424 58
3cfd35fd 59=cut
60
a588ee00 61has 'constraining_value' => (
62 reader=>'constraining_value',
63 writer=>'_set_constraining_value',
64 predicate=>'has_constraining_value',
3cfd35fd 65);
66
67=head2 constraint_generator
68
69A subref or closure that contains the way we validate incoming values against
70a set of type constraints.
71
3cfd35fd 72
73has 'constraint_generator' => (
74 is=>'ro',
75 isa=>'CodeRef',
76 predicate=>'has_constraint_generator',
3a5dab74 77 required=>1,
3cfd35fd 78);
79
80=head1 METHODS
81
82This class defines the following methods.
83
41cf7457 84=head2 validate
85
0712792e 86We intercept validate in order to custom process the message.
41cf7457 87
0712792e 88override 'validate' => sub {
89 my ($self, @args) = @_;
ae1d0652 90 my $compiled_type_constraint = $self->_compiled_type_constraint;
91 my $message = bless {message=>undef}, 'MooseX::Types::Dependent::Message';
92 my $result = $compiled_type_constraint->(@args, $message);
93
41cf7457 94 if($result) {
95 return $result;
96 } else {
ae1d0652 97 my $args = Devel::PartialDump::dump(@args);
98 if(my $message = $message->{message}) {
99 return $self->get_message("$args, Internal Validation Error is: $message");
100 } else {
101 return $self->get_message($args);
41cf7457 102 }
103 }
104};
105
3cfd35fd 106=head2 generate_constraint_for ($type_constraints)
107
108Given some type constraints, use them to generate validation rules for an ref
109of values (to be passed at check time)
110
3cfd35fd 111
112sub generate_constraint_for {
9b6d2e22 113 my ($self, $callback) = @_;
3a5dab74 114 return sub {
41cf7457 115 my $dependent_pair = shift @_;
9b6d2e22 116 my ($dependent, $constraining) = @$dependent_pair;
3313d2a6 117
118 ## First need to test the bits
9b6d2e22 119 unless($self->check_dependent($dependent)) {
ae1d0652 120 $_[0]->{message} = $self->get_message_dependent($dependent)
121 if $_[0];
122 return;
3313d2a6 123 }
124
9b6d2e22 125 unless($self->check_constraining($constraining)) {
ae1d0652 126 $_[0]->{message} = $self->get_message_constraining($constraining)
127 if $_[0];
3313d2a6 128 return;
129 }
130
3cfd35fd 131 my $constraint_generator = $self->constraint_generator;
3a5dab74 132 return $constraint_generator->(
9b6d2e22 133 $dependent,
3a5dab74 134 $callback,
9b6d2e22 135 $constraining,
3a5dab74 136 );
3cfd35fd 137 };
138}
139
3a5dab74 140=head2 parameterize ($dependent, $callback, $constraining)
3cfd35fd 141
142Given a ref of type constraints, create a structured type.
143
144=cut
145
146sub parameterize {
9b6d2e22 147 my ($self, $dependent_tc, $callback, $constraining_tc) = @_;
a588ee00 148
149 die 'something';
150
3cfd35fd 151 my $class = ref $self;
9b6d2e22 152 my $name = $self->_generate_subtype_name($dependent_tc, $callback, $constraining_tc);
3cfd35fd 153 my $constraint_generator = $self->__infer_constraint_generator;
154
155 return $class->new(
156 name => $name,
157 parent => $self,
9b6d2e22 158 dependent_type_constraint=>$dependent_tc,
3a5dab74 159 comparison_callback=>$callback,
3cfd35fd 160 constraint_generator => $constraint_generator,
9b6d2e22 161 constraining_type_constraint => $constraining_tc,
3cfd35fd 162 );
163}
164
165=head2 _generate_subtype_name
166
167Returns a name for the dependent type that should be unique
168
169=cut
170
171sub _generate_subtype_name {
a588ee00 172 my ($self, $parent_tc, $constraining_tc) = @_;
3cfd35fd 173 return sprintf(
a588ee00 174 "%s_depends_on_%s",
175 $parent_tc, $constraining_tc,
3cfd35fd 176 );
177}
178
179=head2 __infer_constraint_generator
180
181This returns a CODEREF which generates a suitable constraint generator. Not
182user servicable, you'll never call this directly.
183
ae1d0652 184 TBD, this is definitely going to need some work. Cargo culted from some
185 code I saw in Moose::Meta::TypeConstraint::Parameterized or similar. I
186 Don't think I need this, since Dependent types require parameters, so
187 will always have a constrain generator.
3cfd35fd 188
189=cut
190
191sub __infer_constraint_generator {
192 my ($self) = @_;
193 if($self->has_constraint_generator) {
194 return $self->constraint_generator;
195 } else {
ae1d0652 196 warn "I'm doing the questionable infer generator thing";
3cfd35fd 197 return sub {
198 ## I'm not sure about this stuff but everything seems to work
199 my $tc = shift @_;
200 my $merged_tc = [
201 @$tc,
3cfd35fd 202 ];
203
204 $self->constraint->($merged_tc, @_);
205 };
206 }
207}
208
209=head2 compile_type_constraint
210
211hook into compile_type_constraint so we can set the correct validation rules.
212
213=cut
214
215around 'compile_type_constraint' => sub {
3a5dab74 216 my ($compile_type_constraint, $self) = @_;
3cfd35fd 217
3a5dab74 218 if($self->has_comparison_callback &&
219 $self->has_constraining_type_constraint) {
220 my $generated_constraint = $self->generate_constraint_for(
221 $self->comparison_callback,
3a5dab74 222 );
ae1d0652 223 $self->_set_constraint($generated_constraint);
3cfd35fd 224 }
225
3a5dab74 226 return $self->$compile_type_constraint;
3cfd35fd 227};
228
229=head2 create_child_type
230
231modifier to make sure we get the constraint_generator
232
9b6d2e22 233=cut
234
3cfd35fd 235around 'create_child_type' => sub {
236 my ($create_child_type, $self, %opts) = @_;
237 return $self->$create_child_type(
238 %opts,
a588ee00 239 #constraint_generator => $self->__infer_constraint_generator,
3cfd35fd 240 );
241};
242
3cfd35fd 243=head2 equals
244
245Override the base class behavior.
246
3cfd35fd 247sub equals {
248 my ( $self, $type_or_name ) = @_;
a5d0a8be 249 my $other = Moose::Util::TypeConstraints::find_type_constraint("$type_or_name");
3cfd35fd 250
3cfd35fd 251 return (
a5d0a8be 252 $other->isa(__PACKAGE__)
253 and
254 $self->dependent_type_constraint->equals($other)
3cfd35fd 255 and
a5d0a8be 256 $self->constraining_type_constraint->equals($other)
257 and
258 $self->parent->equals($other->parent)
3cfd35fd 259 );
260}
261
3cfd35fd 262=head2 get_message
263
ae1d0652 264Give you a better peek into what's causing the error.
3cfd35fd 265
3cfd35fd 266around 'get_message' => sub {
267 my ($get_message, $self, $value) = @_;
ae1d0652 268 return $self->$get_message($value);
3cfd35fd 269};
270
a588ee00 271=head2 _throw_error ($error)
272
273Given a string, delegate to the Moose exception object
274
275=cut
276
277sub _throw_error {
278 my $self = shift @_;
279 my $err = defined $_[0] ? $_[0] : 'Exception Thrown without Message';
280 require Moose; Moose->throw_error($err);
281}
282
3cfd35fd 283=head1 SEE ALSO
284
285The following modules or resources may be of interest.
286
287L<Moose>, L<Moose::Meta::TypeConstraint>
288
289=head1 AUTHOR
290
291John Napiorkowski, C<< <jjnapiork@cpan.org> >>
292
293=head1 COPYRIGHT & LICENSE
294
295This program is free software; you can redistribute it and/or modify
296it under the same terms as Perl itself.
297
298=cut
299
3a5dab74 300__PACKAGE__->meta->make_immutable;