1 package Mouse::Util::TypeConstraints;
2 use Mouse::Util qw(does_role not_supported); # enables strict and warnings
5 use Scalar::Util qw/blessed looks_like_number openhandle/;
7 use Mouse::Meta::TypeConstraint;
10 Mouse::Exporter->setup_import_methods(
12 as where message from via
13 type subtype coerce class_type role_type enum
26 return(where => $_[0])
29 return(message => $_[0])
37 Any => undef, # null check
38 Item => undef, # null check
39 Maybe => undef, # null check
41 Bool => sub { $_[0] ? $_[0] eq '1' : 1 },
42 Undef => sub { !defined($_[0]) },
43 Defined => sub { defined($_[0]) },
44 Value => sub { defined($_[0]) && !ref($_[0]) },
45 Num => sub { !ref($_[0]) && looks_like_number($_[0]) },
46 Int => sub { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ },
47 Str => sub { defined($_[0]) && !ref($_[0]) },
48 Ref => sub { ref($_[0]) },
50 ScalarRef => sub { ref($_[0]) eq 'SCALAR' },
51 ArrayRef => sub { ref($_[0]) eq 'ARRAY' },
52 HashRef => sub { ref($_[0]) eq 'HASH' },
53 CodeRef => sub { ref($_[0]) eq 'CODE' },
54 RegexpRef => sub { ref($_[0]) eq 'Regexp' },
55 GlobRef => sub { ref($_[0]) eq 'GLOB' },
58 ref($_[0]) eq 'GLOB' && openhandle($_[0])
60 blessed($_[0]) && $_[0]->isa("IO::Handle")
63 Object => sub { blessed($_[0]) && blessed($_[0]) ne 'Regexp' },
65 ClassName => sub { Mouse::Util::is_class_loaded($_[0]) },
66 RoleName => sub { (Mouse::Util::find_meta($_[0]) || return 0)->isa('Mouse::Meta::Role') },
69 while (my ($name, $code) = each %builtins) {
70 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
76 sub optimized_constraints { # DEPRECATED
77 Carp::cluck('optimized_constraints() has been deprecated');
81 my @builtins = keys %TYPE;
82 sub list_all_builtin_type_constraints { @builtins }
84 sub list_all_type_constraints { keys %TYPE }
93 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
96 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
100 elsif(@_ % 2){ # @_ : $name => ( where => ... )
103 else{ # @_ : (name => $name, where => ...)
108 if(!defined($name = $args{name})){
115 if($mode eq 'subtype'){
116 $parent = delete $args{as};
118 $parent = delete $args{name};
123 my $package_defined_in = $args{package_defined_in} ||= caller(1);
125 my $existing = $TYPE{$name};
126 if($existing && $existing->{package_defined_in} ne $package_defined_in){
127 confess("The type constraint '$name' has already been created in "
128 . "$existing->{package_defined_in} and cannot be created again in $package_defined_in");
131 $args{constraint} = delete $args{where} if exists $args{where};
132 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
135 if($mode eq 'subtype'){
136 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
139 $constraint = Mouse::Meta::TypeConstraint->new(%args);
142 return $TYPE{$name} = $constraint;
146 return _create_type('type', @_);
150 return _create_type('subtype', @_);
154 my $type_name = shift;
156 my $type = find_type_constraint($type_name)
157 or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
159 $type->_add_type_coercions(@_);
164 my($name, $conf) = @_;
165 if ($conf && $conf->{class}) {
166 # No, you're using this wrong
167 warn "class_type() should be class_type(ClassName). Perhaps you're looking for subtype $name => as '$conf->{class}'?";
168 _create_type 'type', $name => (
169 as => $conf->{class},
175 _create_type 'type', $name => (
176 optimized_as => sub { blessed($_[0]) && $_[0]->isa($name) },
184 my($name, $conf) = @_;
185 my $role = ($conf && $conf->{role}) ? $conf->{role} : $name;
186 _create_type 'type', $name => (
187 optimized_as => sub { blessed($_[0]) && does_role($_[0], $role) },
193 sub typecast_constraints { # DEPRECATED
194 my($class, $pkg, $type, $value) = @_;
195 Carp::croak("wrong arguments count") unless @_ == 4;
197 Carp::cluck("typecast_constraints() has been deprecated, which was an internal utility anyway");
199 return $type->coerce($value);
205 # enum ['small', 'medium', 'large']
206 if (ref($_[0]) eq 'ARRAY') {
207 %valid = map{ $_ => undef } @{ $_[0] };
208 $name = sprintf '(%s)', join '|', sort @{$_[0]};
210 # enum size => 'small', 'medium', 'large'
213 %valid = map{ $_ => undef } @_;
215 return _create_type 'type', $name => (
216 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
222 sub _find_or_create_regular_type{
225 return $TYPE{$spec} if exists $TYPE{$spec};
227 my $meta = Mouse::Util::get_metaclass_by_name($spec);
235 if($meta->isa('Mouse::Meta::Role')){
237 return blessed($_[0]) && $_[0]->does($spec);
243 return blessed($_[0]) && $_[0]->isa($spec);
248 return $TYPE{$spec} = Mouse::Meta::TypeConstraint->new(
256 $TYPE{ArrayRef}{constraint_generator} = sub {
257 my($type_parameter) = @_;
258 my $check = $type_parameter->_compiled_type_constraint;
261 foreach my $value (@{$_}) {
262 return undef unless $check->($value);
267 $TYPE{HashRef}{constraint_generator} = sub {
268 my($type_parameter) = @_;
269 my $check = $type_parameter->_compiled_type_constraint;
272 foreach my $value(values %{$_}){
273 return undef unless $check->($value);
279 # 'Maybe' type accepts 'Any', so it requires parameters
280 $TYPE{Maybe}{constraint_generator} = sub {
281 my($type_parameter) = @_;
282 my $check = $type_parameter->_compiled_type_constraint;
285 return !defined($_) || $check->($_);
289 sub _find_or_create_parameterized_type{
290 my($base, $param) = @_;
292 my $name = sprintf '%s[%s]', $base->name, $param->name;
295 my $generator = $base->{constraint_generator};
298 confess("The $name constraint cannot be used, because $param doesn't subtype from a parameterizable type");
301 Mouse::Meta::TypeConstraint->new(
304 constraint => $generator->($param),
306 type => 'Parameterized',
310 sub _find_or_create_union_type{
311 my @types = sort{ $a cmp $b } map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
313 my $name = join '|', @types;
316 return Mouse::Meta::TypeConstraint->new(
318 type_constraints => \@types,
327 my($spec, $start) = @_;
332 my $len = length $spec;
335 for($i = $start; $i < $len; $i++){
336 my $char = substr($spec, $i, 1);
339 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
342 ($i, $subtype) = _parse_type($spec, $i+1)
344 $start = $i+1; # reset
346 push @list, _find_or_create_parameterized_type($base => $subtype);
353 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
356 # XXX: Mouse creates a new class type, but Moose does not.
357 $type = class_type( substr($spec, $start, $i - $start) );
362 ($i, $subtype) = _parse_type($spec, $i+1)
365 $start = $i+1; # reset
367 push @list, $subtype;
371 push @list, _find_or_create_regular_type(substr $spec, $start, $i - $start);
378 return ($len, $list[0]);
381 return ($len, _find_or_create_union_type(@list));
386 sub find_type_constraint {
388 return $spec if blessed($spec) && $spec->isa('Mouse::Meta::TypeConstraint');
394 sub find_or_parse_type_constraint {
396 return $spec if blessed($spec) && $spec->isa('Mouse::Meta::TypeConstraint');
399 return $TYPE{$spec} || do{
400 my($pos, $type) = _parse_type($spec, 0);
405 sub find_or_create_does_type_constraint{
406 my $type = find_or_parse_type_constraint(@_) || role_type(@_);
408 if($type->{type} && $type->{type} ne 'Role'){
409 Carp::cluck("$type is not a role type");
414 sub find_or_create_isa_type_constraint {
415 return find_or_parse_type_constraint(@_) || class_type(@_);
424 Mouse::Util::TypeConstraints - Type constraint system for Mouse
428 use Mouse::Util::TypeConstraints;
434 subtype 'NaturalLessThanTen'
437 => message { "This number ($_) is not less than ten!" };
443 enum 'RGBColors' => qw(red green blue);
445 no Mouse::Util::TypeConstraints;
449 This module provides Mouse with the ability to create custom type
450 constraints to be used in attribute definition.
452 =head2 Important Caveat
454 This is B<NOT> a type system for Perl 5. These are type constraints,
455 and they are not used by Mouse unless you tell it to. No type
456 inference is performed, expressions are not typed, etc. etc. etc.
458 A type constraint is at heart a small "check if a value is valid"
459 function. A constraint can be associated with an attribute. This
460 simplifies parameter validation, and makes your code clearer to read,
461 because you can refer to constraints by name.
463 =head2 Slightly Less Important Caveat
465 It is B<always> a good idea to quote your type names.
467 This prevents Perl from trying to execute the call as an indirect
468 object call. This can be an issue when you have a subtype with the
469 same name as a valid class.
473 subtype DateTime => as Object => where { $_->isa('DateTime') };
475 will I<just work>, while this:
478 subtype DateTime => as Object => where { $_->isa('DateTime') };
480 will fail silently and cause many headaches. The simple way to solve
481 this, as well as future proof your subtypes from classes which have
482 yet to have been created, is to quote the type name:
485 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
487 =head2 Default Type Constraints
489 This module also provides a simple hierarchy for Perl 5 types, here is
490 that hierarchy represented visually.
514 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
515 parameterized, this means you can say:
517 ArrayRef[Int] # an array of integers
518 HashRef[CodeRef] # a hash of str to CODE ref mappings
519 Maybe[Str] # value may be a string, may be undefined
521 If Mouse finds a name in brackets that it does not recognize as an
522 existing type, it assumes that this is a class name, for example
523 C<ArrayRef[DateTime]>.
525 B<NOTE:> Unless you parameterize a type, then it is invalid to include
526 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
527 name, I<not> as a parameterization of C<ArrayRef>.
529 B<NOTE:> The C<Undef> type constraint for the most part works
530 correctly now, but edge cases may still exist, please use it
533 B<NOTE:> The C<ClassName> type constraint does a complex package
534 existence check. This means that your class B<must> be loaded for this
535 type constraint to pass.
537 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
538 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
539 constraint checks that an I<object does> the named role.
541 =head2 Type Constraint Naming
543 Type name declared via this module can only contain alphanumeric
544 characters, colons (:), and periods (.).
546 Since the types created by this module are global, it is suggested
547 that you namespace your types just as you would namespace your
548 modules. So instead of creating a I<Color> type for your
549 B<My::Graphics> module, you would call the type
550 I<My::Graphics::Types::Color> instead.
552 =head2 Use with Other Constraint Modules
554 This module can play nicely with other constraint modules with some
555 slight tweaking. The C<where> clause in types is expected to be a
556 C<CODE> reference which checks it's first argument and returns a
557 boolean. Since most constraint modules work in a similar way, it
558 should be simple to adapt them to work with Mouse.
560 For instance, this is how you could use it with
561 L<Declare::Constraints::Simple> to declare a completely new type.
563 type 'HashOfArrayOfObjects',
567 -values => IsArrayRef(IsObject)
571 Here is an example of using L<Test::Deep> and it's non-test
572 related C<eq_deeply> function.
574 type 'ArrayOfHashOfBarsAndRandomNumbers'
577 array_each(subhashof({
579 random_number => ignore()
585 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
587 Returns the names of builtin type constraints.
589 =head2 C<< list_all_type_constraints -> (Names) >>
591 Returns the names of all the type constraints.
597 =item C<< subtype 'Name' => as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
599 =item C<< subtype as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
601 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
603 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
605 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
611 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
617 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
621 L<Moose::Util::TypeConstraints>