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