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]) }
86 sub where (&) { (where => $_[0]) }
87 sub message (&) { (message => $_[0]) }
88 sub optimize_as (&) { (optimize_as => $_[0]) }
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 'type', $name => (
231 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
238 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
242 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
245 return _create_type 'type', $name => (
246 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
250 sub _find_or_create_regular_type{
251 my($spec, $create) = @_;
253 return $TYPE{$spec} if exists $TYPE{$spec};
255 my $meta = Mouse::Util::get_metaclass_by_name($spec);
258 return $create ? class_type($spec) : undef;
261 if(Mouse::Util::is_a_metarole($meta)){
262 return role_type($spec);
265 return class_type($spec);
269 sub _find_or_create_parameterized_type{
270 my($base, $param) = @_;
272 my $name = sprintf '%s[%s]', $base->name, $param->name;
274 $TYPE{$name} ||= $base->parameterize($param, $name);
277 sub _find_or_create_union_type{
278 return if grep{ not defined } @_;
279 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
281 my $name = join '|', @types;
284 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
286 type_constraints => \@types,
292 # param : '[' type ']' | NOTHING
296 if($c->{spec} =~ s/^\[//){
297 my $type = _parse_type($c, 1);
299 if($c->{spec} =~ s/^\]//){
302 Carp::croak("Syntax error in type: missing right square bracket in '$c->{orig}'");
310 my($c, $create) = @_;
312 if($c->{spec} =~ s/\A ([\w.:]+) //xms){
313 return _find_or_create_regular_type($1, $create);
315 Carp::croak("Syntax error in type: expect type name near '$c->{spec}' in '$c->{orig}'");
318 # single_type : name param
319 sub _parse_single_type {
320 my($c, $create) = @_;
322 my $type = _parse_name($c, $create);
323 my $param = _parse_param($c);
327 return _find_or_create_parameterized_type($type, $param);
333 elsif(defined $param){
334 Carp::croak("Undefined type with parameter [$param] in '$c->{orig}'");
341 # type : single_type ('|' single_type)*
343 my($c, $create) = @_;
345 my $type = _parse_single_type($c, $create);
346 if($c->{spec}){ # can be an union type
348 while($c->{spec} =~ s/^\|//){
349 push @types, _parse_single_type($c, $create);
352 return _find_or_create_union_type($type, @types);
359 sub find_type_constraint {
361 return $spec if Mouse::Util::is_a_type_constraint($spec);
362 return undef if !defined $spec;
368 sub find_or_parse_type_constraint {
370 return $spec if Mouse::Util::is_a_type_constraint($spec);
371 return undef if !defined $spec;
374 return $TYPE{$spec} || do{
379 my $type = _parse_type($context);
381 if($context->{spec}){
382 Carp::croak("Syntax error: extra elements '$context->{spec}' in '$context->{orig}'");
388 sub find_or_create_does_type_constraint{
389 # XXX: Moose does not register a new role_type, but Mouse does.
390 return find_or_parse_type_constraint(@_) || role_type(@_);
393 sub find_or_create_isa_type_constraint {
394 # XXX: Moose does not register a new class_type, but Mouse does.
395 return find_or_parse_type_constraint(@_) || class_type(@_);
403 Mouse::Util::TypeConstraints - Type constraint system for Mouse
407 This document describes Mouse version 0.50_07
411 use Mouse::Util::TypeConstraints;
417 subtype 'NaturalLessThanTen'
420 => message { "This number ($_) is not less than ten!" };
426 enum 'RGBColors' => qw(red green blue);
428 no Mouse::Util::TypeConstraints;
432 This module provides Mouse with the ability to create custom type
433 constraints to be used in attribute definition.
435 =head2 Important Caveat
437 This is B<NOT> a type system for Perl 5. These are type constraints,
438 and they are not used by Mouse unless you tell it to. No type
439 inference is performed, expressions are not typed, etc. etc. etc.
441 A type constraint is at heart a small "check if a value is valid"
442 function. A constraint can be associated with an attribute. This
443 simplifies parameter validation, and makes your code clearer to read,
444 because you can refer to constraints by name.
446 =head2 Slightly Less Important Caveat
448 It is B<always> a good idea to quote your type names.
450 This prevents Perl from trying to execute the call as an indirect
451 object call. This can be an issue when you have a subtype with the
452 same name as a valid class.
456 subtype DateTime => as Object => where { $_->isa('DateTime') };
458 will I<just work>, while this:
461 subtype DateTime => as Object => where { $_->isa('DateTime') };
463 will fail silently and cause many headaches. The simple way to solve
464 this, as well as future proof your subtypes from classes which have
465 yet to have been created, is to quote the type name:
468 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
470 =head2 Default Type Constraints
472 This module also provides a simple hierarchy for Perl 5 types, here is
473 that hierarchy represented visually.
497 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
498 parameterized, this means you can say:
500 ArrayRef[Int] # an array of integers
501 HashRef[CodeRef] # a hash of str to CODE ref mappings
502 Maybe[Str] # value may be a string, may be undefined
504 If Mouse finds a name in brackets that it does not recognize as an
505 existing type, it assumes that this is a class name, for example
506 C<ArrayRef[DateTime]>.
508 B<NOTE:> Unless you parameterize a type, then it is invalid to include
509 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
510 name, I<not> as a parameterization of C<ArrayRef>.
512 B<NOTE:> The C<Undef> type constraint for the most part works
513 correctly now, but edge cases may still exist, please use it
516 B<NOTE:> The C<ClassName> type constraint does a complex package
517 existence check. This means that your class B<must> be loaded for this
518 type constraint to pass.
520 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
521 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
522 constraint checks that an I<object does> the named role.
524 =head2 Type Constraint Naming
526 Type name declared via this module can only contain alphanumeric
527 characters, colons (:), and periods (.).
529 Since the types created by this module are global, it is suggested
530 that you namespace your types just as you would namespace your
531 modules. So instead of creating a I<Color> type for your
532 B<My::Graphics> module, you would call the type
533 I<My::Graphics::Types::Color> instead.
535 =head2 Use with Other Constraint Modules
537 This module can play nicely with other constraint modules with some
538 slight tweaking. The C<where> clause in types is expected to be a
539 C<CODE> reference which checks it's first argument and returns a
540 boolean. Since most constraint modules work in a similar way, it
541 should be simple to adapt them to work with Mouse.
543 For instance, this is how you could use it with
544 L<Declare::Constraints::Simple> to declare a completely new type.
546 type 'HashOfArrayOfObjects',
550 -values => IsArrayRef(IsObject)
554 Here is an example of using L<Test::Deep> and it's non-test
555 related C<eq_deeply> function.
557 type 'ArrayOfHashOfBarsAndRandomNumbers'
560 array_each(subhashof({
562 random_number => ignore()
568 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
570 Returns the names of builtin type constraints.
572 =head2 C<< list_all_type_constraints -> (Names) >>
574 Returns the names of all the type constraints.
580 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
582 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
584 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
586 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
588 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
590 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
592 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
594 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
596 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
598 =item C<< coerce $type => from $another_type, via { }, ... >>
604 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
610 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
614 L<Moose::Util::TypeConstraints>