changes-and-comments
[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';
9
5204cd52 10our $VERSION = '0.05';
a15dff8d 11
4e036ee4 12use Moose::Meta::TypeConstraint;
2ca63f5d 13use Moose::Meta::TypeCoercion;
4e036ee4 14
a3c7e2fe 15{
16 require Sub::Exporter;
17
fcec2383 18 my @exports = qw[type subtype as where message coerce from via find_type_constraint enum];
a3c7e2fe 19
20 Sub::Exporter->import(
21 -setup => {
22 exports => \@exports,
23 groups => {
24 default => [':all']
25 }
26 }
27 );
a15dff8d 28}
29
182134e8 30{
31 my %TYPES;
446e850f 32 sub find_type_constraint {
33 return $TYPES{$_[0]}->[1]
34 if exists $TYPES{$_[0]};
35 return;
36 }
37
38 sub _dump_type_constraints {
39 require Data::Dumper;
256903b6 40 Data::Dumper::Dumper(\%TYPES);
446e850f 41 }
42
7c13858b 43 sub _create_type_constraint {
76d37e5a 44 my ($name, $parent, $check, $message) = @_;
0e6614c3 45 my $pkg_defined_in = scalar(caller(1));
46 ($TYPES{$name}->[0] eq $pkg_defined_in)
446e850f 47 || confess "The type constraint '$name' has already been created "
0e6614c3 48 if defined $name && exists $TYPES{$name};
49 $parent = find_type_constraint($parent) if defined $parent;
a27aa600 50 my $constraint = Moose::Meta::TypeConstraint->new(
51 name => $name || '__ANON__',
66811d63 52 parent => $parent,
76d37e5a 53 constraint => $check,
54 message => $message,
4e036ee4 55 );
0e6614c3 56 $TYPES{$name} = [ $pkg_defined_in, $constraint ] if defined $name;
a27aa600 57 return $constraint;
182134e8 58 }
182134e8 59
7c13858b 60 sub _install_type_coercions {
a27aa600 61 my ($type_name, $coercion_map) = @_;
0e6614c3 62 my $type = find_type_constraint($type_name);
4e036ee4 63 (!$type->has_coercion)
d46a48f3 64 || confess "The type coercion for '$type_name' has already been registered";
a27aa600 65 my $type_coercion = Moose::Meta::TypeCoercion->new(
66 type_coercion_map => $coercion_map,
67 type_constraint => $type
68 );
69 $type->coercion($type_coercion);
182134e8 70 }
66811d63 71
c07af9d2 72 sub create_type_constraint_union {
73 my (@type_constraint_names) = @_;
74 return Moose::Meta::TypeConstraint->union(
75 map {
76 find_type_constraint($_)
77 } @type_constraint_names
78 );
79 }
80
66811d63 81 sub export_type_contstraints_as_functions {
82 my $pkg = caller();
83 no strict 'refs';
84 foreach my $constraint (keys %TYPES) {
0e6614c3 85 *{"${pkg}::${constraint}"} = find_type_constraint($constraint)->_compiled_type_constraint;
66811d63 86 }
87 }
182134e8 88}
a15dff8d 89
7c13858b 90# type constructors
a15dff8d 91
92sub type ($$) {
93 my ($name, $check) = @_;
7c13858b 94 _create_type_constraint($name, undef, $check);
a15dff8d 95}
96
76d37e5a 97sub subtype ($$;$$) {
98 unshift @_ => undef if scalar @_ <= 2;
7c13858b 99 _create_type_constraint(@_);
a15dff8d 100}
101
4b598ea3 102sub coerce ($@) {
66811d63 103 my ($type_name, @coercion_map) = @_;
7c13858b 104 _install_type_coercions($type_name, \@coercion_map);
182134e8 105}
106
76d37e5a 107sub as ($) { $_[0] }
108sub from ($) { $_[0] }
109sub where (&) { $_[0] }
110sub via (&) { $_[0] }
111sub message (&) { $_[0] }
a15dff8d 112
fcec2383 113sub enum {
114 my ($type_name, @values) = @_;
115 my $regexp = join '|' => @values;
116 _create_type_constraint(
117 $type_name,
118 'Str',
119 sub { qr/^$regexp$/i }
120 );
121}
122
a15dff8d 123# define some basic types
124
f65cb534 125type 'Any' => where { 1 }; # meta-type including all
126type 'Item' => where { 1 }; # base-type
a15dff8d 127
f65cb534 128subtype 'Undef' => as 'Item' => where { !defined($_) };
129subtype 'Defined' => as 'Item' => where { defined($_) };
a15dff8d 130
81dc201f 131subtype 'Bool' => as 'Item' => where { !defined($_) || $_ eq "" || "$_" eq '1' || "$_" eq '0' };
5204cd52 132
5a4c5493 133subtype 'Value' => as 'Defined' => where { !ref($_) };
134subtype 'Ref' => as 'Defined' => where { ref($_) };
135
136subtype 'Str' => as 'Value' => where { 1 };
a15dff8d 137
81dc201f 138subtype 'Num' => as 'Value' => where { Scalar::Util::looks_like_number($_) };
d4634ca2 139subtype 'Int' => as 'Num' => where { "$_" =~ /^-?[0-9]+$/ };
81dc201f 140
141subtype 'ScalarRef' => as 'Ref' => where { ref($_) eq 'SCALAR' };
451c8248 142subtype 'ArrayRef' => as 'Ref' => where { ref($_) eq 'ARRAY' };
143subtype 'HashRef' => as 'Ref' => where { ref($_) eq 'HASH' };
e9ec68d6 144subtype 'CodeRef' => as 'Ref' => where { ref($_) eq 'CODE' };
145subtype 'RegexpRef' => as 'Ref' => where { ref($_) eq 'Regexp' };
a15dff8d 146
147# NOTE:
148# blessed(qr/.../) returns true,.. how odd
e9ec68d6 149subtype 'Object' => as 'Ref' => where { blessed($_) && blessed($_) ne 'Regexp' };
a15dff8d 150
02a0fb52 151subtype 'Role' => as 'Object' => where { $_->can('does') };
152
a15dff8d 1531;
154
155__END__
156
157=pod
158
159=head1 NAME
160
e522431d 161Moose::Util::TypeConstraints - Type constraint system for Moose
a15dff8d 162
163=head1 SYNOPSIS
164
165 use Moose::Util::TypeConstraints;
166
167 type Num => where { Scalar::Util::looks_like_number($_) };
168
169 subtype Natural
170 => as Num
171 => where { $_ > 0 };
172
173 subtype NaturalLessThanTen
174 => as Natural
79592a54 175 => where { $_ < 10 }
176 => message { "This number ($_) is not less than ten!" };
6b8bd8d3 177
178 coerce Num
d6e2d9a1 179 => from Str
180 => via { 0+$_ };
98aae381 181
182 enum RGBColors => qw(red green blue);
a15dff8d 183
184=head1 DESCRIPTION
185
e522431d 186This module provides Moose with the ability to create type contraints
187to be are used in both attribute definitions and for method argument
188validation.
189
6ba6d68c 190=head2 Important Caveat
191
192This is B<NOT> a type system for Perl 5. These are type constraints,
193and they are not used by Moose unless you tell it to. No type
194inference is performed, expression are not typed, etc. etc. etc.
195
196This is simply a means of creating small constraint functions which
a7d0cd00 197can be used to simplify your own type-checking code.
6ba6d68c 198
199=head2 Default Type Constraints
e522431d 200
e522431d 201This module also provides a simple hierarchy for Perl 5 types, this
202could probably use some work, but it works for me at the moment.
203
204 Any
f65cb534 205 Item
5a4c5493 206 Bool
f65cb534 207 Undef
208 Defined
5a4c5493 209 Value
210 Num
211 Int
212 Str
213 Ref
214 ScalarRef
451c8248 215 ArrayRef
216 HashRef
5a4c5493 217 CodeRef
218 RegexpRef
219 Object
220 Role
e522431d 221
6ba6d68c 222Suggestions for improvement are welcome.
e522431d 223
a15dff8d 224=head1 FUNCTIONS
225
182134e8 226=head2 Type Constraint Registry
227
228=over 4
229
230=item B<find_type_constraint ($type_name)>
231
6ba6d68c 232This function can be used to locate a specific type constraint
233meta-object. What you do with it from there is up to you :)
182134e8 234
c07af9d2 235=item B<create_type_constraint_union (@type_constraint_names)>
236
237Given a list of C<@type_constraint_names>, this will return a
238B<Moose::Meta::TypeConstraint::Union> instance.
239
182134e8 240=item B<export_type_contstraints_as_functions>
241
6ba6d68c 242This will export all the current type constraints as functions
243into the caller's namespace. Right now, this is mostly used for
244testing, but it might prove useful to others.
245
182134e8 246=back
247
a15dff8d 248=head2 Type Constraint Constructors
249
6ba6d68c 250The following functions are used to create type constraints.
251They will then register the type constraints in a global store
252where Moose can get to them if it needs to.
a15dff8d 253
6ba6d68c 254See the L<SYNOPOSIS> for an example of how to use these.
a15dff8d 255
6ba6d68c 256=over 4
a15dff8d 257
6ba6d68c 258=item B<type ($name, $where_clause)>
a15dff8d 259
6ba6d68c 260This creates a base type, which has no parent.
a15dff8d 261
79592a54 262=item B<subtype ($name, $parent, $where_clause, ?$message)>
182134e8 263
6ba6d68c 264This creates a named subtype.
d6e2d9a1 265
79592a54 266=item B<subtype ($parent, $where_clause, ?$message)>
182134e8 267
6ba6d68c 268This creates an unnamed subtype and will return the type
269constraint meta-object, which will be an instance of
270L<Moose::Meta::TypeConstraint>.
a15dff8d 271
fcec2383 272=item B<enum ($name, @values)>
273
6ba6d68c 274=item B<as>
a15dff8d 275
6ba6d68c 276This is just sugar for the type constraint construction syntax.
a15dff8d 277
6ba6d68c 278=item B<where>
a15dff8d 279
6ba6d68c 280This is just sugar for the type constraint construction syntax.
76d37e5a 281
282=item B<message>
283
284This is just sugar for the type constraint construction syntax.
a15dff8d 285
6ba6d68c 286=back
a15dff8d 287
6ba6d68c 288=head2 Type Coercion Constructors
a15dff8d 289
6ba6d68c 290Type constraints can also contain type coercions as well. In most
291cases Moose will run the type-coercion code first, followed by the
292type constraint check. This feature should be used carefully as it
293is very powerful and could easily take off a limb if you are not
294careful.
a15dff8d 295
6ba6d68c 296See the L<SYNOPOSIS> for an example of how to use these.
a15dff8d 297
6ba6d68c 298=over 4
a15dff8d 299
6ba6d68c 300=item B<coerce>
a15dff8d 301
6ba6d68c 302=item B<from>
a15dff8d 303
6ba6d68c 304This is just sugar for the type coercion construction syntax.
305
306=item B<via>
a15dff8d 307
6ba6d68c 308This is just sugar for the type coercion construction syntax.
a15dff8d 309
310=back
311
312=head1 BUGS
313
314All complex software has bugs lurking in it, and this module is no
315exception. If you find a bug please either email me, or add the bug
316to cpan-RT.
317
a15dff8d 318=head1 AUTHOR
319
320Stevan Little E<lt>stevan@iinteractive.comE<gt>
321
322=head1 COPYRIGHT AND LICENSE
323
324Copyright 2006 by Infinity Interactive, Inc.
325
326L<http://www.iinteractive.com>
327
328This library is free software; you can redistribute it and/or modify
329it under the same terms as Perl itself.
330
81dc201f 331=cut