1 package Mouse::Util::TypeConstraints;
8 use Scalar::Util qw/blessed looks_like_number openhandle/;
10 use Mouse::Util qw(does_role not_supported);
11 use Mouse::Meta::Module; # class_of
12 use Mouse::Meta::TypeConstraint;
14 use constant _DEBUG => !!$ENV{TC_DEBUG};
16 our @ISA = qw(Exporter);
18 as where message from via type subtype coerce class_type role_type enum
30 return(where => $_[0])
33 return(message => $_[0])
41 Any => undef, # null check
42 Item => undef, # null check
43 Maybe => undef, # null check
45 Bool => sub { $_[0] ? $_[0] eq '1' : 1 },
46 Undef => sub { !defined($_[0]) },
47 Defined => sub { defined($_[0]) },
48 Value => sub { defined($_[0]) && !ref($_[0]) },
49 Num => sub { !ref($_[0]) && looks_like_number($_[0]) },
50 Int => sub { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ },
51 Str => sub { defined($_[0]) && !ref($_[0]) },
52 Ref => sub { ref($_[0]) },
54 ScalarRef => sub { ref($_[0]) eq 'SCALAR' },
55 ArrayRef => sub { ref($_[0]) eq 'ARRAY' },
56 HashRef => sub { ref($_[0]) eq 'HASH' },
57 CodeRef => sub { ref($_[0]) eq 'CODE' },
58 RegexpRef => sub { ref($_[0]) eq 'Regexp' },
59 GlobRef => sub { ref($_[0]) eq 'GLOB' },
62 ref($_[0]) eq 'GLOB' && openhandle($_[0])
64 blessed($_[0]) && $_[0]->isa("IO::Handle")
67 Object => sub { blessed($_[0]) && blessed($_[0]) ne 'Regexp' },
69 ClassName => sub { Mouse::Util::is_class_loaded($_[0]) },
70 RoleName => sub { (Mouse::Util::find_meta($_[0]) || return 0)->isa('Mouse::Meta::Role') },
73 while (my ($name, $code) = each %builtins) {
74 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
80 sub optimized_constraints {
81 Carp::cluck('optimized_constraints() has been deprecated');
85 my @builtins = keys %TYPE;
86 sub list_all_builtin_type_constraints { @builtins }
88 sub list_all_type_constraints { keys %TYPE }
97 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
100 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
104 elsif(@_ % 2){ # @_ : $name => ( where => ... )
107 else{ # @_ : (name => $name, where => ...)
112 if(!defined($name = $args{name})){
119 my $package_defined_in = $args{package_defined_in} ||= caller(1);
121 my $existing = $TYPE{$name};
122 if($existing && $existing->{package_defined_in} ne $package_defined_in){
123 confess("The type constraint '$name' has already been created in "
124 . "$existing->{package_defined_in} and cannot be created again in $package_defined_in");
127 $args{constraint} = delete($args{where}) if exists $args{where};
128 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
131 if($mode eq 'subtype'){
132 my $parent = delete($args{as})
133 or confess('A subtype cannot consist solely of a name, it must have a parent');
135 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
138 $constraint = Mouse::Meta::TypeConstraint->new(%args);
141 return $TYPE{$name} = $constraint;
145 return _create_type('type', @_);
149 return _create_type('subtype', @_);
156 confess "Cannot find type '$name', perhaps you forgot to load it."
159 unless ($COERCE{$name}) {
161 $COERCE_KEYS{$name} = [];
164 my $package_defined_in = caller;
166 while (my($from, $action) = splice @_, 0, 2) {
169 confess "A coercion action already exists for '$from'"
170 if $COERCE{$name}->{$from};
172 my $type = find_or_parse_type_constraint($from, $package_defined_in);
174 confess "Could not find the type constraint ($from) to coerce from"
177 warn "# REGISTER COERCE $name, from $type\n" if _DEBUG;
179 push @{ $COERCE_KEYS{$name} }, $type;
180 $COERCE{$name}->{$from} = $action;
186 my($name, $conf) = @_;
187 if ($conf && $conf->{class}) {
188 # No, you're using this wrong
189 warn "class_type() should be class_type(ClassName). Perhaps you're looking for subtype $name => as '$conf->{class}'?";
190 _create_type 'type', $name => (
191 as => $conf->{class},
197 _create_type 'type', $name => (
198 optimized_as => sub { blessed($_[0]) && $_[0]->isa($name) },
206 my($name, $conf) = @_;
207 my $role = ($conf && $conf->{role}) ? $conf->{role} : $name;
208 _create_type 'type', $name => (
209 optimized_as => sub { blessed($_[0]) && does_role($_[0], $role) },
215 # this is an original method for Mouse
216 sub typecast_constraints {
217 my($class, $pkg, $types, $value) = @_;
218 Carp::croak("wrong arguments count") unless @_ == 4;
221 for my $type ($types->{type_constraints} ? @{$types->{type_constraints}} : $types ) {
222 for my $coerce_type (@{ $COERCE_KEYS{$type}}) {
225 warn sprintf "# COERCE: from %s to %s for %s (%s)\n",
226 $coerce_type, $type, defined($value) ? "'$value'" : 'undef',
227 $coerce_type->check($value) ? "try" : "skip";
230 next if !$coerce_type->check($value);
234 my $coerced = $COERCE{$type}->{$coerce_type}->($value); # coerce
237 warn sprintf "# COERCE: got %s, which is%s %s\n",
238 defined($coerced) ? $coerced : 'undef', $types->check($coerced) ? '' : ' not', $types;
241 # check with $types, not $constraint
242 return $coerced if $types->check($coerced);
245 return $value; # returns original $value
251 # enum ['small', 'medium', 'large']
252 if (ref($_[0]) eq 'ARRAY') {
253 %valid = map{ $_ => undef } @{ $_[0] };
254 $name = sprintf '(%s)', join '|', sort @{$_[0]};
256 # enum size => 'small', 'medium', 'large'
259 %valid = map{ $_ => undef } @_;
261 return _create_type 'type', $name => (
262 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
268 sub _find_or_create_regular_type{
271 return $TYPE{$spec} if exists $TYPE{$spec};
273 my $meta = Mouse::Meta::Module::class_of($spec);
281 if($meta->isa('Mouse::Meta::Role')){
283 return blessed($_[0]) && $_[0]->does($spec);
289 return blessed($_[0]) && $_[0]->isa($spec);
294 warn "#CREATE a $type type for $spec\n" if _DEBUG;
296 return $TYPE{$spec} = Mouse::Meta::TypeConstraint->new(
304 $TYPE{ArrayRef}{constraint_generator} = sub {
305 my($type_parameter) = @_;
306 my $check = $type_parameter->_compiled_type_constraint;
309 foreach my $value (@{$_}) {
310 return undef unless $check->($value);
315 $TYPE{HashRef}{constraint_generator} = sub {
316 my($type_parameter) = @_;
317 my $check = $type_parameter->_compiled_type_constraint;
320 foreach my $value(values %{$_}){
321 return undef unless $check->($value);
327 # 'Maybe' type accepts 'Any', so it requires parameters
328 $TYPE{Maybe}{constraint_generator} = sub {
329 my($type_parameter) = @_;
330 my $check = $type_parameter->_compiled_type_constraint;
333 return !defined($_) || $check->($_);
337 sub _find_or_create_parameterized_type{
338 my($base, $param) = @_;
340 my $name = sprintf '%s[%s]', $base->name, $param->name;
343 warn "#CREATE a Parameterized type for $name\n" if _DEBUG;
345 my $generator = $base->{constraint_generator};
348 confess("The $name constraint cannot be used, because $param doesn't subtype from a parameterizable type");
351 Mouse::Meta::TypeConstraint->new(
354 constraint => $generator->($param),
356 type => 'Parameterized',
360 sub _find_or_create_union_type{
361 my @types = map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
363 my $name = join '|', map{ $_->name } @types;
366 warn "# CREATE a Union type for ", Mouse::Util::english_list(@types),"\n" if _DEBUG;
368 return Mouse::Meta::TypeConstraint->new(
370 type_constraints => \@types,
379 my($spec, $start) = @_;
384 my $len = length $spec;
387 for($i = $start; $i < $len; $i++){
388 my $char = substr($spec, $i, 1);
391 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
394 ($i, $subtype) = _parse_type($spec, $i+1)
396 $start = $i+1; # reset
398 push @list, _find_or_create_parameterized_type($base => $subtype);
405 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
408 # XXX: Mouse creates a new class type, but Moose does not.
409 $type = class_type( substr($spec, $start, $i - $start) );
414 ($i, $subtype) = _parse_type($spec, $i+1)
417 $start = $i+1; # reset
419 push @list, $subtype;
423 push @list, _find_or_create_regular_type(substr $spec, $start, $i - $start);
430 return ($len, $list[0]);
433 return ($len, _find_or_create_union_type(@list));
438 sub find_type_constraint {
440 return $spec if blessed($spec) && $spec->isa('Mouse::Meta::TypeConstraint');
446 sub find_or_parse_type_constraint {
448 return $spec if blessed($spec) && $spec->isa('Mouse::Meta::TypeConstraint');
451 return $TYPE{$spec} || do{
452 my($pos, $type) = _parse_type($spec, 0);
457 sub find_or_create_does_type_constraint{
458 my $type = find_or_parse_type_constriant(@_) || role_type(@_);
460 if($type->{type} && $type->{type} ne 'Role'){
461 Carp::cluck("$type is not a role type");
466 sub find_or_create_isa_type_constraint {
467 return find_or_parse_type_constraint(@_) || class_type(@_);
476 Mouse::Util::TypeConstraints - Type constraint system for Mouse
480 use Mouse::Util::TypeConstraints;
486 subtype 'NaturalLessThanTen'
489 => message { "This number ($_) is not less than ten!" };
495 enum 'RGBColors' => qw(red green blue);
497 no Mouse::Util::TypeConstraints;
501 This module provides Mouse with the ability to create custom type
502 constraints to be used in attribute definition.
504 =head2 Important Caveat
506 This is B<NOT> a type system for Perl 5. These are type constraints,
507 and they are not used by Mouse unless you tell it to. No type
508 inference is performed, expressions are not typed, etc. etc. etc.
510 A type constraint is at heart a small "check if a value is valid"
511 function. A constraint can be associated with an attribute. This
512 simplifies parameter validation, and makes your code clearer to read,
513 because you can refer to constraints by name.
515 =head2 Slightly Less Important Caveat
517 It is B<always> a good idea to quote your type names.
519 This prevents Perl from trying to execute the call as an indirect
520 object call. This can be an issue when you have a subtype with the
521 same name as a valid class.
525 subtype DateTime => as Object => where { $_->isa('DateTime') };
527 will I<just work>, while this:
530 subtype DateTime => as Object => where { $_->isa('DateTime') };
532 will fail silently and cause many headaches. The simple way to solve
533 this, as well as future proof your subtypes from classes which have
534 yet to have been created, is to quote the type name:
537 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
539 =head2 Default Type Constraints
541 This module also provides a simple hierarchy for Perl 5 types, here is
542 that hierarchy represented visually.
566 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
567 parameterized, this means you can say:
569 ArrayRef[Int] # an array of integers
570 HashRef[CodeRef] # a hash of str to CODE ref mappings
571 Maybe[Str] # value may be a string, may be undefined
573 If Mouse finds a name in brackets that it does not recognize as an
574 existing type, it assumes that this is a class name, for example
575 C<ArrayRef[DateTime]>.
577 B<NOTE:> Unless you parameterize a type, then it is invalid to include
578 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
579 name, I<not> as a parameterization of C<ArrayRef>.
581 B<NOTE:> The C<Undef> type constraint for the most part works
582 correctly now, but edge cases may still exist, please use it
585 B<NOTE:> The C<ClassName> type constraint does a complex package
586 existence check. This means that your class B<must> be loaded for this
587 type constraint to pass.
589 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
590 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
591 constraint checks that an I<object does> the named role.
593 =head2 Type Constraint Naming
595 Type name declared via this module can only contain alphanumeric
596 characters, colons (:), and periods (.).
598 Since the types created by this module are global, it is suggested
599 that you namespace your types just as you would namespace your
600 modules. So instead of creating a I<Color> type for your
601 B<My::Graphics> module, you would call the type
602 I<My::Graphics::Types::Color> instead.
604 =head2 Use with Other Constraint Modules
606 This module can play nicely with other constraint modules with some
607 slight tweaking. The C<where> clause in types is expected to be a
608 C<CODE> reference which checks it's first argument and returns a
609 boolean. Since most constraint modules work in a similar way, it
610 should be simple to adapt them to work with Mouse.
612 For instance, this is how you could use it with
613 L<Declare::Constraints::Simple> to declare a completely new type.
615 type 'HashOfArrayOfObjects',
619 -values => IsArrayRef(IsObject)
623 Here is an example of using L<Test::Deep> and it's non-test
624 related C<eq_deeply> function.
626 type 'ArrayOfHashOfBarsAndRandomNumbers'
629 array_each(subhashof({
631 random_number => ignore()
637 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
639 Returns the names of builtin type constraints.
641 =head2 C<< list_all_type_constraints -> (Names) >>
643 Returns the names of all the type constraints.
649 =item C<< subtype 'Name' => as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
651 =item C<< subtype as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
653 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
655 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
657 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
663 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
669 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
673 L<Moose::Util::TypeConstraints>