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