make the 'reset' delegation for Counter respect builders
[gitmo/Moose.git] / lib / Moose / Meta / TypeConstraint.pm
CommitLineData
4e036ee4 1
2package Moose::Meta::TypeConstraint;
3
4use strict;
5use warnings;
6use metaclass;
7
ed90007b 8use overload '0+' => sub { refaddr(shift) }, # id an object
9 '""' => sub { shift->name }, # stringify to tc name
72e389bf 10 bool => sub { 1 },
b644e331 11 fallback => 1;
900466d6 12
dabed765 13use Scalar::Util qw(blessed refaddr);
9f2230e9 14use Sub::Name qw(subname);
66811d63 15
e606ae5f 16use base qw(Class::MOP::Object);
17
3726f905 18__PACKAGE__->meta->add_attribute('name' => (reader => 'name'));
19__PACKAGE__->meta->add_attribute('parent' => (
20 reader => 'parent',
21 predicate => 'has_parent',
22));
baf26cc6 23
24my $null_constraint = sub { 1 };
d67145ed 25__PACKAGE__->meta->add_attribute('constraint' => (
8de73ff1 26 reader => 'constraint',
27 writer => '_set_constraint',
baf26cc6 28 default => sub { $null_constraint }
d67145ed 29));
76d37e5a 30__PACKAGE__->meta->add_attribute('message' => (
31 accessor => 'message',
32 predicate => 'has_message'
33));
a27aa600 34__PACKAGE__->meta->add_attribute('coercion' => (
35 accessor => 'coercion',
36 predicate => 'has_coercion'
37));
70ea9161 38
c8cf9aaa 39__PACKAGE__->meta->add_attribute('hand_optimized_type_constraint' => (
40 init_arg => 'optimized',
41 accessor => 'hand_optimized_type_constraint',
e27dfc11 42 predicate => 'has_hand_optimized_type_constraint',
c8cf9aaa 43));
44
bd72f3c8 45sub parents {
46 my $self;
47 $self->parent;
48}
49
3726f905 50# private accessors
51
52__PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
53 accessor => '_compiled_type_constraint',
54 predicate => '_has_compiled_type_constraint'
55));
22aed3c0 56__PACKAGE__->meta->add_attribute('package_defined_in' => (
57 accessor => '_package_defined_in'
58));
59
e27dfc11 60sub new {
a27aa600 61 my $class = shift;
8534c69a 62 my ($first, @rest) = @_;
63 my %args = ref $first ? %$first : $first ? ($first, @rest) : ();
64 $args{name} = $args{name} ? "$args{name}" : "__ANON__";
70ea9161 65
8534c69a 66 my $self = $class->_new(%args);
3726f905 67 $self->compile_type_constraint()
68 unless $self->_has_compiled_type_constraint;
66811d63 69 return $self;
70}
71
8534c69a 72
73
70ea9161 74sub coerce {
75 my $self = shift;
76
77 my $coercion = $self->coercion;
78
79 unless ($coercion) {
80 require Moose;
81 Moose->throw_error("Cannot coerce without a type coercion");
82 }
83
02679ba4 84 return $_[0] if $self->check($_[0]);
85
70ea9161 86 return $coercion->coerce(@_);
87}
a1257460 88
bb6ac54a 89sub assert_coerce {
90 my $self = shift;
91
92 my $coercion = $self->coercion;
93
94 unless ($coercion) {
95 require Moose;
96 Moose->throw_error("Cannot coerce without a type coercion");
97 }
98
99 return $_[0] if $self->check($_[0]);
100
101 my $result = $coercion->coerce(@_);
102
103 $self->assert_valid($result);
104
105 return $result;
106}
107
a1257460 108sub check {
109 my ($self, @args) = @_;
110 my $constraint_subref = $self->_compiled_type_constraint;
111 return $constraint_subref->(@args) ? 1 : undef;
112}
113
e27dfc11 114sub validate {
76d37e5a 115 my ($self, $value) = @_;
116 if ($self->_compiled_type_constraint->($value)) {
117 return undef;
118 }
119 else {
688fcdda 120 $self->get_message($value);
76d37e5a 121 }
122}
123
c24269b3 124sub assert_valid {
125 my ($self, $value) = @_;
126
127 my $error = $self->validate($value);
128 return 1 if ! defined $error;
129
130 require Moose;
131 Moose->throw_error($error);
132}
133
688fcdda 134sub get_message {
135 my ($self, $value) = @_;
688fcdda 136 if (my $msg = $self->message) {
137 local $_ = $value;
138 return $msg->($value);
139 }
140 else {
d03bd989 141 $value = (defined $value ? overload::StrVal($value) : 'undef');
24a9f75e 142 return "Validation failed for '" . $self->name . "' with value $value";
d03bd989 143 }
688fcdda 144}
145
3726f905 146## type predicates ...
147
d9e17f80 148sub equals {
149 my ( $self, $type_or_name ) = @_;
150
e606ae5f 151 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
dabed765 152
0677975f 153 return 1 if $self == $other;
dabed765 154
155 if ( $self->has_hand_optimized_type_constraint and $other->has_hand_optimized_type_constraint ) {
156 return 1 if $self->hand_optimized_type_constraint == $other->hand_optimized_type_constraint;
157 }
158
159 return unless $self->constraint == $other->constraint;
160
161 if ( $self->has_parent ) {
162 return unless $other->has_parent;
163 return unless $self->parent->equals( $other->parent );
164 } else {
165 return if $other->has_parent;
166 }
d9e17f80 167
05a5763c 168 return;
d9e17f80 169}
170
b26e162e 171sub is_a_type_of {
d9e17f80 172 my ($self, $type_or_name) = @_;
173
e606ae5f 174 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 175
176 ($self->equals($type) || $self->is_subtype_of($type));
b26e162e 177}
178
cce8198b 179sub is_subtype_of {
d9e17f80 180 my ($self, $type_or_name) = @_;
181
e606ae5f 182 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 183
cce8198b 184 my $current = $self;
d9e17f80 185
cce8198b 186 while (my $parent = $current->parent) {
d9e17f80 187 return 1 if $parent->equals($type);
cce8198b 188 $current = $parent;
189 }
d9e17f80 190
cce8198b 191 return 0;
192}
193
3726f905 194## compiling the type constraint
195
196sub compile_type_constraint {
197 my $self = shift;
198 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
451c8248 199}
200
3726f905 201## type compilers ...
202
203sub _actually_compile_type_constraint {
204 my $self = shift;
e27dfc11 205
3726f905 206 return $self->_compile_hand_optimized_type_constraint
207 if $self->has_hand_optimized_type_constraint;
e27dfc11 208
3726f905 209 my $check = $self->constraint;
70ea9161 210 unless ( defined $check ) {
211 require Moose;
212 Moose->throw_error( "Could not compile type constraint '"
e27dfc11 213 . $self->name
70ea9161 214 . "' because no constraint check" );
215 }
e27dfc11 216
3726f905 217 return $self->_compile_subtype($check)
218 if $self->has_parent;
e27dfc11 219
3726f905 220 return $self->_compile_type($check);
221}
222
223sub _compile_hand_optimized_type_constraint {
224 my $self = shift;
e27dfc11 225
3726f905 226 my $type_constraint = $self->hand_optimized_type_constraint;
e27dfc11 227
70ea9161 228 unless ( ref $type_constraint ) {
229 require Moose;
230 Carp::confess ("Hand optimized type constraint for " . $self->name . " is not a code reference");
231 Moose->throw_error("Hand optimized type constraint is not a code reference");
232 }
42bc21a4 233
234 return $type_constraint;
3726f905 235}
236
237sub _compile_subtype {
238 my ($self, $check) = @_;
e27dfc11 239
baf26cc6 240 # gather all the parent constraintss in order
3726f905 241 my @parents;
baf26cc6 242 my $optimized_parent;
3726f905 243 foreach my $parent ($self->_collect_all_parents) {
baf26cc6 244 # if a parent is optimized, the optimized constraint already includes
245 # all of its parents tcs, so we can break the loop
3726f905 246 if ($parent->has_hand_optimized_type_constraint) {
baf26cc6 247 push @parents => $optimized_parent = $parent->hand_optimized_type_constraint;
e27dfc11 248 last;
3726f905 249 }
250 else {
baf26cc6 251 push @parents => $parent->constraint;
3726f905 252 }
253 }
e27dfc11 254
baf26cc6 255 @parents = grep { $_ != $null_constraint } reverse @parents;
256
257 unless ( @parents ) {
258 return $self->_compile_type($check);
259 } elsif( $optimized_parent and @parents == 1 ) {
260 # the case of just one optimized parent is optimized to prevent
261 # looping and the unnecessary localization
b5981f07 262 if ( $check == $null_constraint ) {
263 return $optimized_parent;
264 } else {
9f2230e9 265 return subname($self->name, sub {
b5981f07 266 return undef unless $optimized_parent->($_[0]);
a1257460 267 my (@args) = @_;
268 local $_ = $args[0];
269 $check->(@args);
b5981f07 270 });
271 }
baf26cc6 272 } else {
273 # general case, check all the constraints, from the first parent to ourselves
b5981f07 274 my @checks = @parents;
275 push @checks, $check if $check != $null_constraint;
9f2230e9 276 return subname($self->name => sub {
a1257460 277 my (@args) = @_;
278 local $_ = $args[0];
baf26cc6 279 foreach my $check (@checks) {
a1257460 280 return undef unless $check->(@args);
baf26cc6 281 }
282 return 1;
283 });
284 }
3726f905 285}
286
287sub _compile_type {
288 my ($self, $check) = @_;
baf26cc6 289
290 return $check if $check == $null_constraint; # Item, Any
291
9f2230e9 292 return subname($self->name => sub {
a1257460 293 my (@args) = @_;
294 local $_ = $args[0];
295 $check->(@args);
1b2aea39 296 });
3726f905 297}
298
299## other utils ...
300
301sub _collect_all_parents {
302 my $self = shift;
303 my @parents;
304 my $current = $self->parent;
305 while (defined $current) {
306 push @parents => $current;
307 $current = $current->parent;
308 }
309 return @parents;
310}
311
85a9908f 312sub create_child_type {
9ceb576e 313 my ($self, %opts) = @_;
314 my $class = ref $self;
315 return $class->new(%opts, parent => $self);
316}
317
4e036ee4 3181;
319
ad46f524 320# ABSTRACT: The Moose Type Constraint metaclass
321
4e036ee4 322__END__
323
324=pod
325
4e036ee4 326=head1 DESCRIPTION
327
cfd006f0 328This class represents a single type constraint. Moose's built-in type
76127c77 329constraints, as well as constraints you define, are all stored in a
cfd006f0 330L<Moose::Meta::TypeConstraint::Registry> object as objects of this
331class.
6ba6d68c 332
baf46b9e 333=head1 INHERITANCE
334
335C<Moose::Meta::TypeConstraint> is a subclass of L<Class::MOP::Object>.
336
4e036ee4 337=head1 METHODS
338
339=over 4
340
cfd006f0 341=item B<< Moose::Meta::TypeConstraint->new(%options) >>
4e036ee4 342
cfd006f0 343This creates a new type constraint based on the provided C<%options>:
4e036ee4 344
cfd006f0 345=over 8
d9e17f80 346
cfd006f0 347=item * name
e606ae5f 348
cfd006f0 349The constraint name. If a name is not provided, it will be set to
350"__ANON__".
b26e162e 351
cfd006f0 352=item * parent
b26e162e 353
cfd006f0 354A C<Moose::Meta::TypeConstraint> object which is the parent type for
355the type being created. This is optional.
cce8198b 356
cfd006f0 357=item * constraint
e606ae5f 358
cfd006f0 359This is the subroutine reference that implements the actual constraint
360check. This defaults to a subroutine which always returns true.
6ba6d68c 361
cfd006f0 362=item * message
0a5bd159 363
cfd006f0 364A subroutine reference which is used to generate an error message when
365the constraint fails. This is optional.
0a5bd159 366
cfd006f0 367=item * coercion
76d37e5a 368
cfd006f0 369A L<Moose::Meta::TypeCoercion> object representing the coercions to
370the type. This is optional.
76d37e5a 371
cfd006f0 372=item * optimized
76d37e5a 373
cfd006f0 374This is a variant of the C<constraint> parameter that is somehow
375optimized. Typically, this means incorporating both the type's
376constraint and all of its parents' constraints into a single
377subroutine reference.
6ba6d68c 378
cfd006f0 379=back
4e036ee4 380
cfd006f0 381=item B<< $constraint->equals($type_name_or_object) >>
e606ae5f 382
cfd006f0 383Returns true if the supplied name or type object is the same as the
384current type.
66811d63 385
cfd006f0 386=item B<< $constraint->is_subtype_of($type_name_or_object) >>
e606ae5f 387
cfd006f0 388Returns true if the supplied name or type object is a parent of the
389current type.
3726f905 390
cfd006f0 391=item B<< $constraint->is_a_type_of($type_name_or_object) >>
e606ae5f 392
cfd006f0 393Returns true if the given type is the same as the current type, or is
394a parent of the current type. This is a shortcut for checking
395C<equals> and C<is_subtype_of>.
d9e17f80 396
cfd006f0 397=item B<< $constraint->coerce($value) >>
2f7e4042 398
b2894aea 399This will attempt to coerce the value to the type. If the type does not
cfd006f0 400have any defined coercions this will throw an error.
66811d63 401
572b5187 402If no coercion can produce a value matching C<$constraint>, the original
403value is returned.
404
086c6b6c 405=item B<< $constraint->assert_coerce($value) >>
572b5187 406
407This method behaves just like C<coerce>, but if the result is not valid
408according to C<$constraint>, an error is thrown.
409
cfd006f0 410=item B<< $constraint->check($value) >>
2f7e4042 411
cfd006f0 412Returns true if the given value passes the constraint for the type.
76d37e5a 413
cfd006f0 414=item B<< $constraint->validate($value) >>
2f7e4042 415
cfd006f0 416This is similar to C<check>. However, if the type I<is valid> then the
417method returns an explicit C<undef>. If the type is not valid, we call
418C<< $self->get_message($value) >> internally to generate an error
419message.
76d37e5a 420
952320e0 421=item B<< $constraint->assert_valid($value) >>
422
423Like C<check> and C<validate>, this method checks whether C<$value> is
424valid under the constraint. If it is, it will return true. If it is not,
425an exception will be thrown with the results of
426C<< $self->get_message($value) >>.
427
cfd006f0 428=item B<< $constraint->name >>
2f7e4042 429
cfd006f0 430Returns the type's name, as provided to the constructor.
688fcdda 431
cfd006f0 432=item B<< $constraint->parent >>
2f7e4042 433
cfd006f0 434Returns the type's parent, as provided to the constructor, if any.
4e036ee4 435
cfd006f0 436=item B<< $constraint->has_parent >>
2f7e4042 437
cfd006f0 438Returns true if the type has a parent type.
a27aa600 439
cfd006f0 440=item B<< $constraint->parents >>
2f7e4042 441
cfd006f0 442A synonym for C<parent>. This is useful for polymorphism with types
443that can have more than one parent.
c8cf9aaa 444
cfd006f0 445=item B<< $constraint->constraint >>
c8cf9aaa 446
cfd006f0 447Returns the type's constraint, as provided to the constructor.
9ceb576e 448
cfd006f0 449=item B<< $constraint->get_message($value) >>
4e036ee4 450
cfd006f0 451This generates a method for the given value. If the type does not have
452an explicit message, we generate a default message.
3726f905 453
cfd006f0 454=item B<< $constraint->has_message >>
455
456Returns true if the type has a message.
457
458=item B<< $constraint->message >>
459
460Returns the type's message as a subroutine reference.
461
462=item B<< $constraint->coercion >>
463
464Returns the type's L<Moose::Meta::TypeCoercion> object, if one
465exists.
466
467=item B<< $constraint->has_coercion >>
468
469Returns true if the type has a coercion.
470
471=item B<< $constraint->hand_optimized_type_constraint >>
472
473Returns the type's hand optimized constraint, as provided to the
474constructor via the C<optimized> option.
475
476=item B<< $constraint->has_hand_optimized_type_constraint >>
477
478Returns true if the type has an optimized constraint.
479
480=item B<< $constraint->create_child_type(%options) >>
451c8248 481
cfd006f0 482This returns a new type constraint of the same class using the
483provided C<%options>. The C<parent> option will be the current type.
3726f905 484
cfd006f0 485This method exists so that subclasses of this class can override this
486behavior and change how child types are created.
451c8248 487
488=back
489
4e036ee4 490=head1 BUGS
491
d4048ef3 492See L<Moose/BUGS> for details on reporting bugs.
4e036ee4 493
c8cf9aaa 494=cut