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