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