1 package Mouse::Util::TypeConstraints;
2 use Mouse::Util; # 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 }
108 my $is_subtype = shift;
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 my $parent = delete $args{as};
133 if($is_subtype && !$parent){
134 $parent = delete $args{name};
138 if(defined $parent) {
139 $args{parent} = find_or_create_isa_type_constraint($parent);
143 # set 'package_defined_in' only if it is not a core package
144 my $this = $args{package_defined_in};
147 if($this !~ /\A Mouse \b/xms){
148 $args{package_defined_in} = $this;
153 my $that = $TYPE{$name}->{package_defined_in} || __PACKAGE__;
156 if($that eq __PACKAGE__) {
157 $note = sprintf " ('%s' is %s type constraint)",
159 scalar(grep { $name eq $_ } list_all_builtin_type_constraints())
161 : 'an implicitly created';
163 Carp::croak("The type constraint '$name' has already been created in $that"
164 . " and cannot be created again in $this" . $note);
169 $args{constraint} = delete $args{where} if exists $args{where};
170 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
172 my $constraint = Mouse::Meta::TypeConstraint->new(%args);
175 return $TYPE{$name} = $constraint;
183 return _define_type 0, @_;
187 return _define_type 1, @_;
191 my $type_name = shift;
193 my $type = find_type_constraint($type_name)
194 or Carp::croak("Cannot find type '$type_name', perhaps you forgot to load it.");
196 $type->_add_type_coercions(@_);
201 my($name, $options) = @_;
202 my $class = $options->{class} || $name;
205 return _define_type 1, $name => (
207 optimized_as => Mouse::Util::generate_isa_predicate_for($class),
213 my($name, $options) = @_;
214 my $role = $options->{role} || $name;
217 return _define_type 1, $name => (
219 optimized_as => sub {
220 return Scalar::Util::blessed($_[0])
221 && Mouse::Util::does_role($_[0], $role);
230 if(ref($_[0]) ne 'ARRAY'){
234 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
237 return _define_type 1, $name => (
239 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
242 my @missing = grep { !$object->can($_) } @methods;
244 . ' is missing methods '
245 . Mouse::Util::quoted_english_list(@missing);
247 methods => \@methods,
254 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
258 %valid = map{ $_ => undef }
259 (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
262 return _define_type 1, $name => (
265 return defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]};
270 sub _find_or_create_regular_type{
271 my($spec, $create) = @_;
273 return $TYPE{$spec} if exists $TYPE{$spec};
275 my $meta = Mouse::Util::get_metaclass_by_name($spec);
278 return $create ? class_type($spec) : undef;
281 if(Mouse::Util::is_a_metarole($meta)){
282 return role_type($spec);
285 return class_type($spec);
289 sub _find_or_create_parameterized_type{
290 my($base, $param) = @_;
292 my $name = sprintf '%s[%s]', $base->name, $param->name;
294 $TYPE{$name} ||= $base->parameterize($param, $name);
297 sub _find_or_create_union_type{
298 return if grep{ not defined } @_;
299 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
301 my $name = join '|', @types;
304 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
306 type_constraints => \@types,
312 # param : '[' type ']' | NOTHING
316 if($c->{spec} =~ s/^\[//){
317 my $type = _parse_type($c, 1);
319 if($c->{spec} =~ s/^\]//){
322 Carp::croak("Syntax error in type: missing right square bracket in '$c->{orig}'");
330 my($c, $create) = @_;
332 if($c->{spec} =~ s/\A ([\w.:]+) //xms){
333 return _find_or_create_regular_type($1, $create);
335 Carp::croak("Syntax error in type: expect type name near '$c->{spec}' in '$c->{orig}'");
338 # single_type : name param
339 sub _parse_single_type {
340 my($c, $create) = @_;
342 my $type = _parse_name($c, $create);
343 my $param = _parse_param($c);
347 return _find_or_create_parameterized_type($type, $param);
353 elsif(defined $param){
354 Carp::croak("Undefined type with parameter [$param] in '$c->{orig}'");
361 # type : single_type ('|' single_type)*
363 my($c, $create) = @_;
365 my $type = _parse_single_type($c, $create);
366 if($c->{spec}){ # can be an union type
368 while($c->{spec} =~ s/^\|//){
369 push @types, _parse_single_type($c, $create);
372 return _find_or_create_union_type($type, @types);
379 sub find_type_constraint {
381 return $spec if Mouse::Util::is_a_type_constraint($spec) or not defined $spec;
387 sub register_type_constraint {
388 my($constraint) = @_;
389 Carp::croak("No type supplied / type is not a valid type constraint")
390 unless Mouse::Util::is_a_type_constraint($constraint);
391 my $name = $constraint->name;
392 Carp::croak("Can't register an unnamed type constraint")
393 unless defined $name;
394 return $TYPE{$name} = $constraint;
397 sub find_or_parse_type_constraint {
399 return $spec if Mouse::Util::is_a_type_constraint($spec) or not defined $spec;
402 return $TYPE{$spec} || do{
407 my $type = _parse_type($context);
409 if($context->{spec}){
410 Carp::croak("Syntax error: extra elements '$context->{spec}' in '$context->{orig}'");
416 sub find_or_create_does_type_constraint{
417 # XXX: Moose does not register a new role_type, but Mouse does.
418 return find_or_parse_type_constraint(@_) || role_type(@_);
421 sub find_or_create_isa_type_constraint {
422 # XXX: Moose does not register a new class_type, but Mouse does.
423 return find_or_parse_type_constraint(@_) || class_type(@_);
431 Mouse::Util::TypeConstraints - Type constraint system for Mouse
435 This document describes Mouse version 0.75
439 use Mouse::Util::TypeConstraints;
445 subtype 'NaturalLessThanTen'
448 => message { "This number ($_) is not less than ten!" };
454 enum 'RGBColors' => qw(red green blue);
456 no Mouse::Util::TypeConstraints;
460 This module provides Mouse with the ability to create custom type
461 constraints to be used in attribute definition.
463 =head2 Important Caveat
465 This is B<NOT> a type system for Perl 5. These are type constraints,
466 and they are not used by Mouse unless you tell it to. No type
467 inference is performed, expressions are not typed, etc. etc. etc.
469 A type constraint is at heart a small "check if a value is valid"
470 function. A constraint can be associated with an attribute. This
471 simplifies parameter validation, and makes your code clearer to read,
472 because you can refer to constraints by name.
474 =head2 Slightly Less Important Caveat
476 It is B<always> a good idea to quote your type names.
478 This prevents Perl from trying to execute the call as an indirect
479 object call. This can be an issue when you have a subtype with the
480 same name as a valid class.
484 subtype DateTime => as Object => where { $_->isa('DateTime') };
486 will I<just work>, while this:
489 subtype DateTime => as Object => where { $_->isa('DateTime') };
491 will fail silently and cause many headaches. The simple way to solve
492 this, as well as future proof your subtypes from classes which have
493 yet to have been created, is to quote the type name:
496 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
498 =head2 Default Type Constraints
500 This module also provides a simple hierarchy for Perl 5 types, here is
501 that hierarchy represented visually.
525 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
526 parameterized, this means you can say:
528 ArrayRef[Int] # an array of integers
529 HashRef[CodeRef] # a hash of str to CODE ref mappings
530 Maybe[Str] # value may be a string, may be undefined
532 If Mouse finds a name in brackets that it does not recognize as an
533 existing type, it assumes that this is a class name, for example
534 C<ArrayRef[DateTime]>.
536 B<NOTE:> The C<Undef> type constraint for the most part works
537 correctly now, but edge cases may still exist, please use it
540 B<NOTE:> The C<ClassName> type constraint does a complex package
541 existence check. This means that your class B<must> be loaded for this
542 type constraint to pass.
544 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
545 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
546 constraint checks that an I<object does> the named role.
548 =head2 Type Constraint Naming
550 Type name declared via this module can only contain alphanumeric
551 characters, colons (:), and periods (.).
553 Since the types created by this module are global, it is suggested
554 that you namespace your types just as you would namespace your
555 modules. So instead of creating a I<Color> type for your
556 B<My::Graphics> module, you would call the type
557 I<My::Graphics::Types::Color> instead.
559 =head2 Use with Other Constraint Modules
561 This module can play nicely with other constraint modules with some
562 slight tweaking. The C<where> clause in types is expected to be a
563 C<CODE> reference which checks it's first argument and returns a
564 boolean. Since most constraint modules work in a similar way, it
565 should be simple to adapt them to work with Mouse.
567 For instance, this is how you could use it with
568 L<Declare::Constraints::Simple> to declare a completely new type.
570 type 'HashOfArrayOfObjects',
574 -values => IsArrayRef(IsObject)
578 Here is an example of using L<Test::Deep> and it's non-test
579 related C<eq_deeply> function.
581 type 'ArrayOfHashOfBarsAndRandomNumbers'
584 array_each(subhashof({
586 random_number => ignore()
592 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
594 Returns the names of builtin type constraints.
596 =head2 C<< list_all_type_constraints -> (Names) >>
598 Returns the names of all the type constraints.
604 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
606 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
608 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
610 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
612 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
614 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
616 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
618 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
620 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
622 =item C<< coerce $type => from $another_type, via { }, ... >>
628 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
634 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
638 L<Moose::Util::TypeConstraints>