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
14 type subtype coerce class_type role_type enum
21 sub as ($) { (as => $_[0]) }
22 sub where (&) { (where => $_[0]) }
23 sub message (&) { (message => $_[0]) }
24 sub optimize_as (&) { (optimize_as => $_[0]) }
31 Any => undef, # null check
32 Item => undef, # null check
33 Maybe => undef, # null check
44 ScalarRef => \&ScalarRef,
45 ArrayRef => \&ArrayRef,
48 RegexpRef => \&RegexpRef,
51 FileHandle => \&FileHandle,
55 ClassName => \&ClassName,
56 RoleName => \&RoleName,
59 while (my ($name, $code) = each %builtins) {
60 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
66 sub optimized_constraints { # DEPRECATED
67 Carp::cluck('optimized_constraints() has been deprecated');
71 my @builtins = keys %TYPE;
72 sub list_all_builtin_type_constraints { @builtins }
74 sub list_all_type_constraints { keys %TYPE }
83 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
86 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
90 elsif(@_ % 2){ # @_ : $name => ( where => ... )
93 else{ # @_ : (name => $name, where => ...)
98 if(!defined($name = $args{name})){
105 if($mode eq 'subtype'){
106 $parent = delete $args{as};
108 $parent = delete $args{name};
113 my $package_defined_in = $args{package_defined_in} ||= caller(1);
115 my $existing = $TYPE{$name};
116 if($existing && $existing->{package_defined_in} ne $package_defined_in){
117 confess("The type constraint '$name' has already been created in "
118 . "$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);
132 return $TYPE{$name} = $constraint;
136 return _create_type('type', @_);
140 return _create_type('subtype', @_);
144 my $type_name = shift;
146 my $type = find_type_constraint($type_name)
147 or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
149 $type->_add_type_coercions(@_);
154 my($name, $options) = @_;
155 my $class = $options->{class} || $name;
156 return _create_type 'subtype', $name => (
158 optimized_as => Mouse::Util::_generate_isa_predicate_for($class),
165 my($name, $options) = @_;
166 my $role = $options->{role} || $name;
167 return _create_type 'subtype', $name => (
169 optimized_as => sub { Scalar::Util::blessed($_[0]) && does_role($_[0], $role) },
175 sub typecast_constraints { # DEPRECATED
176 my($class, $pkg, $type, $value) = @_;
177 Carp::croak("wrong arguments count") unless @_ == 4;
179 Carp::cluck("typecast_constraints() has been deprecated, which was an internal utility anyway");
181 return $type->coerce($value);
187 # enum ['small', 'medium', 'large']
188 if (ref($_[0]) eq 'ARRAY') {
189 %valid = map{ $_ => undef } @{ $_[0] };
190 $name = sprintf '(%s)', join '|', sort @{$_[0]};
192 # enum size => 'small', 'medium', 'large'
195 %valid = map{ $_ => undef } @_;
197 return _create_type 'type', $name => (
198 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
204 sub _find_or_create_regular_type{
207 return $TYPE{$spec} if exists $TYPE{$spec};
209 my $meta = Mouse::Util::get_metaclass_by_name($spec)
212 if(Mouse::Util::is_a_metarole($meta)){
213 return role_type($spec);
216 return class_type($spec);
220 $TYPE{ArrayRef}{constraint_generator} = sub {
221 my($type_parameter) = @_;
222 my $check = $type_parameter->_compiled_type_constraint;
225 foreach my $value (@{$_}) {
226 return undef unless $check->($value);
231 $TYPE{HashRef}{constraint_generator} = sub {
232 my($type_parameter) = @_;
233 my $check = $type_parameter->_compiled_type_constraint;
236 foreach my $value(values %{$_}){
237 return undef unless $check->($value);
243 # 'Maybe' type accepts 'Any', so it requires parameters
244 $TYPE{Maybe}{constraint_generator} = sub {
245 my($type_parameter) = @_;
246 my $check = $type_parameter->_compiled_type_constraint;
249 return !defined($_) || $check->($_);
253 sub _find_or_create_parameterized_type{
254 my($base, $param) = @_;
256 my $name = sprintf '%s[%s]', $base->name, $param->name;
258 $TYPE{$name} ||= $base->parameterize($param, $name);
261 sub _find_or_create_union_type{
262 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
264 my $name = join '|', @types;
266 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
268 type_constraints => \@types,
276 my($spec, $start) = @_;
281 my $len = length $spec;
284 for($i = $start; $i < $len; $i++){
285 my $char = substr($spec, $i, 1);
288 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
291 ($i, $subtype) = _parse_type($spec, $i+1)
293 $start = $i+1; # reset
295 push @list, _find_or_create_parameterized_type($base => $subtype);
302 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
305 # XXX: Mouse creates a new class type, but Moose does not.
306 $type = class_type( substr($spec, $start, $i - $start) );
311 ($i, $subtype) = _parse_type($spec, $i+1)
314 $start = $i+1; # reset
316 push @list, $subtype;
320 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
327 # create a new class type
328 push @list, class_type( substr $spec, $start, $i - $start );
336 return ($len, $list[0]);
339 return ($len, _find_or_create_union_type(@list));
344 sub find_type_constraint {
346 return $spec if Mouse::Util::is_a_type_constraint($spec);
352 sub find_or_parse_type_constraint {
354 return $spec if Mouse::Util::is_a_type_constraint($spec);
357 return $TYPE{$spec} || do{
358 my($pos, $type) = _parse_type($spec, 0);
363 sub find_or_create_does_type_constraint{
364 # XXX: Moose does not register a new role_type, but Mouse does.
365 return find_or_parse_type_constraint(@_) || role_type(@_);
368 sub find_or_create_isa_type_constraint {
369 # XXX: Moose does not register a new class_type, but Mouse does.
370 return find_or_parse_type_constraint(@_) || class_type(@_);
379 Mouse::Util::TypeConstraints - Type constraint system for Mouse
383 This document describes Mouse version 0.40_03
387 use Mouse::Util::TypeConstraints;
393 subtype 'NaturalLessThanTen'
396 => message { "This number ($_) is not less than ten!" };
402 enum 'RGBColors' => qw(red green blue);
404 no Mouse::Util::TypeConstraints;
408 This module provides Mouse with the ability to create custom type
409 constraints to be used in attribute definition.
411 =head2 Important Caveat
413 This is B<NOT> a type system for Perl 5. These are type constraints,
414 and they are not used by Mouse unless you tell it to. No type
415 inference is performed, expressions are not typed, etc. etc. etc.
417 A type constraint is at heart a small "check if a value is valid"
418 function. A constraint can be associated with an attribute. This
419 simplifies parameter validation, and makes your code clearer to read,
420 because you can refer to constraints by name.
422 =head2 Slightly Less Important Caveat
424 It is B<always> a good idea to quote your type names.
426 This prevents Perl from trying to execute the call as an indirect
427 object call. This can be an issue when you have a subtype with the
428 same name as a valid class.
432 subtype DateTime => as Object => where { $_->isa('DateTime') };
434 will I<just work>, while this:
437 subtype DateTime => as Object => where { $_->isa('DateTime') };
439 will fail silently and cause many headaches. The simple way to solve
440 this, as well as future proof your subtypes from classes which have
441 yet to have been created, is to quote the type name:
444 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
446 =head2 Default Type Constraints
448 This module also provides a simple hierarchy for Perl 5 types, here is
449 that hierarchy represented visually.
473 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
474 parameterized, this means you can say:
476 ArrayRef[Int] # an array of integers
477 HashRef[CodeRef] # a hash of str to CODE ref mappings
478 Maybe[Str] # value may be a string, may be undefined
480 If Mouse finds a name in brackets that it does not recognize as an
481 existing type, it assumes that this is a class name, for example
482 C<ArrayRef[DateTime]>.
484 B<NOTE:> Unless you parameterize a type, then it is invalid to include
485 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
486 name, I<not> as a parameterization of C<ArrayRef>.
488 B<NOTE:> The C<Undef> type constraint for the most part works
489 correctly now, but edge cases may still exist, please use it
492 B<NOTE:> The C<ClassName> type constraint does a complex package
493 existence check. This means that your class B<must> be loaded for this
494 type constraint to pass.
496 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
497 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
498 constraint checks that an I<object does> the named role.
500 =head2 Type Constraint Naming
502 Type name declared via this module can only contain alphanumeric
503 characters, colons (:), and periods (.).
505 Since the types created by this module are global, it is suggested
506 that you namespace your types just as you would namespace your
507 modules. So instead of creating a I<Color> type for your
508 B<My::Graphics> module, you would call the type
509 I<My::Graphics::Types::Color> instead.
511 =head2 Use with Other Constraint Modules
513 This module can play nicely with other constraint modules with some
514 slight tweaking. The C<where> clause in types is expected to be a
515 C<CODE> reference which checks it's first argument and returns a
516 boolean. Since most constraint modules work in a similar way, it
517 should be simple to adapt them to work with Mouse.
519 For instance, this is how you could use it with
520 L<Declare::Constraints::Simple> to declare a completely new type.
522 type 'HashOfArrayOfObjects',
526 -values => IsArrayRef(IsObject)
530 Here is an example of using L<Test::Deep> and it's non-test
531 related C<eq_deeply> function.
533 type 'ArrayOfHashOfBarsAndRandomNumbers'
536 array_each(subhashof({
538 random_number => ignore()
544 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
546 Returns the names of builtin type constraints.
548 =head2 C<< list_all_type_constraints -> (Names) >>
550 Returns the names of all the type constraints.
556 =item C<< subtype 'Name' => as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
558 =item C<< subtype as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
560 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
562 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
564 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
570 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
576 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
580 L<Moose::Util::TypeConstraints>