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
26 $TYPE{Any} = Mouse::Meta::TypeConstraint->new(
31 # $name => $parent, $code,
37 Maybe => 'Item', undef,
40 Undef => 'Item', \&Undef,
41 Defined => 'Item', \&Defined,
42 Bool => 'Item', \&Bool,
43 Value => 'Defined', \&Value,
44 Str => 'Value', \&Str,
49 Ref => 'Defined', \&Ref,
50 ScalarRef => 'Ref', \&ScalarRef,
51 ArrayRef => 'Ref', \&ArrayRef,
52 HashRef => 'Ref', \&HashRef,
53 CodeRef => 'Ref', \&CodeRef,
54 RegexpRef => 'Ref', \&RegexpRef,
55 GlobRef => 'Ref', \&GlobRef,
58 FileHandle => 'GlobRef', \&FileHandle,
59 Object => 'Ref', \&Object,
61 # special string types
62 ClassName => 'Str', \&ClassName,
63 RoleName => 'ClassName', \&RoleName,
67 while (my ($name, $parent, $code) = splice @builtins, 0, 3) {
68 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
70 parent => $TYPE{$parent},
75 # make it parametarizable
77 $TYPE{Maybe} {constraint_generator} = \&_parameterize_Maybe_for;
78 $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
79 $TYPE{HashRef} {constraint_generator} = \&_parameterize_HashRef_for;
83 sub as ($) { (as => $_[0]) }
84 sub where (&) { (where => $_[0]) }
85 sub message (&) { (message => $_[0]) }
86 sub optimize_as (&) { (optimize_as => $_[0]) }
93 sub optimized_constraints { # DEPRECATED
94 Carp::cluck('optimized_constraints() has been deprecated');
98 undef @builtins; # free the allocated memory
99 @builtins = keys %TYPE; # reuse it
100 sub list_all_builtin_type_constraints { @builtins }
102 sub list_all_type_constraints { keys %TYPE }
110 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
113 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
117 elsif(@_ % 2){ # @_ : $name => ( where => ... )
120 else{ # @_ : (name => $name, where => ...)
130 if($mode eq 'subtype'){
131 $parent = delete $args{as};
133 $parent = delete $args{name};
139 # set 'package_defined_in' only if it is not a core package
140 my $this = $args{package_defined_in};
143 if($this !~ /\A Mouse \b/xms){
144 $args{package_defined_in} = $this;
149 my $that = $TYPE{$name}->{package_defined_in} || __PACKAGE__;
150 ($this eq $that) or confess(
151 "The type constraint '$name' has already been created in $that and cannot be created again in $this"
156 $args{name} = '__ANON__';
159 $args{constraint} = delete $args{where} if exists $args{where};
160 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
163 if($mode eq 'subtype'){
164 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
167 $constraint = Mouse::Meta::TypeConstraint->new(%args);
171 return $TYPE{$name} = $constraint;
179 return _create_type('type', @_);
183 return _create_type('subtype', @_);
187 my $type_name = shift;
189 my $type = find_type_constraint($type_name)
190 or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
192 $type->_add_type_coercions(@_);
197 my($name, $options) = @_;
198 my $class = $options->{class} || $name;
201 return _create_type 'subtype', $name => (
203 optimized_as => Mouse::Util::generate_isa_predicate_for($class),
208 my($name, $options) = @_;
209 my $role = $options->{role} || $name;
212 return _create_type 'subtype', $name => (
214 optimized_as => sub { Scalar::Util::blessed($_[0]) && does_role($_[0], $role) },
221 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
225 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
228 return _create_type 'type', $name => (
229 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
236 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
240 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
243 return _create_type 'type', $name => (
244 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
248 sub _find_or_create_regular_type{
251 return $TYPE{$spec} if exists $TYPE{$spec};
253 my $meta = Mouse::Util::get_metaclass_by_name($spec)
256 if(Mouse::Util::is_a_metarole($meta)){
257 return role_type($spec);
260 return class_type($spec);
264 sub _find_or_create_parameterized_type{
265 my($base, $param) = @_;
267 my $name = sprintf '%s[%s]', $base->name, $param->name;
269 $TYPE{$name} ||= $base->parameterize($param, $name);
272 sub _find_or_create_union_type{
273 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
275 my $name = join '|', @types;
278 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
280 type_constraints => \@types,
286 my($spec, $start) = @_;
291 my $len = length $spec;
294 for($i = $start; $i < $len; $i++){
295 my $char = substr($spec, $i, 1);
298 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
301 ($i, $subtype) = _parse_type($spec, $i+1)
303 $start = $i+1; # reset
305 push @list, _find_or_create_parameterized_type($base => $subtype);
312 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
315 # XXX: Mouse creates a new class type, but Moose does not.
316 $type = class_type( substr($spec, $start, $i - $start) );
321 ($i, $subtype) = _parse_type($spec, $i+1)
324 $start = $i+1; # reset
326 push @list, $subtype;
330 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
337 # create a new class type
338 push @list, class_type( substr $spec, $start, $i - $start );
346 return ($len, $list[0]);
349 return ($len, _find_or_create_union_type(@list));
354 sub find_type_constraint {
356 return $spec if Mouse::Util::is_a_type_constraint($spec);
357 return undef if !defined $spec;
363 sub find_or_parse_type_constraint {
365 return $spec if Mouse::Util::is_a_type_constraint($spec);
366 return undef if !defined $spec;
369 return $TYPE{$spec} || do{
370 my($pos, $type) = _parse_type($spec, 0);
375 sub find_or_create_does_type_constraint{
376 # XXX: Moose does not register a new role_type, but Mouse does.
377 return find_or_parse_type_constraint(@_) || role_type(@_);
380 sub find_or_create_isa_type_constraint {
381 # XXX: Moose does not register a new class_type, but Mouse does.
382 return find_or_parse_type_constraint(@_) || class_type(@_);
390 Mouse::Util::TypeConstraints - Type constraint system for Mouse
394 This document describes Mouse version 0.50_03
398 use Mouse::Util::TypeConstraints;
404 subtype 'NaturalLessThanTen'
407 => message { "This number ($_) is not less than ten!" };
413 enum 'RGBColors' => qw(red green blue);
415 no Mouse::Util::TypeConstraints;
419 This module provides Mouse with the ability to create custom type
420 constraints to be used in attribute definition.
422 =head2 Important Caveat
424 This is B<NOT> a type system for Perl 5. These are type constraints,
425 and they are not used by Mouse unless you tell it to. No type
426 inference is performed, expressions are not typed, etc. etc. etc.
428 A type constraint is at heart a small "check if a value is valid"
429 function. A constraint can be associated with an attribute. This
430 simplifies parameter validation, and makes your code clearer to read,
431 because you can refer to constraints by name.
433 =head2 Slightly Less Important Caveat
435 It is B<always> a good idea to quote your type names.
437 This prevents Perl from trying to execute the call as an indirect
438 object call. This can be an issue when you have a subtype with the
439 same name as a valid class.
443 subtype DateTime => as Object => where { $_->isa('DateTime') };
445 will I<just work>, while this:
448 subtype DateTime => as Object => where { $_->isa('DateTime') };
450 will fail silently and cause many headaches. The simple way to solve
451 this, as well as future proof your subtypes from classes which have
452 yet to have been created, is to quote the type name:
455 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
457 =head2 Default Type Constraints
459 This module also provides a simple hierarchy for Perl 5 types, here is
460 that hierarchy represented visually.
484 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
485 parameterized, this means you can say:
487 ArrayRef[Int] # an array of integers
488 HashRef[CodeRef] # a hash of str to CODE ref mappings
489 Maybe[Str] # value may be a string, may be undefined
491 If Mouse finds a name in brackets that it does not recognize as an
492 existing type, it assumes that this is a class name, for example
493 C<ArrayRef[DateTime]>.
495 B<NOTE:> Unless you parameterize a type, then it is invalid to include
496 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
497 name, I<not> as a parameterization of C<ArrayRef>.
499 B<NOTE:> The C<Undef> type constraint for the most part works
500 correctly now, but edge cases may still exist, please use it
503 B<NOTE:> The C<ClassName> type constraint does a complex package
504 existence check. This means that your class B<must> be loaded for this
505 type constraint to pass.
507 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
508 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
509 constraint checks that an I<object does> the named role.
511 =head2 Type Constraint Naming
513 Type name declared via this module can only contain alphanumeric
514 characters, colons (:), and periods (.).
516 Since the types created by this module are global, it is suggested
517 that you namespace your types just as you would namespace your
518 modules. So instead of creating a I<Color> type for your
519 B<My::Graphics> module, you would call the type
520 I<My::Graphics::Types::Color> instead.
522 =head2 Use with Other Constraint Modules
524 This module can play nicely with other constraint modules with some
525 slight tweaking. The C<where> clause in types is expected to be a
526 C<CODE> reference which checks it's first argument and returns a
527 boolean. Since most constraint modules work in a similar way, it
528 should be simple to adapt them to work with Mouse.
530 For instance, this is how you could use it with
531 L<Declare::Constraints::Simple> to declare a completely new type.
533 type 'HashOfArrayOfObjects',
537 -values => IsArrayRef(IsObject)
541 Here is an example of using L<Test::Deep> and it's non-test
542 related C<eq_deeply> function.
544 type 'ArrayOfHashOfBarsAndRandomNumbers'
547 array_each(subhashof({
549 random_number => ignore()
555 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
557 Returns the names of builtin type constraints.
559 =head2 C<< list_all_type_constraints -> (Names) >>
561 Returns the names of all the type constraints.
567 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
569 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
571 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
573 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
575 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
577 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
579 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
581 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
583 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
585 =item C<< coerce $type => from $another_type, via { }, ... >>
591 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
597 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
601 L<Moose::Util::TypeConstraints>