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