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 }
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__;
155 if($that eq __PACKAGE__) {
156 $note = sprintf " ('%s' is %s type constraint)",
158 scalar(grep { $name eq $_ } list_all_builtin_type_constraints())
160 : 'an implicitly created';
162 Carp::croak("The type constraint '$name' has already been created in $that"
163 . " and cannot be created again in $this" . $note);
168 $args{name} = '__ANON__';
171 $args{constraint} = delete $args{where} if exists $args{where};
172 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
175 if($mode eq 'subtype'){
176 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
179 $constraint = Mouse::Meta::TypeConstraint->new(%args);
183 return $TYPE{$name} = $constraint;
191 return _create_type('type', @_);
195 return _create_type('subtype', @_);
199 my $type_name = shift;
201 my $type = find_type_constraint($type_name)
202 or Carp::croak("Cannot find type '$type_name', perhaps you forgot to load it.");
204 $type->_add_type_coercions(@_);
209 my($name, $options) = @_;
210 my $class = $options->{class} || $name;
213 return _create_type 'subtype', $name => (
215 optimized_as => Mouse::Util::generate_isa_predicate_for($class),
220 my($name, $options) = @_;
221 my $role = $options->{role} || $name;
224 return _create_type 'subtype', $name => (
226 optimized_as => sub {
227 return Scalar::Util::blessed($_[0])
228 && Mouse::Util::does_role($_[0], $role);
236 if(ref($_[0]) ne 'ARRAY'){
240 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
243 return _create_type 'subtype', $name => (
245 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
248 my @missing = grep { !$object->can($_) } @methods;
250 . ' is missing methods '
251 . Mouse::Util::quoted_english_list(@missing);
259 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
263 %valid = map{ $_ => undef }
264 (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
267 return _create_type 'subtype', $name => (
270 return defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]};
275 sub _find_or_create_regular_type{
276 my($spec, $create) = @_;
278 return $TYPE{$spec} if exists $TYPE{$spec};
280 my $meta = Mouse::Util::get_metaclass_by_name($spec);
283 return $create ? class_type($spec) : undef;
286 if(Mouse::Util::is_a_metarole($meta)){
287 return role_type($spec);
290 return class_type($spec);
294 sub _find_or_create_parameterized_type{
295 my($base, $param) = @_;
297 my $name = sprintf '%s[%s]', $base->name, $param->name;
299 $TYPE{$name} ||= $base->parameterize($param, $name);
302 sub _find_or_create_union_type{
303 return if grep{ not defined } @_;
304 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
306 my $name = join '|', @types;
309 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
311 type_constraints => \@types,
317 # param : '[' type ']' | NOTHING
321 if($c->{spec} =~ s/^\[//){
322 my $type = _parse_type($c, 1);
324 if($c->{spec} =~ s/^\]//){
327 Carp::croak("Syntax error in type: missing right square bracket in '$c->{orig}'");
335 my($c, $create) = @_;
337 if($c->{spec} =~ s/\A ([\w.:]+) //xms){
338 return _find_or_create_regular_type($1, $create);
340 Carp::croak("Syntax error in type: expect type name near '$c->{spec}' in '$c->{orig}'");
343 # single_type : name param
344 sub _parse_single_type {
345 my($c, $create) = @_;
347 my $type = _parse_name($c, $create);
348 my $param = _parse_param($c);
352 return _find_or_create_parameterized_type($type, $param);
358 elsif(defined $param){
359 Carp::croak("Undefined type with parameter [$param] in '$c->{orig}'");
366 # type : single_type ('|' single_type)*
368 my($c, $create) = @_;
370 my $type = _parse_single_type($c, $create);
371 if($c->{spec}){ # can be an union type
373 while($c->{spec} =~ s/^\|//){
374 push @types, _parse_single_type($c, $create);
377 return _find_or_create_union_type($type, @types);
384 sub find_type_constraint {
386 return $spec if Mouse::Util::is_a_type_constraint($spec) or not defined $spec;
392 sub register_type_constraint {
393 my($constraint) = @_;
394 Carp::croak("No type supplied / type is not a valid type constraint")
395 unless Mouse::Util::is_a_type_constraint($constraint);
396 my $name = $constraint->name;
397 Carp::croak("can't register an unnamed type constraint")
398 unless defined $name;
399 return $TYPE{$name} = $constraint;
402 sub find_or_parse_type_constraint {
404 return $spec if Mouse::Util::is_a_type_constraint($spec) or not defined $spec;
407 return $TYPE{$spec} || do{
412 my $type = _parse_type($context);
414 if($context->{spec}){
415 Carp::croak("Syntax error: extra elements '$context->{spec}' in '$context->{orig}'");
421 sub find_or_create_does_type_constraint{
422 # XXX: Moose does not register a new role_type, but Mouse does.
423 return find_or_parse_type_constraint(@_) || role_type(@_);
426 sub find_or_create_isa_type_constraint {
427 # XXX: Moose does not register a new class_type, but Mouse does.
428 return find_or_parse_type_constraint(@_) || class_type(@_);
436 Mouse::Util::TypeConstraints - Type constraint system for Mouse
440 This document describes Mouse version 0.71
444 use Mouse::Util::TypeConstraints;
450 subtype 'NaturalLessThanTen'
453 => message { "This number ($_) is not less than ten!" };
459 enum 'RGBColors' => qw(red green blue);
461 no Mouse::Util::TypeConstraints;
465 This module provides Mouse with the ability to create custom type
466 constraints to be used in attribute definition.
468 =head2 Important Caveat
470 This is B<NOT> a type system for Perl 5. These are type constraints,
471 and they are not used by Mouse unless you tell it to. No type
472 inference is performed, expressions are not typed, etc. etc. etc.
474 A type constraint is at heart a small "check if a value is valid"
475 function. A constraint can be associated with an attribute. This
476 simplifies parameter validation, and makes your code clearer to read,
477 because you can refer to constraints by name.
479 =head2 Slightly Less Important Caveat
481 It is B<always> a good idea to quote your type names.
483 This prevents Perl from trying to execute the call as an indirect
484 object call. This can be an issue when you have a subtype with the
485 same name as a valid class.
489 subtype DateTime => as Object => where { $_->isa('DateTime') };
491 will I<just work>, while this:
494 subtype DateTime => as Object => where { $_->isa('DateTime') };
496 will fail silently and cause many headaches. The simple way to solve
497 this, as well as future proof your subtypes from classes which have
498 yet to have been created, is to quote the type name:
501 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
503 =head2 Default Type Constraints
505 This module also provides a simple hierarchy for Perl 5 types, here is
506 that hierarchy represented visually.
530 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
531 parameterized, this means you can say:
533 ArrayRef[Int] # an array of integers
534 HashRef[CodeRef] # a hash of str to CODE ref mappings
535 Maybe[Str] # value may be a string, may be undefined
537 If Mouse finds a name in brackets that it does not recognize as an
538 existing type, it assumes that this is a class name, for example
539 C<ArrayRef[DateTime]>.
541 B<NOTE:> The C<Undef> type constraint for the most part works
542 correctly now, but edge cases may still exist, please use it
545 B<NOTE:> The C<ClassName> type constraint does a complex package
546 existence check. This means that your class B<must> be loaded for this
547 type constraint to pass.
549 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
550 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
551 constraint checks that an I<object does> the named role.
553 =head2 Type Constraint Naming
555 Type name declared via this module can only contain alphanumeric
556 characters, colons (:), and periods (.).
558 Since the types created by this module are global, it is suggested
559 that you namespace your types just as you would namespace your
560 modules. So instead of creating a I<Color> type for your
561 B<My::Graphics> module, you would call the type
562 I<My::Graphics::Types::Color> instead.
564 =head2 Use with Other Constraint Modules
566 This module can play nicely with other constraint modules with some
567 slight tweaking. The C<where> clause in types is expected to be a
568 C<CODE> reference which checks it's first argument and returns a
569 boolean. Since most constraint modules work in a similar way, it
570 should be simple to adapt them to work with Mouse.
572 For instance, this is how you could use it with
573 L<Declare::Constraints::Simple> to declare a completely new type.
575 type 'HashOfArrayOfObjects',
579 -values => IsArrayRef(IsObject)
583 Here is an example of using L<Test::Deep> and it's non-test
584 related C<eq_deeply> function.
586 type 'ArrayOfHashOfBarsAndRandomNumbers'
589 array_each(subhashof({
591 random_number => ignore()
597 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
599 Returns the names of builtin type constraints.
601 =head2 C<< list_all_type_constraints -> (Names) >>
603 Returns the names of all the type constraints.
609 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
611 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
613 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
615 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
617 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
619 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
621 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
623 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
625 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
627 =item C<< coerce $type => from $another_type, via { }, ... >>
633 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
639 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
643 L<Moose::Util::TypeConstraints>