2 package Moose::Util::TypeConstraints;
8 use Scalar::Util 'blessed';
12 our $VERSION = '0.12';
13 our $AUTHORITY = 'cpan:STEVAN';
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
20 sub find_type_constraint ($);
21 sub _create_type_constraint ($$$;$$);
22 sub _install_type_coercions ($$);
23 sub create_type_constraint_union (@);
35 use Moose::Meta::TypeConstraint;
36 use Moose::Meta::TypeCoercion;
39 type subtype as where message optimize_as
45 Sub::Exporter::setup_exporter({
47 groups => { default => [':all'] }
53 # loop through the exports ...
54 foreach my $name (@exports) {
56 if (defined &{$class . '::' . $name}) {
57 my $keyword = \&{$class . '::' . $name};
59 # make sure it is from Moose
60 my $pkg_name = eval { svref_2object($keyword)->GV->STASH->NAME };
62 next if $pkg_name ne 'Moose::Util::TypeConstraints';
64 # and if it is from Moose then undef the slot
65 delete ${$class . '::'}{$name};
72 sub find_type_constraint ($) {
73 return $TYPES{$_[0]}->[1]
74 if exists $TYPES{$_[0]};
78 sub _dump_type_constraints {
80 Data::Dumper::Dumper(\%TYPES);
83 sub _create_type_constraint ($$$;$$) {
88 my ($message, $optimized);
90 $message = $_->{message} if exists $_->{message};
91 $optimized = $_->{optimized} if exists $_->{optimized};
94 my $pkg_defined_in = scalar(caller(0));
96 ($TYPES{$name}->[0] eq $pkg_defined_in)
97 || confess ("The type constraint '$name' has already been created in "
98 . $TYPES{$name}->[0] . " and cannot be created again in "
100 if defined $name && exists $TYPES{$name};
102 $parent = find_type_constraint($parent) if defined $parent;
103 my $constraint = Moose::Meta::TypeConstraint->new(
104 name => $name || '__ANON__',
106 constraint => $check,
108 optimized => $optimized,
110 $TYPES{$name} = [ $pkg_defined_in, $constraint ] if defined $name;
114 sub _install_type_coercions ($$) {
115 my ($type_name, $coercion_map) = @_;
116 my $type = find_type_constraint($type_name);
117 (!$type->has_coercion)
118 || confess "The type coercion for '$type_name' has already been registered";
119 my $type_coercion = Moose::Meta::TypeCoercion->new(
120 type_coercion_map => $coercion_map,
121 type_constraint => $type
123 $type->coercion($type_coercion);
126 sub create_type_constraint_union (@) {
127 my (@type_constraint_names) = @_;
128 return Moose::Meta::TypeConstraint->union(
130 find_type_constraint($_)
131 } @type_constraint_names
135 sub export_type_constraints_as_functions {
138 foreach my $constraint (keys %TYPES) {
139 *{"${pkg}::${constraint}"} = find_type_constraint($constraint)->_compiled_type_constraint;
143 *Moose::Util::TypeConstraints::export_type_contstraints_as_functions = \&export_type_constraints_as_functions;
145 sub list_all_type_constraints { keys %TYPES }
151 splice(@_, 1, 0, undef);
152 goto &_create_type_constraint;
155 sub subtype ($$;$$$) {
156 unshift @_ => undef if scalar @_ <= 2;
157 goto &_create_type_constraint;
161 my ($type_name, @coercion_map) = @_;
162 _install_type_coercions($type_name, \@coercion_map);
166 sub from ($) { $_[0] }
167 sub where (&) { $_[0] }
168 sub via (&) { $_[0] }
170 sub message (&) { +{ message => $_[0] } }
171 sub optimize_as (&) { +{ optimized => $_[0] } }
174 my ($type_name, @values) = @_;
175 (scalar @values >= 2)
176 || confess "You must have at least two values to enumerate through";
177 my $regexp = join '|' => @values;
178 _create_type_constraint(
181 sub { qr/^$regexp$/i }
185 # define some basic types
187 type 'Any' => where { 1 }; # meta-type including all
188 type 'Item' => where { 1 }; # base-type
190 subtype 'Undef' => as 'Item' => where { !defined($_) };
191 subtype 'Defined' => as 'Item' => where { defined($_) };
195 => where { !defined($_) || $_ eq "" || "$_" eq '1' || "$_" eq '0' };
199 => where { !ref($_) }
200 => optimize_as { defined($_[0]) && !ref($_[0]) };
205 => optimize_as { ref($_[0]) };
210 => optimize_as { defined($_[0]) && !ref($_[0]) };
214 => where { Scalar::Util::looks_like_number($_) }
215 => optimize_as { !ref($_[0]) && Scalar::Util::looks_like_number($_[0]) };
219 => where { "$_" =~ /^-?[0-9]+$/ }
220 => optimize_as { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ };
222 subtype 'ScalarRef' => as 'Ref' => where { ref($_) eq 'SCALAR' } => optimize_as { ref($_[0]) eq 'SCALAR' };
223 subtype 'ArrayRef' => as 'Ref' => where { ref($_) eq 'ARRAY' } => optimize_as { ref($_[0]) eq 'ARRAY' };
224 subtype 'HashRef' => as 'Ref' => where { ref($_) eq 'HASH' } => optimize_as { ref($_[0]) eq 'HASH' };
225 subtype 'CodeRef' => as 'Ref' => where { ref($_) eq 'CODE' } => optimize_as { ref($_[0]) eq 'CODE' };
226 subtype 'RegexpRef' => as 'Ref' => where { ref($_) eq 'Regexp' } => optimize_as { ref($_[0]) eq 'Regexp' };
227 subtype 'GlobRef' => as 'Ref' => where { ref($_) eq 'GLOB' } => optimize_as { ref($_[0]) eq 'GLOB' };
230 # scalar filehandles are GLOB refs,
231 # but a GLOB ref is not always a filehandle
234 => where { Scalar::Util::openhandle($_) }
235 => optimize_as { ref($_[0]) eq 'GLOB' && Scalar::Util::openhandle($_[0]) };
238 # blessed(qr/.../) returns true,.. how odd
241 => where { blessed($_) && blessed($_) ne 'Regexp' }
242 => optimize_as { blessed($_[0]) && blessed($_[0]) ne 'Regexp' };
246 => where { $_->can('does') }
247 => optimize_as { blessed($_[0]) && $_[0]->can('does') };
250 my @BUILTINS = list_all_type_constraints();
251 sub list_all_builtin_type_constraints { @BUILTINS }
262 Moose::Util::TypeConstraints - Type constraint system for Moose
266 use Moose::Util::TypeConstraints;
268 type 'Num' => where { Scalar::Util::looks_like_number($_) };
274 subtype 'NaturalLessThanTen'
277 => message { "This number ($_) is not less than ten!" };
283 enum 'RGBColors' => qw(red green blue);
287 This module provides Moose with the ability to create custom type
288 contraints to be used in attribute definition.
290 =head2 Important Caveat
292 This is B<NOT> a type system for Perl 5. These are type constraints,
293 and they are not used by Moose unless you tell it to. No type
294 inference is performed, expression are not typed, etc. etc. etc.
296 This is simply a means of creating small constraint functions which
297 can be used to simplify your own type-checking code.
299 =head2 Slightly Less Important Caveat
301 It is almost always a good idea to quote your type and subtype names.
302 This is to prevent perl from trying to execute the call as an indirect
303 object call. This issue only seems to come up when you have a subtype
304 the same name as a valid class, but when the issue does arise it tends
305 to be quite annoying to debug.
307 So for instance, this:
309 subtype DateTime => as Object => where { $_->isa('DateTime') };
311 will I<Just Work>, while this:
314 subtype DateTime => as Object => where { $_->isa('DateTime') };
316 will fail silently and cause many headaches. The simple way to solve
317 this, as well as future proof your subtypes from classes which have
318 yet to have been created yet, is to simply do this:
321 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
323 =head2 Default Type Constraints
325 This module also provides a simple hierarchy for Perl 5 types, this
326 could probably use some work, but it works for me at the moment.
348 Suggestions for improvement are welcome.
350 B<NOTE:> The C<Undef> type constraint does not work correctly
351 in every occasion, please use it sparringly.
353 =head2 Use with Other Constraint Modules
355 This module should play fairly nicely with other constraint
356 modules with only some slight tweaking. The C<where> clause
357 in types is expected to be a C<CODE> reference which checks
358 it's first argument and returns a bool. Since most constraint
359 modules work in a similar way, it should be simple to adapt
360 them to work with Moose.
362 For instance, this is how you could use it with
363 L<Declare::Constraints::Simple> to declare a completely new type.
365 type 'HashOfArrayOfObjects'
368 -values => IsArrayRef( IsObject ));
370 For more examples see the F<t/204_example_w_DCS.t> test file.
372 Here is an example of using L<Test::Deep> and it's non-test
373 related C<eq_deeply> function.
375 type 'ArrayOfHashOfBarsAndRandomNumbers'
378 array_each(subhashof({
380 random_number => ignore()
384 For a complete example see the F<t/205_example_w_TestDeep.t>
389 =head2 Type Constraint Registry
393 =item B<find_type_constraint ($type_name)>
395 This function can be used to locate a specific type constraint
396 meta-object. What you do with it from there is up to you :)
398 =item B<create_type_constraint_union (@type_constraint_names)>
400 Given a list of C<@type_constraint_names>, this will return a
401 B<Moose::Meta::TypeConstraint::Union> instance.
403 =item B<export_type_constraints_as_functions>
405 This will export all the current type constraints as functions
406 into the caller's namespace. Right now, this is mostly used for
407 testing, but it might prove useful to others.
409 =item B<export_type_contstraints_as_functions>
411 Alias for the above function.
413 =item B<list_all_type_constraints>
415 This will return a list of type constraint names, you can then
416 fetch them using C<find_type_constraint ($type_name)> if you
419 =item B<list_all_builtin_type_constraints>
421 This will return a list of builtin type constraints, meaning,
422 those which are defined in this module. See the section
423 labeled L<Default Type Constraints> for a complete list.
427 =head2 Type Constraint Constructors
429 The following functions are used to create type constraints.
430 They will then register the type constraints in a global store
431 where Moose can get to them if it needs to.
433 See the L<SYNOPSIS> for an example of how to use these.
437 =item B<type ($name, $where_clause)>
439 This creates a base type, which has no parent.
441 =item B<subtype ($name, $parent, $where_clause, ?$message)>
443 This creates a named subtype.
445 =item B<subtype ($parent, $where_clause, ?$message)>
447 This creates an unnamed subtype and will return the type
448 constraint meta-object, which will be an instance of
449 L<Moose::Meta::TypeConstraint>.
451 =item B<enum ($name, @values)>
453 This will create a basic subtype for a given set of strings.
454 The resulting constraint will be a subtype of C<Str> and
455 will match any of the items in C<@values>. See the L<SYNOPSIS>
456 for a simple example.
458 B<NOTE:> This is not a true proper enum type, it is simple
459 a convient constraint builder.
463 This is just sugar for the type constraint construction syntax.
467 This is just sugar for the type constraint construction syntax.
471 This is just sugar for the type constraint construction syntax.
475 This can be used to define a "hand optimized" version of your
476 type constraint which can be used to avoid traversing a subtype
477 constraint heirarchy.
479 B<NOTE:> You should only use this if you know what you are doing,
480 all the built in types use this, so your subtypes (assuming they
481 are shallow) will not likely need to use this.
485 =head2 Type Coercion Constructors
487 Type constraints can also contain type coercions as well. If you
488 ask your accessor too coerce, the Moose will run the type-coercion
489 code first, followed by the type constraint check. This feature
490 should be used carefully as it is very powerful and could easily
491 take off a limb if you are not careful.
493 See the L<SYNOPSIS> for an example of how to use these.
501 This is just sugar for the type coercion construction syntax.
505 This is just sugar for the type coercion construction syntax.
509 =head2 Namespace Management
515 This will remove all the type constraint keywords from the
516 calling class namespace.
522 All complex software has bugs lurking in it, and this module is no
523 exception. If you find a bug please either email me, or add the bug
528 Stevan Little E<lt>stevan@iinteractive.comE<gt>
530 =head1 COPYRIGHT AND LICENSE
532 Copyright 2006, 2007 by Infinity Interactive, Inc.
534 L<http://www.iinteractive.com>
536 This library is free software; you can redistribute it and/or modify
537 it under the same terms as Perl itself.