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 (); |
21df4517 |
6 | use MooseX::Dependent::Meta::TypeCoercion::Dependent; |
0a9f5b94 |
7 | use Scalar::Util qw(blessed); |
9c319add |
8 | use Data::Dump; |
9 | use Digest::MD5; |
10 | |
3cfd35fd |
11 | extends 'Moose::Meta::TypeConstraint'; |
12 | |
13 | =head1 NAME |
14 | |
a588ee00 |
15 | MooseX::Dependent::Meta::TypeConstraint::Dependent - Metaclass for Dependent type constraints. |
3cfd35fd |
16 | |
17 | =head1 DESCRIPTION |
18 | |
a588ee00 |
19 | see L<MooseX::Dependent> for examples and details of how to use dependent |
3cfd35fd |
20 | types. This class is a subclass of L<Moose::Meta::TypeConstraint> which |
21 | provides the gut functionality to enable dependent type constraints. |
22 | |
23 | =head1 ATTRIBUTES |
24 | |
25 | This class defines the following attributes. |
26 | |
a588ee00 |
27 | =head2 parent_type_constraint |
3cfd35fd |
28 | |
a588ee00 |
29 | The type constraint whose validity is being made dependent. |
3cfd35fd |
30 | |
31 | =cut |
32 | |
a588ee00 |
33 | has 'parent_type_constraint' => ( |
3cfd35fd |
34 | is=>'ro', |
3a5dab74 |
35 | isa=>'Object', |
a588ee00 |
36 | default=> sub { |
37 | Moose::Util::TypeConstraints::find_type_constraint("Any"); |
3a5dab74 |
38 | }, |
a588ee00 |
39 | required=>1, |
3cfd35fd |
40 | ); |
41 | |
6c67366e |
42 | |
a588ee00 |
43 | =head2 constraining_value_type_constraint |
3cfd35fd |
44 | |
45 | This is a type constraint which defines what kind of value is allowed to be the |
a588ee00 |
46 | constraining value of the dependent type. |
3cfd35fd |
47 | |
48 | =cut |
49 | |
a588ee00 |
50 | has 'constraining_value_type_constraint' => ( |
3cfd35fd |
51 | is=>'ro', |
3a5dab74 |
52 | isa=>'Object', |
a588ee00 |
53 | default=> sub { |
54 | Moose::Util::TypeConstraints::find_type_constraint("Any"); |
3a5dab74 |
55 | }, |
a588ee00 |
56 | required=>1, |
3cfd35fd |
57 | ); |
58 | |
0a9f5b94 |
59 | =head2 constraining_value |
3cfd35fd |
60 | |
a588ee00 |
61 | This is the actual value that constraints the L</parent_type_constraint> |
01a12424 |
62 | |
3cfd35fd |
63 | =cut |
64 | |
a588ee00 |
65 | has 'constraining_value' => ( |
0a9f5b94 |
66 | is=>'ro', |
a588ee00 |
67 | predicate=>'has_constraining_value', |
3cfd35fd |
68 | ); |
69 | |
3cfd35fd |
70 | =head1 METHODS |
71 | |
72 | This class defines the following methods. |
73 | |
21df4517 |
74 | =head2 BUILD |
75 | |
76 | Do some post build stuff |
77 | |
78 | =cut |
79 | |
80 | sub BUILD { |
81 | my ($self) = @_; |
82 | $self->coercion( |
83 | MooseX::Dependent::Meta::TypeCoercion::Dependent->new( |
84 | type_constraint => $self, |
85 | )); |
86 | } |
87 | |
0a9f5b94 |
88 | =head2 parameterize (@args) |
3cfd35fd |
89 | |
90 | Given a ref of type constraints, create a structured type. |
0a9f5b94 |
91 | |
3cfd35fd |
92 | =cut |
93 | |
94 | sub parameterize { |
0a9f5b94 |
95 | my $self = shift @_; |
3cfd35fd |
96 | my $class = ref $self; |
6c67366e |
97 | |
98 | Moose->throw_error("$self already has a constraining value.") if |
99 | $self->has_constraining_value; |
100 | |
0a9f5b94 |
101 | if(blessed $_[0] && $_[0]->isa('Moose::Meta::TypeConstraint')) { |
102 | my $arg1 = shift @_; |
0a9f5b94 |
103 | |
6c67366e |
104 | if(blessed $_[0] && $_[0]->isa('Moose::Meta::TypeConstraint')) { |
105 | my $arg2 = shift @_ || $self->constraining_value_type_constraint; |
106 | |
107 | ## TODO fix this crap! |
108 | Moose->throw_error("$arg2 is not a type constraint") |
109 | unless $arg2->isa('Moose::Meta::TypeConstraint'); |
110 | |
111 | Moose->throw_error("$arg1 is not a type of: ".$self->parent_type_constraint->name) |
112 | unless $arg1->is_a_type_of($self->parent_type_constraint); |
113 | |
114 | Moose->throw_error("$arg2 is not a type of: ".$self->constraining_value_type_constraint->name) |
115 | unless $arg2->is_a_type_of($self->constraining_value_type_constraint); |
116 | |
117 | Moose->throw_error('Too Many Args! Two are allowed.') if @_; |
118 | |
9c319add |
119 | my $name = $self->_generate_subtype_name($arg1, $arg2); |
120 | if(my $exists = Moose::Util::TypeConstraints::find_type_constraint($name)) { |
121 | return $exists; |
122 | } else { |
123 | my $type_constraint = $class->new( |
124 | name => $name, |
125 | parent => $self, |
126 | constraint => $self->constraint, |
127 | parent_type_constraint=>$arg1, |
128 | constraining_value_type_constraint => $arg2, |
129 | ); |
130 | Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint($type_constraint); |
131 | return $type_constraint; |
132 | } |
6c67366e |
133 | } else { |
134 | Moose->throw_error("$arg1 is not a type of: ".$self->constraining_value_type_constraint->name) |
135 | unless $arg1->is_a_type_of($self->constraining_value_type_constraint); |
136 | |
9c319add |
137 | my $name = $self->_generate_subtype_name($self->parent_type_constraint, $arg1); |
138 | if(my $exists = Moose::Util::TypeConstraints::find_type_constraint($name)) { |
139 | return $exists; |
140 | } else { |
141 | my $type_constraint = $class->new( |
142 | name => $name, |
143 | parent => $self, |
144 | constraint => $self->constraint, |
145 | parent_type_constraint=>$self->parent_type_constraint, |
146 | constraining_value_type_constraint => $arg1, |
147 | ); |
148 | Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint($type_constraint); |
149 | return $type_constraint; |
150 | } |
6c67366e |
151 | } |
0a9f5b94 |
152 | } else { |
0a9f5b94 |
153 | my $args; |
154 | ## Jump through some hoops to let them do tc[key=>10] and tc[{key=>10}] |
155 | if(@_) { |
156 | if($#_) { |
157 | if($self->constraining_value_type_constraint->is_a_type_of('HashRef')) { |
158 | $args = {@_}; |
159 | } else { |
160 | $args = [@_]; |
161 | } |
162 | } else { |
163 | $args = $_[0]; |
164 | } |
165 | |
166 | } else { |
167 | ## TODO: Is there a use case for parameterizing null or undef? |
168 | Moose->throw_error('Cannot Parameterize null values.'); |
169 | } |
170 | |
171 | if(my $err = $self->constraining_value_type_constraint->validate($args)) { |
172 | Moose->throw_error($err); |
173 | } else { |
9c319add |
174 | |
175 | my $sig = $args; |
176 | if(ref $sig) { |
177 | $sig = Digest::MD5::md5_hex(Data::Dump::dump($args)); |
178 | } |
179 | my $name = $self->name."[$sig]"; |
180 | if(my $exists = Moose::Util::TypeConstraints::find_type_constraint($name)) { |
181 | return $exists; |
182 | } else { |
183 | my $type_constraint = $class->new( |
184 | name => $name, |
185 | parent => $self, |
186 | constraint => $self->constraint, |
187 | constraining_value => $args, |
188 | parent_type_constraint=>$self->parent_type_constraint, |
189 | constraining_value_type_constraint => $self->constraining_value_type_constraint, |
190 | ); |
21df4517 |
191 | |
192 | ## TODO This is probably going to have to go away (too many things added to the registry) |
9c319add |
193 | Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint($type_constraint); |
194 | return $type_constraint; |
195 | } |
0a9f5b94 |
196 | } |
197 | } |
3cfd35fd |
198 | } |
199 | |
200 | =head2 _generate_subtype_name |
201 | |
202 | Returns a name for the dependent type that should be unique |
203 | |
204 | =cut |
205 | |
206 | sub _generate_subtype_name { |
a588ee00 |
207 | my ($self, $parent_tc, $constraining_tc) = @_; |
3cfd35fd |
208 | return sprintf( |
0a9f5b94 |
209 | $self."[%s, %s]", |
a588ee00 |
210 | $parent_tc, $constraining_tc, |
3cfd35fd |
211 | ); |
212 | } |
213 | |
0a9f5b94 |
214 | =head2 create_child_type |
3cfd35fd |
215 | |
0a9f5b94 |
216 | modifier to make sure we get the constraint_generator |
3cfd35fd |
217 | |
218 | =cut |
219 | |
0a9f5b94 |
220 | around 'create_child_type' => sub { |
221 | my ($create_child_type, $self, %opts) = @_; |
66efbe23 |
222 | if($self->has_constraining_value) { |
223 | $opts{constraining_value} = $self->constraining_value; |
224 | } |
0a9f5b94 |
225 | return $self->$create_child_type( |
226 | %opts, |
227 | parent=> $self, |
228 | parent_type_constraint=>$self->parent_type_constraint, |
229 | constraining_value_type_constraint => $self->constraining_value_type_constraint, |
230 | ); |
231 | }; |
3cfd35fd |
232 | |
0a9f5b94 |
233 | =head2 equals ($type_constraint) |
3cfd35fd |
234 | |
0a9f5b94 |
235 | Override the base class behavior so that a dependent type equal both the parent |
236 | type and the overall dependent container. This behavior may change if we can |
237 | figure out what a dependent type is (multiply inheritance or a role...) |
1e87d1a7 |
238 | |
0a9f5b94 |
239 | =cut |
3cfd35fd |
240 | |
0a9f5b94 |
241 | around 'equals' => sub { |
242 | my ( $equals, $self, $type_or_name ) = @_; |
3cfd35fd |
243 | |
0a9f5b94 |
244 | my $other = defined $type_or_name ? |
245 | Moose::Util::TypeConstraints::find_type_constraint($type_or_name) : |
246 | Moose->throw_error("Can't call $self ->equals without a parameter"); |
247 | |
248 | Moose->throw_error("$type_or_name is not a registered Type") |
249 | unless $other; |
250 | |
251 | if(my $parent = $other->parent) { |
252 | return $self->$equals($other) |
253 | || $self->parent->equals($parent); |
254 | } else { |
255 | return $self->$equals($other); |
3cfd35fd |
256 | } |
3cfd35fd |
257 | }; |
258 | |
0a9f5b94 |
259 | around 'is_subtype_of' => sub { |
260 | my ( $is_subtype_of, $self, $type_or_name ) = @_; |
3cfd35fd |
261 | |
0a9f5b94 |
262 | my $other = defined $type_or_name ? |
263 | Moose::Util::TypeConstraints::find_type_constraint($type_or_name) : |
264 | Moose->throw_error("Can't call $self ->equals without a parameter"); |
265 | |
266 | Moose->throw_error("$type_or_name is not a registered Type") |
267 | unless $other; |
268 | |
269 | return $self->$is_subtype_of($other) |
270 | || $self->parent_type_constraint->is_subtype_of($other); |
9b6d2e22 |
271 | |
3cfd35fd |
272 | }; |
273 | |
0a9f5b94 |
274 | sub is_a_type_of { |
275 | my ($self, @args) = @_; |
276 | return ($self->equals(@args) || |
277 | $self->is_subtype_of(@args)); |
278 | } |
3cfd35fd |
279 | |
0a9f5b94 |
280 | around 'check' => sub { |
281 | my ($check, $self, @args) = @_; |
6c67366e |
282 | return ( |
283 | $self->parent_type_constraint->check(@args) && |
284 | $self->$check(@args) |
285 | ); |
0a9f5b94 |
286 | }; |
3cfd35fd |
287 | |
0a9f5b94 |
288 | around 'validate' => sub { |
289 | my ($validate, $self, @args) = @_; |
6c67366e |
290 | return ( |
291 | $self->parent_type_constraint->validate(@args) || |
292 | $self->$validate(@args) |
293 | ); |
0a9f5b94 |
294 | }; |
3cfd35fd |
295 | |
66efbe23 |
296 | around '_compiled_type_constraint' => sub { |
297 | my ($method, $self, @args) = @_; |
298 | my $coderef = $self->$method(@args); |
6c67366e |
299 | my $constraining; |
300 | if($self->has_constraining_value) { |
301 | $constraining = $self->constraining_value; |
302 | } |
303 | |
66efbe23 |
304 | return sub { |
305 | my @local_args = @_; |
6c67366e |
306 | if(my $err = $self->constraining_value_type_constraint->validate($constraining)) { |
307 | Moose->throw_error($err); |
308 | } |
309 | $coderef->(@local_args, $constraining); |
66efbe23 |
310 | }; |
311 | }; |
312 | |
9c319add |
313 | around 'coerce' => sub { |
314 | my ($coerce, $self, @args) = @_; |
315 | if($self->coercion) { |
316 | if(my $value = $self->$coerce(@args)) { |
5ae5d765 |
317 | return $value if defined $value; |
9c319add |
318 | } |
319 | } |
320 | return $self->parent->coerce(@args); |
321 | }; |
322 | |
3cfd35fd |
323 | =head2 get_message |
324 | |
ae1d0652 |
325 | Give you a better peek into what's causing the error. |
3cfd35fd |
326 | |
3cfd35fd |
327 | around 'get_message' => sub { |
328 | my ($get_message, $self, $value) = @_; |
ae1d0652 |
329 | return $self->$get_message($value); |
3cfd35fd |
330 | }; |
331 | |
332 | =head1 SEE ALSO |
333 | |
334 | The following modules or resources may be of interest. |
335 | |
336 | L<Moose>, L<Moose::Meta::TypeConstraint> |
337 | |
338 | =head1 AUTHOR |
339 | |
340 | John Napiorkowski, C<< <jjnapiork@cpan.org> >> |
341 | |
342 | =head1 COPYRIGHT & LICENSE |
343 | |
344 | This program is free software; you can redistribute it and/or modify |
345 | it under the same terms as Perl itself. |
346 | |
347 | =cut |
348 | |
1e87d1a7 |
349 | __PACKAGE__->meta->make_immutable(inline_constructor => 0); |
350 | |