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 }
79 _generate_class_type_for('Mouse::Meta::TypeConstraint' => '_is_a_type_constraint');
80 _generate_class_type_for('Mouse::Meta::Class' => '_is_a_metaclass');
81 _generate_class_type_for('Mouse::Meta::Role' => '_is_a_metarole');
91 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
94 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
98 elsif(@_ % 2){ # @_ : $name => ( where => ... )
101 else{ # @_ : (name => $name, where => ...)
106 if(!defined($name = $args{name})){
113 if($mode eq 'subtype'){
114 $parent = delete $args{as};
116 $parent = delete $args{name};
121 my $package_defined_in = $args{package_defined_in} ||= caller(1);
123 my $existing = $TYPE{$name};
124 if($existing && $existing->{package_defined_in} ne $package_defined_in){
125 confess("The type constraint '$name' has already been created in "
126 . "$existing->{package_defined_in} and cannot be created again in $package_defined_in");
129 $args{constraint} = delete $args{where} if exists $args{where};
130 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
133 if($mode eq 'subtype'){
134 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
137 $constraint = Mouse::Meta::TypeConstraint->new(%args);
140 return $TYPE{$name} = $constraint;
144 return _create_type('type', @_);
148 return _create_type('subtype', @_);
152 my $type_name = shift;
154 my $type = find_type_constraint($type_name)
155 or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
157 $type->_add_type_coercions(@_);
162 my($name, $options) = @_;
163 my $class = $options->{class} || $name;
164 return _create_type 'subtype', $name => (
166 optimized_as => _generate_class_type_for($class),
173 my($name, $options) = @_;
174 my $role = $options->{role} || $name;
175 return _create_type 'subtype', $name => (
177 optimized_as => sub { Scalar::Util::blessed($_[0]) && does_role($_[0], $role) },
183 sub typecast_constraints { # DEPRECATED
184 my($class, $pkg, $type, $value) = @_;
185 Carp::croak("wrong arguments count") unless @_ == 4;
187 Carp::cluck("typecast_constraints() has been deprecated, which was an internal utility anyway");
189 return $type->coerce($value);
195 # enum ['small', 'medium', 'large']
196 if (ref($_[0]) eq 'ARRAY') {
197 %valid = map{ $_ => undef } @{ $_[0] };
198 $name = sprintf '(%s)', join '|', sort @{$_[0]};
200 # enum size => 'small', 'medium', 'large'
203 %valid = map{ $_ => undef } @_;
205 return _create_type 'type', $name => (
206 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
212 sub _find_or_create_regular_type{
215 return $TYPE{$spec} if exists $TYPE{$spec};
217 my $meta = Mouse::Util::get_metaclass_by_name($spec)
220 if(_is_a_metarole($meta)){
221 return role_type($spec);
224 return class_type($spec);
228 $TYPE{ArrayRef}{constraint_generator} = sub {
229 my($type_parameter) = @_;
230 my $check = $type_parameter->_compiled_type_constraint;
233 foreach my $value (@{$_}) {
234 return undef unless $check->($value);
239 $TYPE{HashRef}{constraint_generator} = sub {
240 my($type_parameter) = @_;
241 my $check = $type_parameter->_compiled_type_constraint;
244 foreach my $value(values %{$_}){
245 return undef unless $check->($value);
251 # 'Maybe' type accepts 'Any', so it requires parameters
252 $TYPE{Maybe}{constraint_generator} = sub {
253 my($type_parameter) = @_;
254 my $check = $type_parameter->_compiled_type_constraint;
257 return !defined($_) || $check->($_);
261 sub _find_or_create_parameterized_type{
262 my($base, $param) = @_;
264 my $name = sprintf '%s[%s]', $base->name, $param->name;
266 $TYPE{$name} ||= $base->parameterize($param, $name);
269 sub _find_or_create_union_type{
270 my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
272 my $name = join '|', @types;
274 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
276 type_constraints => \@types,
284 my($spec, $start) = @_;
289 my $len = length $spec;
292 for($i = $start; $i < $len; $i++){
293 my $char = substr($spec, $i, 1);
296 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
299 ($i, $subtype) = _parse_type($spec, $i+1)
301 $start = $i+1; # reset
303 push @list, _find_or_create_parameterized_type($base => $subtype);
310 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
313 # XXX: Mouse creates a new class type, but Moose does not.
314 $type = class_type( substr($spec, $start, $i - $start) );
319 ($i, $subtype) = _parse_type($spec, $i+1)
322 $start = $i+1; # reset
324 push @list, $subtype;
328 my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
335 # create a new class type
336 push @list, class_type( substr $spec, $start, $i - $start );
344 return ($len, $list[0]);
347 return ($len, _find_or_create_union_type(@list));
352 sub find_type_constraint {
354 return $spec if _is_a_type_constraint($spec);
360 sub find_or_parse_type_constraint {
362 return $spec if _is_a_type_constraint($spec);
365 return $TYPE{$spec} || do{
366 my($pos, $type) = _parse_type($spec, 0);
371 sub find_or_create_does_type_constraint{
372 # XXX: Moose does not register a new role_type, but Mouse does.
373 return find_or_parse_type_constraint(@_) || role_type(@_);
376 sub find_or_create_isa_type_constraint {
377 # XXX: Moose does not register a new class_type, but Mouse does.
378 return find_or_parse_type_constraint(@_) || class_type(@_);
387 Mouse::Util::TypeConstraints - Type constraint system for Mouse
391 This document describes Mouse version 0.40_03
395 use Mouse::Util::TypeConstraints;
401 subtype 'NaturalLessThanTen'
404 => message { "This number ($_) is not less than ten!" };
410 enum 'RGBColors' => qw(red green blue);
412 no Mouse::Util::TypeConstraints;
416 This module provides Mouse with the ability to create custom type
417 constraints to be used in attribute definition.
419 =head2 Important Caveat
421 This is B<NOT> a type system for Perl 5. These are type constraints,
422 and they are not used by Mouse unless you tell it to. No type
423 inference is performed, expressions are not typed, etc. etc. etc.
425 A type constraint is at heart a small "check if a value is valid"
426 function. A constraint can be associated with an attribute. This
427 simplifies parameter validation, and makes your code clearer to read,
428 because you can refer to constraints by name.
430 =head2 Slightly Less Important Caveat
432 It is B<always> a good idea to quote your type names.
434 This prevents Perl from trying to execute the call as an indirect
435 object call. This can be an issue when you have a subtype with the
436 same name as a valid class.
440 subtype DateTime => as Object => where { $_->isa('DateTime') };
442 will I<just work>, while this:
445 subtype DateTime => as Object => where { $_->isa('DateTime') };
447 will fail silently and cause many headaches. The simple way to solve
448 this, as well as future proof your subtypes from classes which have
449 yet to have been created, is to quote the type name:
452 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
454 =head2 Default Type Constraints
456 This module also provides a simple hierarchy for Perl 5 types, here is
457 that hierarchy represented visually.
481 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
482 parameterized, this means you can say:
484 ArrayRef[Int] # an array of integers
485 HashRef[CodeRef] # a hash of str to CODE ref mappings
486 Maybe[Str] # value may be a string, may be undefined
488 If Mouse finds a name in brackets that it does not recognize as an
489 existing type, it assumes that this is a class name, for example
490 C<ArrayRef[DateTime]>.
492 B<NOTE:> Unless you parameterize a type, then it is invalid to include
493 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
494 name, I<not> as a parameterization of C<ArrayRef>.
496 B<NOTE:> The C<Undef> type constraint for the most part works
497 correctly now, but edge cases may still exist, please use it
500 B<NOTE:> The C<ClassName> type constraint does a complex package
501 existence check. This means that your class B<must> be loaded for this
502 type constraint to pass.
504 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
505 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
506 constraint checks that an I<object does> the named role.
508 =head2 Type Constraint Naming
510 Type name declared via this module can only contain alphanumeric
511 characters, colons (:), and periods (.).
513 Since the types created by this module are global, it is suggested
514 that you namespace your types just as you would namespace your
515 modules. So instead of creating a I<Color> type for your
516 B<My::Graphics> module, you would call the type
517 I<My::Graphics::Types::Color> instead.
519 =head2 Use with Other Constraint Modules
521 This module can play nicely with other constraint modules with some
522 slight tweaking. The C<where> clause in types is expected to be a
523 C<CODE> reference which checks it's first argument and returns a
524 boolean. Since most constraint modules work in a similar way, it
525 should be simple to adapt them to work with Mouse.
527 For instance, this is how you could use it with
528 L<Declare::Constraints::Simple> to declare a completely new type.
530 type 'HashOfArrayOfObjects',
534 -values => IsArrayRef(IsObject)
538 Here is an example of using L<Test::Deep> and it's non-test
539 related C<eq_deeply> function.
541 type 'ArrayOfHashOfBarsAndRandomNumbers'
544 array_each(subhashof({
546 random_number => ignore()
552 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
554 Returns the names of builtin type constraints.
556 =head2 C<< list_all_type_constraints -> (Names) >>
558 Returns the names of all the type constraints.
564 =item C<< subtype 'Name' => as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
566 =item C<< subtype as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
568 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
570 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
572 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
578 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
584 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
588 L<Moose::Util::TypeConstraints>