1 package MooseX::Types::Parameterizable;
6 $VERSION = eval $VERSION;
8 use Moose::Util::TypeConstraints;
9 use MooseX::Meta::TypeConstraint::Parameterizable;
10 use MooseX::Types -declare => [qw(Parameterizable)];
14 MooseX::Types::Parameterizable - Create your own Parameterizable Types.
18 The follow is example usage.
20 package Test::MooseX::Types::Parameterizable::Synopsis;
23 use MooseX::Types::Parameterizable qw(Parameterizable);
24 use MooseX::Types::Moose qw(Str Int ArrayRef);
25 use MooseX::Types -declare=>[qw(Varchar)];
27 ## Create a type constraint that is a string but parameterizes an integer
28 ## that is used as a maximum length constraint on that string, similar to
29 ## a SQL Varchar database type.
32 as Parameterizable[Str,Int],
34 my($string, $int) = @_;
35 $int >= length($string) ? 1:0;
37 message { "'$_' is too long" };
39 ## Coerce an ArrayRef to a string via concatenation.
44 my ($arrayref, $int) = @_;
48 has 'varchar_five' => (isa=>Varchar[5], is=>'ro', coerce=>1);
49 has 'varchar_ten' => (isa=>Varchar[10], is=>'ro');
51 ## Object created since attributes are valid
52 my $object1 = __PACKAGE__->new(
53 varchar_five => '1234',
54 varchar_ten => '123456789',
57 ## Dies with an invalid constraint for 'varchar_five'
58 my $object2 = __PACKAGE__->new(
59 varchar_five => '12345678', ## too long!
60 varchar_ten => '123456789',
63 ## varchar_five coerces as expected
64 my $object3 = __PACKAGE__->new(
65 varchar_five => [qw/aa bb/], ## coerces to "aabb"
66 varchar_ten => '123456789',
69 See t/05-pod-examples.t for runnable versions of all POD code
73 A L<MooseX::Types> library for creating parameterizable types. A parameterizable
74 type constraint for all intents and uses is a subclass of a parent type, but
75 adds additional type parameters which are available to constraint callbacks
76 (such as inside the 'where' clause of a type constraint definition) or in the
79 If you have L<Moose> experience, you probably are familiar with the builtin
80 parameterizable type constraints 'ArrayRef' and 'HashRef'. This type constraint
81 lets you generate your own versions of parameterized constraints that work
82 similarly. See L<Moose::Util::TypeConstraints> for more.
84 Using this type constraint, you can generate new type constraints that have
85 additional runtime advice, such as being able to specify maximum and minimum
86 values for an Int (integer) type constraint:
89 as Dict[max=>Int, min=>Int],
92 return $range->{max} > $range->{min};
96 as Parameterizable[Int, Range],
98 my ($value, $range) = @_;
99 return ($value >= $range->{min} &&
100 $value <= $range->{max});
103 RangedInt([{min=>10,max=>100}])->check(50); ## OK
104 RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
106 The type parameter must be valid against the type constraint given. If you pass
107 an invalid value this throws a hard Moose exception. You'll need to capture it
108 in an eval or related exception catching system (see L<TryCatch> or <Try::Tiny>.)
109 For example the following would throw a hard error (and not just return false)
111 RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
113 If you can't accept a hard exception here, you'll need to test the constraining
116 my $range = {min=>99, max=>10};
117 if(my $err = Range->validate($range)) {
120 RangedInt($range)->check(99);
123 Please note that for ArrayRef or HashRef parameterizable type constraints, as in the
124 example above, as a convenience we automatically ref the incoming type
125 parameters, so that the above could also be written as:
127 RangedInt([min=>10,max=>100])->check(50); ## OK
128 RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
129 RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
131 This is the preferred syntax, as it improve readability and adds to the
132 conciseness of your type constraint declarations. An exception wil be thrown if
133 your type parameters don't match the required reference type.
135 Also note that if you 'chain' parameterization results with a method call like:
137 TypeConstraint([$ob])->method;
139 You need to have the "(...)" around the ArrayRef in the Type Constraint
140 parameters. You can skip the wrapping parenthesis in the most common cases,
141 such as when you use the type constraint in the options section of a L<Moose>
142 attribute declaration, or when defining type libraries.
144 ==head2 Subtyping a Parameterizable type constraints
146 When subclassing a parameterizable type you must be careful to match either the
147 required type parameter type constraint, or if re-parameterizing, the new
148 type constraints are a subtype of the parent. For example:
151 as Parameterizable[Int, Range],
153 my ($value, $range) = @_;
154 return ($value >= $range->{min} &&
155 $value =< $range->{max});
158 Example subtype with additional constraints:
160 subtype PositiveRangedInt,
166 In this case you'd now have a parameterizable type constraint called which
169 Test::More::ok PositiveRangedInt([{min=>-10, max=>75}])->check(5);
170 Test::More::ok !PositiveRangedInt([{min=>-10, max=>75}])->check(-5);
172 Of course the above is somewhat counter-intuitive to the reader, since we have
173 defined our 'RangedInt' in such as way as to let you declare negative ranges.
174 For the moment each type constraint rule is apply without knowledge of any
175 other rule, nor can a rule 'inform' existing rules. This is a limitation of
176 the current system. However, you could instead do the following:
179 ## Subtype of Int for positive numbers
183 my ($value, $range) = @_;
187 ## subtype Range to re-parameterize Range with subtypes
188 subtype PositiveRange,
189 as Range[max=>PositiveInt, min=>PositiveInt];
191 ## create subtype via reparameterizing
192 subtype PositiveRangedInt,
193 as RangedInt[PositiveRange];
195 This would constrain values in the same way as the previous type constraint but
196 have the bonus that you'd throw a hard exception if you try to use an incorrect
199 Test::More::ok PositiveRangedInt([{min=>10, max=>75}])->check(15); ## OK
200 Test::More::ok !PositiveRangedInt([{min=>-10, max=>75}])->check(-5); ## Dies
202 Notice how re-parameterizing the parameterizable type 'RangedInt' works slightly
203 differently from re-parameterizing 'PositiveRange' Although it initially takes
204 two type constraint values to declare a parameterizable type, should you wish to
205 later re-parameterize it, you only use a subtype of the extra type parameter
206 (the parameterizable type constraints) since the first type constraint sets the
207 parent type for the parameterizable type.
209 In other words, given the example above, a type constraint of 'RangedInt' would
210 have a parent of 'Int', not 'Parameterizable' and for all intends and uses you
211 could stick it wherever you'd need an Int.
215 A type coerction is a rule that allows you to transform one type from one or
216 more other types. Please see L<Moose::Cookbook::Basics::Recipe5> for an example
217 of type coercions if you are not familiar with the subject.
219 L<MooseX::Types::Parameterizable> support type coercions in all the ways you
220 would expect. In addition, it also supports a limited form of type coercion
221 inheritance. Generally speaking, type constraints don't inherit coercions since
222 this would rapidly become confusing. However, since your parameterizable type
223 is intended to become parameterized in order to be useful, we support inheriting
224 from a 'base' parameterizable type constraint to its 'child' parameterized sub
227 For the purposes of this discussion, a parameterizable type is a subtype created
228 when you say, "as Parameterizable[..." in your sub type declaration. For example
231 as Parameterizable[Str, Int],
233 my($string, $int) = @_;
234 $int >= length($string) ? 1:0;
236 message { "'$_' is too long" };
238 This is the </SYNOPSIS> example, which creates a new parameterizable subtype of
239 Str which takes a single type parameter which must be an Int. This Int is used
240 to constrain the allowed length of the Str value.
242 Now, this new sub type, "Varchar", is parameterizable since it can take a type
243 parameter. We can apply some coercions to it:
247 via { "$_"; }, ## stringify the object
249 via { join '',@$_ }; ## convert array to string
251 This parameterizable subtype, "Varchar" itself is something you'd never use
252 directly to constraint a value. In other words you'd never do something like:
254 has name => (isa=>Varchar, ...)
256 You are going to do this:
258 has name => (isa=>Varchar[40], ...)
260 Which is actually useful. However, "Varchar[40]" is a parameterized type, it
261 is a subtype of the parameterizable "Varchar" and it inherits coercions from
262 its parent. This may be a bit surprising to L<Moose> developers, but I believe
263 this is the actual desired behavior.
265 You can of course add extra coercions to a parameterized type:
267 subtype Max40CharStr,
273 In which case this new parameterized type would inherit coercions from it's parent
274 parameterizable type (Varchar), as well as the additional coercions you've added.
278 TBD - Need more tests.
280 =head1 TYPE CONSTRAINTS
282 This type library defines the following constraints.
284 =head2 Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint]
286 Create a subtype of ParentTypeConstraint with a dependency on a value that can
287 pass the ParameterizableValueTypeConstraint. If ParameterizableValueTypeConstraint is empty
288 we default to the 'Any' type constraint (see L<Moose::Util::TypeConstraints>).
290 This creates a type constraint which must be further parameterized at later time
291 before it can be used to ->check or ->validate a value. Attempting to do so
292 will cause an exception.
296 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
297 MooseX::Meta::TypeConstraint::Parameterizable->new(
298 name => 'MooseX::Types::Parameterizable::Parameterizable',
299 parent => find_type_constraint('Any'),
300 constraint => sub {1},
306 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
308 =head1 COPYRIGHT & LICENSE
310 This program is free software; you can redistribute it and/or modify
311 it under the same terms as Perl itself.