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::TypeConstraint;
13 our @ISA = qw(Exporter);
15 as where message from via type subtype coerce class_type role_type enum
25 return(where => $_[0])
28 return(message => $_[0])
36 Any => undef, # null check
37 Item => undef, # null check
38 Maybe => undef, # null check
40 Bool => sub { $_[0] ? $_[0] eq '1' : 1 },
41 Undef => sub { !defined($_[0]) },
42 Defined => sub { defined($_[0]) },
43 Value => sub { defined($_[0]) && !ref($_[0]) },
44 Num => sub { !ref($_[0]) && looks_like_number($_[0]) },
45 Int => sub { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ },
46 Str => sub { defined($_[0]) && !ref($_[0]) },
47 Ref => sub { ref($_[0]) },
49 ScalarRef => sub { ref($_[0]) eq 'SCALAR' },
50 ArrayRef => sub { ref($_[0]) eq 'ARRAY' },
51 HashRef => sub { ref($_[0]) eq 'HASH' },
52 CodeRef => sub { ref($_[0]) eq 'CODE' },
53 RegexpRef => sub { ref($_[0]) eq 'Regexp' },
54 GlobRef => sub { ref($_[0]) eq 'GLOB' },
57 ref($_[0]) eq 'GLOB' && openhandle($_[0])
59 blessed($_[0]) && $_[0]->isa("IO::Handle")
62 Object => sub { blessed($_[0]) && blessed($_[0]) ne 'Regexp' },
64 ClassName => sub { Mouse::Util::is_class_loaded($_[0]) },
65 RoleName => sub { (Mouse::Util::find_meta($_[0]) || return 0)->isa('Mouse::Meta::Role') },
68 while (my ($name, $code) = each %builtins) {
69 $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
75 sub optimized_constraints { # DEPRECATED
76 Carp::cluck('optimized_constraints() has been deprecated');
80 my @builtins = keys %TYPE;
81 sub list_all_builtin_type_constraints { @builtins }
83 sub list_all_type_constraints { keys %TYPE }
92 if(@_ == 1 && ref $_[0]){ # @_ : { name => $name, where => ... }
95 elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
99 elsif(@_ % 2){ # @_ : $name => ( where => ... )
102 else{ # @_ : (name => $name, where => ...)
107 if(!defined($name = $args{name})){
114 my $package_defined_in = $args{package_defined_in} ||= caller(1);
116 my $existing = $TYPE{$name};
117 if($existing && $existing->{package_defined_in} ne $package_defined_in){
118 confess("The type constraint '$name' has already been created in "
119 . "$existing->{package_defined_in} and cannot be created again in $package_defined_in");
122 $args{constraint} = delete($args{where}) if exists $args{where};
123 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
126 if($mode eq 'subtype'){
127 my $parent = delete($args{as})
128 or confess('A subtype cannot consist solely of a name, it must have a parent');
130 $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
133 $constraint = Mouse::Meta::TypeConstraint->new(%args);
136 return $TYPE{$name} = $constraint;
140 return _create_type('type', @_);
144 return _create_type('subtype', @_);
148 my $type_name = shift;
150 my $type = find_type_constraint($type_name)
151 or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
153 $type->_add_type_coercions(@_);
158 my($name, $conf) = @_;
159 if ($conf && $conf->{class}) {
160 # No, you're using this wrong
161 warn "class_type() should be class_type(ClassName). Perhaps you're looking for subtype $name => as '$conf->{class}'?";
162 _create_type 'type', $name => (
163 as => $conf->{class},
169 _create_type 'type', $name => (
170 optimized_as => sub { blessed($_[0]) && $_[0]->isa($name) },
178 my($name, $conf) = @_;
179 my $role = ($conf && $conf->{role}) ? $conf->{role} : $name;
180 _create_type 'type', $name => (
181 optimized_as => sub { blessed($_[0]) && does_role($_[0], $role) },
187 sub typecast_constraints { # DEPRECATED
188 my($class, $pkg, $type, $value) = @_;
189 Carp::croak("wrong arguments count") unless @_ == 4;
191 Carp::cluck("typecast_constraints() has been deprecated, which was an internal utility anyway");
193 return $type->coerce($value);
199 # enum ['small', 'medium', 'large']
200 if (ref($_[0]) eq 'ARRAY') {
201 %valid = map{ $_ => undef } @{ $_[0] };
202 $name = sprintf '(%s)', join '|', sort @{$_[0]};
204 # enum size => 'small', 'medium', 'large'
207 %valid = map{ $_ => undef } @_;
209 return _create_type 'type', $name => (
210 optimized_as => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
216 sub _find_or_create_regular_type{
219 return $TYPE{$spec} if exists $TYPE{$spec};
221 my $meta = Mouse::Util::get_metaclass_by_name($spec);
229 if($meta->isa('Mouse::Meta::Role')){
231 return blessed($_[0]) && $_[0]->does($spec);
237 return blessed($_[0]) && $_[0]->isa($spec);
242 return $TYPE{$spec} = Mouse::Meta::TypeConstraint->new(
250 $TYPE{ArrayRef}{constraint_generator} = sub {
251 my($type_parameter) = @_;
252 my $check = $type_parameter->_compiled_type_constraint;
255 foreach my $value (@{$_}) {
256 return undef unless $check->($value);
261 $TYPE{HashRef}{constraint_generator} = sub {
262 my($type_parameter) = @_;
263 my $check = $type_parameter->_compiled_type_constraint;
266 foreach my $value(values %{$_}){
267 return undef unless $check->($value);
273 # 'Maybe' type accepts 'Any', so it requires parameters
274 $TYPE{Maybe}{constraint_generator} = sub {
275 my($type_parameter) = @_;
276 my $check = $type_parameter->_compiled_type_constraint;
279 return !defined($_) || $check->($_);
283 sub _find_or_create_parameterized_type{
284 my($base, $param) = @_;
286 my $name = sprintf '%s[%s]', $base->name, $param->name;
289 my $generator = $base->{constraint_generator};
292 confess("The $name constraint cannot be used, because $param doesn't subtype from a parameterizable type");
295 Mouse::Meta::TypeConstraint->new(
298 constraint => $generator->($param),
300 type => 'Parameterized',
304 sub _find_or_create_union_type{
305 my @types = map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
307 my $name = join '|', map{ $_->name } @types;
310 return Mouse::Meta::TypeConstraint->new(
312 type_constraints => \@types,
321 my($spec, $start) = @_;
326 my $len = length $spec;
329 for($i = $start; $i < $len; $i++){
330 my $char = substr($spec, $i, 1);
333 my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
336 ($i, $subtype) = _parse_type($spec, $i+1)
338 $start = $i+1; # reset
340 push @list, _find_or_create_parameterized_type($base => $subtype);
347 my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
350 # XXX: Mouse creates a new class type, but Moose does not.
351 $type = class_type( substr($spec, $start, $i - $start) );
356 ($i, $subtype) = _parse_type($spec, $i+1)
359 $start = $i+1; # reset
361 push @list, $subtype;
365 push @list, _find_or_create_regular_type(substr $spec, $start, $i - $start);
372 return ($len, $list[0]);
375 return ($len, _find_or_create_union_type(@list));
380 sub find_type_constraint {
382 return $spec if blessed($spec) && $spec->isa('Mouse::Meta::TypeConstraint');
388 sub find_or_parse_type_constraint {
390 return $spec if blessed($spec) && $spec->isa('Mouse::Meta::TypeConstraint');
393 return $TYPE{$spec} || do{
394 my($pos, $type) = _parse_type($spec, 0);
399 sub find_or_create_does_type_constraint{
400 my $type = find_or_parse_type_constriant(@_) || role_type(@_);
402 if($type->{type} && $type->{type} ne 'Role'){
403 Carp::cluck("$type is not a role type");
408 sub find_or_create_isa_type_constraint {
409 return find_or_parse_type_constraint(@_) || class_type(@_);
418 Mouse::Util::TypeConstraints - Type constraint system for Mouse
422 use Mouse::Util::TypeConstraints;
428 subtype 'NaturalLessThanTen'
431 => message { "This number ($_) is not less than ten!" };
437 enum 'RGBColors' => qw(red green blue);
439 no Mouse::Util::TypeConstraints;
443 This module provides Mouse with the ability to create custom type
444 constraints to be used in attribute definition.
446 =head2 Important Caveat
448 This is B<NOT> a type system for Perl 5. These are type constraints,
449 and they are not used by Mouse unless you tell it to. No type
450 inference is performed, expressions are not typed, etc. etc. etc.
452 A type constraint is at heart a small "check if a value is valid"
453 function. A constraint can be associated with an attribute. This
454 simplifies parameter validation, and makes your code clearer to read,
455 because you can refer to constraints by name.
457 =head2 Slightly Less Important Caveat
459 It is B<always> a good idea to quote your type names.
461 This prevents Perl from trying to execute the call as an indirect
462 object call. This can be an issue when you have a subtype with the
463 same name as a valid class.
467 subtype DateTime => as Object => where { $_->isa('DateTime') };
469 will I<just work>, while this:
472 subtype DateTime => as Object => where { $_->isa('DateTime') };
474 will fail silently and cause many headaches. The simple way to solve
475 this, as well as future proof your subtypes from classes which have
476 yet to have been created, is to quote the type name:
479 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
481 =head2 Default Type Constraints
483 This module also provides a simple hierarchy for Perl 5 types, here is
484 that hierarchy represented visually.
508 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
509 parameterized, this means you can say:
511 ArrayRef[Int] # an array of integers
512 HashRef[CodeRef] # a hash of str to CODE ref mappings
513 Maybe[Str] # value may be a string, may be undefined
515 If Mouse finds a name in brackets that it does not recognize as an
516 existing type, it assumes that this is a class name, for example
517 C<ArrayRef[DateTime]>.
519 B<NOTE:> Unless you parameterize a type, then it is invalid to include
520 the square brackets. I.e. C<ArrayRef[]> will be treated as a new type
521 name, I<not> as a parameterization of C<ArrayRef>.
523 B<NOTE:> The C<Undef> type constraint for the most part works
524 correctly now, but edge cases may still exist, please use it
527 B<NOTE:> The C<ClassName> type constraint does a complex package
528 existence check. This means that your class B<must> be loaded for this
529 type constraint to pass.
531 B<NOTE:> The C<RoleName> constraint checks a string is a I<package
532 name> which is a role, like C<'MyApp::Role::Comparable'>. The C<Role>
533 constraint checks that an I<object does> the named role.
535 =head2 Type Constraint Naming
537 Type name declared via this module can only contain alphanumeric
538 characters, colons (:), and periods (.).
540 Since the types created by this module are global, it is suggested
541 that you namespace your types just as you would namespace your
542 modules. So instead of creating a I<Color> type for your
543 B<My::Graphics> module, you would call the type
544 I<My::Graphics::Types::Color> instead.
546 =head2 Use with Other Constraint Modules
548 This module can play nicely with other constraint modules with some
549 slight tweaking. The C<where> clause in types is expected to be a
550 C<CODE> reference which checks it's first argument and returns a
551 boolean. Since most constraint modules work in a similar way, it
552 should be simple to adapt them to work with Mouse.
554 For instance, this is how you could use it with
555 L<Declare::Constraints::Simple> to declare a completely new type.
557 type 'HashOfArrayOfObjects',
561 -values => IsArrayRef(IsObject)
565 Here is an example of using L<Test::Deep> and it's non-test
566 related C<eq_deeply> function.
568 type 'ArrayOfHashOfBarsAndRandomNumbers'
571 array_each(subhashof({
573 random_number => ignore()
579 =head2 C<< list_all_builtin_type_constraints -> (Names) >>
581 Returns the names of builtin type constraints.
583 =head2 C<< list_all_type_constraints -> (Names) >>
585 Returns the names of all the type constraints.
591 =item C<< subtype 'Name' => as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
593 =item C<< subtype as 'Parent' => where { } ... -> Mouse::Meta::TypeConstraint >>
595 =item C<< class_type ($class, ?$options) -> Mouse::Meta::TypeConstraint >>
597 =item C<< role_type ($role, ?$options) -> Mouse::Meta::TypeConstraint >>
599 =item C<< enum (\@values) -> Mouse::Meta::TypeConstraint >>
605 =item C<< find_type_constraint(Type) -> Mouse::Meta::TypeConstraint >>
611 Much of this documentation was taken from C<Moose::Util::TypeConstraints>
615 L<Moose::Util::TypeConstraints>