1 package Mouse::Util::TypeConstraints;
2 use Mouse::Util qw(does_role not_supported); # enables strict and warnings
7 use Mouse::Meta::TypeConstraint;
10 Mouse::Exporter->setup_import_methods(
12 as where message optimize_as
15 type subtype class_type role_type duck_type
23 our @CARP_NOT = qw(Mouse::Meta::Attribute);
28 $TYPE{Any} = Mouse::Meta::TypeConstraint->new(
33 # $name => $parent, $code,
39 Maybe => 'Item', undef,
42 Undef => 'Item', \&Undef,
43 Defined => 'Item', \&Defined,
44 Bool => 'Item', \&Bool,
45 Value => 'Defined', \&Value,
46 Str => 'Value', \&Str,
51 Ref => 'Defined', \&Ref,
52 ScalarRef => 'Ref', \&ScalarRef,
53 ArrayRef => 'Ref', \&ArrayRef,
54 HashRef => 'Ref', \&HashRef,
55 CodeRef => 'Ref', \&CodeRef,
56 RegexpRef => 'Ref', \&RegexpRef,
57 GlobRef => 'Ref', \&GlobRef,
60 FileHandle => 'GlobRef', \&FileHandle,
61 Object => 'Ref', \&Object,
63 # special string types
64 ClassName => 'Str', \&ClassName,
65 RoleName => 'ClassName', \&RoleName,
69 while (my ($name, $parent, $code) = splice @builtins, 0, 3) {
70 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
72 parent => $TYPE{$parent},
77 # make it parametarizable
79 $TYPE{Maybe} {constraint_generator} = \&_parameterize_Maybe_for;
80 $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
81 $TYPE{HashRef} {constraint_generator} = \&_parameterize_HashRef_for;
85 sub as ($) { (as => $_[0]) } ## no critic
86 sub where (&) { (where => $_[0]) } ## no critic
87 sub message (&) { (message => $_[0]) } ## no critic
88 sub optimize_as (&) { (optimize_as => $_[0]) } ## no critic
91 sub via (&) { $_[0] } ## no critic
95 sub optimized_constraints { # DEPRECATED
96 Carp::cluck('optimized_constraints() has been deprecated');
100 undef @builtins; # free the allocated memory
101 @builtins = keys %TYPE; # reuse it
102 sub list_all_builtin_type_constraints { @builtins }
104 sub list_all_type_constraints { keys %TYPE }
112 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
115 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
119 elsif(@_ % 2){ # @_ : $name => ( where => ... )
122 else{ # @_ : (name => $name, where => ...)
132 if($mode eq 'subtype'){
133 $parent = delete $args{as};
135 $parent = delete $args{name};
141 # set 'package_defined_in' only if it is not a core package
142 my $this = $args{package_defined_in};
145 if($this !~ /\A Mouse \b/xms){
146 $args{package_defined_in} = $this;
151 my $that = $TYPE{$name}->{package_defined_in} || __PACKAGE__;
152 ($this eq $that) or Carp::croak(
153 "The type constraint '$name' has already been created in $that and cannot be created again in $this"
158 $args{name} = '__ANON__';
161 $args{constraint} = delete $args{where} if exists $args{where};
162 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
165 if($mode eq 'subtype'){
166 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
169 $constraint = Mouse::Meta::TypeConstraint->new(%args);
173 return $TYPE{$name} = $constraint;
181 return _create_type('type', @_);
185 return _create_type('subtype', @_);
189 my $type_name = shift;
191 my $type = find_type_constraint($type_name)
192 or Carp::croak("Cannot find type '$type_name', perhaps you forgot to load it.");
194 $type->_add_type_coercions(@_);
199 my($name, $options) = @_;
200 my $class = $options->{class} || $name;
203 return _create_type 'subtype', $name => (
205 optimized_as => Mouse::Util::generate_isa_predicate_for($class),
210 my($name, $options) = @_;
211 my $role = $options->{role} || $name;
214 return _create_type 'subtype', $name => (
216 optimized_as => sub { Scalar::Util::blessed($_[0]) && does_role($_[0], $role) },
223 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
227 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
230 return _create_type 'subtype', $name => (
232 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
239 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
243 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
246 return _create_type 'subtype', $name => (
248 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
252 sub _find_or_create_regular_type{
253 my($spec, $create) = @_;
255 return $TYPE{$spec} if exists $TYPE{$spec};
257 my $meta = Mouse::Util::get_metaclass_by_name($spec);
260 return $create ? class_type($spec) : undef;
263 if(Mouse::Util::is_a_metarole($meta)){
264 return role_type($spec);
267 return class_type($spec);
271 sub _find_or_create_parameterized_type{
272 my($base, $param) = @_;
274 my $name = sprintf '%s[%s]', $base->name, $param->name;
276 $TYPE{$name} ||= $base->parameterize($param, $name);
279 sub _find_or_create_union_type{
280 return if grep{ not defined } @_;
281 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
283 my $name = join '|', @types;
286 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
288 type_constraints => \@types,
294 # param : '[' type ']' | NOTHING
298 if($c->{spec} =~ s/^\[//){
299 my $type = _parse_type($c, 1);
301 if($c->{spec} =~ s/^\]//){
304 Carp::croak("Syntax error in type: missing right square bracket in '$c->{orig}'");
312 my($c, $create) = @_;
314 if($c->{spec} =~ s/\A ([\w.:]+) //xms){
315 return _find_or_create_regular_type($1, $create);
317 Carp::croak("Syntax error in type: expect type name near '$c->{spec}' in '$c->{orig}'");
320 # single_type : name param
321 sub _parse_single_type {
322 my($c, $create) = @_;
324 my $type = _parse_name($c, $create);
325 my $param = _parse_param($c);
329 return _find_or_create_parameterized_type($type, $param);
335 elsif(defined $param){
336 Carp::croak("Undefined type with parameter [$param] in '$c->{orig}'");
343 # type : single_type ('|' single_type)*
345 my($c, $create) = @_;
347 my $type = _parse_single_type($c, $create);
348 if($c->{spec}){ # can be an union type
350 while($c->{spec} =~ s/^\|//){
351 push @types, _parse_single_type($c, $create);
354 return _find_or_create_union_type($type, @types);
361 sub find_type_constraint {
363 return $spec if Mouse::Util::is_a_type_constraint($spec);
364 return undef if !defined $spec;
370 sub find_or_parse_type_constraint {
372 return $spec if Mouse::Util::is_a_type_constraint($spec);
373 return undef if !defined $spec;
376 return $TYPE{$spec} || do{
381 my $type = _parse_type($context);
383 if($context->{spec}){
384 Carp::croak("Syntax error: extra elements '$context->{spec}' in '$context->{orig}'");
390 sub find_or_create_does_type_constraint{
391 # XXX: Moose does not register a new role_type, but Mouse does.
392 return find_or_parse_type_constraint(@_) || role_type(@_);
395 sub find_or_create_isa_type_constraint {
396 # XXX: Moose does not register a new class_type, but Mouse does.
397 return find_or_parse_type_constraint(@_) || class_type(@_);
405 Mouse::Util::TypeConstraints - Type constraint system for Mouse
409 This document describes Mouse version 0.56
413 use Mouse::Util::TypeConstraints;
419 subtype 'NaturalLessThanTen'
422 => message { "This number ($_) is not less than ten!" };
428 enum 'RGBColors' => qw(red green blue);
430 no Mouse::Util::TypeConstraints;
434 This module provides Mouse with the ability to create custom type
435 constraints to be used in attribute definition.
437 =head2 Important Caveat
439 This is B<NOT> a type system for Perl 5. These are type constraints,
440 and they are not used by Mouse unless you tell it to. No type
441 inference is performed, expressions are not typed, etc. etc. etc.
443 A type constraint is at heart a small "check if a value is valid"
444 function. A constraint can be associated with an attribute. This
445 simplifies parameter validation, and makes your code clearer to read,
446 because you can refer to constraints by name.
448 =head2 Slightly Less Important Caveat
450 It is B<always> a good idea to quote your type names.
452 This prevents Perl from trying to execute the call as an indirect
453 object call. This can be an issue when you have a subtype with the
454 same name as a valid class.
458 subtype DateTime => as Object => where { $_->isa('DateTime') };
460 will I<just work>, while this:
463 subtype DateTime => as Object => where { $_->isa('DateTime') };
465 will fail silently and cause many headaches. The simple way to solve
466 this, as well as future proof your subtypes from classes which have
467 yet to have been created, is to quote the type name:
470 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
472 =head2 Default Type Constraints
474 This module also provides a simple hierarchy for Perl 5 types, here is
475 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:> The C<Undef> type constraint for the most part works
511 correctly now, but edge cases may still exist, please use it
514 B<NOTE:> The C<ClassName> type constraint does a complex package
515 existence check. This means that your class B<must> be loaded for this
516 type constraint to pass.
518 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
519 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
520 constraint checks that an I<object does> the named role.
522 =head2 Type Constraint Naming
524 Type name declared via this module can only contain alphanumeric
525 characters, colons (:), and periods (.).
527 Since the types created by this module are global, it is suggested
528 that you namespace your types just as you would namespace your
529 modules. So instead of creating a I<Color> type for your
530 B<My::Graphics> module, you would call the type
531 I<My::Graphics::Types::Color> instead.
533 =head2 Use with Other Constraint Modules
535 This module can play nicely with other constraint modules with some
536 slight tweaking. The C<where> clause in types is expected to be a
537 C<CODE> reference which checks it's first argument and returns a
538 boolean. Since most constraint modules work in a similar way, it
539 should be simple to adapt them to work with Mouse.
541 For instance, this is how you could use it with
542 L<Declare::Constraints::Simple> to declare a completely new type.
544 type 'HashOfArrayOfObjects',
548 -values => IsArrayRef(IsObject)
552 Here is an example of using L<Test::Deep> and it's non-test
553 related C<eq_deeply> function.
555 type 'ArrayOfHashOfBarsAndRandomNumbers'
558 array_each(subhashof({
560 random_number => ignore()
566 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
568 Returns the names of builtin type constraints.
570 =head2 C<< list_all_type_constraints -> (Names) >>
572 Returns the names of all the type constraints.
578 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
580 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
582 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
584 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
586 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
588 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
590 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
592 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
594 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
596 =item C<< coerce $type => from $another_type, via { }, ... >>
602 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
608 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
612 L<Moose::Util::TypeConstraints>