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