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