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 => ...)
104 if($mode eq 'subtype'){
105 $parent = delete $args{as};
107 $parent = delete $args{name};
113 my $package_defined_in = $args{package_defined_in} ||= caller(1);
114 my $existing = $TYPE{$name};
115 if($existing && $existing->{package_defined_in} ne $package_defined_in){
116 confess("The type constraint '$name' has already been created in "
117 . "$existing->{package_defined_in} and cannot be created again in $package_defined_in");
121 $args{constraint} = delete $args{where} if exists $args{where};
122 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
125 if($mode eq 'subtype'){
126 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
129 $constraint = Mouse::Meta::TypeConstraint->new(%args);
133 return $TYPE{$name} = $constraint;
141 return _create_type('type', @_);
145 return _create_type('subtype', @_);
149 my $type_name = shift;
151 my $type = find_type_constraint($type_name)
152 or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
154 $type->_add_type_coercions(@_);
159 my($name, $options) = @_;
160 my $class = $options->{class} || $name;
161 return _create_type 'subtype', $name => (
163 optimized_as => Mouse::Util::generate_isa_predicate_for($class),
170 my($name, $options) = @_;
171 my $role = $options->{role} || $name;
172 return _create_type 'subtype', $name => (
174 optimized_as => sub { Scalar::Util::blessed($_[0]) && does_role($_[0], $role) },
183 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
187 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
189 return _create_type 'type', $name => (
190 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
199 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
203 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
205 return _create_type 'type', $name => (
206 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
212 sub _find_or_create_regular_type{
215 return $TYPE{$spec} if exists $TYPE{$spec};
217 my $meta = Mouse::Util::get_metaclass_by_name($spec)
220 if(Mouse::Util::is_a_metarole($meta)){
221 return role_type($spec);
224 return class_type($spec);
228 $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
229 $TYPE{HashRef}{constraint_generator} = \&_parameterize_HashRef_for;
230 $TYPE{Maybe}{constraint_generator} = \&_parameterize_Maybe_for;
232 sub _find_or_create_parameterized_type{
233 my($base, $param) = @_;
235 my $name = sprintf '%s[%s]', $base->name, $param->name;
237 $TYPE{$name} ||= $base->parameterize($param, $name);
240 sub _find_or_create_union_type{
241 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
243 my $name = join '|', @types;
245 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
247 type_constraints => \@types,
255 my($spec, $start) = @_;
260 my $len = length $spec;
263 for($i = $start; $i < $len; $i++){
264 my $char = substr($spec, $i, 1);
267 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
270 ($i, $subtype) = _parse_type($spec, $i+1)
272 $start = $i+1; # reset
274 push @list, _find_or_create_parameterized_type($base => $subtype);
281 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
284 # XXX: Mouse creates a new class type, but Moose does not.
285 $type = class_type( substr($spec, $start, $i - $start) );
290 ($i, $subtype) = _parse_type($spec, $i+1)
293 $start = $i+1; # reset
295 push @list, $subtype;
299 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
306 # create a new class type
307 push @list, class_type( substr $spec, $start, $i - $start );
315 return ($len, $list[0]);
318 return ($len, _find_or_create_union_type(@list));
323 sub find_type_constraint {
325 return $spec if Mouse::Util::is_a_type_constraint($spec);
331 sub find_or_parse_type_constraint {
333 return $spec if Mouse::Util::is_a_type_constraint($spec);
336 return $TYPE{$spec} || do{
337 my($pos, $type) = _parse_type($spec, 0);
342 sub find_or_create_does_type_constraint{
343 # XXX: Moose does not register a new role_type, but Mouse does.
344 return find_or_parse_type_constraint(@_) || role_type(@_);
347 sub find_or_create_isa_type_constraint {
348 # XXX: Moose does not register a new class_type, but Mouse does.
349 return find_or_parse_type_constraint(@_) || class_type(@_);
357 Mouse::Util::TypeConstraints - Type constraint system for Mouse
361 This document describes Mouse version 0.45
365 use Mouse::Util::TypeConstraints;
371 subtype 'NaturalLessThanTen'
374 => message { "This number ($_) is not less than ten!" };
380 enum 'RGBColors' => qw(red green blue);
382 no Mouse::Util::TypeConstraints;
386 This module provides Mouse with the ability to create custom type
387 constraints to be used in attribute definition.
389 =head2 Important Caveat
391 This is B<NOT> a type system for Perl 5. These are type constraints,
392 and they are not used by Mouse unless you tell it to. No type
393 inference is performed, expressions are not typed, etc. etc. etc.
395 A type constraint is at heart a small "check if a value is valid"
396 function. A constraint can be associated with an attribute. This
397 simplifies parameter validation, and makes your code clearer to read,
398 because you can refer to constraints by name.
400 =head2 Slightly Less Important Caveat
402 It is B<always> a good idea to quote your type names.
404 This prevents Perl from trying to execute the call as an indirect
405 object call. This can be an issue when you have a subtype with the
406 same name as a valid class.
410 subtype DateTime => as Object => where { $_->isa('DateTime') };
412 will I<just work>, while this:
415 subtype DateTime => as Object => where { $_->isa('DateTime') };
417 will fail silently and cause many headaches. The simple way to solve
418 this, as well as future proof your subtypes from classes which have
419 yet to have been created, is to quote the type name:
422 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
424 =head2 Default Type Constraints
426 This module also provides a simple hierarchy for Perl 5 types, here is
427 that hierarchy represented visually.
451 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
452 parameterized, this means you can say:
454 ArrayRef[Int] # an array of integers
455 HashRef[CodeRef] # a hash of str to CODE ref mappings
456 Maybe[Str] # value may be a string, may be undefined
458 If Mouse finds a name in brackets that it does not recognize as an
459 existing type, it assumes that this is a class name, for example
460 C<ArrayRef[DateTime]>.
462 B<NOTE:> Unless you parameterize a type, then it is invalid to include
463 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
464 name, I<not> as a parameterization of C<ArrayRef>.
466 B<NOTE:> The C<Undef> type constraint for the most part works
467 correctly now, but edge cases may still exist, please use it
470 B<NOTE:> The C<ClassName> type constraint does a complex package
471 existence check. This means that your class B<must> be loaded for this
472 type constraint to pass.
474 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
475 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
476 constraint checks that an I<object does> the named role.
478 =head2 Type Constraint Naming
480 Type name declared via this module can only contain alphanumeric
481 characters, colons (:), and periods (.).
483 Since the types created by this module are global, it is suggested
484 that you namespace your types just as you would namespace your
485 modules. So instead of creating a I<Color> type for your
486 B<My::Graphics> module, you would call the type
487 I<My::Graphics::Types::Color> instead.
489 =head2 Use with Other Constraint Modules
491 This module can play nicely with other constraint modules with some
492 slight tweaking. The C<where> clause in types is expected to be a
493 C<CODE> reference which checks it's first argument and returns a
494 boolean. Since most constraint modules work in a similar way, it
495 should be simple to adapt them to work with Mouse.
497 For instance, this is how you could use it with
498 L<Declare::Constraints::Simple> to declare a completely new type.
500 type 'HashOfArrayOfObjects',
504 -values => IsArrayRef(IsObject)
508 Here is an example of using L<Test::Deep> and it's non-test
509 related C<eq_deeply> function.
511 type 'ArrayOfHashOfBarsAndRandomNumbers'
514 array_each(subhashof({
516 random_number => ignore()
522 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
524 Returns the names of builtin type constraints.
526 =head2 C<< list_all_type_constraints -> (Names) >>
528 Returns the names of all the type constraints.
534 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
536 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
538 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
540 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
542 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
544 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
546 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
548 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
550 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
552 =item C<< coerce $type => from $another_type, via { }, ... >>
558 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
564 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
568 L<Moose::Util::TypeConstraints>