foo
[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+$_ };
a15dff8d 181
182=head1 DESCRIPTION
183
e522431d 184This module provides Moose with the ability to create type contraints
185to be are used in both attribute definitions and for method argument
186validation.
187
6ba6d68c 188=head2 Important Caveat
189
190This is B<NOT> a type system for Perl 5. These are type constraints,
191and they are not used by Moose unless you tell it to. No type
192inference is performed, expression are not typed, etc. etc. etc.
193
194This is simply a means of creating small constraint functions which
a7d0cd00 195can be used to simplify your own type-checking code.
6ba6d68c 196
197=head2 Default Type Constraints
e522431d 198
e522431d 199This module also provides a simple hierarchy for Perl 5 types, this
200could probably use some work, but it works for me at the moment.
201
202 Any
f65cb534 203 Item
5a4c5493 204 Bool
f65cb534 205 Undef
206 Defined
5a4c5493 207 Value
208 Num
209 Int
210 Str
211 Ref
212 ScalarRef
451c8248 213 ArrayRef
214 HashRef
5a4c5493 215 CodeRef
216 RegexpRef
217 Object
218 Role
e522431d 219
6ba6d68c 220Suggestions for improvement are welcome.
e522431d 221
a15dff8d 222=head1 FUNCTIONS
223
182134e8 224=head2 Type Constraint Registry
225
226=over 4
227
228=item B<find_type_constraint ($type_name)>
229
6ba6d68c 230This function can be used to locate a specific type constraint
231meta-object. What you do with it from there is up to you :)
182134e8 232
c07af9d2 233=item B<create_type_constraint_union (@type_constraint_names)>
234
235Given a list of C<@type_constraint_names>, this will return a
236B<Moose::Meta::TypeConstraint::Union> instance.
237
182134e8 238=item B<export_type_contstraints_as_functions>
239
6ba6d68c 240This will export all the current type constraints as functions
241into the caller's namespace. Right now, this is mostly used for
242testing, but it might prove useful to others.
243
182134e8 244=back
245
a15dff8d 246=head2 Type Constraint Constructors
247
6ba6d68c 248The following functions are used to create type constraints.
249They will then register the type constraints in a global store
250where Moose can get to them if it needs to.
a15dff8d 251
6ba6d68c 252See the L<SYNOPOSIS> for an example of how to use these.
a15dff8d 253
6ba6d68c 254=over 4
a15dff8d 255
6ba6d68c 256=item B<type ($name, $where_clause)>
a15dff8d 257
6ba6d68c 258This creates a base type, which has no parent.
a15dff8d 259
79592a54 260=item B<subtype ($name, $parent, $where_clause, ?$message)>
182134e8 261
6ba6d68c 262This creates a named subtype.
d6e2d9a1 263
79592a54 264=item B<subtype ($parent, $where_clause, ?$message)>
182134e8 265
6ba6d68c 266This creates an unnamed subtype and will return the type
267constraint meta-object, which will be an instance of
268L<Moose::Meta::TypeConstraint>.
a15dff8d 269
fcec2383 270=item B<enum ($name, @values)>
271
6ba6d68c 272=item B<as>
a15dff8d 273
6ba6d68c 274This is just sugar for the type constraint construction syntax.
a15dff8d 275
6ba6d68c 276=item B<where>
a15dff8d 277
6ba6d68c 278This is just sugar for the type constraint construction syntax.
76d37e5a 279
280=item B<message>
281
282This is just sugar for the type constraint construction syntax.
a15dff8d 283
6ba6d68c 284=back
a15dff8d 285
6ba6d68c 286=head2 Type Coercion Constructors
a15dff8d 287
6ba6d68c 288Type constraints can also contain type coercions as well. In most
289cases Moose will run the type-coercion code first, followed by the
290type constraint check. This feature should be used carefully as it
291is very powerful and could easily take off a limb if you are not
292careful.
a15dff8d 293
6ba6d68c 294See the L<SYNOPOSIS> for an example of how to use these.
a15dff8d 295
6ba6d68c 296=over 4
a15dff8d 297
6ba6d68c 298=item B<coerce>
a15dff8d 299
6ba6d68c 300=item B<from>
a15dff8d 301
6ba6d68c 302This is just sugar for the type coercion construction syntax.
303
304=item B<via>
a15dff8d 305
6ba6d68c 306This is just sugar for the type coercion construction syntax.
a15dff8d 307
308=back
309
310=head1 BUGS
311
312All complex software has bugs lurking in it, and this module is no
313exception. If you find a bug please either email me, or add the bug
314to cpan-RT.
315
a15dff8d 316=head1 AUTHOR
317
318Stevan Little E<lt>stevan@iinteractive.comE<gt>
319
320=head1 COPYRIGHT AND LICENSE
321
322Copyright 2006 by Infinity Interactive, Inc.
323
324L<http://www.iinteractive.com>
325
326This library is free software; you can redistribute it and/or modify
327it under the same terms as Perl itself.
328
81dc201f 329=cut