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