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 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
191 %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
193 return _create_type 'type', $name => (
194 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
200 sub _find_or_create_regular_type{
203 return $TYPE{$spec} if exists $TYPE{$spec};
205 my $meta = Mouse::Util::get_metaclass_by_name($spec)
208 if(Mouse::Util::is_a_metarole($meta)){
209 return role_type($spec);
212 return class_type($spec);
216 $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
217 $TYPE{HashRef}{constraint_generator} = \&_parameterize_HashRef_for;
218 $TYPE{Maybe}{constraint_generator} = \&_parameterize_Maybe_for;
220 sub _find_or_create_parameterized_type{
221 my($base, $param) = @_;
223 my $name = sprintf '%s[%s]', $base->name, $param->name;
225 $TYPE{$name} ||= $base->parameterize($param, $name);
228 sub _find_or_create_union_type{
229 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
231 my $name = join '|', @types;
233 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
235 type_constraints => \@types,
243 my($spec, $start) = @_;
248 my $len = length $spec;
251 for($i = $start; $i < $len; $i++){
252 my $char = substr($spec, $i, 1);
255 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
258 ($i, $subtype) = _parse_type($spec, $i+1)
260 $start = $i+1; # reset
262 push @list, _find_or_create_parameterized_type($base => $subtype);
269 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
272 # XXX: Mouse creates a new class type, but Moose does not.
273 $type = class_type( substr($spec, $start, $i - $start) );
278 ($i, $subtype) = _parse_type($spec, $i+1)
281 $start = $i+1; # reset
283 push @list, $subtype;
287 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
294 # create a new class type
295 push @list, class_type( substr $spec, $start, $i - $start );
303 return ($len, $list[0]);
306 return ($len, _find_or_create_union_type(@list));
311 sub find_type_constraint {
313 return $spec if Mouse::Util::is_a_type_constraint($spec);
319 sub find_or_parse_type_constraint {
321 return $spec if Mouse::Util::is_a_type_constraint($spec);
324 return $TYPE{$spec} || do{
325 my($pos, $type) = _parse_type($spec, 0);
330 sub find_or_create_does_type_constraint{
331 # XXX: Moose does not register a new role_type, but Mouse does.
332 return find_or_parse_type_constraint(@_) || role_type(@_);
335 sub find_or_create_isa_type_constraint {
336 # XXX: Moose does not register a new class_type, but Mouse does.
337 return find_or_parse_type_constraint(@_) || class_type(@_);
345 Mouse::Util::TypeConstraints - Type constraint system for Mouse
349 This document describes Mouse version 0.43
353 use Mouse::Util::TypeConstraints;
359 subtype 'NaturalLessThanTen'
362 => message { "This number ($_) is not less than ten!" };
368 enum 'RGBColors' => qw(red green blue);
370 no Mouse::Util::TypeConstraints;
374 This module provides Mouse with the ability to create custom type
375 constraints to be used in attribute definition.
377 =head2 Important Caveat
379 This is B<NOT> a type system for Perl 5. These are type constraints,
380 and they are not used by Mouse unless you tell it to. No type
381 inference is performed, expressions are not typed, etc. etc. etc.
383 A type constraint is at heart a small "check if a value is valid"
384 function. A constraint can be associated with an attribute. This
385 simplifies parameter validation, and makes your code clearer to read,
386 because you can refer to constraints by name.
388 =head2 Slightly Less Important Caveat
390 It is B<always> a good idea to quote your type names.
392 This prevents Perl from trying to execute the call as an indirect
393 object call. This can be an issue when you have a subtype with the
394 same name as a valid class.
398 subtype DateTime => as Object => where { $_->isa('DateTime') };
400 will I<just work>, while this:
403 subtype DateTime => as Object => where { $_->isa('DateTime') };
405 will fail silently and cause many headaches. The simple way to solve
406 this, as well as future proof your subtypes from classes which have
407 yet to have been created, is to quote the type name:
410 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
412 =head2 Default Type Constraints
414 This module also provides a simple hierarchy for Perl 5 types, here is
415 that hierarchy represented visually.
439 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
440 parameterized, this means you can say:
442 ArrayRef[Int] # an array of integers
443 HashRef[CodeRef] # a hash of str to CODE ref mappings
444 Maybe[Str] # value may be a string, may be undefined
446 If Mouse finds a name in brackets that it does not recognize as an
447 existing type, it assumes that this is a class name, for example
448 C<ArrayRef[DateTime]>.
450 B<NOTE:> Unless you parameterize a type, then it is invalid to include
451 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
452 name, I<not> as a parameterization of C<ArrayRef>.
454 B<NOTE:> The C<Undef> type constraint for the most part works
455 correctly now, but edge cases may still exist, please use it
458 B<NOTE:> The C<ClassName> type constraint does a complex package
459 existence check. This means that your class B<must> be loaded for this
460 type constraint to pass.
462 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
463 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
464 constraint checks that an I<object does> the named role.
466 =head2 Type Constraint Naming
468 Type name declared via this module can only contain alphanumeric
469 characters, colons (:), and periods (.).
471 Since the types created by this module are global, it is suggested
472 that you namespace your types just as you would namespace your
473 modules. So instead of creating a I<Color> type for your
474 B<My::Graphics> module, you would call the type
475 I<My::Graphics::Types::Color> instead.
477 =head2 Use with Other Constraint Modules
479 This module can play nicely with other constraint modules with some
480 slight tweaking. The C<where> clause in types is expected to be a
481 C<CODE> reference which checks it's first argument and returns a
482 boolean. Since most constraint modules work in a similar way, it
483 should be simple to adapt them to work with Mouse.
485 For instance, this is how you could use it with
486 L<Declare::Constraints::Simple> to declare a completely new type.
488 type 'HashOfArrayOfObjects',
492 -values => IsArrayRef(IsObject)
496 Here is an example of using L<Test::Deep> and it's non-test
497 related C<eq_deeply> function.
499 type 'ArrayOfHashOfBarsAndRandomNumbers'
502 array_each(subhashof({
504 random_number => ignore()
510 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
512 Returns the names of builtin type constraints.
514 =head2 C<< list_all_type_constraints -> (Names) >>
516 Returns the names of all the type constraints.
522 =item C<< subtype 'Name' => as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
524 =item C<< subtype as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
526 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
528 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
530 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
536 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
542 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
546 L<Moose::Util::TypeConstraints>