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 my @builtins = keys %TYPE;
71 sub list_all_builtin_type_constraints { @builtins }
73 sub list_all_type_constraints { keys %TYPE }
82 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
85 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
89 elsif(@_ % 2){ # @_ : $name => ( where => ... )
92 else{ # @_ : (name => $name, where => ...)
102 if($mode eq 'subtype'){
103 $parent = delete $args{as};
105 $parent = delete $args{name};
111 my $package_defined_in = $args{package_defined_in} ||= caller(1);
112 my $existing = $TYPE{$name};
113 if($existing && $existing->{package_defined_in} ne $package_defined_in){
114 confess("The type constraint '$name' has already been created in "
115 . "$existing->{package_defined_in} and cannot be created again in $package_defined_in");
119 $args{name} = '__ANON__';
122 $args{constraint} = delete $args{where} if exists $args{where};
123 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
126 if($mode eq 'subtype'){
127 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
130 $constraint = Mouse::Meta::TypeConstraint->new(%args);
134 return $TYPE{$name} = $constraint;
142 return _create_type('type', @_);
146 return _create_type('subtype', @_);
150 my $type_name = shift;
152 my $type = find_type_constraint($type_name)
153 or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
155 $type->_add_type_coercions(@_);
160 my($name, $options) = @_;
161 my $class = $options->{class} || $name;
162 return _create_type 'subtype', $name => (
164 optimized_as => Mouse::Util::generate_isa_predicate_for($class),
171 my($name, $options) = @_;
172 my $role = $options->{role} || $name;
173 return _create_type 'subtype', $name => (
175 optimized_as => sub { Scalar::Util::blessed($_[0]) && does_role($_[0], $role) },
184 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
188 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
190 return _create_type 'type', $name => (
191 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
200 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
204 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
206 return _create_type 'type', $name => (
207 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
213 sub _find_or_create_regular_type{
216 return $TYPE{$spec} if exists $TYPE{$spec};
218 my $meta = Mouse::Util::get_metaclass_by_name($spec)
221 if(Mouse::Util::is_a_metarole($meta)){
222 return role_type($spec);
225 return class_type($spec);
229 $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
230 $TYPE{HashRef}{constraint_generator} = \&_parameterize_HashRef_for;
231 $TYPE{Maybe}{constraint_generator} = \&_parameterize_Maybe_for;
233 sub _find_or_create_parameterized_type{
234 my($base, $param) = @_;
236 my $name = sprintf '%s[%s]', $base->name, $param->name;
238 $TYPE{$name} ||= $base->parameterize($param, $name);
241 sub _find_or_create_union_type{
242 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
244 my $name = join '|', @types;
246 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
248 type_constraints => \@types,
256 my($spec, $start) = @_;
261 my $len = length $spec;
264 for($i = $start; $i < $len; $i++){
265 my $char = substr($spec, $i, 1);
268 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
271 ($i, $subtype) = _parse_type($spec, $i+1)
273 $start = $i+1; # reset
275 push @list, _find_or_create_parameterized_type($base => $subtype);
282 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
285 # XXX: Mouse creates a new class type, but Moose does not.
286 $type = class_type( substr($spec, $start, $i - $start) );
291 ($i, $subtype) = _parse_type($spec, $i+1)
294 $start = $i+1; # reset
296 push @list, $subtype;
300 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
307 # create a new class type
308 push @list, class_type( substr $spec, $start, $i - $start );
316 return ($len, $list[0]);
319 return ($len, _find_or_create_union_type(@list));
324 sub find_type_constraint {
326 return $spec if Mouse::Util::is_a_type_constraint($spec);
332 sub find_or_parse_type_constraint {
334 return $spec if Mouse::Util::is_a_type_constraint($spec);
337 return $TYPE{$spec} || do{
338 my($pos, $type) = _parse_type($spec, 0);
343 sub find_or_create_does_type_constraint{
344 # XXX: Moose does not register a new role_type, but Mouse does.
345 return find_or_parse_type_constraint(@_) || role_type(@_);
348 sub find_or_create_isa_type_constraint {
349 # XXX: Moose does not register a new class_type, but Mouse does.
350 return find_or_parse_type_constraint(@_) || class_type(@_);
358 Mouse::Util::TypeConstraints - Type constraint system for Mouse
362 This document describes Mouse version 0.45
366 use Mouse::Util::TypeConstraints;
372 subtype 'NaturalLessThanTen'
375 => message { "This number ($_) is not less than ten!" };
381 enum 'RGBColors' => qw(red green blue);
383 no Mouse::Util::TypeConstraints;
387 This module provides Mouse with the ability to create custom type
388 constraints to be used in attribute definition.
390 =head2 Important Caveat
392 This is B<NOT> a type system for Perl 5. These are type constraints,
393 and they are not used by Mouse unless you tell it to. No type
394 inference is performed, expressions are not typed, etc. etc. etc.
396 A type constraint is at heart a small "check if a value is valid"
397 function. A constraint can be associated with an attribute. This
398 simplifies parameter validation, and makes your code clearer to read,
399 because you can refer to constraints by name.
401 =head2 Slightly Less Important Caveat
403 It is B<always> a good idea to quote your type names.
405 This prevents Perl from trying to execute the call as an indirect
406 object call. This can be an issue when you have a subtype with the
407 same name as a valid class.
411 subtype DateTime => as Object => where { $_->isa('DateTime') };
413 will I<just work>, while this:
416 subtype DateTime => as Object => where { $_->isa('DateTime') };
418 will fail silently and cause many headaches. The simple way to solve
419 this, as well as future proof your subtypes from classes which have
420 yet to have been created, is to quote the type name:
423 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
425 =head2 Default Type Constraints
427 This module also provides a simple hierarchy for Perl 5 types, here is
428 that hierarchy represented visually.
452 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
453 parameterized, this means you can say:
455 ArrayRef[Int] # an array of integers
456 HashRef[CodeRef] # a hash of str to CODE ref mappings
457 Maybe[Str] # value may be a string, may be undefined
459 If Mouse finds a name in brackets that it does not recognize as an
460 existing type, it assumes that this is a class name, for example
461 C<ArrayRef[DateTime]>.
463 B<NOTE:> Unless you parameterize a type, then it is invalid to include
464 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
465 name, I<not> as a parameterization of C<ArrayRef>.
467 B<NOTE:> The C<Undef> type constraint for the most part works
468 correctly now, but edge cases may still exist, please use it
471 B<NOTE:> The C<ClassName> type constraint does a complex package
472 existence check. This means that your class B<must> be loaded for this
473 type constraint to pass.
475 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
476 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
477 constraint checks that an I<object does> the named role.
479 =head2 Type Constraint Naming
481 Type name declared via this module can only contain alphanumeric
482 characters, colons (:), and periods (.).
484 Since the types created by this module are global, it is suggested
485 that you namespace your types just as you would namespace your
486 modules. So instead of creating a I<Color> type for your
487 B<My::Graphics> module, you would call the type
488 I<My::Graphics::Types::Color> instead.
490 =head2 Use with Other Constraint Modules
492 This module can play nicely with other constraint modules with some
493 slight tweaking. The C<where> clause in types is expected to be a
494 C<CODE> reference which checks it's first argument and returns a
495 boolean. Since most constraint modules work in a similar way, it
496 should be simple to adapt them to work with Mouse.
498 For instance, this is how you could use it with
499 L<Declare::Constraints::Simple> to declare a completely new type.
501 type 'HashOfArrayOfObjects',
505 -values => IsArrayRef(IsObject)
509 Here is an example of using L<Test::Deep> and it's non-test
510 related C<eq_deeply> function.
512 type 'ArrayOfHashOfBarsAndRandomNumbers'
515 array_each(subhashof({
517 random_number => ignore()
523 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
525 Returns the names of builtin type constraints.
527 =head2 C<< list_all_type_constraints -> (Names) >>
529 Returns the names of all the type constraints.
535 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
537 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
539 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
541 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
543 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
545 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
547 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
549 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
551 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
553 =item C<< coerce $type => from $another_type, via { }, ... >>
559 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
565 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
569 L<Moose::Util::TypeConstraints>