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