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
25 sub as ($) { (as => $_[0]) }
26 sub where (&) { (where => $_[0]) }
27 sub message (&) { (message => $_[0]) }
28 sub optimize_as (&) { (optimize_as => $_[0]) }
35 Any => undef, # null check
36 Item => undef, # null check
37 Maybe => undef, # null check
48 ScalarRef => \&ScalarRef,
49 ArrayRef => \&ArrayRef,
52 RegexpRef => \&RegexpRef,
55 FileHandle => \&FileHandle,
59 ClassName => \&ClassName,
60 RoleName => \&RoleName,
63 while (my ($name, $code) = each %builtins) {
64 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
70 sub optimized_constraints { # DEPRECATED
71 Carp::cluck('optimized_constraints() has been deprecated');
75 my @builtins = keys %TYPE;
76 sub list_all_builtin_type_constraints { @builtins }
78 sub list_all_type_constraints { keys %TYPE }
87 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
90 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
94 elsif(@_ % 2){ # @_ : $name => ( where => ... )
97 else{ # @_ : (name => $name, where => ...)
107 if($mode eq 'subtype'){
108 $parent = delete $args{as};
110 $parent = delete $args{name};
116 my $package_defined_in = $args{package_defined_in} ||= caller(1);
117 my $existing = $TYPE{$name};
118 if($existing && $existing->{package_defined_in} ne $package_defined_in){
119 confess("The type constraint '$name' has already been created in "
120 . "$existing->{package_defined_in} and cannot be created again in $package_defined_in");
124 $args{name} = '__ANON__';
127 $args{constraint} = delete $args{where} if exists $args{where};
128 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
131 if($mode eq 'subtype'){
132 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
135 $constraint = Mouse::Meta::TypeConstraint->new(%args);
139 return $TYPE{$name} = $constraint;
147 return _create_type('type', @_);
151 return _create_type('subtype', @_);
155 my $type_name = shift;
157 my $type = find_type_constraint($type_name)
158 or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
160 $type->_add_type_coercions(@_);
165 my($name, $options) = @_;
166 my $class = $options->{class} || $name;
167 return _create_type 'subtype', $name => (
169 optimized_as => Mouse::Util::generate_isa_predicate_for($class),
176 my($name, $options) = @_;
177 my $role = $options->{role} || $name;
178 return _create_type 'subtype', $name => (
180 optimized_as => sub { Scalar::Util::blessed($_[0]) && does_role($_[0], $role) },
189 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
193 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
195 return _create_type 'type', $name => (
196 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
205 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
209 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
211 return _create_type 'type', $name => (
212 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
218 sub _find_or_create_regular_type{
221 return $TYPE{$spec} if exists $TYPE{$spec};
223 my $meta = Mouse::Util::get_metaclass_by_name($spec)
226 if(Mouse::Util::is_a_metarole($meta)){
227 return role_type($spec);
230 return class_type($spec);
234 $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
235 $TYPE{HashRef}{constraint_generator} = \&_parameterize_HashRef_for;
236 $TYPE{Maybe}{constraint_generator} = \&_parameterize_Maybe_for;
238 sub _find_or_create_parameterized_type{
239 my($base, $param) = @_;
241 my $name = sprintf '%s[%s]', $base->name, $param->name;
243 $TYPE{$name} ||= $base->parameterize($param, $name);
246 sub _find_or_create_union_type{
247 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
249 my $name = join '|', @types;
251 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
253 type_constraints => \@types,
261 my($spec, $start) = @_;
266 my $len = length $spec;
269 for($i = $start; $i < $len; $i++){
270 my $char = substr($spec, $i, 1);
273 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
276 ($i, $subtype) = _parse_type($spec, $i+1)
278 $start = $i+1; # reset
280 push @list, _find_or_create_parameterized_type($base => $subtype);
287 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
290 # XXX: Mouse creates a new class type, but Moose does not.
291 $type = class_type( substr($spec, $start, $i - $start) );
296 ($i, $subtype) = _parse_type($spec, $i+1)
299 $start = $i+1; # reset
301 push @list, $subtype;
305 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
312 # create a new class type
313 push @list, class_type( substr $spec, $start, $i - $start );
321 return ($len, $list[0]);
324 return ($len, _find_or_create_union_type(@list));
329 sub find_type_constraint {
331 return $spec if Mouse::Util::is_a_type_constraint($spec);
337 sub find_or_parse_type_constraint {
339 return $spec if Mouse::Util::is_a_type_constraint($spec);
342 return $TYPE{$spec} || do{
343 my($pos, $type) = _parse_type($spec, 0);
348 sub find_or_create_does_type_constraint{
349 # XXX: Moose does not register a new role_type, but Mouse does.
350 return find_or_parse_type_constraint(@_) || role_type(@_);
353 sub find_or_create_isa_type_constraint {
354 # XXX: Moose does not register a new class_type, but Mouse does.
355 return find_or_parse_type_constraint(@_) || class_type(@_);
363 Mouse::Util::TypeConstraints - Type constraint system for Mouse
367 This document describes Mouse version 0.47
371 use Mouse::Util::TypeConstraints;
377 subtype 'NaturalLessThanTen'
380 => message { "This number ($_) is not less than ten!" };
386 enum 'RGBColors' => qw(red green blue);
388 no Mouse::Util::TypeConstraints;
392 This module provides Mouse with the ability to create custom type
393 constraints to be used in attribute definition.
395 =head2 Important Caveat
397 This is B<NOT> a type system for Perl 5. These are type constraints,
398 and they are not used by Mouse unless you tell it to. No type
399 inference is performed, expressions are not typed, etc. etc. etc.
401 A type constraint is at heart a small "check if a value is valid"
402 function. A constraint can be associated with an attribute. This
403 simplifies parameter validation, and makes your code clearer to read,
404 because you can refer to constraints by name.
406 =head2 Slightly Less Important Caveat
408 It is B<always> a good idea to quote your type names.
410 This prevents Perl from trying to execute the call as an indirect
411 object call. This can be an issue when you have a subtype with the
412 same name as a valid class.
416 subtype DateTime => as Object => where { $_->isa('DateTime') };
418 will I<just work>, while this:
421 subtype DateTime => as Object => where { $_->isa('DateTime') };
423 will fail silently and cause many headaches. The simple way to solve
424 this, as well as future proof your subtypes from classes which have
425 yet to have been created, is to quote the type name:
428 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
430 =head2 Default Type Constraints
432 This module also provides a simple hierarchy for Perl 5 types, here is
433 that hierarchy represented visually.
457 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
458 parameterized, this means you can say:
460 ArrayRef[Int] # an array of integers
461 HashRef[CodeRef] # a hash of str to CODE ref mappings
462 Maybe[Str] # value may be a string, may be undefined
464 If Mouse finds a name in brackets that it does not recognize as an
465 existing type, it assumes that this is a class name, for example
466 C<ArrayRef[DateTime]>.
468 B<NOTE:> Unless you parameterize a type, then it is invalid to include
469 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
470 name, I<not> as a parameterization of C<ArrayRef>.
472 B<NOTE:> The C<Undef> type constraint for the most part works
473 correctly now, but edge cases may still exist, please use it
476 B<NOTE:> The C<ClassName> type constraint does a complex package
477 existence check. This means that your class B<must> be loaded for this
478 type constraint to pass.
480 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
481 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
482 constraint checks that an I<object does> the named role.
484 =head2 Type Constraint Naming
486 Type name declared via this module can only contain alphanumeric
487 characters, colons (:), and periods (.).
489 Since the types created by this module are global, it is suggested
490 that you namespace your types just as you would namespace your
491 modules. So instead of creating a I<Color> type for your
492 B<My::Graphics> module, you would call the type
493 I<My::Graphics::Types::Color> instead.
495 =head2 Use with Other Constraint Modules
497 This module can play nicely with other constraint modules with some
498 slight tweaking. The C<where> clause in types is expected to be a
499 C<CODE> reference which checks it's first argument and returns a
500 boolean. Since most constraint modules work in a similar way, it
501 should be simple to adapt them to work with Mouse.
503 For instance, this is how you could use it with
504 L<Declare::Constraints::Simple> to declare a completely new type.
506 type 'HashOfArrayOfObjects',
510 -values => IsArrayRef(IsObject)
514 Here is an example of using L<Test::Deep> and it's non-test
515 related C<eq_deeply> function.
517 type 'ArrayOfHashOfBarsAndRandomNumbers'
520 array_each(subhashof({
522 random_number => ignore()
528 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
530 Returns the names of builtin type constraints.
532 =head2 C<< list_all_type_constraints -> (Names) >>
534 Returns the names of all the type constraints.
540 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
542 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
544 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
546 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
548 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
550 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
552 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
554 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
556 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
558 =item C<< coerce $type => from $another_type, via { }, ... >>
564 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
570 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
574 L<Moose::Util::TypeConstraints>