foo
[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 Scalar::Util 'blessed';
571dd39f 9use B 'svref_2object';
10use Sub::Exporter;
a15dff8d 11
d44714be 12our $VERSION = '0.11';
13our $AUTHORITY = 'cpan:STEVAN';
a15dff8d 14
4e036ee4 15use Moose::Meta::TypeConstraint;
2ca63f5d 16use Moose::Meta::TypeCoercion;
4e036ee4 17
571dd39f 18my @exports = qw/
8ecb1fa0 19 type subtype as where message optimize_as
571dd39f 20 coerce from via
21 enum
22 find_type_constraint
23/;
24
25Sub::Exporter::setup_exporter({
26 exports => \@exports,
27 groups => { default => [':all'] }
28});
29
30sub unimport {
31 no strict 'refs';
32 my $class = caller();
33 # loop through the exports ...
34 foreach my $name (@exports) {
35 # if we find one ...
36 if (defined &{$class . '::' . $name}) {
37 my $keyword = \&{$class . '::' . $name};
38
39 # make sure it is from Moose
40 my $pkg_name = eval { svref_2object($keyword)->GV->STASH->NAME };
41 next if $@;
42 next if $pkg_name ne 'Moose::Util::TypeConstraints';
43
44 # and if it is from Moose then undef the slot
45 delete ${$class . '::'}{$name};
46 }
2c0cbef7 47 }
571dd39f 48}
a15dff8d 49
182134e8 50{
51 my %TYPES;
2c0cbef7 52 sub find_type_constraint ($) {
446e850f 53 return $TYPES{$_[0]}->[1]
54 if exists $TYPES{$_[0]};
55 return;
56 }
57
58 sub _dump_type_constraints {
59 require Data::Dumper;
256903b6 60 Data::Dumper::Dumper(\%TYPES);
446e850f 61 }
62
8ecb1fa0 63 sub _create_type_constraint ($$$;$$) {
64 my $name = shift;
65 my $parent = shift;
66 my $check = shift;;
67
68 my ($message, $optimized);
69 for (@_) {
815ec671 70 $message = $_->{message} if exists $_->{message};
8ecb1fa0 71 $optimized = $_->{optimized} if exists $_->{optimized};
72 }
587ae0d2 73
0e6614c3 74 my $pkg_defined_in = scalar(caller(1));
75 ($TYPES{$name}->[0] eq $pkg_defined_in)
446e850f 76 || confess "The type constraint '$name' has already been created "
0e6614c3 77 if defined $name && exists $TYPES{$name};
78 $parent = find_type_constraint($parent) if defined $parent;
a27aa600 79 my $constraint = Moose::Meta::TypeConstraint->new(
80 name => $name || '__ANON__',
66811d63 81 parent => $parent,
76d37e5a 82 constraint => $check,
83 message => $message,
8ecb1fa0 84 optimized => $optimized,
4e036ee4 85 );
0e6614c3 86 $TYPES{$name} = [ $pkg_defined_in, $constraint ] if defined $name;
a27aa600 87 return $constraint;
182134e8 88 }
182134e8 89
2c0cbef7 90 sub _install_type_coercions ($$) {
a27aa600 91 my ($type_name, $coercion_map) = @_;
0e6614c3 92 my $type = find_type_constraint($type_name);
4e036ee4 93 (!$type->has_coercion)
d46a48f3 94 || confess "The type coercion for '$type_name' has already been registered";
a27aa600 95 my $type_coercion = Moose::Meta::TypeCoercion->new(
96 type_coercion_map => $coercion_map,
97 type_constraint => $type
98 );
99 $type->coercion($type_coercion);
182134e8 100 }
66811d63 101
2c0cbef7 102 sub create_type_constraint_union (@) {
c07af9d2 103 my (@type_constraint_names) = @_;
104 return Moose::Meta::TypeConstraint->union(
105 map {
106 find_type_constraint($_)
107 } @type_constraint_names
108 );
109 }
110
66811d63 111 sub export_type_contstraints_as_functions {
112 my $pkg = caller();
113 no strict 'refs';
114 foreach my $constraint (keys %TYPES) {
0e6614c3 115 *{"${pkg}::${constraint}"} = find_type_constraint($constraint)->_compiled_type_constraint;
66811d63 116 }
117 }
182134e8 118}
a15dff8d 119
7c13858b 120# type constructors
a15dff8d 121
815ec671 122sub type ($$;$$) {
123 my ($name, $check, @rest) = @_;
124 _create_type_constraint($name, undef, $check, @rest);
a15dff8d 125}
126
8ecb1fa0 127sub subtype ($$;$$$) {
128 unshift @_ => undef if scalar @_ <= 2;
2c0cbef7 129 goto &_create_type_constraint;
a15dff8d 130}
131
4b598ea3 132sub coerce ($@) {
66811d63 133 my ($type_name, @coercion_map) = @_;
7c13858b 134 _install_type_coercions($type_name, \@coercion_map);
182134e8 135}
136
76d37e5a 137sub as ($) { $_[0] }
138sub from ($) { $_[0] }
139sub where (&) { $_[0] }
140sub via (&) { $_[0] }
8ecb1fa0 141
142sub message (&) { +{ message => $_[0] } }
143sub optimize_as (&) { +{ optimized => $_[0] } }
a15dff8d 144
2c0cbef7 145sub enum ($;@) {
fcec2383 146 my ($type_name, @values) = @_;
2c0cbef7 147 (scalar @values >= 2)
148 || confess "You must have at least two values to enumerate through";
fcec2383 149 my $regexp = join '|' => @values;
150 _create_type_constraint(
151 $type_name,
152 'Str',
153 sub { qr/^$regexp$/i }
154 );
155}
156
a15dff8d 157# define some basic types
158
f65cb534 159type 'Any' => where { 1 }; # meta-type including all
160type 'Item' => where { 1 }; # base-type
a15dff8d 161
f65cb534 162subtype 'Undef' => as 'Item' => where { !defined($_) };
163subtype 'Defined' => as 'Item' => where { defined($_) };
a15dff8d 164
8ecb1fa0 165subtype 'Bool'
166 => as 'Item'
167 => where { !defined($_) || $_ eq "" || "$_" eq '1' || "$_" eq '0' };
5a4c5493 168
8ecb1fa0 169subtype 'Value'
170 => as 'Defined'
171 => where { !ref($_) }
172 => optimize_as { defined($_[0]) && !ref($_[0]) };
173
174subtype 'Ref'
175 => as 'Defined'
176 => where { ref($_) }
177 => optimize_as { ref($_[0]) };
178
179subtype 'Str'
180 => as 'Value'
181 => where { 1 }
182 => optimize_as { defined($_[0]) && !ref($_[0]) };
183
184subtype 'Num'
185 => as 'Value'
186 => where { Scalar::Util::looks_like_number($_) }
187 => optimize_as { !ref($_[0]) && Scalar::Util::looks_like_number($_[0]) };
188
189subtype 'Int'
190 => as 'Num'
191 => where { "$_" =~ /^-?[0-9]+$/ }
192 => optimize_as { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ };
193
194subtype 'ScalarRef' => as 'Ref' => where { ref($_) eq 'SCALAR' } => optimize_as { ref($_[0]) eq 'SCALAR' };
195subtype 'ArrayRef' => as 'Ref' => where { ref($_) eq 'ARRAY' } => optimize_as { ref($_[0]) eq 'ARRAY' };
196subtype 'HashRef' => as 'Ref' => where { ref($_) eq 'HASH' } => optimize_as { ref($_[0]) eq 'HASH' };
197subtype 'CodeRef' => as 'Ref' => where { ref($_) eq 'CODE' } => optimize_as { ref($_[0]) eq 'CODE' };
198subtype 'RegexpRef' => as 'Ref' => where { ref($_) eq 'Regexp' } => optimize_as { ref($_[0]) eq 'Regexp' };
199subtype 'GlobRef' => as 'Ref' => where { ref($_) eq 'GLOB' } => optimize_as { ref($_[0]) eq 'GLOB' };
a15dff8d 200
0a5bd159 201# NOTE:
202# scalar filehandles are GLOB refs,
203# but a GLOB ref is not always a filehandle
8ecb1fa0 204subtype 'FileHandle'
205 => as 'GlobRef'
206 => where { Scalar::Util::openhandle($_) }
207 => optimize_as { ref($_[0]) eq 'GLOB' && Scalar::Util::openhandle($_[0]) };
0a5bd159 208
a15dff8d 209# NOTE:
210# blessed(qr/.../) returns true,.. how odd
8ecb1fa0 211subtype 'Object'
212 => as 'Ref'
213 => where { blessed($_) && blessed($_) ne 'Regexp' }
214 => optimize_as { blessed($_[0]) && blessed($_[0]) ne 'Regexp' };
a15dff8d 215
8ecb1fa0 216subtype 'Role'
217 => as 'Object'
218 => where { $_->can('does') }
219 => optimize_as { blessed($_[0]) && $_[0]->can('does') };
02a0fb52 220
a15dff8d 2211;
222
223__END__
224
225=pod
226
227=head1 NAME
228
e522431d 229Moose::Util::TypeConstraints - Type constraint system for Moose
a15dff8d 230
231=head1 SYNOPSIS
232
233 use Moose::Util::TypeConstraints;
234
2c0cbef7 235 type 'Num' => where { Scalar::Util::looks_like_number($_) };
a15dff8d 236
2c0cbef7 237 subtype 'Natural'
238 => as 'Num'
a15dff8d 239 => where { $_ > 0 };
240
2c0cbef7 241 subtype 'NaturalLessThanTen'
242 => as 'Natural'
79592a54 243 => where { $_ < 10 }
244 => message { "This number ($_) is not less than ten!" };
6b8bd8d3 245
2c0cbef7 246 coerce 'Num'
247 => from 'Str'
d6e2d9a1 248 => via { 0+$_ };
98aae381 249
2c0cbef7 250 enum 'RGBColors' => qw(red green blue);
a15dff8d 251
252=head1 DESCRIPTION
253
d44714be 254This module provides Moose with the ability to create custom type
255contraints to be used in attribute definition.
e522431d 256
6ba6d68c 257=head2 Important Caveat
258
259This is B<NOT> a type system for Perl 5. These are type constraints,
260and they are not used by Moose unless you tell it to. No type
261inference is performed, expression are not typed, etc. etc. etc.
262
263This is simply a means of creating small constraint functions which
a7d0cd00 264can be used to simplify your own type-checking code.
6ba6d68c 265
2c0cbef7 266=head2 Slightly Less Important Caveat
267
268It is almost always a good idea to quote your type and subtype names.
43d599e5 269This is to prevent perl from trying to execute the call as an indirect
2c0cbef7 270object call. This issue only seems to come up when you have a subtype
271the same name as a valid class, but when the issue does arise it tends
272to be quite annoying to debug.
273
274So for instance, this:
275
276 subtype DateTime => as Object => where { $_->isa('DateTime') };
277
278will I<Just Work>, while this:
279
280 use DateTime;
281 subtype DateTime => as Object => where { $_->isa('DateTime') };
282
283will fail silently and cause many headaches. The simple way to solve
284this, as well as future proof your subtypes from classes which have
285yet to have been created yet, is to simply do this:
286
287 use DateTime;
d44714be 288 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
2c0cbef7 289
6ba6d68c 290=head2 Default Type Constraints
e522431d 291
e522431d 292This module also provides a simple hierarchy for Perl 5 types, this
293could probably use some work, but it works for me at the moment.
294
295 Any
f65cb534 296 Item
5a4c5493 297 Bool
f65cb534 298 Undef
299 Defined
5a4c5493 300 Value
301 Num
302 Int
303 Str
304 Ref
305 ScalarRef
451c8248 306 ArrayRef
307 HashRef
5a4c5493 308 CodeRef
309 RegexpRef
3f7376b0 310 GlobRef
0a5bd159 311 FileHandle
5a4c5493 312 Object
313 Role
e522431d 314
6ba6d68c 315Suggestions for improvement are welcome.
2c0cbef7 316
317B<NOTE:> The C<Undef> type constraint does not work correctly
318in every occasion, please use it sparringly.
703e92fb 319
320=head2 Use with Other Constraint Modules
321
322This module should play fairly nicely with other constraint
323modules with only some slight tweaking. The C<where> clause
324in types is expected to be a C<CODE> reference which checks
325it's first argument and returns a bool. Since most constraint
326modules work in a similar way, it should be simple to adapt
327them to work with Moose.
328
329For instance, this is how you could use it with
330L<Declare::Constraint::Simple> to declare a completely new type.
331
332 type 'HashOfArrayOfObjects'
333 => IsHashRef(
334 -keys => HasLength,
335 -values => IsArrayRef( IsObject ));
336
337For more examples see the F<t/204_example_w_DCS.t> test file.
338
339Here is an example of using L<Test::Deep> and it's non-test
340related C<eq_deeply> function.
341
342 type 'ArrayOfHashOfBarsAndRandomNumbers'
343 => where {
344 eq_deeply($_,
345 array_each(subhashof({
346 bar => isa('Bar'),
347 random_number => ignore()
348 })))
349 };
350
351For a complete example see the F<t/205_example_w_TestDeep.t>
352test file.
e522431d 353
a15dff8d 354=head1 FUNCTIONS
355
182134e8 356=head2 Type Constraint Registry
357
358=over 4
359
360=item B<find_type_constraint ($type_name)>
361
6ba6d68c 362This function can be used to locate a specific type constraint
363meta-object. What you do with it from there is up to you :)
182134e8 364
c07af9d2 365=item B<create_type_constraint_union (@type_constraint_names)>
366
367Given a list of C<@type_constraint_names>, this will return a
368B<Moose::Meta::TypeConstraint::Union> instance.
369
182134e8 370=item B<export_type_contstraints_as_functions>
371
6ba6d68c 372This will export all the current type constraints as functions
373into the caller's namespace. Right now, this is mostly used for
374testing, but it might prove useful to others.
375
182134e8 376=back
377
a15dff8d 378=head2 Type Constraint Constructors
379
6ba6d68c 380The following functions are used to create type constraints.
381They will then register the type constraints in a global store
382where Moose can get to them if it needs to.
a15dff8d 383
6ba6d68c 384See the L<SYNOPOSIS> for an example of how to use these.
a15dff8d 385
6ba6d68c 386=over 4
a15dff8d 387
6ba6d68c 388=item B<type ($name, $where_clause)>
a15dff8d 389
6ba6d68c 390This creates a base type, which has no parent.
a15dff8d 391
79592a54 392=item B<subtype ($name, $parent, $where_clause, ?$message)>
182134e8 393
6ba6d68c 394This creates a named subtype.
d6e2d9a1 395
79592a54 396=item B<subtype ($parent, $where_clause, ?$message)>
182134e8 397
6ba6d68c 398This creates an unnamed subtype and will return the type
399constraint meta-object, which will be an instance of
400L<Moose::Meta::TypeConstraint>.
a15dff8d 401
fcec2383 402=item B<enum ($name, @values)>
403
2c0cbef7 404This will create a basic subtype for a given set of strings.
405The resulting constraint will be a subtype of C<Str> and
406will match any of the items in C<@values>. See the L<SYNOPSIS>
407for a simple example.
408
409B<NOTE:> This is not a true proper enum type, it is simple
410a convient constraint builder.
411
6ba6d68c 412=item B<as>
a15dff8d 413
6ba6d68c 414This is just sugar for the type constraint construction syntax.
a15dff8d 415
6ba6d68c 416=item B<where>
a15dff8d 417
6ba6d68c 418This is just sugar for the type constraint construction syntax.
76d37e5a 419
420=item B<message>
421
422This is just sugar for the type constraint construction syntax.
a15dff8d 423
8ecb1fa0 424=item B<optimize_as>
425
d44714be 426This can be used to define a "hand optimized" version of your
427type constraint which can be used to avoid traversing a subtype
428constraint heirarchy.
429
430B<NOTE:> You should only use this if you know what you are doing,
431all the built in types use this, so your subtypes (assuming they
432are shallow) will not likely need to use this.
433
6ba6d68c 434=back
a15dff8d 435
6ba6d68c 436=head2 Type Coercion Constructors
a15dff8d 437
587ae0d2 438Type constraints can also contain type coercions as well. If you
439ask your accessor too coerce, the Moose will run the type-coercion
440code first, followed by the type constraint check. This feature
441should be used carefully as it is very powerful and could easily
442take off a limb if you are not careful.
a15dff8d 443
6ba6d68c 444See the L<SYNOPOSIS> for an example of how to use these.
a15dff8d 445
6ba6d68c 446=over 4
a15dff8d 447
6ba6d68c 448=item B<coerce>
a15dff8d 449
6ba6d68c 450=item B<from>
a15dff8d 451
6ba6d68c 452This is just sugar for the type coercion construction syntax.
453
454=item B<via>
a15dff8d 455
6ba6d68c 456This is just sugar for the type coercion construction syntax.
a15dff8d 457
458=back
459
571dd39f 460=head2 Namespace Management
461
462=over 4
463
464=item B<unimport>
465
466This will remove all the type constraint keywords from the
467calling class namespace.
468
469=back
470
a15dff8d 471=head1 BUGS
472
473All complex software has bugs lurking in it, and this module is no
474exception. If you find a bug please either email me, or add the bug
475to cpan-RT.
476
a15dff8d 477=head1 AUTHOR
478
479Stevan Little E<lt>stevan@iinteractive.comE<gt>
480
481=head1 COPYRIGHT AND LICENSE
482
b77fdbed 483Copyright 2006, 2007 by Infinity Interactive, Inc.
a15dff8d 484
485L<http://www.iinteractive.com>
486
487This library is free software; you can redistribute it and/or modify
488it under the same terms as Perl itself.
489
81dc201f 490=cut