1 package Mouse::Util::TypeConstraints;
7 use Scalar::Util qw/blessed looks_like_number openhandle/;
10 use Mouse::Meta::TypeConstraint;
13 as where message from via type subtype coerce class_type role_type enum
26 return(where => $_[0])
29 return(message => $_[0])
40 Bool => sub { $_[0] ? $_[0] eq '1' : 1 },
41 Undef => sub { !defined($_[0]) },
42 Defined => sub { defined($_[0]) },
43 Value => sub { defined($_[0]) && !ref($_[0]) },
44 Num => sub { !ref($_[0]) && looks_like_number($_[0]) },
45 Int => sub { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ },
46 Str => sub { defined($_[0]) && !ref($_[0]) },
47 Ref => sub { ref($_[0]) },
49 ScalarRef => sub { ref($_[0]) eq 'SCALAR' },
50 ArrayRef => sub { ref($_[0]) eq 'ARRAY' },
51 HashRef => sub { ref($_[0]) eq 'HASH' },
52 CodeRef => sub { ref($_[0]) eq 'CODE' },
53 RegexpRef => sub { ref($_[0]) eq 'Regexp' },
54 GlobRef => sub { ref($_[0]) eq 'GLOB' },
57 ref($_[0]) eq 'GLOB' && openhandle($_[0])
59 blessed($_[0]) && $_[0]->isa("IO::Handle")
62 Object => sub { blessed($_[0]) && blessed($_[0]) ne 'Regexp' },
64 ClassName => sub { Mouse::Util::is_class_loaded($_[0]) },
65 RoleName => sub { (Mouse::Util::find_meta($_[0]) || return 0)->isa('Mouse::Meta::Role') },
68 while (my ($name, $code) = each %builtins) {
69 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
71 _compiled_type_constraint => $code,
73 $TYPE_SOURCE{$name} = __PACKAGE__;
76 sub optimized_constraints { \%TYPE }
78 my @builtins = keys %TYPE;
79 sub list_all_builtin_type_constraints { @builtins }
81 sub list_all_type_constraints { keys %TYPE }
88 if(@_ == 1 && ref $_[0]){ # type { where => ... }
91 elsif(@_ == 2 && ref $_[1]){ # type $name => { where => ... }*
95 elsif(@_ % 2){ # odd number of arguments
103 $name = '__ANON__' if !defined $name;
107 if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
108 Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
111 my $constraint = $conf{where} || do {
112 my $as = delete $conf{as} || 'Any';
113 ($TYPE{$as} ||= _build_type_constraint($as))->{_compiled_type_constraint};
116 my $tc = Mouse::Meta::TypeConstraint->new(
118 _compiled_type_constraint => sub {
120 return &{$constraint};
124 $TYPE_SOURCE{$name} = $pkg;
134 if(@_ == 1 && ref $_[0]){ # type { where => ... }
137 elsif(@_ == 2 && ref $_[1]){ # type $name => { where => ... }*
141 elsif(@_ % 2){ # odd number of arguments
149 $name = '__ANON__' if !defined $name;
153 if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
154 Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
157 my $constraint = delete $conf{where};
158 my $as_constraint = find_or_create_isa_type_constraint(delete $conf{as} || 'Any')
159 ->{_compiled_type_constraint};
161 my $tc = Mouse::Meta::TypeConstraint->new(
163 _compiled_type_constraint => (
167 $as_constraint->($_[0]) && $constraint->($_[0])
171 $as_constraint->($_[0]);
177 $TYPE_SOURCE{$name} = $pkg;
186 Carp::croak "Cannot find type '$name', perhaps you forgot to load it."
189 unless ($COERCE{$name}) {
191 $COERCE_KEYS{$name} = [];
194 while (my($type, $code) = splice @_, 0, 2) {
195 Carp::croak "A coercion action already exists for '$type'"
196 if $COERCE{$name}->{$type};
198 if (! $TYPE{$type}) {
199 # looks parameterized
200 if ($type =~ /^[^\[]+\[.+\]$/) {
201 $TYPE{$type} = _build_type_constraint($type);
203 Carp::croak "Could not find the type constraint ($type) to coerce from"
207 push @{ $COERCE_KEYS{$name} }, $type;
208 $COERCE{$name}->{$type} = $code;
214 my($name, $conf) = @_;
215 if ($conf && $conf->{class}) {
216 # No, you're using this wrong
217 warn "class_type() should be class_type(ClassName). Perhaps you're looking for subtype $name => as '$conf->{class}'?";
218 subtype($name, as => $conf->{class});
221 $name => where => sub { $_->isa($name) }
227 my($name, $conf) = @_;
228 my $role = $conf->{role};
230 $name => where => sub {
231 return unless defined $_ && ref($_) && $_->isa('Mouse::Object');
232 $_->meta->does_role($role);
237 # this is an original method for Mouse
238 sub typecast_constraints {
239 my($class, $pkg, $types, $value) = @_;
240 Carp::croak("wrong arguments count") unless @_==4;
243 for my $type ( split /\|/, $types ) {
244 next unless $COERCE{$type};
245 for my $coerce_type (@{ $COERCE_KEYS{$type}}) {
247 next unless $TYPE{$coerce_type}->check($value);
249 $_ = $COERCE{$type}->{$coerce_type}->($value);
250 return $_ if $types->check($_);
258 # enum ['small', 'medium', 'large']
259 if (ref($_[0]) eq 'ARRAY') {
260 my @elements = @{ shift @_ };
262 my $name = 'Mouse::Util::TypeConstaints::Enum::Serial::'
264 enum($name, @elements);
268 # enum size => 'small', 'medium', 'large'
270 my %is_valid = map { $_ => 1 } @_;
273 $name => where => sub { $is_valid{$_} }
277 sub _build_type_constraint {
282 if ($spec =~ /^([^\[]+)\[(.+)\]$/) {
287 if ($constraint eq 'Maybe') {
288 $parent = _build_type_constraint('Undef');
290 $parent = _build_type_constraint($constraint);
292 my $child = _build_type_constraint($param);
293 if ($constraint eq 'ArrayRef') {
295 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
297 " if (\$parent->check(\$_[0])) {\n" .
298 " foreach my \$e (\@{\$_[0]}) {\n" .
299 " return () unless \$child->check(\$e);\n" .
306 $code = eval $code_str or Carp::confess("Failed to generate inline type constraint: $@");
307 } elsif ($constraint eq 'HashRef') {
309 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
311 " if (\$parent->check(\$_[0])) {\n" .
312 " foreach my \$e (values \%{\$_[0]}) {\n" .
313 " return () unless \$child->check(\$e);\n" .
320 $code = eval $code_str or Carp::confess($@);
321 } elsif ($constraint eq 'Maybe') {
323 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
325 " return \$child->check(\$_[0]) || \$parent->check(\$_[0]);\n" .
328 $code = eval $code_str or Carp::confess($@);
330 Carp::confess("Support for parameterized types other than Maybe, ArrayRef or HashRef is not implemented yet");
332 $TYPE{$spec} = Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
334 $code = $TYPE{ $spec };
336 # is $spec a known role? If so, constrain with 'does' instead of 'isa'
337 require Mouse::Meta::Role;
338 my $check = Mouse::Meta::Role->_metaclass_cache($spec)?
341 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
343 " Scalar::Util::blessed(\$_[0]) && \$_[0]->$check('$spec');\n" .
346 $code = eval $code_str or Carp::confess($@);
347 $TYPE{$spec} = Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
350 return Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
353 sub find_type_constraint {
355 if(blessed($type) && $type->isa('Mouse::Meta::TypeConstraint')){
363 sub find_or_create_isa_type_constraint {
364 my $type_constraint = shift;
366 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)")
367 if $type_constraint =~ /\A ( [^\[]+ ) \[\.+\] \z/xms &&
375 $type_constraint =~ s/\s+//g;
377 $code = $TYPE{$type_constraint};
379 my @type_constraints = split /\|/, $type_constraint;
380 if (@type_constraints == 1) {
381 $code = $TYPE{$type_constraints[0]} ||
382 _build_type_constraint($type_constraints[0]);
384 my @code_list = map {
385 $TYPE{$_} || _build_type_constraint($_)
387 $code = Mouse::Meta::TypeConstraint->new(
388 _compiled_type_constraint => sub {
390 for my $code (@code_list) {
391 return 1 if $code->check($_[0]);
395 name => $type_constraint,
408 Mouse::Util::TypeConstraints - Type constraint system for Mouse
412 use Mouse::Util::TypeConstraints;
418 subtype 'NaturalLessThanTen'
421 => message { "This number ($_) is not less than ten!" };
427 enum 'RGBColors' => qw(red green blue);
429 no Mouse::Util::TypeConstraints;
433 This module provides Mouse with the ability to create custom type
434 constraints to be used in attribute definition.
436 =head2 Important Caveat
438 This is B<NOT> a type system for Perl 5. These are type constraints,
439 and they are not used by Mouse unless you tell it to. No type
440 inference is performed, expressions are not typed, etc. etc. etc.
442 A type constraint is at heart a small "check if a value is valid"
443 function. A constraint can be associated with an attribute. This
444 simplifies parameter validation, and makes your code clearer to read,
445 because you can refer to constraints by name.
447 =head2 Slightly Less Important Caveat
449 It is B<always> a good idea to quote your type names.
451 This prevents Perl from trying to execute the call as an indirect
452 object call. This can be an issue when you have a subtype with the
453 same name as a valid class.
457 subtype DateTime => as Object => where { $_->isa('DateTime') };
459 will I<just work>, while this:
462 subtype DateTime => as Object => where { $_->isa('DateTime') };
464 will fail silently and cause many headaches. The simple way to solve
465 this, as well as future proof your subtypes from classes which have
466 yet to have been created, is to quote the type name:
469 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
471 =head2 Default Type Constraints
473 This module also provides a simple hierarchy for Perl 5 types, here is
474 that hierarchy represented visually.
499 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
500 parameterized, this means you can say:
502 ArrayRef[Int] # an array of integers
503 HashRef[CodeRef] # a hash of str to CODE ref mappings
504 Maybe[Str] # value may be a string, may be undefined
506 If Mouse finds a name in brackets that it does not recognize as an
507 existing type, it assumes that this is a class name, for example
508 C<ArrayRef[DateTime]>.
510 B<NOTE:> Unless you parameterize a type, then it is invalid to include
511 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
512 name, I<not> as a parameterization of C<ArrayRef>.
514 B<NOTE:> The C<Undef> type constraint for the most part works
515 correctly now, but edge cases may still exist, please use it
518 B<NOTE:> The C<ClassName> type constraint does a complex package
519 existence check. This means that your class B<must> be loaded for this
520 type constraint to pass.
522 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
523 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
524 constraint checks that an I<object does> the named role.
526 =head2 Type Constraint Naming
528 Type name declared via this module can only contain alphanumeric
529 characters, colons (:), and periods (.).
531 Since the types created by this module are global, it is suggested
532 that you namespace your types just as you would namespace your
533 modules. So instead of creating a I<Color> type for your
534 B<My::Graphics> module, you would call the type
535 I<My::Graphics::Types::Color> instead.
537 =head2 Use with Other Constraint Modules
539 This module can play nicely with other constraint modules with some
540 slight tweaking. The C<where> clause in types is expected to be a
541 C<CODE> reference which checks it's first argument and returns a
542 boolean. Since most constraint modules work in a similar way, it
543 should be simple to adapt them to work with Mouse.
545 For instance, this is how you could use it with
546 L<Declare::Constraints::Simple> to declare a completely new type.
548 type 'HashOfArrayOfObjects',
552 -values => IsArrayRef(IsObject)
556 Here is an example of using L<Test::Deep> and it's non-test
557 related C<eq_deeply> function.
559 type 'ArrayOfHashOfBarsAndRandomNumbers'
562 array_each(subhashof({
564 random_number => ignore()
570 =head2 optimized_constraints -> HashRef[CODE]
572 Returns the simple type constraints that Mouse understands.
578 =item B<subtype 'Name' => as 'Parent' => where { } ...>
580 =item B<subtype as 'Parent' => where { } ...>
582 =item B<class_type ($class, ?$options)>
584 =item B<role_type ($role, ?$options)>
586 =item B<enum (\@values)>
592 Much of this documentation was taken from L<Moose::Util::TypeConstraints>