Add definition context to every accessor defined internally
[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
477a812e 13use Eval::Closure;
dabed765 14use Scalar::Util qw(blessed refaddr);
9f2230e9 15use Sub::Name qw(subname);
5a18346b 16use Try::Tiny;
66811d63 17
e606ae5f 18use base qw(Class::MOP::Object);
19
dc2b7cc8 20__PACKAGE__->meta->add_attribute('name' => (
21 reader => 'name',
22 Class::MOP::_definition_context(),
23));
3726f905 24__PACKAGE__->meta->add_attribute('parent' => (
25 reader => 'parent',
26 predicate => 'has_parent',
dc2b7cc8 27 Class::MOP::_definition_context(),
3726f905 28));
baf26cc6 29
30my $null_constraint = sub { 1 };
d67145ed 31__PACKAGE__->meta->add_attribute('constraint' => (
8de73ff1 32 reader => 'constraint',
33 writer => '_set_constraint',
dc2b7cc8 34 default => sub { $null_constraint },
35 Class::MOP::_definition_context(),
d67145ed 36));
dc2b7cc8 37
76d37e5a 38__PACKAGE__->meta->add_attribute('message' => (
39 accessor => 'message',
dc2b7cc8 40 predicate => 'has_message',
41 Class::MOP::_definition_context(),
76d37e5a 42));
dc2b7cc8 43
92a88343 44__PACKAGE__->meta->add_attribute('_default_message' => (
45 accessor => '_default_message',
dc2b7cc8 46 Class::MOP::_definition_context(),
92a88343 47));
dc2b7cc8 48
92a88343 49# can't make this a default because it has to close over the type name, and
50# cmop attributes don't have lazy
51my $_default_message_generator = sub {
52 my $name = shift;
53 sub {
54 my $value = shift;
55 # have to load it late like this, since it uses Moose itself
56 my $can_partialdump = try {
57 # versions prior to 0.14 had a potential infinite loop bug
58 Class::MOP::load_class('Devel::PartialDump', { -version => 0.14 });
59 1;
60 };
61 if ($can_partialdump) {
62 $value = Devel::PartialDump->new->dump($value);
63 }
64 else {
65 $value = (defined $value ? overload::StrVal($value) : 'undef');
66 }
67 return "Validation failed for '" . $name . "' with value $value";
68 }
69};
a27aa600 70__PACKAGE__->meta->add_attribute('coercion' => (
71 accessor => 'coercion',
dc2b7cc8 72 predicate => 'has_coercion',
73 Class::MOP::_definition_context(),
a27aa600 74));
70ea9161 75
c8cf9aaa 76__PACKAGE__->meta->add_attribute('hand_optimized_type_constraint' => (
77 init_arg => 'optimized',
78 accessor => 'hand_optimized_type_constraint',
e27dfc11 79 predicate => 'has_hand_optimized_type_constraint',
dc2b7cc8 80 Class::MOP::_definition_context(),
c8cf9aaa 81));
82
4e36cf24 83__PACKAGE__->meta->add_attribute('inlined' => (
7487e61c 84 init_arg => 'inlined',
4e36cf24 85 accessor => 'inlined',
7487e61c 86 predicate => '_has_inlined_type_constraint',
dc2b7cc8 87 Class::MOP::_definition_context(),
4e36cf24 88));
89
9c44971f 90__PACKAGE__->meta->add_attribute('inline_environment' => (
91 init_arg => 'inline_environment',
ca789903 92 accessor => '_inline_environment',
9c44971f 93 default => sub { {} },
dc2b7cc8 94 Class::MOP::_definition_context(),
9c44971f 95));
96
bd72f3c8 97sub parents {
98 my $self;
99 $self->parent;
100}
101
3726f905 102# private accessors
103
104__PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
105 accessor => '_compiled_type_constraint',
dc2b7cc8 106 predicate => '_has_compiled_type_constraint',
107 Class::MOP::_definition_context(),
3726f905 108));
dc2b7cc8 109
22aed3c0 110__PACKAGE__->meta->add_attribute('package_defined_in' => (
dc2b7cc8 111 accessor => '_package_defined_in',
112 Class::MOP::_definition_context(),
22aed3c0 113));
114
e27dfc11 115sub new {
a27aa600 116 my $class = shift;
8534c69a 117 my ($first, @rest) = @_;
118 my %args = ref $first ? %$first : $first ? ($first, @rest) : ();
119 $args{name} = $args{name} ? "$args{name}" : "__ANON__";
70ea9161 120
8534c69a 121 my $self = $class->_new(%args);
3726f905 122 $self->compile_type_constraint()
123 unless $self->_has_compiled_type_constraint;
92a88343 124 $self->_default_message($_default_message_generator->($self->name))
125 unless $self->has_message;
66811d63 126 return $self;
127}
128
8534c69a 129
130
70ea9161 131sub coerce {
132 my $self = shift;
133
134 my $coercion = $self->coercion;
135
136 unless ($coercion) {
137 require Moose;
138 Moose->throw_error("Cannot coerce without a type coercion");
139 }
140
02679ba4 141 return $_[0] if $self->check($_[0]);
142
70ea9161 143 return $coercion->coerce(@_);
144}
a1257460 145
bb6ac54a 146sub assert_coerce {
147 my $self = shift;
148
149 my $coercion = $self->coercion;
150
151 unless ($coercion) {
152 require Moose;
153 Moose->throw_error("Cannot coerce without a type coercion");
154 }
155
156 return $_[0] if $self->check($_[0]);
157
158 my $result = $coercion->coerce(@_);
159
160 $self->assert_valid($result);
161
162 return $result;
163}
164
a1257460 165sub check {
166 my ($self, @args) = @_;
167 my $constraint_subref = $self->_compiled_type_constraint;
168 return $constraint_subref->(@args) ? 1 : undef;
169}
170
e27dfc11 171sub validate {
76d37e5a 172 my ($self, $value) = @_;
173 if ($self->_compiled_type_constraint->($value)) {
174 return undef;
175 }
176 else {
688fcdda 177 $self->get_message($value);
76d37e5a 178 }
179}
180
7c047a36 181sub can_be_inlined {
7487e61c 182 my $self = shift;
183
92efe680 184 if ( $self->has_parent && $self->constraint == $null_constraint ) {
7c047a36 185 return $self->parent->can_be_inlined;
7487e61c 186 }
187
188 return $self->_has_inlined_type_constraint;
189}
190
4e36cf24 191sub _inline_check {
192 my $self = shift;
193
7c047a36 194 unless ( $self->can_be_inlined ) {
3f9c18f3 195 require Moose;
196 Moose->throw_error( 'Cannot inline a type constraint check for ' . $self->name );
197 }
4e36cf24 198
92efe680 199 if ( $self->has_parent && $self->constraint == $null_constraint ) {
7487e61c 200 return $self->parent->_inline_check(@_);
201 }
202
e6fff671 203 return '( do { ' . $self->inlined->( $self, @_ ) . ' } )';
4e36cf24 204}
205
ca789903 206sub inline_environment {
207 my $self = shift;
208
209 if ( $self->has_parent && $self->constraint == $null_constraint ) {
210 return $self->parent->inline_environment;
211 }
212
213 return $self->_inline_environment;
214}
215
c24269b3 216sub assert_valid {
217 my ($self, $value) = @_;
218
219 my $error = $self->validate($value);
220 return 1 if ! defined $error;
221
222 require Moose;
223 Moose->throw_error($error);
224}
225
688fcdda 226sub get_message {
227 my ($self, $value) = @_;
92a88343 228 my $msg = $self->message || $self->_default_message;
229 local $_ = $value;
230 return $msg->($value);
688fcdda 231}
232
3726f905 233## type predicates ...
234
d9e17f80 235sub equals {
236 my ( $self, $type_or_name ) = @_;
237
e606ae5f 238 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
dabed765 239
0677975f 240 return 1 if $self == $other;
dabed765 241
242 if ( $self->has_hand_optimized_type_constraint and $other->has_hand_optimized_type_constraint ) {
243 return 1 if $self->hand_optimized_type_constraint == $other->hand_optimized_type_constraint;
244 }
245
246 return unless $self->constraint == $other->constraint;
247
248 if ( $self->has_parent ) {
249 return unless $other->has_parent;
250 return unless $self->parent->equals( $other->parent );
251 } else {
252 return if $other->has_parent;
253 }
d9e17f80 254
05a5763c 255 return;
d9e17f80 256}
257
b26e162e 258sub is_a_type_of {
d9e17f80 259 my ($self, $type_or_name) = @_;
260
e606ae5f 261 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 262
263 ($self->equals($type) || $self->is_subtype_of($type));
b26e162e 264}
265
cce8198b 266sub is_subtype_of {
d9e17f80 267 my ($self, $type_or_name) = @_;
268
e606ae5f 269 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 270
cce8198b 271 my $current = $self;
d9e17f80 272
cce8198b 273 while (my $parent = $current->parent) {
d9e17f80 274 return 1 if $parent->equals($type);
cce8198b 275 $current = $parent;
276 }
d9e17f80 277
cce8198b 278 return 0;
279}
280
3726f905 281## compiling the type constraint
282
283sub compile_type_constraint {
284 my $self = shift;
285 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
451c8248 286}
287
3726f905 288## type compilers ...
289
290sub _actually_compile_type_constraint {
291 my $self = shift;
e27dfc11 292
3726f905 293 return $self->_compile_hand_optimized_type_constraint
294 if $self->has_hand_optimized_type_constraint;
e27dfc11 295
7c047a36 296 if ( $self->can_be_inlined ) {
477a812e 297 return eval_closure(
9c44971f 298 source => 'sub { ' . $self->_inline_check('$_[0]') . ' }',
299 environment => $self->inline_environment,
477a812e 300 );
43837b8a 301 }
302
3726f905 303 my $check = $self->constraint;
70ea9161 304 unless ( defined $check ) {
305 require Moose;
306 Moose->throw_error( "Could not compile type constraint '"
e27dfc11 307 . $self->name
70ea9161 308 . "' because no constraint check" );
309 }
e27dfc11 310
3726f905 311 return $self->_compile_subtype($check)
312 if $self->has_parent;
e27dfc11 313
3726f905 314 return $self->_compile_type($check);
315}
316
317sub _compile_hand_optimized_type_constraint {
318 my $self = shift;
e27dfc11 319
3726f905 320 my $type_constraint = $self->hand_optimized_type_constraint;
e27dfc11 321
70ea9161 322 unless ( ref $type_constraint ) {
323 require Moose;
70ea9161 324 Moose->throw_error("Hand optimized type constraint is not a code reference");
325 }
42bc21a4 326
327 return $type_constraint;
3726f905 328}
329
330sub _compile_subtype {
331 my ($self, $check) = @_;
e27dfc11 332
baf26cc6 333 # gather all the parent constraintss in order
3726f905 334 my @parents;
baf26cc6 335 my $optimized_parent;
3726f905 336 foreach my $parent ($self->_collect_all_parents) {
baf26cc6 337 # if a parent is optimized, the optimized constraint already includes
338 # all of its parents tcs, so we can break the loop
3726f905 339 if ($parent->has_hand_optimized_type_constraint) {
baf26cc6 340 push @parents => $optimized_parent = $parent->hand_optimized_type_constraint;
e27dfc11 341 last;
3726f905 342 }
343 else {
baf26cc6 344 push @parents => $parent->constraint;
3726f905 345 }
346 }
e27dfc11 347
baf26cc6 348 @parents = grep { $_ != $null_constraint } reverse @parents;
349
350 unless ( @parents ) {
351 return $self->_compile_type($check);
352 } elsif( $optimized_parent and @parents == 1 ) {
353 # the case of just one optimized parent is optimized to prevent
354 # looping and the unnecessary localization
b5981f07 355 if ( $check == $null_constraint ) {
356 return $optimized_parent;
357 } else {
9f2230e9 358 return subname($self->name, sub {
b5981f07 359 return undef unless $optimized_parent->($_[0]);
a1257460 360 my (@args) = @_;
361 local $_ = $args[0];
362 $check->(@args);
b5981f07 363 });
364 }
baf26cc6 365 } else {
366 # general case, check all the constraints, from the first parent to ourselves
b5981f07 367 my @checks = @parents;
368 push @checks, $check if $check != $null_constraint;
9f2230e9 369 return subname($self->name => sub {
a1257460 370 my (@args) = @_;
371 local $_ = $args[0];
baf26cc6 372 foreach my $check (@checks) {
a1257460 373 return undef unless $check->(@args);
baf26cc6 374 }
375 return 1;
376 });
377 }
3726f905 378}
379
380sub _compile_type {
381 my ($self, $check) = @_;
baf26cc6 382
383 return $check if $check == $null_constraint; # Item, Any
384
9f2230e9 385 return subname($self->name => sub {
a1257460 386 my (@args) = @_;
387 local $_ = $args[0];
388 $check->(@args);
1b2aea39 389 });
3726f905 390}
391
392## other utils ...
393
394sub _collect_all_parents {
395 my $self = shift;
396 my @parents;
397 my $current = $self->parent;
398 while (defined $current) {
399 push @parents => $current;
400 $current = $current->parent;
401 }
402 return @parents;
403}
404
85a9908f 405sub create_child_type {
9ceb576e 406 my ($self, %opts) = @_;
407 my $class = ref $self;
408 return $class->new(%opts, parent => $self);
409}
410
4e036ee4 4111;
412
ad46f524 413# ABSTRACT: The Moose Type Constraint metaclass
414
4e036ee4 415__END__
416
417=pod
418
4e036ee4 419=head1 DESCRIPTION
420
cfd006f0 421This class represents a single type constraint. Moose's built-in type
76127c77 422constraints, as well as constraints you define, are all stored in a
cfd006f0 423L<Moose::Meta::TypeConstraint::Registry> object as objects of this
424class.
6ba6d68c 425
baf46b9e 426=head1 INHERITANCE
427
428C<Moose::Meta::TypeConstraint> is a subclass of L<Class::MOP::Object>.
429
4e036ee4 430=head1 METHODS
431
432=over 4
433
cfd006f0 434=item B<< Moose::Meta::TypeConstraint->new(%options) >>
4e036ee4 435
cfd006f0 436This creates a new type constraint based on the provided C<%options>:
4e036ee4 437
cfd006f0 438=over 8
d9e17f80 439
cfd006f0 440=item * name
e606ae5f 441
cfd006f0 442The constraint name. If a name is not provided, it will be set to
443"__ANON__".
b26e162e 444
cfd006f0 445=item * parent
b26e162e 446
cfd006f0 447A C<Moose::Meta::TypeConstraint> object which is the parent type for
448the type being created. This is optional.
cce8198b 449
cfd006f0 450=item * constraint
e606ae5f 451
cfd006f0 452This is the subroutine reference that implements the actual constraint
453check. This defaults to a subroutine which always returns true.
6ba6d68c 454
cfd006f0 455=item * message
0a5bd159 456
cfd006f0 457A subroutine reference which is used to generate an error message when
458the constraint fails. This is optional.
0a5bd159 459
cfd006f0 460=item * coercion
76d37e5a 461
cfd006f0 462A L<Moose::Meta::TypeCoercion> object representing the coercions to
463the type. This is optional.
76d37e5a 464
7142d232 465=item * inlined
466
467A subroutine which returns a string suitable for inlining this type
468constraint. It will be called as a method on the type constraint object, and
469will receive a single additional parameter, a variable name to be tested
470(usually C<"$_"> or C<"$_[0]">.
471
472This is optional.
473
474=item * inline_environment
475
476A hash reference of variables to close over. The keys are variables names, and
477the values are I<references> to the variables.
478
cfd006f0 479=item * optimized
76d37e5a 480
7142d232 481B<This option is deprecated.>
482
cfd006f0 483This is a variant of the C<constraint> parameter that is somehow
484optimized. Typically, this means incorporating both the type's
485constraint and all of its parents' constraints into a single
486subroutine reference.
6ba6d68c 487
cfd006f0 488=back
4e036ee4 489
cfd006f0 490=item B<< $constraint->equals($type_name_or_object) >>
e606ae5f 491
cfd006f0 492Returns true if the supplied name or type object is the same as the
493current type.
66811d63 494
cfd006f0 495=item B<< $constraint->is_subtype_of($type_name_or_object) >>
e606ae5f 496
cfd006f0 497Returns true if the supplied name or type object is a parent of the
498current type.
3726f905 499
cfd006f0 500=item B<< $constraint->is_a_type_of($type_name_or_object) >>
e606ae5f 501
cfd006f0 502Returns true if the given type is the same as the current type, or is
503a parent of the current type. This is a shortcut for checking
504C<equals> and C<is_subtype_of>.
d9e17f80 505
cfd006f0 506=item B<< $constraint->coerce($value) >>
2f7e4042 507
b2894aea 508This will attempt to coerce the value to the type. If the type does not
cfd006f0 509have any defined coercions this will throw an error.
66811d63 510
572b5187 511If no coercion can produce a value matching C<$constraint>, the original
512value is returned.
513
086c6b6c 514=item B<< $constraint->assert_coerce($value) >>
572b5187 515
516This method behaves just like C<coerce>, but if the result is not valid
517according to C<$constraint>, an error is thrown.
518
cfd006f0 519=item B<< $constraint->check($value) >>
2f7e4042 520
cfd006f0 521Returns true if the given value passes the constraint for the type.
76d37e5a 522
cfd006f0 523=item B<< $constraint->validate($value) >>
2f7e4042 524
cfd006f0 525This is similar to C<check>. However, if the type I<is valid> then the
526method returns an explicit C<undef>. If the type is not valid, we call
527C<< $self->get_message($value) >> internally to generate an error
528message.
76d37e5a 529
952320e0 530=item B<< $constraint->assert_valid($value) >>
531
532Like C<check> and C<validate>, this method checks whether C<$value> is
533valid under the constraint. If it is, it will return true. If it is not,
534an exception will be thrown with the results of
535C<< $self->get_message($value) >>.
536
cfd006f0 537=item B<< $constraint->name >>
2f7e4042 538
cfd006f0 539Returns the type's name, as provided to the constructor.
688fcdda 540
cfd006f0 541=item B<< $constraint->parent >>
2f7e4042 542
cfd006f0 543Returns the type's parent, as provided to the constructor, if any.
4e036ee4 544
cfd006f0 545=item B<< $constraint->has_parent >>
2f7e4042 546
cfd006f0 547Returns true if the type has a parent type.
a27aa600 548
cfd006f0 549=item B<< $constraint->parents >>
2f7e4042 550
cfd006f0 551A synonym for C<parent>. This is useful for polymorphism with types
552that can have more than one parent.
c8cf9aaa 553
cfd006f0 554=item B<< $constraint->constraint >>
c8cf9aaa 555
cfd006f0 556Returns the type's constraint, as provided to the constructor.
9ceb576e 557
cfd006f0 558=item B<< $constraint->get_message($value) >>
4e036ee4 559
cfd006f0 560This generates a method for the given value. If the type does not have
561an explicit message, we generate a default message.
3726f905 562
cfd006f0 563=item B<< $constraint->has_message >>
564
565Returns true if the type has a message.
566
567=item B<< $constraint->message >>
568
569Returns the type's message as a subroutine reference.
570
571=item B<< $constraint->coercion >>
572
573Returns the type's L<Moose::Meta::TypeCoercion> object, if one
574exists.
575
576=item B<< $constraint->has_coercion >>
577
578Returns true if the type has a coercion.
579
7142d232 580=item B<< $constraint->can_be_inlined >>
581
582Returns true if this type constraint can be inlined. A type constraint which
583subtypes an inlinable constraint and does not add an additional constraint
584"inherits" its parent type's inlining.
585
cfd006f0 586=item B<< $constraint->hand_optimized_type_constraint >>
587
7142d232 588B<This method is deprecated.>
589
cfd006f0 590Returns the type's hand optimized constraint, as provided to the
591constructor via the C<optimized> option.
592
593=item B<< $constraint->has_hand_optimized_type_constraint >>
594
7142d232 595B<This method is deprecated.>
596
cfd006f0 597Returns true if the type has an optimized constraint.
598
599=item B<< $constraint->create_child_type(%options) >>
451c8248 600
cfd006f0 601This returns a new type constraint of the same class using the
602provided C<%options>. The C<parent> option will be the current type.
3726f905 603
cfd006f0 604This method exists so that subclasses of this class can override this
605behavior and change how child types are created.
451c8248 606
607=back
608
4e036ee4 609=head1 BUGS
610
d4048ef3 611See L<Moose/BUGS> for details on reporting bugs.
4e036ee4 612
c8cf9aaa 613=cut