Commit | Line | Data |
a15dff8d |
1 | |
2 | package Moose::Util::TypeConstraints; |
3 | |
4 | use strict; |
5 | use warnings; |
6 | |
e90c03d0 |
7 | use Carp 'confess'; |
a15dff8d |
8 | use Sub::Name 'subname'; |
9 | use Scalar::Util 'blessed'; |
10 | |
5569c072 |
11 | our $VERSION = '0.02'; |
a15dff8d |
12 | |
4e036ee4 |
13 | use Moose::Meta::TypeConstraint; |
14 | |
a15dff8d |
15 | sub import { |
16 | shift; |
17 | my $pkg = shift || caller(); |
18 | return if $pkg eq ':no_export'; |
19 | no strict 'refs'; |
d6e2d9a1 |
20 | foreach my $export (qw(type subtype as where coerce from via)) { |
a15dff8d |
21 | *{"${pkg}::${export}"} = \&{"${export}"}; |
a15dff8d |
22 | } |
a15dff8d |
23 | } |
24 | |
182134e8 |
25 | { |
26 | my %TYPES; |
27 | sub find_type_constraint { |
28 | my $type_name = shift; |
01bf4112 |
29 | $TYPES{$type_name}; |
182134e8 |
30 | } |
31 | |
32 | sub register_type_constraint { |
33 | my ($type_name, $type_constraint) = @_; |
d46a48f3 |
34 | (not exists $TYPES{$type_name}) |
35 | || confess "The type constraint '$type_name' has already been registered"; |
4e036ee4 |
36 | $TYPES{$type_name} = Moose::Meta::TypeConstraint->new( |
37 | name => $type_name, |
38 | constraint_code => $type_constraint |
39 | ); |
182134e8 |
40 | } |
41 | |
4b598ea3 |
42 | sub dump_type_constraints { |
43 | require Data::Dumper; |
44 | $Data::Dumper::Deparse = 1; |
45 | Data::Dumper::Dumper(\%TYPES); |
46 | } |
47 | |
182134e8 |
48 | sub export_type_contstraints_as_functions { |
49 | my $pkg = caller(); |
50 | no strict 'refs'; |
51 | foreach my $constraint (keys %TYPES) { |
4e036ee4 |
52 | *{"${pkg}::${constraint}"} = $TYPES{$constraint}->constraint_code; |
182134e8 |
53 | } |
54 | } |
182134e8 |
55 | |
182134e8 |
56 | sub find_type_coercion { |
57 | my $type_name = shift; |
4e036ee4 |
58 | $TYPES{$type_name}->coercion_code; |
182134e8 |
59 | } |
60 | |
61 | sub register_type_coercion { |
62 | my ($type_name, $type_coercion) = @_; |
4e036ee4 |
63 | my $type = $TYPES{$type_name}; |
64 | (!$type->has_coercion) |
d46a48f3 |
65 | || confess "The type coercion for '$type_name' has already been registered"; |
4e036ee4 |
66 | $type->set_coercion_code($type_coercion); |
182134e8 |
67 | } |
68 | } |
a15dff8d |
69 | |
a15dff8d |
70 | |
71 | sub type ($$) { |
72 | my ($name, $check) = @_; |
182134e8 |
73 | my $full_name = caller() . "::${name}"; |
74 | register_type_constraint($name => subname $full_name => sub { |
a15dff8d |
75 | local $_ = $_[0]; |
76 | return undef unless $check->($_[0]); |
77 | $_[0]; |
182134e8 |
78 | }); |
a15dff8d |
79 | } |
80 | |
81 | sub subtype ($$;$) { |
82 | my ($name, $parent, $check) = @_; |
83 | if (defined $check) { |
182134e8 |
84 | my $full_name = caller() . "::${name}"; |
01bf4112 |
85 | $parent = find_type_constraint($parent)->constraint_code |
182134e8 |
86 | unless $parent && ref($parent) eq 'CODE'; |
6b8bd8d3 |
87 | register_type_constraint($name => subname $full_name => sub { |
a15dff8d |
88 | local $_ = $_[0]; |
89 | return undef unless defined $parent->($_[0]) && $check->($_[0]); |
90 | $_[0]; |
182134e8 |
91 | }); |
a15dff8d |
92 | } |
93 | else { |
94 | ($parent, $check) = ($name, $parent); |
01bf4112 |
95 | $parent = find_type_constraint($parent)->constraint_code |
182134e8 |
96 | unless $parent && ref($parent) eq 'CODE'; |
97 | return subname '__anon_subtype__' => sub { |
a15dff8d |
98 | local $_ = $_[0]; |
99 | return undef unless defined $parent->($_[0]) && $check->($_[0]); |
100 | $_[0]; |
5569c072 |
101 | }; |
a15dff8d |
102 | } |
103 | } |
104 | |
4b598ea3 |
105 | sub coerce ($@) { |
e90c03d0 |
106 | my ($type_name, @coercion_map) = @_; |
4b598ea3 |
107 | #use Data::Dumper; |
108 | #warn Dumper \@coercion_map; |
e90c03d0 |
109 | my @coercions; |
110 | while (@coercion_map) { |
111 | my ($constraint_name, $action) = splice(@coercion_map, 0, 2); |
01bf4112 |
112 | my $constraint = find_type_constraint($constraint_name)->constraint_code; |
e90c03d0 |
113 | (defined $constraint) |
114 | || confess "Could not find the type constraint ($constraint_name)"; |
115 | push @coercions => [ $constraint, $action ]; |
116 | } |
182134e8 |
117 | register_type_coercion($type_name, sub { |
e90c03d0 |
118 | my $thing = shift; |
119 | foreach my $coercion (@coercions) { |
120 | my ($constraint, $converter) = @$coercion; |
121 | if (defined $constraint->($thing)) { |
b841b2a3 |
122 | local $_ = $thing; |
e90c03d0 |
123 | return $converter->($thing); |
124 | } |
125 | } |
126 | return $thing; |
182134e8 |
127 | }); |
128 | } |
129 | |
a15dff8d |
130 | sub as ($) { $_[0] } |
d6e2d9a1 |
131 | sub from ($) { $_[0] } |
a15dff8d |
132 | sub where (&) { $_[0] } |
d6e2d9a1 |
133 | sub via (&) { $_[0] } |
a15dff8d |
134 | |
135 | # define some basic types |
136 | |
137 | type Any => where { 1 }; |
138 | |
139 | type Value => where { !ref($_) }; |
140 | type Ref => where { ref($_) }; |
141 | |
142 | subtype Int => as Value => where { Scalar::Util::looks_like_number($_) }; |
143 | subtype Str => as Value => where { !Scalar::Util::looks_like_number($_) }; |
144 | |
145 | subtype ScalarRef => as Ref => where { ref($_) eq 'SCALAR' }; |
146 | subtype ArrayRef => as Ref => where { ref($_) eq 'ARRAY' }; |
147 | subtype HashRef => as Ref => where { ref($_) eq 'HASH' }; |
148 | subtype CodeRef => as Ref => where { ref($_) eq 'CODE' }; |
149 | subtype RegexpRef => as Ref => where { ref($_) eq 'Regexp' }; |
150 | |
151 | # NOTE: |
152 | # blessed(qr/.../) returns true,.. how odd |
153 | subtype Object => as Ref => where { blessed($_) && blessed($_) ne 'Regexp' }; |
154 | |
155 | 1; |
156 | |
157 | __END__ |
158 | |
159 | =pod |
160 | |
161 | =head1 NAME |
162 | |
e522431d |
163 | Moose::Util::TypeConstraints - Type constraint system for Moose |
a15dff8d |
164 | |
165 | =head1 SYNOPSIS |
166 | |
167 | use Moose::Util::TypeConstraints; |
168 | |
169 | type Num => where { Scalar::Util::looks_like_number($_) }; |
170 | |
171 | subtype Natural |
172 | => as Num |
173 | => where { $_ > 0 }; |
174 | |
175 | subtype NaturalLessThanTen |
176 | => as Natural |
177 | => where { $_ < 10 }; |
6b8bd8d3 |
178 | |
179 | coerce Num |
d6e2d9a1 |
180 | => from Str |
181 | => via { 0+$_ }; |
a15dff8d |
182 | |
183 | =head1 DESCRIPTION |
184 | |
e522431d |
185 | This module provides Moose with the ability to create type contraints |
186 | to be are used in both attribute definitions and for method argument |
187 | validation. |
188 | |
189 | This is B<NOT> a type system for Perl 5. |
190 | |
e522431d |
191 | This module also provides a simple hierarchy for Perl 5 types, this |
192 | could probably use some work, but it works for me at the moment. |
193 | |
194 | Any |
195 | Value |
196 | Int |
197 | Str |
198 | Ref |
199 | ScalarRef |
200 | ArrayRef |
201 | HashRef |
202 | CodeRef |
203 | RegexpRef |
204 | Object |
205 | |
206 | Suggestions for improvement are welcome. |
207 | |
a15dff8d |
208 | =head1 FUNCTIONS |
209 | |
182134e8 |
210 | =head2 Type Constraint Registry |
211 | |
212 | =over 4 |
213 | |
214 | =item B<find_type_constraint ($type_name)> |
215 | |
216 | =item B<register_type_constraint ($type_name, $type_constraint)> |
217 | |
218 | =item B<find_type_coercion> |
219 | |
220 | =item B<register_type_coercion> |
221 | |
222 | =item B<export_type_contstraints_as_functions> |
223 | |
4b598ea3 |
224 | =item B<dump_type_constraints> |
225 | |
182134e8 |
226 | =back |
227 | |
a15dff8d |
228 | =head2 Type Constraint Constructors |
229 | |
230 | =over 4 |
231 | |
232 | =item B<type> |
233 | |
234 | =item B<subtype> |
235 | |
236 | =item B<as> |
237 | |
238 | =item B<where> |
239 | |
182134e8 |
240 | =item B<coerce> |
241 | |
d6e2d9a1 |
242 | =item B<from> |
243 | |
244 | =item B<via> |
182134e8 |
245 | |
a15dff8d |
246 | =back |
247 | |
248 | =head2 Built-in Type Constraints |
249 | |
250 | =over 4 |
251 | |
252 | =item B<Any> |
253 | |
254 | =item B<Value> |
255 | |
256 | =item B<Int> |
257 | |
258 | =item B<Str> |
259 | |
260 | =item B<Ref> |
261 | |
262 | =item B<ArrayRef> |
263 | |
264 | =item B<CodeRef> |
265 | |
266 | =item B<HashRef> |
267 | |
268 | =item B<RegexpRef> |
269 | |
270 | =item B<ScalarRef> |
271 | |
272 | =item B<Object> |
273 | |
274 | =back |
275 | |
276 | =head1 BUGS |
277 | |
278 | All complex software has bugs lurking in it, and this module is no |
279 | exception. If you find a bug please either email me, or add the bug |
280 | to cpan-RT. |
281 | |
a15dff8d |
282 | =head1 AUTHOR |
283 | |
284 | Stevan Little E<lt>stevan@iinteractive.comE<gt> |
285 | |
286 | =head1 COPYRIGHT AND LICENSE |
287 | |
288 | Copyright 2006 by Infinity Interactive, Inc. |
289 | |
290 | L<http://www.iinteractive.com> |
291 | |
292 | This library is free software; you can redistribute it and/or modify |
293 | it under the same terms as Perl itself. |
294 | |
295 | =cut |