bump version to 0.75_01
[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
74397c13 15our $VERSION = '0.75_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
4e036ee4 2881;
289
290__END__
291
292=pod
293
294=head1 NAME
295
6ba6d68c 296Moose::Meta::TypeConstraint - The Moose Type Constraint metaclass
4e036ee4 297
298=head1 DESCRIPTION
299
cfd006f0 300This class represents a single type constraint. Moose's built-in type
301constraints, as well as constraints you define, are all store in a
302L<Moose::Meta::TypeConstraint::Registry> object as objects of this
303class.
6ba6d68c 304
baf46b9e 305=head1 INHERITANCE
306
307C<Moose::Meta::TypeConstraint> is a subclass of L<Class::MOP::Object>.
308
4e036ee4 309=head1 METHODS
310
311=over 4
312
cfd006f0 313=item B<< Moose::Meta::TypeConstraint->new(%options) >>
4e036ee4 314
cfd006f0 315This creates a new type constraint based on the provided C<%options>:
4e036ee4 316
cfd006f0 317=over 8
d9e17f80 318
cfd006f0 319=item * name
e606ae5f 320
cfd006f0 321The constraint name. If a name is not provided, it will be set to
322"__ANON__".
b26e162e 323
cfd006f0 324=item * parent
b26e162e 325
cfd006f0 326A C<Moose::Meta::TypeConstraint> object which is the parent type for
327the type being created. This is optional.
cce8198b 328
cfd006f0 329=item * constraint
e606ae5f 330
cfd006f0 331This is the subroutine reference that implements the actual constraint
332check. This defaults to a subroutine which always returns true.
6ba6d68c 333
cfd006f0 334=item * message
0a5bd159 335
cfd006f0 336A subroutine reference which is used to generate an error message when
337the constraint fails. This is optional.
0a5bd159 338
cfd006f0 339=item * coercion
76d37e5a 340
cfd006f0 341A L<Moose::Meta::TypeCoercion> object representing the coercions to
342the type. This is optional.
76d37e5a 343
cfd006f0 344=item * optimized
76d37e5a 345
cfd006f0 346This is a variant of the C<constraint> parameter that is somehow
347optimized. Typically, this means incorporating both the type's
348constraint and all of its parents' constraints into a single
349subroutine reference.
6ba6d68c 350
cfd006f0 351=back
4e036ee4 352
cfd006f0 353=item B<< $constraint->equals($type_name_or_object) >>
e606ae5f 354
cfd006f0 355Returns true if the supplied name or type object is the same as the
356current type.
66811d63 357
cfd006f0 358=item B<< $constraint->is_subtype_of($type_name_or_object) >>
e606ae5f 359
cfd006f0 360Returns true if the supplied name or type object is a parent of the
361current type.
3726f905 362
cfd006f0 363=item B<< $constraint->is_a_type_of($type_name_or_object) >>
e606ae5f 364
cfd006f0 365Returns true if the given type is the same as the current type, or is
366a parent of the current type. This is a shortcut for checking
367C<equals> and C<is_subtype_of>.
d9e17f80 368
cfd006f0 369=item B<< $constraint->coerce($value) >>
2f7e4042 370
cfd006f0 371This will attempt to coerce the value to the type. If the type does
372have any defined coercions this will throw an error.
66811d63 373
cfd006f0 374=item B<< $constraint->check($value) >>
2f7e4042 375
cfd006f0 376Returns true if the given value passes the constraint for the type.
76d37e5a 377
cfd006f0 378=item B<< $constraint->validate($value) >>
2f7e4042 379
cfd006f0 380This is similar to C<check>. However, if the type I<is valid> then the
381method returns an explicit C<undef>. If the type is not valid, we call
382C<< $self->get_message($value) >> internally to generate an error
383message.
76d37e5a 384
cfd006f0 385=item B<< $constraint->name >>
2f7e4042 386
cfd006f0 387Returns the type's name, as provided to the constructor.
688fcdda 388
cfd006f0 389=item B<< $constraint->parent >>
2f7e4042 390
cfd006f0 391Returns the type's parent, as provided to the constructor, if any.
4e036ee4 392
cfd006f0 393=item B<< $constraint->has_parent >>
2f7e4042 394
cfd006f0 395Returns true if the type has a parent type.
a27aa600 396
cfd006f0 397=item B<< $constraint->parents >>
2f7e4042 398
cfd006f0 399A synonym for C<parent>. This is useful for polymorphism with types
400that can have more than one parent.
c8cf9aaa 401
cfd006f0 402=item B<< $constraint->constraint >>
c8cf9aaa 403
cfd006f0 404Returns the type's constraint, as provided to the constructor.
9ceb576e 405
cfd006f0 406=item B<< $constraint->get_message($value) >>
4e036ee4 407
cfd006f0 408This generates a method for the given value. If the type does not have
409an explicit message, we generate a default message.
3726f905 410
cfd006f0 411=item B<< $constraint->has_message >>
412
413Returns true if the type has a message.
414
415=item B<< $constraint->message >>
416
417Returns the type's message as a subroutine reference.
418
419=item B<< $constraint->coercion >>
420
421Returns the type's L<Moose::Meta::TypeCoercion> object, if one
422exists.
423
424=item B<< $constraint->has_coercion >>
425
426Returns true if the type has a coercion.
427
428=item B<< $constraint->hand_optimized_type_constraint >>
429
430Returns the type's hand optimized constraint, as provided to the
431constructor via the C<optimized> option.
432
433=item B<< $constraint->has_hand_optimized_type_constraint >>
434
435Returns true if the type has an optimized constraint.
436
437=item B<< $constraint->create_child_type(%options) >>
451c8248 438
cfd006f0 439This returns a new type constraint of the same class using the
440provided C<%options>. The C<parent> option will be the current type.
3726f905 441
cfd006f0 442This method exists so that subclasses of this class can override this
443behavior and change how child types are created.
451c8248 444
445=back
446
4e036ee4 447=head1 BUGS
448
e27dfc11 449All complex software has bugs lurking in it, and this module is no
4e036ee4 450exception. If you find a bug please either email me, or add the bug
451to cpan-RT.
452
453=head1 AUTHOR
454
455Stevan Little E<lt>stevan@iinteractive.comE<gt>
456
457=head1 COPYRIGHT AND LICENSE
458
2840a3b2 459Copyright 2006-2009 by Infinity Interactive, Inc.
4e036ee4 460
461L<http://www.iinteractive.com>
462
463This library is free software; you can redistribute it and/or modify
e27dfc11 464it under the same terms as Perl itself.
4e036ee4 465
c8cf9aaa 466=cut