2 MooseX::Types::Parameterizable - Create your own Parameterizable Types.
5 The follow is example usage.
7 package Test::MooseX::Types::Parameterizable::Synopsis;
10 use MooseX::Types::Parameterizable qw(Parameterizable);
11 use MooseX::Types::Moose qw(Str Int ArrayRef);
12 use MooseX::Types -declare=>[qw(Varchar)];
14 ## Create a type constraint that is a string but parameterizes an integer
15 ## that is used as a maximum length constraint on that string, similar to
16 ## a SQL Varchar database type.
19 as Parameterizable[Str,Int],
21 my($string, $int) = @_;
22 $int >= length($string) ? 1:0;
24 message { "'$_' is too long" };
29 my ($arrayref, $int) = @_;
33 has 'varchar_five' => (isa=>Varchar[5], is=>'ro', coerce=>1);
34 has 'varchar_ten' => (isa=>Varchar[10], is=>'ro');
36 ## Object created since attributes are valid
37 my $object1 = __PACKAGE__->new(
38 varchar_five => '1234',
39 varchar_ten => '123456789',
42 ## Dies with an invalid constraint for 'varchar_five'
43 my $object2 = __PACKAGE__->new(
44 varchar_five => '12345678', ## too long!
45 varchar_ten => '123456789',
48 ## varchar_five coerces as expected
49 my $object3 = __PACKAGE__->new(
50 varchar_five => [qw/aa bb/], ## coerces to "aabb"
51 varchar_ten => '123456789',
54 See t/05-pod-examples.t for runnable versions of all POD code
57 A MooseX::Types library for creating parameterizable types. A
58 parameterizable type constraint for all intents and uses is a subclass
59 of a parent type, but adds additional type parameters which are
60 available to constraint callbacks (such as inside the 'where' clause of
61 a type constraint definition) or in the coercions.
63 If you have Moose experience, you probably are familiar with the builtin
64 parameterizable type constraints 'ArrayRef' and 'HashRef'. This type
65 constraint lets you generate your own versions of parameterized
66 constraints that work similarly. See Moose::Util::TypeConstraints for
69 Using this type constraint, you can generate new type constraints that
70 have additional runtime advice, such as being able to specify maximum
71 and minimum values for an Int (integer) type constraint:
74 as Dict[max=>Int, min=>Int],
77 return $range->{max} > $range->{min};
81 as Parameterizable[Int, Range],
83 my ($value, $range) = @_;
84 return ($value >= $range->{min} &&
85 $value <= $range->{max});
88 RangedInt([{min=>10,max=>100}])->check(50); ## OK
89 RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
91 The type parameter must be valid against the type constraint given. If
92 you pass an invalid value this throws a hard Moose exception. You'll
93 need to capture it in an eval or related exception catching system (see
94 TryCatch or <Try::Tiny>.) For example the following would throw a hard
95 error (and not just return false)
97 RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
99 If you can't accept a hard exception here, you'll need to test the
100 constraining values first, as in:
102 my $range = {min=>99, max=>10};
103 if(my $err = Range->validate($range)) {
106 RangedInt($range)->check(99);
109 Please note that for ArrayRef or HashRef parameterizable type
110 constraints, as in the example above, as a convenience we automatically
111 ref the incoming type parameters, so that the above could also be
114 RangedInt([min=>10,max=>100])->check(50); ## OK
115 RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
116 RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
118 This is the preferred syntax, as it improve readability and adds to the
119 conciseness of your type constraint declarations. An exception wil be
120 thrown if your type parameters don't match the required reference type.
122 Also not that if you 'chain' parameterization results with a method call
125 TypeConstraint([$ob])->method;
127 You need to have the "(...)" around the ArrayRef in the Type Constraint
128 parameters. This seems to have something to do with the precendent level
129 of "->". Patches or thoughts welcomed. You only need to do this in the
130 above case which I imagine is not a very common case.
132 ==head2 Subtyping a Parameterizable type constraints
134 When subclassing a parameterizable type you must be careful to match
135 either the required type parameter type constraint, or if
136 re-parameterizing, the new type constraints are a subtype of the parent.
140 as Parameterizable[Int, Range],
142 my ($value, $range) = @_;
143 return ($value >= $range->{min} &&
144 $value =< $range->{max});
147 Example subtype with additional constraints:
149 subtype PositiveRangedInt,
155 Or you could have done the following instead:
157 ## Subtype of Int for positive numbers
161 my ($value, $range) = @_;
165 ## subtype Range to re-parameterize Range with subtypes
166 subtype PositiveRange,
167 as Range[max=>PositiveInt, min=>PositiveInt];
169 ## create subtype via reparameterizing
170 subtype PositiveRangedInt,
171 as RangedInt[PositiveRange];
173 Notice how re-parameterizing the parameterizable type 'RangedInt' works
174 slightly differently from re-parameterizing 'PositiveRange' Although it
175 initially takes two type constraint values to declare a parameterizable
176 type, should you wish to later re-parameterize it, you only use a
177 subtype of the second type parameter (the parameterizable type
178 constraint) since the first type constraint sets the parent type for the
179 parameterizable type. In other words, given the example above, a type
180 constraint of 'RangedInt' would have a parent of 'Int', not
181 'Parameterizable' and for all intends and uses you could stick it
182 wherever you'd need an Int.
187 ## re-parameterized subtypes of NameAge containing a Parameterizable Int
188 subtype NameBetween18and35Age,
191 PositiveRangedInt[min=>18,max=>35],
194 One caveat is that you can't stick an unparameterized parameterizable
195 type inside a structure, such as MooseX::Types::Structured since that
196 would require the ability to convert a 'containing' type constraint into
197 a parameterizable type, which is a capacity we current don't have.
200 Parameterizable types have some limited support for coercions. Several
201 things must be kept in mind. The first is that the coercion targets the
202 type constraint which is being made parameterizable, Not the
203 parameterizable type. So for example if you create a Parameterizable
206 subtype RequiredAgeInYears,
209 subtype PersonOverAge,
210 as Parameterizable[Person, RequiredAgeInYears]
212 my ($person, $required_years_old) = @_;
213 return $person->years_old > $required_years_old;
216 This would validate the following:
218 my $person = Person->new(age=>35);
219 PersonOverAge([18])->check($person);
221 You can then apply the following coercion
223 coerce PersonOverAge,
225 via {Person->new(%$_)},
227 via {Person->new(age=>$_)};
229 This coercion would then apply to all the following:
231 PersonOverAge([18])->check(30); ## via the Int coercion
232 PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
234 However, you are not allowed to place coercions on parameterizable types
235 that have had their constraining value filled, nor subtypes of such. For
238 coerce PersonOverAge[18],
242 That would generate a hard exception. This is a limitation for now until
243 I can devise a smarter way to cache the generated type constraints.
244 However, I doubt it will be a significant limitation, since the general
245 use case is supported.
247 Lastly, the constraining value is available in the coercion in much the
248 same way it is available to the constraint.
250 ## Create a type constraint where a Person must be in the set
252 as Parameterizable[Person, PersonSet],
254 my ($person, $person_set) = @_;
255 $person_set->find($person);
261 my ($hashref, $person_set) = @_;
262 return $person_set->create($hash_ref);
266 TBD - Need more tests.
269 This type library defines the following constraints.
271 Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint]
272 Create a subtype of ParentTypeConstraint with a dependency on a value
273 that can pass the ParameterizableValueTypeConstraint. If
274 ParameterizableValueTypeConstraint is empty we default to the 'Any' type
275 constraint (see Moose::Util::TypeConstraints).
277 This creates a type constraint which must be further parameterized at
278 later time before it can be used to ->check or ->validate a value.
279 Attempting to do so will cause an exception.
282 John Napiorkowski, "<jjnapiork@cpan.org>"
285 This program is free software; you can redistribute it and/or modify it
286 under the same terms as Perl itself.