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