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 => ...)
102 if(!defined($name = $args{name})){
109 if($mode eq 'subtype'){
110 $parent = delete $args{as};
112 $parent = delete $args{name};
117 my $package_defined_in = $args{package_defined_in} ||= caller(1);
119 my $existing = $TYPE{$name};
120 if($existing && $existing->{package_defined_in} ne $package_defined_in){
121 confess("The type constraint '$name' has already been created in "
122 . "$existing->{package_defined_in} and cannot be created again in $package_defined_in");
125 $args{constraint} = delete $args{where} if exists $args{where};
126 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
129 if($mode eq 'subtype'){
130 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
133 $constraint = Mouse::Meta::TypeConstraint->new(%args);
136 return $TYPE{$name} = $constraint;
140 return _create_type('type', @_);
144 return _create_type('subtype', @_);
148 my $type_name = shift;
150 my $type = find_type_constraint($type_name)
151 or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
153 $type->_add_type_coercions(@_);
158 my($name, $options) = @_;
159 my $class = $options->{class} || $name;
160 return _create_type 'subtype', $name => (
162 optimized_as => Mouse::Util::generate_isa_predicate_for($class),
169 my($name, $options) = @_;
170 my $role = $options->{role} || $name;
171 return _create_type 'subtype', $name => (
173 optimized_as => sub { Scalar::Util::blessed($_[0]) && does_role($_[0], $role) },
182 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
186 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
188 return _create_type 'type', $name => (
189 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
195 sub typecast_constraints { # DEPRECATED
196 my($class, $pkg, $type, $value) = @_;
197 Carp::croak("wrong arguments count") unless @_ == 4;
199 Carp::cluck("typecast_constraints() has been deprecated, which was an internal utility anyway");
201 return $type->coerce($value);
207 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
211 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
213 return _create_type 'type', $name => (
214 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
220 sub _find_or_create_regular_type{
223 return $TYPE{$spec} if exists $TYPE{$spec};
225 my $meta = Mouse::Util::get_metaclass_by_name($spec)
228 if(Mouse::Util::is_a_metarole($meta)){
229 return role_type($spec);
232 return class_type($spec);
236 $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
237 $TYPE{HashRef}{constraint_generator} = \&_parameterize_HashRef_for;
238 $TYPE{Maybe}{constraint_generator} = \&_parameterize_Maybe_for;
240 sub _find_or_create_parameterized_type{
241 my($base, $param) = @_;
243 my $name = sprintf '%s[%s]', $base->name, $param->name;
245 $TYPE{$name} ||= $base->parameterize($param, $name);
248 sub _find_or_create_union_type{
249 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
251 my $name = join '|', @types;
253 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
255 type_constraints => \@types,
263 my($spec, $start) = @_;
268 my $len = length $spec;
271 for($i = $start; $i < $len; $i++){
272 my $char = substr($spec, $i, 1);
275 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
278 ($i, $subtype) = _parse_type($spec, $i+1)
280 $start = $i+1; # reset
282 push @list, _find_or_create_parameterized_type($base => $subtype);
289 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
292 # XXX: Mouse creates a new class type, but Moose does not.
293 $type = class_type( substr($spec, $start, $i - $start) );
298 ($i, $subtype) = _parse_type($spec, $i+1)
301 $start = $i+1; # reset
303 push @list, $subtype;
307 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
314 # create a new class type
315 push @list, class_type( substr $spec, $start, $i - $start );
323 return ($len, $list[0]);
326 return ($len, _find_or_create_union_type(@list));
331 sub find_type_constraint {
333 return $spec if Mouse::Util::is_a_type_constraint($spec);
339 sub find_or_parse_type_constraint {
341 return $spec if Mouse::Util::is_a_type_constraint($spec);
344 return $TYPE{$spec} || do{
345 my($pos, $type) = _parse_type($spec, 0);
350 sub find_or_create_does_type_constraint{
351 # XXX: Moose does not register a new role_type, but Mouse does.
352 return find_or_parse_type_constraint(@_) || role_type(@_);
355 sub find_or_create_isa_type_constraint {
356 # XXX: Moose does not register a new class_type, but Mouse does.
357 return find_or_parse_type_constraint(@_) || class_type(@_);
365 Mouse::Util::TypeConstraints - Type constraint system for Mouse
369 This document describes Mouse version 0.43
373 use Mouse::Util::TypeConstraints;
379 subtype 'NaturalLessThanTen'
382 => message { "This number ($_) is not less than ten!" };
388 enum 'RGBColors' => qw(red green blue);
390 no Mouse::Util::TypeConstraints;
394 This module provides Mouse with the ability to create custom type
395 constraints to be used in attribute definition.
397 =head2 Important Caveat
399 This is B<NOT> a type system for Perl 5. These are type constraints,
400 and they are not used by Mouse unless you tell it to. No type
401 inference is performed, expressions are not typed, etc. etc. etc.
403 A type constraint is at heart a small "check if a value is valid"
404 function. A constraint can be associated with an attribute. This
405 simplifies parameter validation, and makes your code clearer to read,
406 because you can refer to constraints by name.
408 =head2 Slightly Less Important Caveat
410 It is B<always> a good idea to quote your type names.
412 This prevents Perl from trying to execute the call as an indirect
413 object call. This can be an issue when you have a subtype with the
414 same name as a valid class.
418 subtype DateTime => as Object => where { $_->isa('DateTime') };
420 will I<just work>, while this:
423 subtype DateTime => as Object => where { $_->isa('DateTime') };
425 will fail silently and cause many headaches. The simple way to solve
426 this, as well as future proof your subtypes from classes which have
427 yet to have been created, is to quote the type name:
430 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
432 =head2 Default Type Constraints
434 This module also provides a simple hierarchy for Perl 5 types, here is
435 that hierarchy represented visually.
459 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
460 parameterized, this means you can say:
462 ArrayRef[Int] # an array of integers
463 HashRef[CodeRef] # a hash of str to CODE ref mappings
464 Maybe[Str] # value may be a string, may be undefined
466 If Mouse finds a name in brackets that it does not recognize as an
467 existing type, it assumes that this is a class name, for example
468 C<ArrayRef[DateTime]>.
470 B<NOTE:> Unless you parameterize a type, then it is invalid to include
471 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
472 name, I<not> as a parameterization of C<ArrayRef>.
474 B<NOTE:> The C<Undef> type constraint for the most part works
475 correctly now, but edge cases may still exist, please use it
478 B<NOTE:> The C<ClassName> type constraint does a complex package
479 existence check. This means that your class B<must> be loaded for this
480 type constraint to pass.
482 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
483 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
484 constraint checks that an I<object does> the named role.
486 =head2 Type Constraint Naming
488 Type name declared via this module can only contain alphanumeric
489 characters, colons (:), and periods (.).
491 Since the types created by this module are global, it is suggested
492 that you namespace your types just as you would namespace your
493 modules. So instead of creating a I<Color> type for your
494 B<My::Graphics> module, you would call the type
495 I<My::Graphics::Types::Color> instead.
497 =head2 Use with Other Constraint Modules
499 This module can play nicely with other constraint modules with some
500 slight tweaking. The C<where> clause in types is expected to be a
501 C<CODE> reference which checks it's first argument and returns a
502 boolean. Since most constraint modules work in a similar way, it
503 should be simple to adapt them to work with Mouse.
505 For instance, this is how you could use it with
506 L<Declare::Constraints::Simple> to declare a completely new type.
508 type 'HashOfArrayOfObjects',
512 -values => IsArrayRef(IsObject)
516 Here is an example of using L<Test::Deep> and it's non-test
517 related C<eq_deeply> function.
519 type 'ArrayOfHashOfBarsAndRandomNumbers'
522 array_each(subhashof({
524 random_number => ignore()
530 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
532 Returns the names of builtin type constraints.
534 =head2 C<< list_all_type_constraints -> (Names) >>
536 Returns the names of all the type constraints.
542 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
544 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
546 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
548 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
550 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
552 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
554 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
556 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
558 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
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>