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