d987f0e76e7e903d684ddff0317883c211c3c30f
[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 'Value' => as 'Item' => where { !ref($_) };
113 subtype 'Ref'   => as 'Item' => where {  ref($_) };
114
115 subtype 'Bool'  => as 'Item' => where { "$_" eq '1' || "$_" eq '0' };
116
117 subtype 'Int' => as 'Value' => where {  Scalar::Util::looks_like_number($_) };
118 subtype 'Str' => as 'Value' => where { !Scalar::Util::looks_like_number($_) };
119
120 subtype 'ScalarRef' => as 'Ref' => where { ref($_) eq 'SCALAR' };       
121
122 subtype 'CollectionRef' => as 'Ref' => where { ref($_) eq 'ARRAY' || ref($_) eq 'HASH' };
123
124 subtype 'ArrayRef' => as 'CollectionRef' => where { ref($_) eq 'ARRAY'  };
125 subtype 'HashRef'  => as 'CollectionRef' => where { ref($_) eq 'HASH'   };      
126
127 subtype 'CodeRef'   => as 'Ref' => where { ref($_) eq 'CODE'   };
128 subtype 'RegexpRef' => as 'Ref' => where { ref($_) eq 'Regexp' };       
129
130 # NOTE: 
131 # blessed(qr/.../) returns true,.. how odd
132 subtype 'Object' => as 'Ref' => where { blessed($_) && blessed($_) ne 'Regexp' };
133
134 subtype 'Role' => as 'Object' => where { $_->can('does') };
135
136 1;
137
138 __END__
139
140 =pod
141
142 =head1 NAME
143
144 Moose::Util::TypeConstraints - Type constraint system for Moose
145
146 =head1 SYNOPSIS
147
148   use Moose::Util::TypeConstraints;
149
150   type Num => where { Scalar::Util::looks_like_number($_) };
151   
152   subtype Natural 
153       => as Num 
154       => where { $_ > 0 };
155   
156   subtype NaturalLessThanTen 
157       => as Natural
158       => where { $_ < 10 }
159       => message { "This number ($_) is not less than ten!" };
160       
161   coerce Num 
162       => from Str
163         => via { 0+$_ }; 
164
165 =head1 DESCRIPTION
166
167 This module provides Moose with the ability to create type contraints 
168 to be are used in both attribute definitions and for method argument 
169 validation. 
170
171 =head2 Important Caveat
172
173 This is B<NOT> a type system for Perl 5. These are type constraints, 
174 and they are not used by Moose unless you tell it to. No type 
175 inference is performed, expression are not typed, etc. etc. etc. 
176
177 This is simply a means of creating small constraint functions which 
178 can be used to simplify your own type-checking code.
179
180 =head2 Default Type Constraints
181
182 This module also provides a simple hierarchy for Perl 5 types, this 
183 could probably use some work, but it works for me at the moment.
184
185   Any
186   
187   Item 
188       Undef
189       Defined
190       Bool
191       Value
192           Int
193           Str
194       Ref
195           ScalarRef
196           CollectionRef
197               ArrayRef
198               HashRef
199           CodeRef
200           RegexpRef
201           Object        
202               Role
203
204 Suggestions for improvement are welcome.
205     
206 =head1 FUNCTIONS
207
208 =head2 Type Constraint Registry
209
210 =over 4
211
212 =item B<find_type_constraint ($type_name)>
213
214 This function can be used to locate a specific type constraint 
215 meta-object. What you do with it from there is up to you :)
216
217 =item B<export_type_contstraints_as_functions>
218
219 This will export all the current type constraints as functions 
220 into the caller's namespace. Right now, this is mostly used for 
221 testing, but it might prove useful to others.
222
223 =back
224
225 =head2 Type Constraint Constructors
226
227 The following functions are used to create type constraints. 
228 They will then register the type constraints in a global store 
229 where Moose can get to them if it needs to. 
230
231 See the L<SYNOPOSIS> for an example of how to use these.
232
233 =over 4
234
235 =item B<type ($name, $where_clause)>
236
237 This creates a base type, which has no parent. 
238
239 =item B<subtype ($name, $parent, $where_clause, ?$message)>
240
241 This creates a named subtype. 
242
243 =item B<subtype ($parent, $where_clause, ?$message)>
244
245 This creates an unnamed subtype and will return the type 
246 constraint meta-object, which will be an instance of 
247 L<Moose::Meta::TypeConstraint>. 
248
249 =item B<as>
250
251 This is just sugar for the type constraint construction syntax.
252
253 =item B<where>
254
255 This is just sugar for the type constraint construction syntax.
256
257 =item B<message>
258
259 This is just sugar for the type constraint construction syntax.
260
261 =back
262
263 =head2 Type Coercion Constructors
264
265 Type constraints can also contain type coercions as well. In most 
266 cases Moose will run the type-coercion code first, followed by the 
267 type constraint check. This feature should be used carefully as it 
268 is very powerful and could easily take off a limb if you are not 
269 careful.
270
271 See the L<SYNOPOSIS> for an example of how to use these.
272
273 =over 4
274
275 =item B<coerce>
276
277 =item B<from>
278
279 This is just sugar for the type coercion construction syntax.
280
281 =item B<via>
282
283 This is just sugar for the type coercion construction syntax.
284
285 =back
286
287 =head1 BUGS
288
289 All complex software has bugs lurking in it, and this module is no 
290 exception. If you find a bug please either email me, or add the bug
291 to cpan-RT.
292
293 =head1 AUTHOR
294
295 Stevan Little E<lt>stevan@iinteractive.comE<gt>
296
297 =head1 COPYRIGHT AND LICENSE
298
299 Copyright 2006 by Infinity Interactive, Inc.
300
301 L<http://www.iinteractive.com>
302
303 This library is free software; you can redistribute it and/or modify
304 it under the same terms as Perl itself. 
305
306 =cut