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
20 register_type_constraint
24 our @CARP_NOT = qw(Mouse::Meta::Attribute);
29 $TYPE{Any} = Mouse::Meta::TypeConstraint->new(
34 # $name => $parent, $code,
40 Maybe => 'Item', undef,
43 Undef => 'Item', \&Undef,
44 Defined => 'Item', \&Defined,
45 Bool => 'Item', \&Bool,
46 Value => 'Defined', \&Value,
47 Str => 'Value', \&Str,
52 Ref => 'Defined', \&Ref,
53 ScalarRef => 'Ref', \&ScalarRef,
54 ArrayRef => 'Ref', \&ArrayRef,
55 HashRef => 'Ref', \&HashRef,
56 CodeRef => 'Ref', \&CodeRef,
57 RegexpRef => 'Ref', \&RegexpRef,
58 GlobRef => 'Ref', \&GlobRef,
61 FileHandle => 'GlobRef', \&FileHandle,
62 Object => 'Ref', \&Object,
64 # special string types
65 ClassName => 'Str', \&ClassName,
66 RoleName => 'ClassName', \&RoleName,
70 while (my ($name, $parent, $code) = splice @builtins, 0, 3) {
71 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
73 parent => $TYPE{$parent},
78 # make it parametarizable
80 $TYPE{Maybe} {constraint_generator} = \&_parameterize_Maybe_for;
81 $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
82 $TYPE{HashRef} {constraint_generator} = \&_parameterize_HashRef_for;
86 sub as ($) { (as => $_[0]) } ## no critic
87 sub where (&) { (where => $_[0]) } ## no critic
88 sub message (&) { (message => $_[0]) } ## no critic
89 sub optimize_as (&) { (optimize_as => $_[0]) } ## no critic
92 sub via (&) { $_[0] } ## no critic
96 sub optimized_constraints { # DEPRECATED
97 Carp::cluck('optimized_constraints() has been deprecated');
101 undef @builtins; # free the allocated memory
102 @builtins = keys %TYPE; # reuse it
103 sub list_all_builtin_type_constraints { @builtins }
105 sub list_all_type_constraints { keys %TYPE }
113 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
116 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
120 elsif(@_ % 2){ # @_ : $name => ( where => ... )
123 else{ # @_ : (name => $name, where => ...)
133 if($mode eq 'subtype'){
134 $parent = delete $args{as};
136 $parent = delete $args{name};
142 # set 'package_defined_in' only if it is not a core package
143 my $this = $args{package_defined_in};
146 if($this !~ /\A Mouse \b/xms){
147 $args{package_defined_in} = $this;
152 my $that = $TYPE{$name}->{package_defined_in} || __PACKAGE__;
153 ($this eq $that) or Carp::croak(
154 "The type constraint '$name' has already been created in $that and cannot be created again in $this"
159 $args{name} = '__ANON__';
162 $args{constraint} = delete $args{where} if exists $args{where};
163 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
166 if($mode eq 'subtype'){
167 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
170 $constraint = Mouse::Meta::TypeConstraint->new(%args);
174 return $TYPE{$name} = $constraint;
182 return _create_type('type', @_);
186 return _create_type('subtype', @_);
190 my $type_name = shift;
192 my $type = find_type_constraint($type_name)
193 or Carp::croak("Cannot find type '$type_name', perhaps you forgot to load it.");
195 $type->_add_type_coercions(@_);
200 my($name, $options) = @_;
201 my $class = $options->{class} || $name;
204 return _create_type 'subtype', $name => (
206 optimized_as => Mouse::Util::generate_isa_predicate_for($class),
211 my($name, $options) = @_;
212 my $role = $options->{role} || $name;
215 return _create_type 'subtype', $name => (
217 optimized_as => sub { Scalar::Util::blessed($_[0]) && does_role($_[0], $role) },
224 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
228 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
231 return _create_type 'subtype', $name => (
233 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
240 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
244 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
247 return _create_type 'subtype', $name => (
249 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
253 sub _find_or_create_regular_type{
254 my($spec, $create) = @_;
256 return $TYPE{$spec} if exists $TYPE{$spec};
258 my $meta = Mouse::Util::get_metaclass_by_name($spec);
261 return $create ? class_type($spec) : undef;
264 if(Mouse::Util::is_a_metarole($meta)){
265 return role_type($spec);
268 return class_type($spec);
272 sub _find_or_create_parameterized_type{
273 my($base, $param) = @_;
275 my $name = sprintf '%s[%s]', $base->name, $param->name;
277 $TYPE{$name} ||= $base->parameterize($param, $name);
280 sub _find_or_create_union_type{
281 return if grep{ not defined } @_;
282 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
284 my $name = join '|', @types;
287 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
289 type_constraints => \@types,
295 # param : '[' type ']' | NOTHING
299 if($c->{spec} =~ s/^\[//){
300 my $type = _parse_type($c, 1);
302 if($c->{spec} =~ s/^\]//){
305 Carp::croak("Syntax error in type: missing right square bracket in '$c->{orig}'");
313 my($c, $create) = @_;
315 if($c->{spec} =~ s/\A ([\w.:]+) //xms){
316 return _find_or_create_regular_type($1, $create);
318 Carp::croak("Syntax error in type: expect type name near '$c->{spec}' in '$c->{orig}'");
321 # single_type : name param
322 sub _parse_single_type {
323 my($c, $create) = @_;
325 my $type = _parse_name($c, $create);
326 my $param = _parse_param($c);
330 return _find_or_create_parameterized_type($type, $param);
336 elsif(defined $param){
337 Carp::croak("Undefined type with parameter [$param] in '$c->{orig}'");
344 # type : single_type ('|' single_type)*
346 my($c, $create) = @_;
348 my $type = _parse_single_type($c, $create);
349 if($c->{spec}){ # can be an union type
351 while($c->{spec} =~ s/^\|//){
352 push @types, _parse_single_type($c, $create);
355 return _find_or_create_union_type($type, @types);
362 sub find_type_constraint {
364 return $spec if Mouse::Util::is_a_type_constraint($spec);
365 return undef if !defined $spec;
371 sub register_type_constraint {
372 my($constraint) = @_;
373 Carp::croak("No type supplied / type is not a valid type constraint")
374 unless Mouse::Util::is_a_type_constraint($constraint);
375 my $name = $constraint->name;
376 Carp::croak("can't register an unnamed type constraint")
377 unless defined $name;
378 return $TYPE{$name} = $constraint;
381 sub find_or_parse_type_constraint {
383 return $spec if Mouse::Util::is_a_type_constraint($spec);
384 return undef if !defined $spec;
387 return $TYPE{$spec} || do{
392 my $type = _parse_type($context);
394 if($context->{spec}){
395 Carp::croak("Syntax error: extra elements '$context->{spec}' in '$context->{orig}'");
401 sub find_or_create_does_type_constraint{
402 # XXX: Moose does not register a new role_type, but Mouse does.
403 return find_or_parse_type_constraint(@_) || role_type(@_);
406 sub find_or_create_isa_type_constraint {
407 # XXX: Moose does not register a new class_type, but Mouse does.
408 return find_or_parse_type_constraint(@_) || class_type(@_);
416 Mouse::Util::TypeConstraints - Type constraint system for Mouse
420 This document describes Mouse version 0.64
424 use Mouse::Util::TypeConstraints;
430 subtype 'NaturalLessThanTen'
433 => message { "This number ($_) is not less than ten!" };
439 enum 'RGBColors' => qw(red green blue);
441 no Mouse::Util::TypeConstraints;
445 This module provides Mouse with the ability to create custom type
446 constraints to be used in attribute definition.
448 =head2 Important Caveat
450 This is B<NOT> a type system for Perl 5. These are type constraints,
451 and they are not used by Mouse unless you tell it to. No type
452 inference is performed, expressions are not typed, etc. etc. etc.
454 A type constraint is at heart a small "check if a value is valid"
455 function. A constraint can be associated with an attribute. This
456 simplifies parameter validation, and makes your code clearer to read,
457 because you can refer to constraints by name.
459 =head2 Slightly Less Important Caveat
461 It is B<always> a good idea to quote your type names.
463 This prevents Perl from trying to execute the call as an indirect
464 object call. This can be an issue when you have a subtype with the
465 same name as a valid class.
469 subtype DateTime => as Object => where { $_->isa('DateTime') };
471 will I<just work>, while this:
474 subtype DateTime => as Object => where { $_->isa('DateTime') };
476 will fail silently and cause many headaches. The simple way to solve
477 this, as well as future proof your subtypes from classes which have
478 yet to have been created, is to quote the type name:
481 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
483 =head2 Default Type Constraints
485 This module also provides a simple hierarchy for Perl 5 types, here is
486 that hierarchy represented visually.
510 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
511 parameterized, this means you can say:
513 ArrayRef[Int] # an array of integers
514 HashRef[CodeRef] # a hash of str to CODE ref mappings
515 Maybe[Str] # value may be a string, may be undefined
517 If Mouse finds a name in brackets that it does not recognize as an
518 existing type, it assumes that this is a class name, for example
519 C<ArrayRef[DateTime]>.
521 B<NOTE:> The C<Undef> type constraint for the most part works
522 correctly now, but edge cases may still exist, please use it
525 B<NOTE:> The C<ClassName> type constraint does a complex package
526 existence check. This means that your class B<must> be loaded for this
527 type constraint to pass.
529 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
530 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
531 constraint checks that an I<object does> the named role.
533 =head2 Type Constraint Naming
535 Type name declared via this module can only contain alphanumeric
536 characters, colons (:), and periods (.).
538 Since the types created by this module are global, it is suggested
539 that you namespace your types just as you would namespace your
540 modules. So instead of creating a I<Color> type for your
541 B<My::Graphics> module, you would call the type
542 I<My::Graphics::Types::Color> instead.
544 =head2 Use with Other Constraint Modules
546 This module can play nicely with other constraint modules with some
547 slight tweaking. The C<where> clause in types is expected to be a
548 C<CODE> reference which checks it's first argument and returns a
549 boolean. Since most constraint modules work in a similar way, it
550 should be simple to adapt them to work with Mouse.
552 For instance, this is how you could use it with
553 L<Declare::Constraints::Simple> to declare a completely new type.
555 type 'HashOfArrayOfObjects',
559 -values => IsArrayRef(IsObject)
563 Here is an example of using L<Test::Deep> and it's non-test
564 related C<eq_deeply> function.
566 type 'ArrayOfHashOfBarsAndRandomNumbers'
569 array_each(subhashof({
571 random_number => ignore()
577 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
579 Returns the names of builtin type constraints.
581 =head2 C<< list_all_type_constraints -> (Names) >>
583 Returns the names of all the type constraints.
589 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
591 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
593 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
595 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
597 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
599 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
601 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
603 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
605 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
607 =item C<< coerce $type => from $another_type, via { }, ... >>
613 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
619 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
623 L<Moose::Util::TypeConstraints>