lots of more refactored goodness in the TC system
[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 B 'svref_2object';
10use Sub::Exporter;
a15dff8d 11
22aed3c0 12our $VERSION = '0.14';
d44714be 13our $AUTHORITY = 'cpan:STEVAN';
a15dff8d 14
8c4acc60 15# Prototyped subs must be predeclared because we have a circular dependency
16# with Moose::Meta::Attribute et. al. so in case of us being use'd first the
17# predeclaration ensures the prototypes are in scope when consumers are
18# compiled
19
183ba44e 20sub find_type_constraint ($);
21sub _create_type_constraint ($$$;$$);
22sub _install_type_coercions ($$);
8c4acc60 23sub create_type_constraint_union (@);
183ba44e 24sub type ($$;$$);
25sub subtype ($$;$$$);
26sub coerce ($@);
27sub as ($);
28sub from ($);
29sub where (&);
30sub via (&);
31sub message (&);
32sub optimize_as (&);
33sub enum ($;@);
8c4acc60 34
4e036ee4 35use Moose::Meta::TypeConstraint;
3726f905 36use Moose::Meta::TypeConstraint::Union;
37use Moose::Meta::TypeConstraint::Container;
2ca63f5d 38use Moose::Meta::TypeCoercion;
3726f905 39use Moose::Meta::TypeCoercion::Union;
22aed3c0 40use Moose::Meta::TypeConstraint::Registry;
4e036ee4 41
571dd39f 42my @exports = qw/
8ecb1fa0 43 type subtype as where message optimize_as
571dd39f 44 coerce from via
45 enum
46 find_type_constraint
47/;
48
49Sub::Exporter::setup_exporter({
50 exports => \@exports,
51 groups => { default => [':all'] }
52});
53
54sub unimport {
55 no strict 'refs';
56 my $class = caller();
57 # loop through the exports ...
58 foreach my $name (@exports) {
59 # if we find one ...
60 if (defined &{$class . '::' . $name}) {
61 my $keyword = \&{$class . '::' . $name};
62
63 # make sure it is from Moose
64 my $pkg_name = eval { svref_2object($keyword)->GV->STASH->NAME };
65 next if $@;
66 next if $pkg_name ne 'Moose::Util::TypeConstraints';
67
68 # and if it is from Moose then undef the slot
69 delete ${$class . '::'}{$name};
70 }
2c0cbef7 71 }
571dd39f 72}
a15dff8d 73
22aed3c0 74my $REGISTRY = Moose::Meta::TypeConstraint::Registry->new;
587ae0d2 75
22aed3c0 76sub _get_type_constraint_registry { $REGISTRY }
77sub _dump_type_constraints { $REGISTRY->dump }
182134e8 78
4f8f3aab 79# NOTE:
80# this method breaks down the sugar
81# from the functions below.
22aed3c0 82sub _create_type_constraint ($$$;$$) {
83 my $name = shift;
84 my $parent = shift;
85 my $check = shift || sub { 1 };
66811d63 86
22aed3c0 87 my ($message, $optimized);
88 for (@_) {
89 $message = $_->{message} if exists $_->{message};
90 $optimized = $_->{optimized} if exists $_->{optimized};
429ccc11 91 }
22aed3c0 92
93 my $pkg_defined_in = scalar(caller(0));
429ccc11 94
22aed3c0 95 if (defined $name) {
96 my $type = $REGISTRY->get_type_constraint($name);
b1e01e3c 97
22aed3c0 98 ($type->_package_defined_in eq $pkg_defined_in)
99 || confess ("The type constraint '$name' has already been created in "
100 . $type->_package_defined_in . " and cannot be created again in "
101 . $pkg_defined_in)
102 if defined $type;
103 }
104
105 $parent = $REGISTRY->get_type_constraint($parent) if defined $parent;
4f8f3aab 106
22aed3c0 107 my $constraint = Moose::Meta::TypeConstraint->new(
108 name => $name || '__ANON__',
109 parent => $parent,
110 constraint => $check,
111 message => $message,
112 optimized => $optimized,
113 package_defined_in => $pkg_defined_in,
114 );
115
116 $REGISTRY->add_type_constraint($constraint)
117 if defined $name;
118
119 return $constraint;
120}
121
122sub _install_type_coercions ($$) {
123 my ($type_name, $coercion_map) = @_;
124 my $type = $REGISTRY->get_type_constraint($type_name);
125 (!$type->has_coercion)
126 || confess "The type coercion for '$type_name' has already been registered";
127 my $type_coercion = Moose::Meta::TypeCoercion->new(
128 type_coercion_map => $coercion_map,
129 type_constraint => $type
130 );
131 $type->coercion($type_coercion);
132}
133
134sub create_type_constraint_union (@) {
135 my (@type_constraint_names) = @_;
3726f905 136 (scalar @type_constraint_names >= 2)
137 || confess "You must pass in at least 2 type names to make a union";
138 return Moose::Meta::TypeConstraint::Union->new(
139 type_constraints => [
140 map {
141 $REGISTRY->get_type_constraint($_)
142 } @type_constraint_names
143 ],
144 );
182134e8 145}
a15dff8d 146
22aed3c0 147sub export_type_constraints_as_functions {
148 my $pkg = caller();
149 no strict 'refs';
150 foreach my $constraint (keys %{$REGISTRY->type_constraints}) {
151 *{"${pkg}::${constraint}"} = $REGISTRY->get_type_constraint($constraint)
152 ->_compiled_type_constraint;
153 }
154}
155
156*Moose::Util::TypeConstraints::export_type_contstraints_as_functions = \&export_type_constraints_as_functions;
157
158sub list_all_type_constraints { keys %{$REGISTRY->type_constraints} }
159
160## --------------------------------------------------------
161## exported functions ...
162## --------------------------------------------------------
163
164sub find_type_constraint ($) { $REGISTRY->get_type_constraint(@_) }
165
7c13858b 166# type constructors
a15dff8d 167
815ec671 168sub type ($$;$$) {
1b7df21f 169 splice(@_, 1, 0, undef);
170 goto &_create_type_constraint;
a15dff8d 171}
172
8ecb1fa0 173sub subtype ($$;$$$) {
86629f93 174 # NOTE:
175 # this adds an undef for the name
176 # if this is an anon-subtype:
177 # subtype(Num => where { $_ % 2 == 0 }) # anon 'even' subtype
178 # but if the last arg is not a code
179 # ref then it is a subtype alias:
180 # subtype(MyNumbers => as Num); # now MyNumbers is the same as Num
181 # ... yeah I know it's ugly code
182 # - SL
183 unshift @_ => undef if scalar @_ <= 2 && (reftype($_[1]) || '') eq 'CODE';
2c0cbef7 184 goto &_create_type_constraint;
a15dff8d 185}
186
4b598ea3 187sub coerce ($@) {
66811d63 188 my ($type_name, @coercion_map) = @_;
7c13858b 189 _install_type_coercions($type_name, \@coercion_map);
182134e8 190}
191
76d37e5a 192sub as ($) { $_[0] }
193sub from ($) { $_[0] }
194sub where (&) { $_[0] }
195sub via (&) { $_[0] }
8ecb1fa0 196
197sub message (&) { +{ message => $_[0] } }
198sub optimize_as (&) { +{ optimized => $_[0] } }
a15dff8d 199
2c0cbef7 200sub enum ($;@) {
fcec2383 201 my ($type_name, @values) = @_;
2c0cbef7 202 (scalar @values >= 2)
203 || confess "You must have at least two values to enumerate through";
fcec2383 204 my $regexp = join '|' => @values;
205 _create_type_constraint(
206 $type_name,
207 'Str',
208 sub { qr/^$regexp$/i }
209 );
210}
211
a15dff8d 212# define some basic types
213
f65cb534 214type 'Any' => where { 1 }; # meta-type including all
215type 'Item' => where { 1 }; # base-type
a15dff8d 216
f65cb534 217subtype 'Undef' => as 'Item' => where { !defined($_) };
218subtype 'Defined' => as 'Item' => where { defined($_) };
a15dff8d 219
8ecb1fa0 220subtype 'Bool'
221 => as 'Item'
222 => where { !defined($_) || $_ eq "" || "$_" eq '1' || "$_" eq '0' };
5a4c5493 223
8ecb1fa0 224subtype 'Value'
225 => as 'Defined'
226 => where { !ref($_) }
227 => optimize_as { defined($_[0]) && !ref($_[0]) };
228
229subtype 'Ref'
230 => as 'Defined'
231 => where { ref($_) }
232 => optimize_as { ref($_[0]) };
233
234subtype 'Str'
235 => as 'Value'
236 => where { 1 }
237 => optimize_as { defined($_[0]) && !ref($_[0]) };
238
239subtype 'Num'
240 => as 'Value'
241 => where { Scalar::Util::looks_like_number($_) }
242 => optimize_as { !ref($_[0]) && Scalar::Util::looks_like_number($_[0]) };
243
244subtype 'Int'
245 => as 'Num'
246 => where { "$_" =~ /^-?[0-9]+$/ }
247 => optimize_as { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ };
248
249subtype 'ScalarRef' => as 'Ref' => where { ref($_) eq 'SCALAR' } => optimize_as { ref($_[0]) eq 'SCALAR' };
250subtype 'ArrayRef' => as 'Ref' => where { ref($_) eq 'ARRAY' } => optimize_as { ref($_[0]) eq 'ARRAY' };
251subtype 'HashRef' => as 'Ref' => where { ref($_) eq 'HASH' } => optimize_as { ref($_[0]) eq 'HASH' };
252subtype 'CodeRef' => as 'Ref' => where { ref($_) eq 'CODE' } => optimize_as { ref($_[0]) eq 'CODE' };
253subtype 'RegexpRef' => as 'Ref' => where { ref($_) eq 'Regexp' } => optimize_as { ref($_[0]) eq 'Regexp' };
254subtype 'GlobRef' => as 'Ref' => where { ref($_) eq 'GLOB' } => optimize_as { ref($_[0]) eq 'GLOB' };
a15dff8d 255
0a5bd159 256# NOTE:
257# scalar filehandles are GLOB refs,
258# but a GLOB ref is not always a filehandle
8ecb1fa0 259subtype 'FileHandle'
260 => as 'GlobRef'
261 => where { Scalar::Util::openhandle($_) }
262 => optimize_as { ref($_[0]) eq 'GLOB' && Scalar::Util::openhandle($_[0]) };
0a5bd159 263
a15dff8d 264# NOTE:
265# blessed(qr/.../) returns true,.. how odd
8ecb1fa0 266subtype 'Object'
267 => as 'Ref'
268 => where { blessed($_) && blessed($_) ne 'Regexp' }
269 => optimize_as { blessed($_[0]) && blessed($_[0]) ne 'Regexp' };
a15dff8d 270
8ecb1fa0 271subtype 'Role'
272 => as 'Object'
273 => where { $_->can('does') }
274 => optimize_as { blessed($_[0]) && $_[0]->can('does') };
9af1d28b 275
276subtype 'ClassName'
277 => as 'Str'
278 => where { eval { $_->isa('UNIVERSAL') } }
279 => optimize_as { !ref($_[0]) && eval { $_[0]->isa('UNIVERSAL') } };
02a0fb52 280
943596a6 281{
282 my @BUILTINS = list_all_type_constraints();
283 sub list_all_builtin_type_constraints { @BUILTINS }
284}
285
a15dff8d 2861;
287
288__END__
289
290=pod
291
292=head1 NAME
293
e522431d 294Moose::Util::TypeConstraints - Type constraint system for Moose
a15dff8d 295
296=head1 SYNOPSIS
297
298 use Moose::Util::TypeConstraints;
299
2c0cbef7 300 type 'Num' => where { Scalar::Util::looks_like_number($_) };
a15dff8d 301
2c0cbef7 302 subtype 'Natural'
303 => as 'Num'
a15dff8d 304 => where { $_ > 0 };
305
2c0cbef7 306 subtype 'NaturalLessThanTen'
307 => as 'Natural'
79592a54 308 => where { $_ < 10 }
309 => message { "This number ($_) is not less than ten!" };
6b8bd8d3 310
2c0cbef7 311 coerce 'Num'
312 => from 'Str'
d6e2d9a1 313 => via { 0+$_ };
98aae381 314
2c0cbef7 315 enum 'RGBColors' => qw(red green blue);
a15dff8d 316
317=head1 DESCRIPTION
318
d44714be 319This module provides Moose with the ability to create custom type
320contraints to be used in attribute definition.
e522431d 321
6ba6d68c 322=head2 Important Caveat
323
324This is B<NOT> a type system for Perl 5. These are type constraints,
325and they are not used by Moose unless you tell it to. No type
326inference is performed, expression are not typed, etc. etc. etc.
327
328This is simply a means of creating small constraint functions which
a7d0cd00 329can be used to simplify your own type-checking code.
6ba6d68c 330
2c0cbef7 331=head2 Slightly Less Important Caveat
332
333It is almost always a good idea to quote your type and subtype names.
43d599e5 334This is to prevent perl from trying to execute the call as an indirect
2c0cbef7 335object call. This issue only seems to come up when you have a subtype
336the same name as a valid class, but when the issue does arise it tends
337to be quite annoying to debug.
338
339So for instance, this:
340
341 subtype DateTime => as Object => where { $_->isa('DateTime') };
342
343will I<Just Work>, while this:
344
345 use DateTime;
346 subtype DateTime => as Object => where { $_->isa('DateTime') };
347
348will fail silently and cause many headaches. The simple way to solve
349this, as well as future proof your subtypes from classes which have
350yet to have been created yet, is to simply do this:
351
352 use DateTime;
d44714be 353 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
2c0cbef7 354
6ba6d68c 355=head2 Default Type Constraints
e522431d 356
e522431d 357This module also provides a simple hierarchy for Perl 5 types, this
358could probably use some work, but it works for me at the moment.
359
360 Any
f65cb534 361 Item
5a4c5493 362 Bool
f65cb534 363 Undef
364 Defined
5a4c5493 365 Value
366 Num
367 Int
368 Str
9af1d28b 369 ClassName
5a4c5493 370 Ref
371 ScalarRef
451c8248 372 ArrayRef
373 HashRef
5a4c5493 374 CodeRef
375 RegexpRef
3f7376b0 376 GlobRef
0a5bd159 377 FileHandle
5a4c5493 378 Object
379 Role
e522431d 380
6ba6d68c 381Suggestions for improvement are welcome.
2c0cbef7 382
383B<NOTE:> The C<Undef> type constraint does not work correctly
384in every occasion, please use it sparringly.
703e92fb 385
9af1d28b 386B<NOTE:> The C<ClassName> type constraint is simply a subtype
387of string which responds true to C<isa('UNIVERSAL')>. This means
388that your class B<must> be loaded for this type constraint to
389pass. I know this is not ideal for all, but it is a saner
c2a69ef1 390restriction than most others.
9af1d28b 391
703e92fb 392=head2 Use with Other Constraint Modules
393
394This module should play fairly nicely with other constraint
395modules with only some slight tweaking. The C<where> clause
396in types is expected to be a C<CODE> reference which checks
397it's first argument and returns a bool. Since most constraint
398modules work in a similar way, it should be simple to adapt
399them to work with Moose.
400
401For instance, this is how you could use it with
66c57662 402L<Declare::Constraints::Simple> to declare a completely new type.
703e92fb 403
404 type 'HashOfArrayOfObjects'
405 => IsHashRef(
406 -keys => HasLength,
407 -values => IsArrayRef( IsObject ));
408
409For more examples see the F<t/204_example_w_DCS.t> test file.
410
411Here is an example of using L<Test::Deep> and it's non-test
412related C<eq_deeply> function.
413
414 type 'ArrayOfHashOfBarsAndRandomNumbers'
415 => where {
416 eq_deeply($_,
417 array_each(subhashof({
418 bar => isa('Bar'),
419 random_number => ignore()
420 })))
421 };
422
423For a complete example see the F<t/205_example_w_TestDeep.t>
424test file.
e522431d 425
a15dff8d 426=head1 FUNCTIONS
427
182134e8 428=head2 Type Constraint Registry
429
430=over 4
431
432=item B<find_type_constraint ($type_name)>
433
c2a69ef1 434This function can be used to locate a specific type constraint
435meta-object, of the class L<Moose::Meta::TypeConstraint> or a
436derivative. What you do with it from there is up to you :)
182134e8 437
c07af9d2 438=item B<create_type_constraint_union (@type_constraint_names)>
439
440Given a list of C<@type_constraint_names>, this will return a
441B<Moose::Meta::TypeConstraint::Union> instance.
442
429ccc11 443=item B<export_type_constraints_as_functions>
182134e8 444
6ba6d68c 445This will export all the current type constraints as functions
446into the caller's namespace. Right now, this is mostly used for
447testing, but it might prove useful to others.
448
429ccc11 449=item B<export_type_contstraints_as_functions>
450
451Alias for the above function.
452
b1e01e3c 453=item B<list_all_type_constraints>
454
455This will return a list of type constraint names, you can then
456fetch them using C<find_type_constraint ($type_name)> if you
457want to.
458
943596a6 459=item B<list_all_builtin_type_constraints>
460
461This will return a list of builtin type constraints, meaning,
462those which are defined in this module. See the section
463labeled L<Default Type Constraints> for a complete list.
464
182134e8 465=back
466
a15dff8d 467=head2 Type Constraint Constructors
468
6ba6d68c 469The following functions are used to create type constraints.
470They will then register the type constraints in a global store
471where Moose can get to them if it needs to.
a15dff8d 472
25f2c3fc 473See the L<SYNOPSIS> for an example of how to use these.
a15dff8d 474
6ba6d68c 475=over 4
a15dff8d 476
6ba6d68c 477=item B<type ($name, $where_clause)>
a15dff8d 478
6ba6d68c 479This creates a base type, which has no parent.
a15dff8d 480
79592a54 481=item B<subtype ($name, $parent, $where_clause, ?$message)>
182134e8 482
6ba6d68c 483This creates a named subtype.
d6e2d9a1 484
79592a54 485=item B<subtype ($parent, $where_clause, ?$message)>
182134e8 486
6ba6d68c 487This creates an unnamed subtype and will return the type
488constraint meta-object, which will be an instance of
489L<Moose::Meta::TypeConstraint>.
a15dff8d 490
fcec2383 491=item B<enum ($name, @values)>
492
2c0cbef7 493This will create a basic subtype for a given set of strings.
494The resulting constraint will be a subtype of C<Str> and
495will match any of the items in C<@values>. See the L<SYNOPSIS>
496for a simple example.
497
498B<NOTE:> This is not a true proper enum type, it is simple
499a convient constraint builder.
500
6ba6d68c 501=item B<as>
a15dff8d 502
6ba6d68c 503This is just sugar for the type constraint construction syntax.
a15dff8d 504
6ba6d68c 505=item B<where>
a15dff8d 506
6ba6d68c 507This is just sugar for the type constraint construction syntax.
76d37e5a 508
509=item B<message>
510
511This is just sugar for the type constraint construction syntax.
a15dff8d 512
8ecb1fa0 513=item B<optimize_as>
514
d44714be 515This can be used to define a "hand optimized" version of your
516type constraint which can be used to avoid traversing a subtype
517constraint heirarchy.
518
519B<NOTE:> You should only use this if you know what you are doing,
520all the built in types use this, so your subtypes (assuming they
521are shallow) will not likely need to use this.
522
6ba6d68c 523=back
a15dff8d 524
6ba6d68c 525=head2 Type Coercion Constructors
a15dff8d 526
587ae0d2 527Type constraints can also contain type coercions as well. If you
c2a69ef1 528ask your accessor to coerce, then Moose will run the type-coercion
587ae0d2 529code first, followed by the type constraint check. This feature
530should be used carefully as it is very powerful and could easily
531take off a limb if you are not careful.
a15dff8d 532
25f2c3fc 533See the L<SYNOPSIS> for an example of how to use these.
a15dff8d 534
6ba6d68c 535=over 4
a15dff8d 536
6ba6d68c 537=item B<coerce>
a15dff8d 538
6ba6d68c 539=item B<from>
a15dff8d 540
6ba6d68c 541This is just sugar for the type coercion construction syntax.
542
543=item B<via>
a15dff8d 544
6ba6d68c 545This is just sugar for the type coercion construction syntax.
a15dff8d 546
547=back
548
571dd39f 549=head2 Namespace Management
550
551=over 4
552
553=item B<unimport>
554
555This will remove all the type constraint keywords from the
556calling class namespace.
557
558=back
559
a15dff8d 560=head1 BUGS
561
562All complex software has bugs lurking in it, and this module is no
563exception. If you find a bug please either email me, or add the bug
564to cpan-RT.
565
a15dff8d 566=head1 AUTHOR
567
568Stevan Little E<lt>stevan@iinteractive.comE<gt>
569
570=head1 COPYRIGHT AND LICENSE
571
b77fdbed 572Copyright 2006, 2007 by Infinity Interactive, Inc.
a15dff8d 573
574L<http://www.iinteractive.com>
575
576This library is free software; you can redistribute it and/or modify
577it under the same terms as Perl itself.
578
81dc201f 579=cut