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