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),
202 sub typecast_constraints { # DEPRECATED
203 my($class, $pkg, $type, $value) = @_;
204 Carp::croak("wrong arguments count") unless @_ == 4;
206 Carp::cluck("typecast_constraints() has been deprecated, which was an internal utility anyway");
208 return $type->coerce($value);
214 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
218 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
220 return _create_type 'type', $name => (
221 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
227 sub _find_or_create_regular_type{
230 return $TYPE{$spec} if exists $TYPE{$spec};
232 my $meta = Mouse::Util::get_metaclass_by_name($spec)
235 if(Mouse::Util::is_a_metarole($meta)){
236 return role_type($spec);
239 return class_type($spec);
243 $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
244 $TYPE{HashRef}{constraint_generator} = \&_parameterize_HashRef_for;
245 $TYPE{Maybe}{constraint_generator} = \&_parameterize_Maybe_for;
247 sub _find_or_create_parameterized_type{
248 my($base, $param) = @_;
250 my $name = sprintf '%s[%s]', $base->name, $param->name;
252 $TYPE{$name} ||= $base->parameterize($param, $name);
255 sub _find_or_create_union_type{
256 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
258 my $name = join '|', @types;
260 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
262 type_constraints => \@types,
270 my($spec, $start) = @_;
275 my $len = length $spec;
278 for($i = $start; $i < $len; $i++){
279 my $char = substr($spec, $i, 1);
282 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
285 ($i, $subtype) = _parse_type($spec, $i+1)
287 $start = $i+1; # reset
289 push @list, _find_or_create_parameterized_type($base => $subtype);
296 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
299 # XXX: Mouse creates a new class type, but Moose does not.
300 $type = class_type( substr($spec, $start, $i - $start) );
305 ($i, $subtype) = _parse_type($spec, $i+1)
308 $start = $i+1; # reset
310 push @list, $subtype;
314 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
321 # create a new class type
322 push @list, class_type( substr $spec, $start, $i - $start );
330 return ($len, $list[0]);
333 return ($len, _find_or_create_union_type(@list));
338 sub find_type_constraint {
340 return $spec if Mouse::Util::is_a_type_constraint($spec);
346 sub find_or_parse_type_constraint {
348 return $spec if Mouse::Util::is_a_type_constraint($spec);
351 return $TYPE{$spec} || do{
352 my($pos, $type) = _parse_type($spec, 0);
357 sub find_or_create_does_type_constraint{
358 # XXX: Moose does not register a new role_type, but Mouse does.
359 return find_or_parse_type_constraint(@_) || role_type(@_);
362 sub find_or_create_isa_type_constraint {
363 # XXX: Moose does not register a new class_type, but Mouse does.
364 return find_or_parse_type_constraint(@_) || class_type(@_);
372 Mouse::Util::TypeConstraints - Type constraint system for Mouse
376 This document describes Mouse version 0.4501
380 use Mouse::Util::TypeConstraints;
386 subtype 'NaturalLessThanTen'
389 => message { "This number ($_) is not less than ten!" };
395 enum 'RGBColors' => qw(red green blue);
397 no Mouse::Util::TypeConstraints;
401 This module provides Mouse with the ability to create custom type
402 constraints to be used in attribute definition.
404 =head2 Important Caveat
406 This is B<NOT> a type system for Perl 5. These are type constraints,
407 and they are not used by Mouse unless you tell it to. No type
408 inference is performed, expressions are not typed, etc. etc. etc.
410 A type constraint is at heart a small "check if a value is valid"
411 function. A constraint can be associated with an attribute. This
412 simplifies parameter validation, and makes your code clearer to read,
413 because you can refer to constraints by name.
415 =head2 Slightly Less Important Caveat
417 It is B<always> a good idea to quote your type names.
419 This prevents Perl from trying to execute the call as an indirect
420 object call. This can be an issue when you have a subtype with the
421 same name as a valid class.
425 subtype DateTime => as Object => where { $_->isa('DateTime') };
427 will I<just work>, while this:
430 subtype DateTime => as Object => where { $_->isa('DateTime') };
432 will fail silently and cause many headaches. The simple way to solve
433 this, as well as future proof your subtypes from classes which have
434 yet to have been created, is to quote the type name:
437 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
439 =head2 Default Type Constraints
441 This module also provides a simple hierarchy for Perl 5 types, here is
442 that hierarchy represented visually.
466 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
467 parameterized, this means you can say:
469 ArrayRef[Int] # an array of integers
470 HashRef[CodeRef] # a hash of str to CODE ref mappings
471 Maybe[Str] # value may be a string, may be undefined
473 If Mouse finds a name in brackets that it does not recognize as an
474 existing type, it assumes that this is a class name, for example
475 C<ArrayRef[DateTime]>.
477 B<NOTE:> Unless you parameterize a type, then it is invalid to include
478 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
479 name, I<not> as a parameterization of C<ArrayRef>.
481 B<NOTE:> The C<Undef> type constraint for the most part works
482 correctly now, but edge cases may still exist, please use it
485 B<NOTE:> The C<ClassName> type constraint does a complex package
486 existence check. This means that your class B<must> be loaded for this
487 type constraint to pass.
489 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
490 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
491 constraint checks that an I<object does> the named role.
493 =head2 Type Constraint Naming
495 Type name declared via this module can only contain alphanumeric
496 characters, colons (:), and periods (.).
498 Since the types created by this module are global, it is suggested
499 that you namespace your types just as you would namespace your
500 modules. So instead of creating a I<Color> type for your
501 B<My::Graphics> module, you would call the type
502 I<My::Graphics::Types::Color> instead.
504 =head2 Use with Other Constraint Modules
506 This module can play nicely with other constraint modules with some
507 slight tweaking. The C<where> clause in types is expected to be a
508 C<CODE> reference which checks it's first argument and returns a
509 boolean. Since most constraint modules work in a similar way, it
510 should be simple to adapt them to work with Mouse.
512 For instance, this is how you could use it with
513 L<Declare::Constraints::Simple> to declare a completely new type.
515 type 'HashOfArrayOfObjects',
519 -values => IsArrayRef(IsObject)
523 Here is an example of using L<Test::Deep> and it's non-test
524 related C<eq_deeply> function.
526 type 'ArrayOfHashOfBarsAndRandomNumbers'
529 array_each(subhashof({
531 random_number => ignore()
537 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
539 Returns the names of builtin type constraints.
541 =head2 C<< list_all_type_constraints -> (Names) >>
543 Returns the names of all the type constraints.
549 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
551 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
553 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
555 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
557 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
559 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
561 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
563 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
565 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
567 =item C<< coerce $type => from $another_type, via { }, ... >>
573 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
579 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
583 L<Moose::Util::TypeConstraints>