Commit | Line | Data |
3cfd35fd |
1 | package ## Hide from PAUSE |
a588ee00 |
2 | MooseX::Dependent::Meta::TypeConstraint::Dependent; |
3cfd35fd |
3 | |
4 | use Moose; |
5 | use Moose::Util::TypeConstraints (); |
3cfd35fd |
6 | extends 'Moose::Meta::TypeConstraint'; |
7 | |
8 | =head1 NAME |
9 | |
a588ee00 |
10 | MooseX::Dependent::Meta::TypeConstraint::Dependent - Metaclass for Dependent type constraints. |
3cfd35fd |
11 | |
12 | =head1 DESCRIPTION |
13 | |
a588ee00 |
14 | see L<MooseX::Dependent> for examples and details of how to use dependent |
3cfd35fd |
15 | types. This class is a subclass of L<Moose::Meta::TypeConstraint> which |
16 | provides the gut functionality to enable dependent type constraints. |
17 | |
18 | =head1 ATTRIBUTES |
19 | |
20 | This class defines the following attributes. |
21 | |
a588ee00 |
22 | =head2 parent_type_constraint |
3cfd35fd |
23 | |
a588ee00 |
24 | The type constraint whose validity is being made dependent. |
3cfd35fd |
25 | |
26 | =cut |
27 | |
a588ee00 |
28 | has '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 | |
40 | This is a type constraint which defines what kind of value is allowed to be the |
a588ee00 |
41 | constraining value of the dependent type. |
3cfd35fd |
42 | |
43 | =cut |
44 | |
a588ee00 |
45 | has '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 |
57 | This is the actual value that constraints the L</parent_type_constraint> |
01a12424 |
58 | |
3cfd35fd |
59 | =cut |
60 | |
a588ee00 |
61 | has 'constraining_value' => ( |
62 | reader=>'constraining_value', |
63 | writer=>'_set_constraining_value', |
64 | predicate=>'has_constraining_value', |
3cfd35fd |
65 | ); |
66 | |
67 | =head2 constraint_generator |
68 | |
69 | A subref or closure that contains the way we validate incoming values against |
70 | a set of type constraints. |
71 | |
3cfd35fd |
72 | |
73 | has 'constraint_generator' => ( |
74 | is=>'ro', |
75 | isa=>'CodeRef', |
76 | predicate=>'has_constraint_generator', |
3a5dab74 |
77 | required=>1, |
3cfd35fd |
78 | ); |
79 | |
80 | =head1 METHODS |
81 | |
82 | This class defines the following methods. |
83 | |
41cf7457 |
84 | =head2 validate |
85 | |
0712792e |
86 | We intercept validate in order to custom process the message. |
41cf7457 |
87 | |
0712792e |
88 | override '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 | |
108 | Given some type constraints, use them to generate validation rules for an ref |
109 | of values (to be passed at check time) |
110 | |
3cfd35fd |
111 | |
112 | sub 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 | |
142 | Given a ref of type constraints, create a structured type. |
143 | |
144 | =cut |
145 | |
146 | sub 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 | |
167 | Returns a name for the dependent type that should be unique |
168 | |
169 | =cut |
170 | |
171 | sub _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 | |
181 | This returns a CODEREF which generates a suitable constraint generator. Not |
182 | user 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 | |
191 | sub __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 | |
211 | hook into compile_type_constraint so we can set the correct validation rules. |
212 | |
213 | =cut |
214 | |
215 | around '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 | |
231 | modifier to make sure we get the constraint_generator |
232 | |
9b6d2e22 |
233 | =cut |
234 | |
3cfd35fd |
235 | around '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 | |
245 | Override the base class behavior. |
246 | |
3cfd35fd |
247 | sub 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 |
264 | Give you a better peek into what's causing the error. |
3cfd35fd |
265 | |
3cfd35fd |
266 | around '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 | |
273 | Given a string, delegate to the Moose exception object |
274 | |
275 | =cut |
276 | |
277 | sub _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 | |
285 | The following modules or resources may be of interest. |
286 | |
287 | L<Moose>, L<Moose::Meta::TypeConstraint> |
288 | |
289 | =head1 AUTHOR |
290 | |
291 | John Napiorkowski, C<< <jjnapiork@cpan.org> >> |
292 | |
293 | =head1 COPYRIGHT & LICENSE |
294 | |
295 | This program is free software; you can redistribute it and/or modify |
296 | it under the same terms as Perl itself. |
297 | |
298 | =cut |
299 | |
3a5dab74 |
300 | __PACKAGE__->meta->make_immutable; |