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
26 $TYPE{Any} = Mouse::Meta::TypeConstraint->new(
31 # $name => $parent, $code,
37 Maybe => 'Item', undef,
40 Undef => 'Item', \&Undef,
41 Defined => 'Item', \&Defined,
42 Bool => 'Item', \&Bool,
43 Value => 'Defined', \&Value,
44 Str => 'Value', \&Str,
49 Ref => 'Defined', \&Ref,
50 ScalarRef => 'Ref', \&ScalarRef,
51 ArrayRef => 'Ref', \&ArrayRef,
52 HashRef => 'Ref', \&HashRef,
53 CodeRef => 'Ref', \&CodeRef,
54 RegexpRef => 'Ref', \&RegexpRef,
55 GlobRef => 'Ref', \&GlobRef,
58 FileHandle => 'GlobRef', \&FileHandle,
59 Object => 'Ref', \&Object,
61 # special string types
62 ClassName => 'Str', \&ClassName,
63 RoleName => 'ClassName', \&RoleName,
67 while (my ($name, $parent, $code) = splice @builtins, 0, 3) {
68 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
70 parent => $TYPE{$parent},
75 # make it parametarizable
77 $TYPE{Maybe} {constraint_generator} = \&_parameterize_Maybe_for;
78 $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
79 $TYPE{HashRef} {constraint_generator} = \&_parameterize_HashRef_for;
83 sub as ($) { (as => $_[0]) }
84 sub where (&) { (where => $_[0]) }
85 sub message (&) { (message => $_[0]) }
86 sub optimize_as (&) { (optimize_as => $_[0]) }
93 sub optimized_constraints { # DEPRECATED
94 Carp::cluck('optimized_constraints() has been deprecated');
98 undef @builtins; # free the allocated memory
99 @builtins = keys %TYPE; # reuse it
100 sub list_all_builtin_type_constraints { @builtins }
102 sub list_all_type_constraints { keys %TYPE }
111 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
114 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
118 elsif(@_ % 2){ # @_ : $name => ( where => ... )
121 else{ # @_ : (name => $name, where => ...)
131 if($mode eq 'subtype'){
132 $parent = delete $args{as};
134 $parent = delete $args{name};
140 # set 'package_defined_in' only if it is not a core package
141 my $this = $args{package_defined_in};
144 if($this !~ /\A Mouse \b/xms){
145 $args{package_defined_in} = $this;
150 my $that = $TYPE{$name}->{package_defined_in} || __PACKAGE__;
151 ($this eq $that) or confess(
152 "The type constraint '$name' has already been created in $that and cannot be created again in $this"
157 $args{name} = '__ANON__';
160 $args{constraint} = delete $args{where} if exists $args{where};
161 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
164 if($mode eq 'subtype'){
165 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
168 $constraint = Mouse::Meta::TypeConstraint->new(%args);
172 return $TYPE{$name} = $constraint;
180 return _create_type('type', @_);
184 return _create_type('subtype', @_);
188 my $type_name = shift;
190 my $type = find_type_constraint($type_name)
191 or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
193 $type->_add_type_coercions(@_);
198 my($name, $options) = @_;
199 my $class = $options->{class} || $name;
202 return _create_type 'subtype', $name => (
204 optimized_as => Mouse::Util::generate_isa_predicate_for($class),
209 my($name, $options) = @_;
210 my $role = $options->{role} || $name;
213 return _create_type 'subtype', $name => (
215 optimized_as => sub { Scalar::Util::blessed($_[0]) && does_role($_[0], $role) },
222 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
226 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
229 return _create_type 'type', $name => (
230 optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
237 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
241 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
244 return _create_type 'type', $name => (
245 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
249 sub _find_or_create_regular_type{
252 return $TYPE{$spec} if exists $TYPE{$spec};
254 my $meta = Mouse::Util::get_metaclass_by_name($spec)
257 if(Mouse::Util::is_a_metarole($meta)){
258 return role_type($spec);
261 return class_type($spec);
265 sub _find_or_create_parameterized_type{
266 my($base, $param) = @_;
268 my $name = sprintf '%s[%s]', $base->name, $param->name;
270 $TYPE{$name} ||= $base->parameterize($param, $name);
273 sub _find_or_create_union_type{
274 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
276 my $name = join '|', @types;
279 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
281 type_constraints => \@types,
287 my($spec, $start) = @_;
292 my $len = length $spec;
295 for($i = $start; $i < $len; $i++){
296 my $char = substr($spec, $i, 1);
299 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
302 ($i, $subtype) = _parse_type($spec, $i+1)
304 $start = $i+1; # reset
306 push @list, _find_or_create_parameterized_type($base => $subtype);
313 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
316 # XXX: Mouse creates a new class type, but Moose does not.
317 $type = class_type( substr($spec, $start, $i - $start) );
322 ($i, $subtype) = _parse_type($spec, $i+1)
325 $start = $i+1; # reset
327 push @list, $subtype;
331 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
338 # create a new class type
339 push @list, class_type( substr $spec, $start, $i - $start );
347 return ($len, $list[0]);
350 return ($len, _find_or_create_union_type(@list));
355 sub find_type_constraint {
357 return $spec if Mouse::Util::is_a_type_constraint($spec);
358 return undef if !defined $spec;
364 sub find_or_parse_type_constraint {
366 return $spec if Mouse::Util::is_a_type_constraint($spec);
367 return undef if !defined $spec;
370 return $TYPE{$spec} || do{
371 my($pos, $type) = _parse_type($spec, 0);
376 sub find_or_create_does_type_constraint{
377 # XXX: Moose does not register a new role_type, but Mouse does.
378 return find_or_parse_type_constraint(@_) || role_type(@_);
381 sub find_or_create_isa_type_constraint {
382 # XXX: Moose does not register a new class_type, but Mouse does.
383 return find_or_parse_type_constraint(@_) || class_type(@_);
391 Mouse::Util::TypeConstraints - Type constraint system for Mouse
395 This document describes Mouse version 0.50_02
399 use Mouse::Util::TypeConstraints;
405 subtype 'NaturalLessThanTen'
408 => message { "This number ($_) is not less than ten!" };
414 enum 'RGBColors' => qw(red green blue);
416 no Mouse::Util::TypeConstraints;
420 This module provides Mouse with the ability to create custom type
421 constraints to be used in attribute definition.
423 =head2 Important Caveat
425 This is B<NOT> a type system for Perl 5. These are type constraints,
426 and they are not used by Mouse unless you tell it to. No type
427 inference is performed, expressions are not typed, etc. etc. etc.
429 A type constraint is at heart a small "check if a value is valid"
430 function. A constraint can be associated with an attribute. This
431 simplifies parameter validation, and makes your code clearer to read,
432 because you can refer to constraints by name.
434 =head2 Slightly Less Important Caveat
436 It is B<always> a good idea to quote your type names.
438 This prevents Perl from trying to execute the call as an indirect
439 object call. This can be an issue when you have a subtype with the
440 same name as a valid class.
444 subtype DateTime => as Object => where { $_->isa('DateTime') };
446 will I<just work>, while this:
449 subtype DateTime => as Object => where { $_->isa('DateTime') };
451 will fail silently and cause many headaches. The simple way to solve
452 this, as well as future proof your subtypes from classes which have
453 yet to have been created, is to quote the type name:
456 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
458 =head2 Default Type Constraints
460 This module also provides a simple hierarchy for Perl 5 types, here is
461 that hierarchy represented visually.
485 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
486 parameterized, this means you can say:
488 ArrayRef[Int] # an array of integers
489 HashRef[CodeRef] # a hash of str to CODE ref mappings
490 Maybe[Str] # value may be a string, may be undefined
492 If Mouse finds a name in brackets that it does not recognize as an
493 existing type, it assumes that this is a class name, for example
494 C<ArrayRef[DateTime]>.
496 B<NOTE:> Unless you parameterize a type, then it is invalid to include
497 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
498 name, I<not> as a parameterization of C<ArrayRef>.
500 B<NOTE:> The C<Undef> type constraint for the most part works
501 correctly now, but edge cases may still exist, please use it
504 B<NOTE:> The C<ClassName> type constraint does a complex package
505 existence check. This means that your class B<must> be loaded for this
506 type constraint to pass.
508 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
509 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
510 constraint checks that an I<object does> the named role.
512 =head2 Type Constraint Naming
514 Type name declared via this module can only contain alphanumeric
515 characters, colons (:), and periods (.).
517 Since the types created by this module are global, it is suggested
518 that you namespace your types just as you would namespace your
519 modules. So instead of creating a I<Color> type for your
520 B<My::Graphics> module, you would call the type
521 I<My::Graphics::Types::Color> instead.
523 =head2 Use with Other Constraint Modules
525 This module can play nicely with other constraint modules with some
526 slight tweaking. The C<where> clause in types is expected to be a
527 C<CODE> reference which checks it's first argument and returns a
528 boolean. Since most constraint modules work in a similar way, it
529 should be simple to adapt them to work with Mouse.
531 For instance, this is how you could use it with
532 L<Declare::Constraints::Simple> to declare a completely new type.
534 type 'HashOfArrayOfObjects',
538 -values => IsArrayRef(IsObject)
542 Here is an example of using L<Test::Deep> and it's non-test
543 related C<eq_deeply> function.
545 type 'ArrayOfHashOfBarsAndRandomNumbers'
548 array_each(subhashof({
550 random_number => ignore()
556 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
558 Returns the names of builtin type constraints.
560 =head2 C<< list_all_type_constraints -> (Names) >>
562 Returns the names of all the type constraints.
568 =item C<< type $name => where { } ... -> Mouse::Meta::TypeConstraint >>
570 =item C<< subtype $name => as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
572 =item C<< subtype as $parent => where { } ... -> Mouse::Meta::TypeConstraint >>
574 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
576 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
578 =item C<< duck_type($name, @methods | \@methods) -> Mouse::Meta::TypeConstraint >>
580 =item C<< duck_type(\@methods) -> Mouse::Meta::TypeConstraint >>
582 =item C<< enum($name, @values | \@values) -> Mouse::Meta::TypeConstraint >>
584 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
586 =item C<< coerce $type => from $another_type, via { }, ... >>
592 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
598 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
602 L<Moose::Util::TypeConstraints>