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