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