rudementary support for attribute traits
[gitmo/Moose.git] / lib / Moose / Util / TypeConstraints.pm
CommitLineData
a15dff8d 1
2package Moose::Util::TypeConstraints;
3
4use strict;
5use warnings;
6
e90c03d0 7use Carp 'confess';
86629f93 8use Scalar::Util 'blessed', 'reftype';
571dd39f 9use Sub::Exporter;
a15dff8d 10
28412c0b 11our $VERSION = '0.20';
d44714be 12our $AUTHORITY = 'cpan:STEVAN';
a15dff8d 13
d9b40005 14## --------------------------------------------------------
e85d2a5d 15# Prototyped subs must be predeclared because we have a
16# circular dependency with Moose::Meta::Attribute et. al.
17# so in case of us being use'd first the predeclaration
d9b40005 18# ensures the prototypes are in scope when consumers are
19# compiled.
20
21# creation and location
0fbd4b0a 22sub find_type_constraint ($);
3fef8ce8 23sub register_type_constraint ($);
0fbd4b0a 24sub find_or_create_type_constraint ($;$);
25sub create_type_constraint_union (@);
26sub create_parameterized_type_constraint ($);
3fef8ce8 27sub create_class_type_constraint ($);
d9b40005 28
29# dah sugah!
30sub type ($$;$$);
31sub subtype ($$;$$$);
3fef8ce8 32sub class_type ($);
d9b40005 33sub coerce ($@);
34sub as ($);
35sub from ($);
36sub where (&);
37sub via (&);
38sub message (&);
39sub optimize_as (&);
40sub enum ($;@);
41
e85d2a5d 42## private stuff ...
d9b40005 43sub _create_type_constraint ($$$;$$);
44sub _install_type_coercions ($$);
45
46## --------------------------------------------------------
8c4acc60 47
4e036ee4 48use Moose::Meta::TypeConstraint;
3726f905 49use Moose::Meta::TypeConstraint::Union;
0fbd4b0a 50use Moose::Meta::TypeConstraint::Parameterized;
7e4e1ad4 51use Moose::Meta::TypeConstraint::Parameterizable;
2ca63f5d 52use Moose::Meta::TypeCoercion;
3726f905 53use Moose::Meta::TypeCoercion::Union;
22aed3c0 54use Moose::Meta::TypeConstraint::Registry;
28ffb449 55use Moose::Util::TypeConstraints::OptimizedConstraints;
4e036ee4 56
571dd39f 57my @exports = qw/
3fef8ce8 58 type subtype class_type as where message optimize_as
e85d2a5d 59 coerce from via
571dd39f 60 enum
61 find_type_constraint
3fef8ce8 62 register_type_constraint
571dd39f 63/;
64
e85d2a5d 65Sub::Exporter::setup_exporter({
571dd39f 66 exports => \@exports,
67 groups => { default => [':all'] }
68});
69
70sub unimport {
e85d2a5d 71 no strict 'refs';
571dd39f 72 my $class = caller();
73 # loop through the exports ...
74 foreach my $name (@exports) {
75 # if we find one ...
76 if (defined &{$class . '::' . $name}) {
77 my $keyword = \&{$class . '::' . $name};
e85d2a5d 78
571dd39f 79 # make sure it is from Moose
53dd42d8 80 my ($pkg_name) = Class::MOP::get_code_info($keyword);
571dd39f 81 next if $@;
82 next if $pkg_name ne 'Moose::Util::TypeConstraints';
e85d2a5d 83
571dd39f 84 # and if it is from Moose then undef the slot
85 delete ${$class . '::'}{$name};
86 }
2c0cbef7 87 }
571dd39f 88}
a15dff8d 89
d9b40005 90## --------------------------------------------------------
91## type registry and some useful functions for it
92## --------------------------------------------------------
93
22aed3c0 94my $REGISTRY = Moose::Meta::TypeConstraint::Registry->new;
587ae0d2 95
d9b40005 96sub get_type_constraint_registry { $REGISTRY }
e85d2a5d 97sub list_all_type_constraints { keys %{$REGISTRY->type_constraints} }
d9b40005 98sub export_type_constraints_as_functions {
99 my $pkg = caller();
100 no strict 'refs';
a0f8153d 101 foreach my $constraint (keys %{$REGISTRY->type_constraints}) {
42bc21a4 102 my $tc = $REGISTRY->get_type_constraint($constraint)->_compiled_type_constraint;
103 *{"${pkg}::${constraint}"} = sub { $tc->($_[0]) ? 1 : undef };
a0f8153d 104 }
d9b40005 105}
182134e8 106
d9b40005 107sub create_type_constraint_union (@) {
108 my @type_constraint_names;
e85d2a5d 109
f1917f58 110 if (scalar @_ == 1 && _detect_type_constraint_union($_[0])) {
111 @type_constraint_names = _parse_type_constraint_union($_[0]);
d9b40005 112 }
113 else {
114 @type_constraint_names = @_;
429ccc11 115 }
e85d2a5d 116
3726f905 117 (scalar @type_constraint_names >= 2)
e85d2a5d 118 || confess "You must pass in at least 2 type names to make a union";
119
d9b40005 120 ($REGISTRY->has_type_constraint($_))
121 || confess "Could not locate type constraint ($_) for the union"
122 foreach @type_constraint_names;
e85d2a5d 123
3726f905 124 return Moose::Meta::TypeConstraint::Union->new(
125 type_constraints => [
e85d2a5d 126 map {
127 $REGISTRY->get_type_constraint($_)
128 } @type_constraint_names
3726f905 129 ],
e85d2a5d 130 );
182134e8 131}
a15dff8d 132
0fbd4b0a 133sub create_parameterized_type_constraint ($) {
d9b40005 134 my $type_constraint_name = shift;
e85d2a5d 135
0fbd4b0a 136 my ($base_type, $type_parameter) = _parse_parameterized_type_constraint($type_constraint_name);
e85d2a5d 137
0fbd4b0a 138 (defined $base_type && defined $type_parameter)
d9b40005 139 || confess "Could not parse type name ($type_constraint_name) correctly";
e85d2a5d 140
d9b40005 141 ($REGISTRY->has_type_constraint($base_type))
142 || confess "Could not locate the base type ($base_type)";
e85d2a5d 143
0fbd4b0a 144 return Moose::Meta::TypeConstraint::Parameterized->new(
d9b40005 145 name => $type_constraint_name,
146 parent => $REGISTRY->get_type_constraint($base_type),
0fbd4b0a 147 type_parameter => find_or_create_type_constraint(
148 $type_parameter => {
f1917f58 149 parent => $REGISTRY->get_type_constraint('Object'),
0fbd4b0a 150 constraint => sub { $_[0]->isa($type_parameter) }
f1917f58 151 }
152 ),
e85d2a5d 153 );
22aed3c0 154}
155
3fef8ce8 156sub create_class_type_constraint ($) {
157 my $class = shift;
158
159 # too early for this check
160 #find_type_constraint("ClassName")->check($class)
161 # || confess "Can't create a class type constraint because '$class' is not a class name";
162
163 Moose::Meta::TypeConstraint::Class->new( name => $class );
164}
165
d9b40005 166sub find_or_create_type_constraint ($;$) {
167 my ($type_constraint_name, $options_for_anon_type) = @_;
e85d2a5d 168
d9b40005 169 return $REGISTRY->get_type_constraint($type_constraint_name)
170 if $REGISTRY->has_type_constraint($type_constraint_name);
e85d2a5d 171
d9b40005 172 my $constraint;
e85d2a5d 173
f1917f58 174 if (_detect_type_constraint_union($type_constraint_name)) {
d9b40005 175 $constraint = create_type_constraint_union($type_constraint_name);
176 }
0fbd4b0a 177 elsif (_detect_parameterized_type_constraint($type_constraint_name)) {
e85d2a5d 178 $constraint = create_parameterized_type_constraint($type_constraint_name);
d9b40005 179 }
180 else {
181 # NOTE:
f3c4e20e 182 # if there is no $options_for_anon_type
183 # specified, then we assume they don't
184 # want to create one, and return nothing.
185 return unless defined $options_for_anon_type;
186
187 # NOTE:
d9b40005 188 # otherwise assume that we should create
e85d2a5d 189 # an ANON type with the $options_for_anon_type
d9b40005 190 # options which can be passed in. It should
e85d2a5d 191 # be noted that these don't get registered
d9b40005 192 # so we need to return it.
193 # - SL
194 return Moose::Meta::TypeConstraint->new(
195 name => '__ANON__',
e85d2a5d 196 %{$options_for_anon_type}
d9b40005 197 );
198 }
e85d2a5d 199
d9b40005 200 $REGISTRY->add_type_constraint($constraint);
e85d2a5d 201 return $constraint;
d9b40005 202}
22aed3c0 203
204## --------------------------------------------------------
205## exported functions ...
206## --------------------------------------------------------
207
208sub find_type_constraint ($) { $REGISTRY->get_type_constraint(@_) }
209
3fef8ce8 210sub register_type_constraint ($) {
211 my $constraint = shift;
212 confess "can't register an unnamed type constraint" unless defined $constraint->name;
213 $REGISTRY->add_type_constraint($constraint);
214}
215
7c13858b 216# type constructors
a15dff8d 217
815ec671 218sub type ($$;$$) {
1b7df21f 219 splice(@_, 1, 0, undef);
a0f8153d 220 goto &_create_type_constraint;
a15dff8d 221}
222
8ecb1fa0 223sub subtype ($$;$$$) {
86629f93 224 # NOTE:
225 # this adds an undef for the name
226 # if this is an anon-subtype:
227 # subtype(Num => where { $_ % 2 == 0 }) # anon 'even' subtype
228 # but if the last arg is not a code
229 # ref then it is a subtype alias:
230 # subtype(MyNumbers => as Num); # now MyNumbers is the same as Num
e85d2a5d 231 # ... yeah I know it's ugly code
86629f93 232 # - SL
a0f8153d 233 unshift @_ => undef if scalar @_ <= 2 && (reftype($_[1]) || '') eq 'CODE';
234 goto &_create_type_constraint;
a15dff8d 235}
236
3fef8ce8 237sub class_type ($) {
238 register_type_constraint( create_class_type_constraint(shift) );
239}
240
4b598ea3 241sub coerce ($@) {
e85d2a5d 242 my ($type_name, @coercion_map) = @_;
7c13858b 243 _install_type_coercions($type_name, \@coercion_map);
182134e8 244}
245
76d37e5a 246sub as ($) { $_[0] }
247sub from ($) { $_[0] }
248sub where (&) { $_[0] }
249sub via (&) { $_[0] }
8ecb1fa0 250
251sub message (&) { +{ message => $_[0] } }
252sub optimize_as (&) { +{ optimized => $_[0] } }
a15dff8d 253
2c0cbef7 254sub enum ($;@) {
fcec2383 255 my ($type_name, @values) = @_;
2c0cbef7 256 (scalar @values >= 2)
257 || confess "You must have at least two values to enumerate through";
c4fe165f 258 my %valid = map { $_ => 1 } @values;
a0f8153d 259 _create_type_constraint(
260 $type_name,
261 'Str',
262 sub { $valid{$_} }
263 );
fcec2383 264}
265
d9b40005 266## --------------------------------------------------------
267## desugaring functions ...
268## --------------------------------------------------------
269
e85d2a5d 270sub _create_type_constraint ($$$;$$) {
d9b40005 271 my $name = shift;
272 my $parent = shift;
8de73ff1 273 my $check = shift;
e85d2a5d 274
d9b40005 275 my ($message, $optimized);
276 for (@_) {
277 $message = $_->{message} if exists $_->{message};
e85d2a5d 278 $optimized = $_->{optimized} if exists $_->{optimized};
d9b40005 279 }
280
281 my $pkg_defined_in = scalar(caller(0));
e85d2a5d 282
d9b40005 283 if (defined $name) {
284 my $type = $REGISTRY->get_type_constraint($name);
e85d2a5d 285
d9b40005 286 ($type->_package_defined_in eq $pkg_defined_in)
e85d2a5d 287 || confess ("The type constraint '$name' has already been created in "
d9b40005 288 . $type->_package_defined_in . " and cannot be created again in "
289 . $pkg_defined_in)
e85d2a5d 290 if defined $type;
291 }
292
a0f8153d 293 $parent = find_or_create_type_constraint($parent) if defined $parent;
8de73ff1 294
d9b40005 295 my $constraint = Moose::Meta::TypeConstraint->new(
296 name => $name || '__ANON__',
d9b40005 297 package_defined_in => $pkg_defined_in,
e85d2a5d 298
299 ($parent ? (parent => $parent ) : ()),
300 ($check ? (constraint => $check) : ()),
301 ($message ? (message => $message) : ()),
302 ($optimized ? (optimized => $optimized) : ()),
d9b40005 303 );
8de73ff1 304
305 # NOTE:
306 # if we have a type constraint union, and no
307 # type check, this means we are just aliasing
308 # the union constraint, which means we need to
309 # handle this differently.
310 # - SL
6f9ff1af 311 if (not(defined $check)
8de73ff1 312 && $parent->isa('Moose::Meta::TypeConstraint::Union')
313 && $parent->has_coercion
314 ){
315 $constraint->coercion(Moose::Meta::TypeCoercion::Union->new(
316 type_constraint => $parent
317 ));
318 }
d9b40005 319
320 $REGISTRY->add_type_constraint($constraint)
321 if defined $name;
322
323 return $constraint;
324}
325
e85d2a5d 326sub _install_type_coercions ($$) {
d9b40005 327 my ($type_name, $coercion_map) = @_;
328 my $type = $REGISTRY->get_type_constraint($type_name);
6f9ff1af 329 (defined $type)
330 || confess "Cannot find type '$type_name', perhaps you forgot to load it.";
41e007e4 331 if ($type->has_coercion) {
332 $type->coercion->add_type_coercions(@$coercion_map);
333 }
334 else {
335 my $type_coercion = Moose::Meta::TypeCoercion->new(
336 type_coercion_map => $coercion_map,
337 type_constraint => $type
338 );
339 $type->coercion($type_coercion);
340 }
d9b40005 341}
342
343## --------------------------------------------------------
f1917f58 344## type notation parsing ...
345## --------------------------------------------------------
346
347{
e85d2a5d 348 # All I have to say is mugwump++ cause I know
349 # do not even have enough regexp-fu to be able
350 # to have written this (I can only barely
f1917f58 351 # understand it as it is)
e85d2a5d 352 # - SL
353
f1917f58 354 use re "eval";
355
3796382a 356 my $valid_chars = qr{[\w:]};
f1917f58 357 my $type_atom = qr{ $valid_chars+ };
358
359 my $type = qr{ $valid_chars+ (?: \[ (??{$any}) \] )? }x;
360 my $type_capture_parts = qr{ ($valid_chars+) (?: \[ ((??{$any})) \] )? }x;
361 my $type_with_parameter = qr{ $valid_chars+ \[ (??{$any}) \] }x;
362
3796382a 363 my $op_union = qr{ \s* \| \s* }x;
f1917f58 364 my $union = qr{ $type (?: $op_union $type )+ }x;
365
366 our $any = qr{ $type | $union }x;
367
0fbd4b0a 368 sub _parse_parameterized_type_constraint {
e85d2a5d 369 $_[0] =~ m{ $type_capture_parts }x;
370 return ($1, $2);
f1917f58 371 }
372
0fbd4b0a 373 sub _detect_parameterized_type_constraint {
e85d2a5d 374 $_[0] =~ m{ ^ $type_with_parameter $ }x;
f1917f58 375 }
376
377 sub _parse_type_constraint_union {
e85d2a5d 378 my $given = shift;
379 my @rv;
380 while ( $given =~ m{ \G (?: $op_union )? ($type) }gcx ) {
381 push @rv => $1;
382 }
383 (pos($given) eq length($given))
384 || confess "'$given' didn't parse (parse-pos="
385 . pos($given)
386 . " and str-length="
387 . length($given)
388 . ")";
389 @rv;
f1917f58 390 }
391
392 sub _detect_type_constraint_union {
e85d2a5d 393 $_[0] =~ m{^ $type $op_union $type ( $op_union .* )? $}x;
f1917f58 394 }
395}
396
397## --------------------------------------------------------
d9b40005 398# define some basic built-in types
399## --------------------------------------------------------
a15dff8d 400
f65cb534 401type 'Any' => where { 1 }; # meta-type including all
e85d2a5d 402type 'Item' => where { 1 }; # base-type
a15dff8d 403
f65cb534 404subtype 'Undef' => as 'Item' => where { !defined($_) };
405subtype 'Defined' => as 'Item' => where { defined($_) };
a15dff8d 406
8ecb1fa0 407subtype 'Bool'
e85d2a5d 408 => as 'Item'
8ecb1fa0 409 => where { !defined($_) || $_ eq "" || "$_" eq '1' || "$_" eq '0' };
5a4c5493 410
e85d2a5d 411subtype 'Value'
412 => as 'Defined'
413 => where { !ref($_) }
28ffb449 414 => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::Value;
e85d2a5d 415
8ecb1fa0 416subtype 'Ref'
e85d2a5d 417 => as 'Defined'
418 => where { ref($_) }
28ffb449 419 => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::Ref;
8ecb1fa0 420
e85d2a5d 421subtype 'Str'
422 => as 'Value'
423 => where { 1 }
28ffb449 424 => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::Str;
8ecb1fa0 425
e85d2a5d 426subtype 'Num'
427 => as 'Value'
428 => where { Scalar::Util::looks_like_number($_) }
28ffb449 429 => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::Num;
e85d2a5d 430
431subtype 'Int'
432 => as 'Num'
8ecb1fa0 433 => where { "$_" =~ /^-?[0-9]+$/ }
28ffb449 434 => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::Int;
8ecb1fa0 435
28ffb449 436subtype 'ScalarRef' => as 'Ref' => where { ref($_) eq 'SCALAR' } => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::ScalarRef;
28ffb449 437subtype 'CodeRef' => as 'Ref' => where { ref($_) eq 'CODE' } => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::CodeRef;
438subtype 'RegexpRef' => as 'Ref' => where { ref($_) eq 'Regexp' } => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::RegexpRef;
439subtype 'GlobRef' => as 'Ref' => where { ref($_) eq 'GLOB' } => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::GlobRef;
a15dff8d 440
0a5bd159 441# NOTE:
e85d2a5d 442# scalar filehandles are GLOB refs,
0a5bd159 443# but a GLOB ref is not always a filehandle
e85d2a5d 444subtype 'FileHandle'
445 => as 'GlobRef'
8ecb1fa0 446 => where { Scalar::Util::openhandle($_) }
28ffb449 447 => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::FileHandle;
0a5bd159 448
e85d2a5d 449# NOTE:
a15dff8d 450# blessed(qr/.../) returns true,.. how odd
e85d2a5d 451subtype 'Object'
452 => as 'Ref'
8ecb1fa0 453 => where { blessed($_) && blessed($_) ne 'Regexp' }
28ffb449 454 => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::Object;
a15dff8d 455
e85d2a5d 456subtype 'Role'
457 => as 'Object'
8ecb1fa0 458 => where { $_->can('does') }
28ffb449 459 => optimize_as \&Moose::Util::TypeConstraints::OptimizedConstraints::Role;
e85d2a5d 460
0e0709ea 461my $_class_name_checker = sub {
462 return if ref($_[0]);
463 return unless defined($_[0]) && length($_[0]);
464
465 # walk the symbol table tree to avoid autovififying
466 # \*{${main::}{"Foo::"}} == \*main::Foo::
467
468 my $pack = \*::;
469 foreach my $part (split('::', $_[0])) {
470 return unless exists ${$$pack}{"${part}::"};
471 $pack = \*{${$$pack}{"${part}::"}};
472 }
473
474 # check for $VERSION or @ISA
475 return 1 if exists ${$$pack}{VERSION}
476 && defined *{${$$pack}{VERSION}}{SCALAR};
477 return 1 if exists ${$$pack}{ISA}
478 && defined *{${$$pack}{ISA}}{ARRAY};
479
480 # check for any method
481 foreach ( keys %{$$pack} ) {
482 next if substr($_, -2, 2) eq '::';
483 return 1 if defined *{${$$pack}{$_}}{CODE};
484 }
485
486 # fail
487 return;
488};
489
e85d2a5d 490subtype 'ClassName'
491 => as 'Str'
0e0709ea 492 => $_class_name_checker # where ...
493 => { optimize => $_class_name_checker };
02a0fb52 494
d9b40005 495## --------------------------------------------------------
7e4e1ad4 496# parameterizable types ...
497
498$REGISTRY->add_type_constraint(
499 Moose::Meta::TypeConstraint::Parameterizable->new(
500 name => 'ArrayRef',
501 package_defined_in => __PACKAGE__,
502 parent => find_type_constraint('Ref'),
503 constraint => sub { ref($_) eq 'ARRAY' },
504 optimized => \&Moose::Util::TypeConstraints::OptimizedConstraints::ArrayRef,
505 constraint_generator => sub {
506 my $type_parameter = shift;
507 return sub {
508 foreach my $x (@$_) {
509 ($type_parameter->check($x)) || return
510 } 1;
511 }
512 }
513 )
514);
515
516$REGISTRY->add_type_constraint(
517 Moose::Meta::TypeConstraint::Parameterizable->new(
518 name => 'HashRef',
519 package_defined_in => __PACKAGE__,
520 parent => find_type_constraint('Ref'),
521 constraint => sub { ref($_) eq 'HASH' },
522 optimized => \&Moose::Util::TypeConstraints::OptimizedConstraints::HashRef,
523 constraint_generator => sub {
524 my $type_parameter = shift;
525 return sub {
526 foreach my $x (values %$_) {
527 ($type_parameter->check($x)) || return
528 } 1;
529 }
530 }
531 )
532);
533
534$REGISTRY->add_type_constraint(
535 Moose::Meta::TypeConstraint::Parameterizable->new(
536 name => 'Maybe',
537 package_defined_in => __PACKAGE__,
538 parent => find_type_constraint('Item'),
539 constraint => sub { 1 },
540 constraint_generator => sub {
541 my $type_parameter = shift;
542 return sub {
543 return 1 if not(defined($_)) || $type_parameter->check($_);
544 return;
545 }
546 }
547 )
548);
549
550my @PARAMETERIZABLE_TYPES = map {
551 $REGISTRY->get_type_constraint($_)
552} qw[ArrayRef HashRef Maybe];
553
554sub get_all_parameterizable_types { @PARAMETERIZABLE_TYPES }
555sub add_parameterizable_type {
556 my $type = shift;
557 (blessed $type && $type->isa('Moose::Meta::TypeConstraint::Parameterizable'))
558 || confess "Type must be a Moose::Meta::TypeConstraint::Parameterizable not $type";
559 push @PARAMETERIZABLE_TYPES => $type;
560}
561
562## --------------------------------------------------------
d9b40005 563# end of built-in types ...
564## --------------------------------------------------------
565
943596a6 566{
567 my @BUILTINS = list_all_type_constraints();
568 sub list_all_builtin_type_constraints { @BUILTINS }
569}
570
a15dff8d 5711;
572
573__END__
574
575=pod
576
577=head1 NAME
578
e522431d 579Moose::Util::TypeConstraints - Type constraint system for Moose
a15dff8d 580
581=head1 SYNOPSIS
582
583 use Moose::Util::TypeConstraints;
584
2c0cbef7 585 type 'Num' => where { Scalar::Util::looks_like_number($_) };
e85d2a5d 586
587 subtype 'Natural'
588 => as 'Num'
a15dff8d 589 => where { $_ > 0 };
e85d2a5d 590
591 subtype 'NaturalLessThanTen'
2c0cbef7 592 => as 'Natural'
79592a54 593 => where { $_ < 10 }
594 => message { "This number ($_) is not less than ten!" };
e85d2a5d 595
596 coerce 'Num'
2c0cbef7 597 => from 'Str'
e85d2a5d 598 => via { 0+$_ };
599
2c0cbef7 600 enum 'RGBColors' => qw(red green blue);
a15dff8d 601
602=head1 DESCRIPTION
603
e85d2a5d 604This module provides Moose with the ability to create custom type
605contraints to be used in attribute definition.
e522431d 606
6ba6d68c 607=head2 Important Caveat
608
e85d2a5d 609This is B<NOT> a type system for Perl 5. These are type constraints,
610and they are not used by Moose unless you tell it to. No type
611inference is performed, expression are not typed, etc. etc. etc.
6ba6d68c 612
e85d2a5d 613This is simply a means of creating small constraint functions which
a7d0cd00 614can be used to simplify your own type-checking code.
6ba6d68c 615
2c0cbef7 616=head2 Slightly Less Important Caveat
617
e85d2a5d 618It is almost always a good idea to quote your type and subtype names.
619This is to prevent perl from trying to execute the call as an indirect
2c0cbef7 620object call. This issue only seems to come up when you have a subtype
e85d2a5d 621the same name as a valid class, but when the issue does arise it tends
622to be quite annoying to debug.
2c0cbef7 623
624So for instance, this:
e85d2a5d 625
2c0cbef7 626 subtype DateTime => as Object => where { $_->isa('DateTime') };
627
628will I<Just Work>, while this:
629
630 use DateTime;
631 subtype DateTime => as Object => where { $_->isa('DateTime') };
632
e85d2a5d 633will fail silently and cause many headaches. The simple way to solve
634this, as well as future proof your subtypes from classes which have
2c0cbef7 635yet to have been created yet, is to simply do this:
636
637 use DateTime;
d44714be 638 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
2c0cbef7 639
6ba6d68c 640=head2 Default Type Constraints
e522431d 641
e85d2a5d 642This module also provides a simple hierarchy for Perl 5 types, this
e522431d 643could probably use some work, but it works for me at the moment.
644
645 Any
e85d2a5d 646 Item
5a4c5493 647 Bool
7e4e1ad4 648 Maybe[`a]
f65cb534 649 Undef
650 Defined
5a4c5493 651 Value
652 Num
653 Int
654 Str
9af1d28b 655 ClassName
5a4c5493 656 Ref
657 ScalarRef
7e4e1ad4 658 ArrayRef[`a]
659 HashRef[`a]
5a4c5493 660 CodeRef
661 RegexpRef
3f7376b0 662 GlobRef
0a5bd159 663 FileHandle
e85d2a5d 664 Object
5a4c5493 665 Role
e522431d 666
6ba6d68c 667Suggestions for improvement are welcome.
2c0cbef7 668
7e4e1ad4 669B<NOTE:> Any type followed by a type parameter C<[`a]> can be
670parameterized, this means you can say:
671
672 ArrayRef[Int] # an array of intergers
673 HashRef[CodeRef] # a hash of str to CODE ref mappings
674 Maybe[Str] # value may be a string, may be undefined
675
676B<NOTE:> The C<Undef> type constraint for the most part works
677correctly now, but edge cases may still exist, please use it
678sparringly.
703e92fb 679
7e4e1ad4 680B<NOTE:> The C<ClassName> type constraint does a complex package
681existence check. This means that your class B<must> be loaded for
682this type constraint to pass. I know this is not ideal for all,
683but it is a saner restriction than most others.
9af1d28b 684
703e92fb 685=head2 Use with Other Constraint Modules
686
e85d2a5d 687This module should play fairly nicely with other constraint
688modules with only some slight tweaking. The C<where> clause
703e92fb 689in types is expected to be a C<CODE> reference which checks
690it's first argument and returns a bool. Since most constraint
e85d2a5d 691modules work in a similar way, it should be simple to adapt
703e92fb 692them to work with Moose.
693
e85d2a5d 694For instance, this is how you could use it with
695L<Declare::Constraints::Simple> to declare a completely new type.
703e92fb 696
e85d2a5d 697 type 'HashOfArrayOfObjects'
703e92fb 698 => IsHashRef(
699 -keys => HasLength,
700 -values => IsArrayRef( IsObject ));
701
702For more examples see the F<t/204_example_w_DCS.t> test file.
703
e85d2a5d 704Here is an example of using L<Test::Deep> and it's non-test
705related C<eq_deeply> function.
703e92fb 706
e85d2a5d 707 type 'ArrayOfHashOfBarsAndRandomNumbers'
703e92fb 708 => where {
e85d2a5d 709 eq_deeply($_,
703e92fb 710 array_each(subhashof({
711 bar => isa('Bar'),
712 random_number => ignore()
e85d2a5d 713 })))
703e92fb 714 };
715
e85d2a5d 716For a complete example see the F<t/205_example_w_TestDeep.t>
717test file.
718
a15dff8d 719=head1 FUNCTIONS
720
d9b40005 721=head2 Type Constraint Construction & Locating
182134e8 722
723=over 4
724
d9b40005 725=item B<create_type_constraint_union ($pipe_seperated_types | @type_constraint_names)>
182134e8 726
e85d2a5d 727Given string with C<$pipe_seperated_types> or a list of C<@type_constraint_names>,
d9b40005 728this will return a L<Moose::Meta::TypeConstraint::Union> instance.
182134e8 729
0fbd4b0a 730=item B<create_parameterized_type_constraint ($type_name)>
c07af9d2 731
d9b40005 732Given a C<$type_name> in the form of:
c07af9d2 733
d9b40005 734 BaseType[ContainerType]
182134e8 735
e85d2a5d 736this will extract the base type and container type and build an instance of
7e4e1ad4 737L<Moose::Meta::TypeConstraint::Parameterized> for it.
d9b40005 738
3fef8ce8 739=item B<create_class_type_constraint ($class)>
740
741Given a class name it will create a new L<Moose::Meta::TypeConstraint::Class>
742object for that class name.
743
d9b40005 744=item B<find_or_create_type_constraint ($type_name, ?$options_for_anon_type)>
6ba6d68c 745
e85d2a5d 746This will attempt to find or create a type constraint given the a C<$type_name>.
747If it cannot find it in the registry, it will see if it should be a union or
748container type an create one if appropriate, and lastly if nothing can be
749found or created that way, it will create an anon-type using the
f3c4e20e 750C<$options_for_anon_type> HASH ref to populate it. If the C<$options_for_anon_type>
751is not specified (it is C<undef>), then it will not create anything and simply
752return.
429ccc11 753
d9b40005 754=item B<find_type_constraint ($type_name)>
755
756This function can be used to locate a specific type constraint
757meta-object, of the class L<Moose::Meta::TypeConstraint> or a
758derivative. What you do with it from there is up to you :)
759
3fef8ce8 760=item B<register_type_constraint ($type_object)>
761
762This function will register a named type constraint with the type registry.
763
d9b40005 764=item B<get_type_constraint_registry>
765
e85d2a5d 766Fetch the L<Moose::Meta::TypeConstraint::Registry> object which
d9b40005 767keeps track of all type constraints.
429ccc11 768
b1e01e3c 769=item B<list_all_type_constraints>
770
e85d2a5d 771This will return a list of type constraint names, you can then
772fetch them using C<find_type_constraint ($type_name)> if you
b1e01e3c 773want to.
774
943596a6 775=item B<list_all_builtin_type_constraints>
776
e85d2a5d 777This will return a list of builtin type constraints, meaning,
778those which are defined in this module. See the section
943596a6 779labeled L<Default Type Constraints> for a complete list.
780
d9b40005 781=item B<export_type_constraints_as_functions>
782
e85d2a5d 783This will export all the current type constraints as functions
784into the caller's namespace. Right now, this is mostly used for
d9b40005 785testing, but it might prove useful to others.
786
7e4e1ad4 787=item B<get_all_parameterizable_types>
788
789This returns all the parameterizable types that have been registered.
790
791=item B<add_parameterizable_type ($type)>
792
793Adds C<$type> to the list of parameterizable types
794
182134e8 795=back
796
a15dff8d 797=head2 Type Constraint Constructors
798
e85d2a5d 799The following functions are used to create type constraints.
800They will then register the type constraints in a global store
801where Moose can get to them if it needs to.
a15dff8d 802
25f2c3fc 803See the L<SYNOPSIS> for an example of how to use these.
a15dff8d 804
6ba6d68c 805=over 4
a15dff8d 806
6ba6d68c 807=item B<type ($name, $where_clause)>
a15dff8d 808
e85d2a5d 809This creates a base type, which has no parent.
a15dff8d 810
79592a54 811=item B<subtype ($name, $parent, $where_clause, ?$message)>
182134e8 812
e85d2a5d 813This creates a named subtype.
d6e2d9a1 814
79592a54 815=item B<subtype ($parent, $where_clause, ?$message)>
182134e8 816
e85d2a5d 817This creates an unnamed subtype and will return the type
818constraint meta-object, which will be an instance of
819L<Moose::Meta::TypeConstraint>.
a15dff8d 820
3fef8ce8 821=item B<class_type ($class)>
822
823Creates a type constraint with the name C<$class> and the metaclass
824L<Moose::Meta::TypeConstraint::Class>.
825
fcec2383 826=item B<enum ($name, @values)>
827
e85d2a5d 828This will create a basic subtype for a given set of strings.
829The resulting constraint will be a subtype of C<Str> and
4ce56d04 830will match any of the items in C<@values>. It is case sensitive.
831See the L<SYNOPSIS> for a simple example.
2c0cbef7 832
e85d2a5d 833B<NOTE:> This is not a true proper enum type, it is simple
2c0cbef7 834a convient constraint builder.
835
6ba6d68c 836=item B<as>
a15dff8d 837
6ba6d68c 838This is just sugar for the type constraint construction syntax.
a15dff8d 839
6ba6d68c 840=item B<where>
a15dff8d 841
6ba6d68c 842This is just sugar for the type constraint construction syntax.
76d37e5a 843
844=item B<message>
845
846This is just sugar for the type constraint construction syntax.
a15dff8d 847
8ecb1fa0 848=item B<optimize_as>
849
e85d2a5d 850This can be used to define a "hand optimized" version of your
d44714be 851type constraint which can be used to avoid traversing a subtype
e85d2a5d 852constraint heirarchy.
d44714be 853
e85d2a5d 854B<NOTE:> You should only use this if you know what you are doing,
855all the built in types use this, so your subtypes (assuming they
d44714be 856are shallow) will not likely need to use this.
857
6ba6d68c 858=back
a15dff8d 859
6ba6d68c 860=head2 Type Coercion Constructors
a15dff8d 861
e85d2a5d 862Type constraints can also contain type coercions as well. If you
863ask your accessor to coerce, then Moose will run the type-coercion
864code first, followed by the type constraint check. This feature
865should be used carefully as it is very powerful and could easily
587ae0d2 866take off a limb if you are not careful.
a15dff8d 867
25f2c3fc 868See the L<SYNOPSIS> for an example of how to use these.
a15dff8d 869
6ba6d68c 870=over 4
a15dff8d 871
6ba6d68c 872=item B<coerce>
a15dff8d 873
6ba6d68c 874=item B<from>
a15dff8d 875
6ba6d68c 876This is just sugar for the type coercion construction syntax.
877
878=item B<via>
a15dff8d 879
6ba6d68c 880This is just sugar for the type coercion construction syntax.
a15dff8d 881
882=back
883
571dd39f 884=head2 Namespace Management
885
886=over 4
887
888=item B<unimport>
889
e85d2a5d 890This will remove all the type constraint keywords from the
571dd39f 891calling class namespace.
892
893=back
894
a15dff8d 895=head1 BUGS
896
e85d2a5d 897All complex software has bugs lurking in it, and this module is no
a15dff8d 898exception. If you find a bug please either email me, or add the bug
899to cpan-RT.
900
a15dff8d 901=head1 AUTHOR
902
903Stevan Little E<lt>stevan@iinteractive.comE<gt>
904
905=head1 COPYRIGHT AND LICENSE
906
778db3ac 907Copyright 2006-2008 by Infinity Interactive, Inc.
a15dff8d 908
909L<http://www.iinteractive.com>
910
911This library is free software; you can redistribute it and/or modify
e85d2a5d 912it under the same terms as Perl itself.
a15dff8d 913
81dc201f 914=cut