Skip class_type etc stack frames when checking the package that defines a subtype
[gitmo/Mouse.git] / lib / Mouse / Util / TypeConstraints.pm
CommitLineData
3b46bd49 1package Mouse::Util::TypeConstraints;
d60c78b9 2use strict;
3use warnings;
f3bb863f 4
5use Exporter;
9baf5d6b 6
61a02a3a 7use Carp ();
6c169c50 8use Scalar::Util qw/blessed looks_like_number openhandle/;
6d28c5cf 9
2efc0af1 10use Mouse::Util qw(does_role not_supported);
a497c7d3 11use Mouse::Meta::Module; # class_of
684db121 12use Mouse::Meta::TypeConstraint;
d60c78b9 13
f3bb863f 14our @ISA = qw(Exporter);
139d92d2 15our @EXPORT = qw(
d44f0d03 16 as where message from via type subtype coerce class_type role_type enum
ccf44227 17 find_type_constraint
139d92d2 18);
19
cceb0e25 20my %TYPE;
7dbebb1b 21my %TYPE_SOURCE;
8a7f2a8a 22my %COERCE;
23my %COERCE_KEYS;
4188b837 24
139d92d2 25sub as ($) {
cd2b9201 26 return(as => $_[0]);
61a02a3a 27}
139d92d2 28sub where (&) {
cd2b9201 29 return(where => $_[0])
61a02a3a 30}
0f1dae9a 31sub message (&) {
cd2b9201 32 return(message => $_[0])
61a02a3a 33}
34
73766a27 35sub from { @_ }
cd2b9201 36sub via (&) { $_[0] }
61a02a3a 37
321e5271 38BEGIN {
993e62a7 39 my %builtins = (
381f326a 40 Any => sub { 1 },
41 Item => sub { 1 },
73766a27 42
43 Bool => sub { $_[0] ? $_[0] eq '1' : 1 },
c91d12e0 44 Undef => sub { !defined($_[0]) },
45 Defined => sub { defined($_[0]) },
46 Value => sub { defined($_[0]) && !ref($_[0]) },
47 Num => sub { !ref($_[0]) && looks_like_number($_[0]) },
48 Int => sub { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ },
49 Str => sub { defined($_[0]) && !ref($_[0]) },
c91d12e0 50 Ref => sub { ref($_[0]) },
51
52 ScalarRef => sub { ref($_[0]) eq 'SCALAR' },
53 ArrayRef => sub { ref($_[0]) eq 'ARRAY' },
54 HashRef => sub { ref($_[0]) eq 'HASH' },
55 CodeRef => sub { ref($_[0]) eq 'CODE' },
56 RegexpRef => sub { ref($_[0]) eq 'Regexp' },
57 GlobRef => sub { ref($_[0]) eq 'GLOB' },
381f326a 58
59 FileHandle => sub {
c91d12e0 60 ref($_[0]) eq 'GLOB' && openhandle($_[0])
381f326a 61 or
c91d12e0 62 blessed($_[0]) && $_[0]->isa("IO::Handle")
abe4e887 63 },
381f326a 64
c91d12e0 65 Object => sub { blessed($_[0]) && blessed($_[0]) ne 'Regexp' },
73766a27 66
67 ClassName => sub { Mouse::Util::is_class_loaded($_[0]) },
d4571def 68 RoleName => sub { (Mouse::Util::find_meta($_[0]) || return 0)->isa('Mouse::Meta::Role') },
8a7f2a8a 69 );
993e62a7 70
71 while (my ($name, $code) = each %builtins) {
73766a27 72 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
73 name => $name,
74 _compiled_type_constraint => $code,
75 );
d4571def 76 $TYPE_SOURCE{$name} = __PACKAGE__;
78b13827 77 }
d3982c7e 78
cceb0e25 79 sub optimized_constraints { \%TYPE }
d4571def 80
993e62a7 81 my @builtins = keys %TYPE;
82 sub list_all_builtin_type_constraints { @builtins }
83
84 sub list_all_type_constraints { keys %TYPE }
381f326a 85}
d3982c7e 86
139d92d2 87sub type {
73766a27 88 my $name;
89 my %conf;
90
91 if(@_ == 1 && ref $_[0]){ # type { where => ... }
92 %conf = %{$_[0]};
93 }
94 elsif(@_ == 2 && ref $_[1]){ # type $name => { where => ... }*
95 $name = $_[0];
96 %conf = %{$_[1]};
97 }
98 elsif(@_ % 2){ # odd number of arguments
99 $name = shift;
100 %conf = @_;
101 }
102 else{
103 %conf = @_;
104 }
105
106 $name = '__ANON__' if !defined $name;
107
108 my $pkg = caller;
cd2b9201 109
0d062abb 110 if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
7dbebb1b 111 Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
cd2b9201 112 }
d4571def 113
321e5271 114 my $constraint = $conf{where} || do {
115 my $as = delete $conf{as} || 'Any';
d4571def 116 ($TYPE{$as} ||= _build_type_constraint($as))->{_compiled_type_constraint};
321e5271 117 };
7dbebb1b 118
d4571def 119 my $tc = Mouse::Meta::TypeConstraint->new(
120 name => $name,
684db121 121 _compiled_type_constraint => sub {
122 local $_ = $_[0];
d4571def 123 return &{$constraint};
124 },
684db121 125 );
d4571def 126
127 $TYPE_SOURCE{$name} = $pkg;
128 $TYPE{$name} = $tc;
129
130 return $tc;
0d9fea22 131}
132
139d92d2 133sub subtype {
7a50b450 134 my $name;
135 my %conf;
136
73766a27 137 if(@_ == 1 && ref $_[0]){ # type { where => ... }
138 %conf = %{$_[0]};
139 }
140 elsif(@_ == 2 && ref $_[1]){ # type $name => { where => ... }*
141 $name = $_[0];
142 %conf = %{$_[1]};
143 }
144 elsif(@_ % 2){ # odd number of arguments
7a50b450 145 $name = shift;
146 %conf = @_;
147 }
148 else{
149 %conf = @_;
7a50b450 150 }
151
73766a27 152 $name = '__ANON__' if !defined $name;
153
0608c8c8 154 my $pkg = caller($conf{_caller_level} || 1);
73766a27 155
0d062abb 156 if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
7dbebb1b 157 Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
73766a27 158 }
7dbebb1b 159
d4571def 160 my $constraint = delete $conf{where};
161 my $as_constraint = find_or_create_isa_type_constraint(delete $conf{as} || 'Any')
162 ->{_compiled_type_constraint};
163
164 my $tc = Mouse::Meta::TypeConstraint->new(
684db121 165 name => $name,
166 _compiled_type_constraint => (
167 $constraint ?
168 sub {
169 local $_ = $_[0];
d4571def 170 $as_constraint->($_[0]) && $constraint->($_[0])
684db121 171 } :
172 sub {
173 local $_ = $_[0];
d4571def 174 $as_constraint->($_[0]);
684db121 175 }
176 ),
d4571def 177 %conf,
684db121 178 );
7dbebb1b 179
d4571def 180 $TYPE_SOURCE{$name} = $pkg;
181 $TYPE{$name} = $tc;
182
183 return $tc;
4188b837 184}
185
139d92d2 186sub coerce {
cd2b9201 187 my $name = shift;
61a02a3a 188
189 Carp::croak "Cannot find type '$name', perhaps you forgot to load it."
cceb0e25 190 unless $TYPE{$name};
61a02a3a 191
8a7f2a8a 192 unless ($COERCE{$name}) {
193 $COERCE{$name} = {};
194 $COERCE_KEYS{$name} = [];
195 }
cd2b9201 196
197 while (my($type, $code) = splice @_, 0, 2) {
61a02a3a 198 Carp::croak "A coercion action already exists for '$type'"
8a7f2a8a 199 if $COERCE{$name}->{$type};
61a02a3a 200
310ad28b 201 if (! $TYPE{$type}) {
202 # looks parameterized
203 if ($type =~ /^[^\[]+\[.+\]$/) {
94593ae8 204 $TYPE{$type} = _build_type_constraint($type);
310ad28b 205 } else {
206 Carp::croak "Could not find the type constraint ($type) to coerce from"
207 }
208 }
61a02a3a 209
cd2b9201 210 push @{ $COERCE_KEYS{$name} }, $type;
8a7f2a8a 211 $COERCE{$name}->{$type} = $code;
61a02a3a 212 }
cd2b9201 213 return;
4188b837 214}
215
139d92d2 216sub class_type {
ecc6e3b1 217 my($name, $conf) = @_;
d9f8c878 218 if ($conf && $conf->{class}) {
219 # No, you're using this wrong
220 warn "class_type() should be class_type(ClassName). Perhaps you're looking for subtype $name => as '$conf->{class}'?";
0608c8c8 221 subtype $name => (
222 as => $conf->{class},
223 caller_level => (($conf->{_caller_level}||0) + 1),
224 );
a497c7d3 225 }
226 else {
227 subtype $name => (
228 where => sub { blessed($_) && $_->isa($name) },
0608c8c8 229 caller_level => (($conf->{_caller_level}||0) + 1),
d9f8c878 230 );
231 }
ecc6e3b1 232}
233
139d92d2 234sub role_type {
47f36c05 235 my($name, $conf) = @_;
236 my $role = $conf->{role};
a497c7d3 237 subtype $name => (
2efc0af1 238 where => sub { does_role($_, $role) },
0608c8c8 239 caller_level => (($conf->{_caller_level}||0) + 1),
61a02a3a 240 );
47f36c05 241}
242
684db121 243# this is an original method for Mouse
4188b837 244sub typecast_constraints {
684db121 245 my($class, $pkg, $types, $value) = @_;
2efc0af1 246 Carp::croak("wrong arguments count") unless @_ == 4;
eec1bb49 247
b3b74cc6 248 local $_;
684db121 249 for my $type ( split /\|/, $types ) {
8a7f2a8a 250 next unless $COERCE{$type};
8a7f2a8a 251 for my $coerce_type (@{ $COERCE_KEYS{$type}}) {
b3b74cc6 252 $_ = $value;
684db121 253 next unless $TYPE{$coerce_type}->check($value);
b3b74cc6 254 $_ = $value;
c91d12e0 255 $_ = $COERCE{$type}->{$coerce_type}->($value);
684db121 256 return $_ if $types->check($_);
4188b837 257 }
258 }
4188b837 259 return $value;
260}
261
01904723 262my $serial_enum = 0;
d44f0d03 263sub enum {
01904723 264 # enum ['small', 'medium', 'large']
265 if (ref($_[0]) eq 'ARRAY') {
266 my @elements = @{ shift @_ };
267
268 my $name = 'Mouse::Util::TypeConstaints::Enum::Serial::'
269 . ++$serial_enum;
270 enum($name, @elements);
271 return $name;
272 }
273
274 # enum size => 'small', 'medium', 'large'
d44f0d03 275 my $name = shift;
276 my %is_valid = map { $_ => 1 } @_;
277
0608c8c8 278 subtype $name => (
279 where => sub { $is_valid{$_} },
280 _caller_level => 1,
d44f0d03 281 );
282}
283
321e5271 284sub _build_type_constraint {
2efc0af1 285 my($spec) = @_;
321e5271 286
321e5271 287 my $code;
94593ae8 288 $spec =~ s/\s+//g;
2efc0af1 289
290 if ($spec =~ /\A (\w+) \[ (.+) \] \z/xms) {
321e5271 291 # parameterized
292 my $constraint = $1;
293 my $param = $2;
294 my $parent;
2efc0af1 295
321e5271 296 if ($constraint eq 'Maybe') {
297 $parent = _build_type_constraint('Undef');
2efc0af1 298 }
299 else {
321e5271 300 $parent = _build_type_constraint($constraint);
301 }
302 my $child = _build_type_constraint($param);
303 if ($constraint eq 'ArrayRef') {
304 my $code_str =
305 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
306 "sub {\n" .
684db121 307 " if (\$parent->check(\$_[0])) {\n" .
321e5271 308 " foreach my \$e (\@{\$_[0]}) {\n" .
684db121 309 " return () unless \$child->check(\$e);\n" .
321e5271 310 " }\n" .
311 " return 1;\n" .
312 " }\n" .
313 " return ();\n" .
314 "};\n"
315 ;
316 $code = eval $code_str or Carp::confess("Failed to generate inline type constraint: $@");
317 } elsif ($constraint eq 'HashRef') {
318 my $code_str =
319 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
320 "sub {\n" .
684db121 321 " if (\$parent->check(\$_[0])) {\n" .
321e5271 322 " foreach my \$e (values \%{\$_[0]}) {\n" .
684db121 323 " return () unless \$child->check(\$e);\n" .
321e5271 324 " }\n" .
325 " return 1;\n" .
326 " }\n" .
327 " return ();\n" .
328 "};\n"
329 ;
330 $code = eval $code_str or Carp::confess($@);
331 } elsif ($constraint eq 'Maybe') {
332 my $code_str =
333 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
334 "sub {\n" .
684db121 335 " return \$child->check(\$_[0]) || \$parent->check(\$_[0]);\n" .
321e5271 336 "};\n"
337 ;
338 $code = eval $code_str or Carp::confess($@);
339 } else {
766534c2 340 Carp::confess("Support for parameterized types other than Maybe, ArrayRef or HashRef is not implemented yet");
321e5271 341 }
684db121 342 $TYPE{$spec} = Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
321e5271 343 } else {
344 $code = $TYPE{ $spec };
345 if (! $code) {
5c5a61e0 346 # is $spec a known role? If so, constrain with 'does' instead of 'isa'
347 require Mouse::Meta::Role;
348 my $check = Mouse::Meta::Role->_metaclass_cache($spec)?
349 'does' : 'isa';
321e5271 350 my $code_str =
351 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
352 "sub {\n" .
5c5a61e0 353 " Scalar::Util::blessed(\$_[0]) && \$_[0]->$check('$spec');\n" .
321e5271 354 "}"
355 ;
356 $code = eval $code_str or Carp::confess($@);
684db121 357 $TYPE{$spec} = Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
321e5271 358 }
359 }
684db121 360 return Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
321e5271 361}
362
363sub find_type_constraint {
993e62a7 364 my($type) = @_;
365 if(blessed($type) && $type->isa('Mouse::Meta::TypeConstraint')){
366 return $type;
367 }
368 else{
369 return $TYPE{$type};
370 }
321e5271 371}
372
2efc0af1 373sub find_or_create_does_type_constraint{
374 not_supported;
375}
376
321e5271 377sub find_or_create_isa_type_constraint {
378 my $type_constraint = shift;
379
9c85e9dc 380 Carp::confess("Got isa => type_constraints, but Mouse does not yet support parameterized types for containers other than ArrayRef and HashRef and Maybe (rt.cpan.org #39795)")
381 if $type_constraint =~ /\A ( [^\[]+ ) \[\.+\] \z/xms &&
382 $1 ne 'ArrayRef' &&
383 $1 ne 'HashRef' &&
384 $1 ne 'Maybe'
385 ;
386
321e5271 387
388 $type_constraint =~ s/\s+//g;
94593ae8 389
2efc0af1 390 my $tc = find_type_constraint($type_constraint);
391 if (!$tc) {
94593ae8 392 my @type_constraints = split /\|/, $type_constraint;
393 if (@type_constraints == 1) {
2efc0af1 394 $tc = $TYPE{$type_constraints[0]} ||
94593ae8 395 _build_type_constraint($type_constraints[0]);
2efc0af1 396 }
397 else {
94593ae8 398 my @code_list = map {
399 $TYPE{$_} || _build_type_constraint($_)
400 } @type_constraints;
2efc0af1 401
402 $tc = Mouse::Meta::TypeConstraint->new(
403 name => $type_constraint,
404
684db121 405 _compiled_type_constraint => sub {
2efc0af1 406 foreach my $code (@code_list) {
684db121 407 return 1 if $code->check($_[0]);
408 }
409 return 0;
410 },
684db121 411 );
94593ae8 412 }
321e5271 413 }
2efc0af1 414 return $tc;
321e5271 415}
416
d60c78b9 4171;
418
6feb83f1 419__END__
420
421=head1 NAME
422
5893ee36 423Mouse::Util::TypeConstraints - Type constraint system for Mouse
424
425=head2 SYNOPSIS
426
427 use Mouse::Util::TypeConstraints;
428
429 subtype 'Natural'
430 => as 'Int'
431 => where { $_ > 0 };
432
433 subtype 'NaturalLessThanTen'
434 => as 'Natural'
435 => where { $_ < 10 }
436 => message { "This number ($_) is not less than ten!" };
437
438 coerce 'Num'
439 => from 'Str'
440 => via { 0+$_ };
441
442 enum 'RGBColors' => qw(red green blue);
443
444 no Mouse::Util::TypeConstraints;
445
446=head1 DESCRIPTION
447
448This module provides Mouse with the ability to create custom type
449constraints to be used in attribute definition.
450
451=head2 Important Caveat
452
453This is B<NOT> a type system for Perl 5. These are type constraints,
454and they are not used by Mouse unless you tell it to. No type
455inference is performed, expressions are not typed, etc. etc. etc.
456
457A type constraint is at heart a small "check if a value is valid"
458function. A constraint can be associated with an attribute. This
459simplifies parameter validation, and makes your code clearer to read,
460because you can refer to constraints by name.
461
462=head2 Slightly Less Important Caveat
463
464It is B<always> a good idea to quote your type names.
465
466This prevents Perl from trying to execute the call as an indirect
467object call. This can be an issue when you have a subtype with the
468same name as a valid class.
469
470For instance:
471
472 subtype DateTime => as Object => where { $_->isa('DateTime') };
473
474will I<just work>, while this:
475
476 use DateTime;
477 subtype DateTime => as Object => where { $_->isa('DateTime') };
478
479will fail silently and cause many headaches. The simple way to solve
480this, as well as future proof your subtypes from classes which have
481yet to have been created, is to quote the type name:
482
483 use DateTime;
484 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
485
486=head2 Default Type Constraints
487
488This module also provides a simple hierarchy for Perl 5 types, here is
489that hierarchy represented visually.
490
491 Any
492 Item
493 Bool
494 Maybe[`a]
495 Undef
496 Defined
497 Value
498 Num
499 Int
500 Str
501 ClassName
502 RoleName
503 Ref
504 ScalarRef
505 ArrayRef[`a]
506 HashRef[`a]
507 CodeRef
508 RegexpRef
509 GlobRef
510 FileHandle
511 Object
512 Role
513
514B<NOTE:> Any type followed by a type parameter C<[`a]> can be
515parameterized, this means you can say:
516
517 ArrayRef[Int] # an array of integers
518 HashRef[CodeRef] # a hash of str to CODE ref mappings
519 Maybe[Str] # value may be a string, may be undefined
520
521If Mouse finds a name in brackets that it does not recognize as an
522existing type, it assumes that this is a class name, for example
523C<ArrayRef[DateTime]>.
524
525B<NOTE:> Unless you parameterize a type, then it is invalid to include
526the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
527name, I<not> as a parameterization of C<ArrayRef>.
528
529B<NOTE:> The C<Undef> type constraint for the most part works
530correctly now, but edge cases may still exist, please use it
531sparingly.
532
533B<NOTE:> The C<ClassName> type constraint does a complex package
534existence check. This means that your class B<must> be loaded for this
535type constraint to pass.
536
537B<NOTE:> The C<RoleName> constraint checks a string is a I<package
538name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
539constraint checks that an I<object does> the named role.
540
541=head2 Type Constraint Naming
542
543Type name declared via this module can only contain alphanumeric
544characters, colons (:), and periods (.).
545
546Since the types created by this module are global, it is suggested
547that you namespace your types just as you would namespace your
548modules. So instead of creating a I<Color> type for your
549B<My::Graphics> module, you would call the type
550I<My::Graphics::Types::Color> instead.
551
552=head2 Use with Other Constraint Modules
553
554This module can play nicely with other constraint modules with some
555slight tweaking. The C<where> clause in types is expected to be a
556C<CODE> reference which checks it's first argument and returns a
557boolean. Since most constraint modules work in a similar way, it
558should be simple to adapt them to work with Mouse.
559
560For instance, this is how you could use it with
561L<Declare::Constraints::Simple> to declare a completely new type.
562
563 type 'HashOfArrayOfObjects',
564 {
565 where => IsHashRef(
566 -keys => HasLength,
567 -values => IsArrayRef(IsObject)
568 )
569 };
570
571Here is an example of using L<Test::Deep> and it's non-test
572related C<eq_deeply> function.
573
574 type 'ArrayOfHashOfBarsAndRandomNumbers'
575 => where {
576 eq_deeply($_,
577 array_each(subhashof({
578 bar => isa('Bar'),
579 random_number => ignore()
580 })))
581 };
6feb83f1 582
583=head1 METHODS
584
585=head2 optimized_constraints -> HashRef[CODE]
586
587Returns the simple type constraints that Mouse understands.
588
c91d12e0 589=head1 FUNCTIONS
590
591=over 4
592
1820fffe 593=item C<< subtype 'Name' => as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
c91d12e0 594
1820fffe 595=item C<< subtype as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
c91d12e0 596
1820fffe 597=item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
c91d12e0 598
1820fffe 599=item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
c91d12e0 600
1820fffe 601=item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
602
603=back
604
605=over 4
606
607=item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
c91d12e0 608
609=back
610
5893ee36 611=head1 THANKS
612
1820fffe 613Much of this documentation was taken from C<Moose::Util::TypeConstraints>
614
615=head1 SEE ALSO
616
617L<Moose::Util::TypeConstraints>
5893ee36 618
6feb83f1 619=cut
620
621