getting-there
[gitmo/Moose.git] / lib / Moose / Util / TypeConstraints.pm
CommitLineData
a15dff8d 1
2package Moose::Util::TypeConstraints;
3
4use strict;
5use warnings;
6
e90c03d0 7use Carp 'confess';
a15dff8d 8use Sub::Name 'subname';
9use Scalar::Util 'blessed';
10
5569c072 11our $VERSION = '0.02';
a15dff8d 12
4e036ee4 13use Moose::Meta::TypeConstraint;
14
a15dff8d 15sub 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
71sub 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
81sub 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 105sub 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 130sub as ($) { $_[0] }
d6e2d9a1 131sub from ($) { $_[0] }
a15dff8d 132sub where (&) { $_[0] }
d6e2d9a1 133sub via (&) { $_[0] }
a15dff8d 134
135# define some basic types
136
137type Any => where { 1 };
138
139type Value => where { !ref($_) };
140type Ref => where { ref($_) };
141
142subtype Int => as Value => where { Scalar::Util::looks_like_number($_) };
143subtype Str => as Value => where { !Scalar::Util::looks_like_number($_) };
144
145subtype ScalarRef => as Ref => where { ref($_) eq 'SCALAR' };
146subtype ArrayRef => as Ref => where { ref($_) eq 'ARRAY' };
147subtype HashRef => as Ref => where { ref($_) eq 'HASH' };
148subtype CodeRef => as Ref => where { ref($_) eq 'CODE' };
149subtype RegexpRef => as Ref => where { ref($_) eq 'Regexp' };
150
151# NOTE:
152# blessed(qr/.../) returns true,.. how odd
153subtype Object => as Ref => where { blessed($_) && blessed($_) ne 'Regexp' };
154
1551;
156
157__END__
158
159=pod
160
161=head1 NAME
162
e522431d 163Moose::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 185This module provides Moose with the ability to create type contraints
186to be are used in both attribute definitions and for method argument
187validation.
188
189This is B<NOT> a type system for Perl 5.
190
e522431d 191This module also provides a simple hierarchy for Perl 5 types, this
192could 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
206Suggestions 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
278All complex software has bugs lurking in it, and this module is no
279exception. If you find a bug please either email me, or add the bug
280to cpan-RT.
281
a15dff8d 282=head1 AUTHOR
283
284Stevan Little E<lt>stevan@iinteractive.comE<gt>
285
286=head1 COPYRIGHT AND LICENSE
287
288Copyright 2006 by Infinity Interactive, Inc.
289
290L<http://www.iinteractive.com>
291
292This library is free software; you can redistribute it and/or modify
293it under the same terms as Perl itself.
294
295=cut