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(@_);
349 Mouse::Util::TypeConstraints - Type constraint system for Mouse
353 This document describes Mouse version 0.42
357 use Mouse::Util::TypeConstraints;
363 subtype 'NaturalLessThanTen'
366 => message { "This number ($_) is not less than ten!" };
372 enum 'RGBColors' => qw(red green blue);
374 no Mouse::Util::TypeConstraints;
378 This module provides Mouse with the ability to create custom type
379 constraints to be used in attribute definition.
381 =head2 Important Caveat
383 This is B<NOT> a type system for Perl 5. These are type constraints,
384 and they are not used by Mouse unless you tell it to. No type
385 inference is performed, expressions are not typed, etc. etc. etc.
387 A type constraint is at heart a small "check if a value is valid"
388 function. A constraint can be associated with an attribute. This
389 simplifies parameter validation, and makes your code clearer to read,
390 because you can refer to constraints by name.
392 =head2 Slightly Less Important Caveat
394 It is B<always> a good idea to quote your type names.
396 This prevents Perl from trying to execute the call as an indirect
397 object call. This can be an issue when you have a subtype with the
398 same name as a valid class.
402 subtype DateTime => as Object => where { $_->isa('DateTime') };
404 will I<just work>, while this:
407 subtype DateTime => as Object => where { $_->isa('DateTime') };
409 will fail silently and cause many headaches. The simple way to solve
410 this, as well as future proof your subtypes from classes which have
411 yet to have been created, is to quote the type name:
414 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
416 =head2 Default Type Constraints
418 This module also provides a simple hierarchy for Perl 5 types, here is
419 that hierarchy represented visually.
443 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
444 parameterized, this means you can say:
446 ArrayRef[Int] # an array of integers
447 HashRef[CodeRef] # a hash of str to CODE ref mappings
448 Maybe[Str] # value may be a string, may be undefined
450 If Mouse finds a name in brackets that it does not recognize as an
451 existing type, it assumes that this is a class name, for example
452 C<ArrayRef[DateTime]>.
454 B<NOTE:> Unless you parameterize a type, then it is invalid to include
455 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
456 name, I<not> as a parameterization of C<ArrayRef>.
458 B<NOTE:> The C<Undef> type constraint for the most part works
459 correctly now, but edge cases may still exist, please use it
462 B<NOTE:> The C<ClassName> type constraint does a complex package
463 existence check. This means that your class B<must> be loaded for this
464 type constraint to pass.
466 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
467 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
468 constraint checks that an I<object does> the named role.
470 =head2 Type Constraint Naming
472 Type name declared via this module can only contain alphanumeric
473 characters, colons (:), and periods (.).
475 Since the types created by this module are global, it is suggested
476 that you namespace your types just as you would namespace your
477 modules. So instead of creating a I<Color> type for your
478 B<My::Graphics> module, you would call the type
479 I<My::Graphics::Types::Color> instead.
481 =head2 Use with Other Constraint Modules
483 This module can play nicely with other constraint modules with some
484 slight tweaking. The C<where> clause in types is expected to be a
485 C<CODE> reference which checks it's first argument and returns a
486 boolean. Since most constraint modules work in a similar way, it
487 should be simple to adapt them to work with Mouse.
489 For instance, this is how you could use it with
490 L<Declare::Constraints::Simple> to declare a completely new type.
492 type 'HashOfArrayOfObjects',
496 -values => IsArrayRef(IsObject)
500 Here is an example of using L<Test::Deep> and it's non-test
501 related C<eq_deeply> function.
503 type 'ArrayOfHashOfBarsAndRandomNumbers'
506 array_each(subhashof({
508 random_number => ignore()
514 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
516 Returns the names of builtin type constraints.
518 =head2 C<< list_all_type_constraints -> (Names) >>
520 Returns the names of all the type constraints.
526 =item C<< subtype 'Name' => as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
528 =item C<< subtype as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
530 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
532 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
534 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
540 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
546 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
550 L<Moose::Util::TypeConstraints>