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} = \&_parameterize_ArrayRef_for;
221 $TYPE{HashRef}{constraint_generator} = \&_parameterize_HashRef_for;
222 $TYPE{Maybe}{constraint_generator} = \&_parameterize_Maybe_for;
224 sub _find_or_create_parameterized_type{
225 my($base, $param) = @_;
227 my $name = sprintf '%s[%s]', $base->name, $param->name;
229 $TYPE{$name} ||= $base->parameterize($param, $name);
232 sub _find_or_create_union_type{
233 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
235 my $name = join '|', @types;
237 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
239 type_constraints => \@types,
247 my($spec, $start) = @_;
252 my $len = length $spec;
255 for($i = $start; $i < $len; $i++){
256 my $char = substr($spec, $i, 1);
259 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
262 ($i, $subtype) = _parse_type($spec, $i+1)
264 $start = $i+1; # reset
266 push @list, _find_or_create_parameterized_type($base => $subtype);
273 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
276 # XXX: Mouse creates a new class type, but Moose does not.
277 $type = class_type( substr($spec, $start, $i - $start) );
282 ($i, $subtype) = _parse_type($spec, $i+1)
285 $start = $i+1; # reset
287 push @list, $subtype;
291 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
298 # create a new class type
299 push @list, class_type( substr $spec, $start, $i - $start );
307 return ($len, $list[0]);
310 return ($len, _find_or_create_union_type(@list));
315 sub find_type_constraint {
317 return $spec if Mouse::Util::is_a_type_constraint($spec);
323 sub find_or_parse_type_constraint {
325 return $spec if Mouse::Util::is_a_type_constraint($spec);
328 return $TYPE{$spec} || do{
329 my($pos, $type) = _parse_type($spec, 0);
334 sub find_or_create_does_type_constraint{
335 # XXX: Moose does not register a new role_type, but Mouse does.
336 return find_or_parse_type_constraint(@_) || role_type(@_);
339 sub find_or_create_isa_type_constraint {
340 # XXX: Moose does not register a new class_type, but Mouse does.
341 return find_or_parse_type_constraint(@_) || class_type(@_);
350 Mouse::Util::TypeConstraints - Type constraint system for Mouse
354 This document describes Mouse version 0.40_06
358 use Mouse::Util::TypeConstraints;
364 subtype 'NaturalLessThanTen'
367 => message { "This number ($_) is not less than ten!" };
373 enum 'RGBColors' => qw(red green blue);
375 no Mouse::Util::TypeConstraints;
379 This module provides Mouse with the ability to create custom type
380 constraints to be used in attribute definition.
382 =head2 Important Caveat
384 This is B<NOT> a type system for Perl 5. These are type constraints,
385 and they are not used by Mouse unless you tell it to. No type
386 inference is performed, expressions are not typed, etc. etc. etc.
388 A type constraint is at heart a small "check if a value is valid"
389 function. A constraint can be associated with an attribute. This
390 simplifies parameter validation, and makes your code clearer to read,
391 because you can refer to constraints by name.
393 =head2 Slightly Less Important Caveat
395 It is B<always> a good idea to quote your type names.
397 This prevents Perl from trying to execute the call as an indirect
398 object call. This can be an issue when you have a subtype with the
399 same name as a valid class.
403 subtype DateTime => as Object => where { $_->isa('DateTime') };
405 will I<just work>, while this:
408 subtype DateTime => as Object => where { $_->isa('DateTime') };
410 will fail silently and cause many headaches. The simple way to solve
411 this, as well as future proof your subtypes from classes which have
412 yet to have been created, is to quote the type name:
415 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
417 =head2 Default Type Constraints
419 This module also provides a simple hierarchy for Perl 5 types, here is
420 that hierarchy represented visually.
444 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
445 parameterized, this means you can say:
447 ArrayRef[Int] # an array of integers
448 HashRef[CodeRef] # a hash of str to CODE ref mappings
449 Maybe[Str] # value may be a string, may be undefined
451 If Mouse finds a name in brackets that it does not recognize as an
452 existing type, it assumes that this is a class name, for example
453 C<ArrayRef[DateTime]>.
455 B<NOTE:> Unless you parameterize a type, then it is invalid to include
456 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
457 name, I<not> as a parameterization of C<ArrayRef>.
459 B<NOTE:> The C<Undef> type constraint for the most part works
460 correctly now, but edge cases may still exist, please use it
463 B<NOTE:> The C<ClassName> type constraint does a complex package
464 existence check. This means that your class B<must> be loaded for this
465 type constraint to pass.
467 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
468 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
469 constraint checks that an I<object does> the named role.
471 =head2 Type Constraint Naming
473 Type name declared via this module can only contain alphanumeric
474 characters, colons (:), and periods (.).
476 Since the types created by this module are global, it is suggested
477 that you namespace your types just as you would namespace your
478 modules. So instead of creating a I<Color> type for your
479 B<My::Graphics> module, you would call the type
480 I<My::Graphics::Types::Color> instead.
482 =head2 Use with Other Constraint Modules
484 This module can play nicely with other constraint modules with some
485 slight tweaking. The C<where> clause in types is expected to be a
486 C<CODE> reference which checks it's first argument and returns a
487 boolean. Since most constraint modules work in a similar way, it
488 should be simple to adapt them to work with Mouse.
490 For instance, this is how you could use it with
491 L<Declare::Constraints::Simple> to declare a completely new type.
493 type 'HashOfArrayOfObjects',
497 -values => IsArrayRef(IsObject)
501 Here is an example of using L<Test::Deep> and it's non-test
502 related C<eq_deeply> function.
504 type 'ArrayOfHashOfBarsAndRandomNumbers'
507 array_each(subhashof({
509 random_number => ignore()
515 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
517 Returns the names of builtin type constraints.
519 =head2 C<< list_all_type_constraints -> (Names) >>
521 Returns the names of all the type constraints.
527 =item C<< subtype 'Name' => as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
529 =item C<< subtype as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
531 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
533 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
535 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
541 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
547 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
551 L<Moose::Util::TypeConstraints>