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" };
42 my ($arrayref, $int) = @_;
46 has 'varchar_five' => (isa=>Varchar[5], is=>'ro', coerce=>1);
47 has 'varchar_ten' => (isa=>Varchar[10], is=>'ro');
49 ## Object created since attributes are valid
50 my $object1 = __PACKAGE__->new(
51 varchar_five => '1234',
52 varchar_ten => '123456789',
55 ## Dies with an invalid constraint for 'varchar_five'
56 my $object2 = __PACKAGE__->new(
57 varchar_five => '12345678', ## too long!
58 varchar_ten => '123456789',
61 ## varchar_five coerces as expected
62 my $object3 = __PACKAGE__->new(
63 varchar_five => [qw/aa bb/], ## coerces to "aabb"
64 varchar_ten => '123456789',
67 See t/05-pod-examples.t for runnable versions of all POD code
71 A L<MooseX::Types> library for creating parameterizable types. A parameterizable
72 type constraint for all intents and uses is a subclass of a parent type, but
73 adds additional type parameters which are available to constraint callbacks
74 (such as inside the 'where' clause of a type constraint definition) or in the
77 If you have L<Moose> experience, you probably are familiar with the builtin
78 parameterizable type constraints 'ArrayRef' and 'HashRef'. This type constraint
79 lets you generate your own versions of parameterized constraints that work
80 similarly. See L<Moose::Util::TypeConstraints> for more.
82 Using this type constraint, you can generate new type constraints that have
83 additional runtime advice, such as being able to specify maximum and minimum
84 values for an Int (integer) type constraint:
87 as Dict[max=>Int, min=>Int],
90 return $range->{max} > $range->{min};
94 as Parameterizable[Int, Range],
96 my ($value, $range) = @_;
97 return ($value >= $range->{min} &&
98 $value <= $range->{max});
101 RangedInt([{min=>10,max=>100}])->check(50); ## OK
102 RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
104 The type parameter must be valid against the type constraint given. If you pass
105 an invalid value this throws a hard Moose exception. You'll need to capture it
106 in an eval or related exception catching system (see L<TryCatch> or <Try::Tiny>.)
107 For example the following would throw a hard error (and not just return false)
109 RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
111 If you can't accept a hard exception here, you'll need to test the constraining
114 my $range = {min=>99, max=>10};
115 if(my $err = Range->validate($range)) {
118 RangedInt($range)->check(99);
121 Please note that for ArrayRef or HashRef parameterizable type constraints, as in the
122 example above, as a convenience we automatically ref the incoming type
123 parameters, so that the above could also be written as:
125 RangedInt([min=>10,max=>100])->check(50); ## OK
126 RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
127 RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
129 This is the preferred syntax, as it improve readability and adds to the
130 conciseness of your type constraint declarations. An exception wil be thrown if
131 your type parameters don't match the required reference type.
133 Also not that if you 'chain' parameterization results with a method call like:
135 TypeConstraint([$ob])->method;
137 You need to have the "(...)" around the ArrayRef in the Type Constraint
138 parameters. This seems to have something to do with the precendent level of
139 "->". Patches or thoughts welcomed. You only need to do this in the above
140 case which I imagine is not a very common case.
142 ==head2 Subtyping a Parameterizable type constraints
144 When subclassing a parameterizable type you must be careful to match either the
145 required type parameter type constraint, or if re-parameterizing, the new
146 type constraints are a subtype of the parent. For example:
149 as Parameterizable[Int, Range],
151 my ($value, $range) = @_;
152 return ($value >= $range->{min} &&
153 $value =< $range->{max});
156 Example subtype with additional constraints:
158 subtype PositiveRangedInt,
164 Or you could have done the following instead:
166 ## Subtype of Int for positive numbers
170 my ($value, $range) = @_;
174 ## subtype Range to re-parameterize Range with subtypes
175 subtype PositiveRange,
176 as Range[max=>PositiveInt, min=>PositiveInt];
178 ## create subtype via reparameterizing
179 subtype PositiveRangedInt,
180 as RangedInt[PositiveRange];
182 Notice how re-parameterizing the parameterizable type 'RangedInt' works slightly
183 differently from re-parameterizing 'PositiveRange' Although it initially takes
184 two type constraint values to declare a parameterizable type, should you wish to
185 later re-parameterize it, you only use a subtype of the second type parameter
186 (the parameterizable type constraint) since the first type constraint sets the parent
187 type for the parameterizable type. In other words, given the example above, a type
188 constraint of 'RangedInt' would have a parent of 'Int', not 'Parameterizable' and for
189 all intends and uses you could stick it wherever you'd need an Int.
194 ## re-parameterized subtypes of NameAge containing a Parameterizable Int
195 subtype NameBetween18and35Age,
198 PositiveRangedInt[min=>18,max=>35],
201 One caveat is that you can't stick an unparameterized parameterizable type inside a
202 structure, such as L<MooseX::Types::Structured> since that would require the
203 ability to convert a 'containing' type constraint into a parameterizable type, which
204 is a capacity we current don't have.
208 Parameterizable types have some limited support for coercions. Several things must
209 be kept in mind. The first is that the coercion targets the type constraint
210 which is being made parameterizable, Not the parameterizable type. So for example if you
211 create a Parameterizable type like:
213 subtype RequiredAgeInYears,
216 subtype PersonOverAge,
217 as Parameterizable[Person, RequiredAgeInYears]
219 my ($person, $required_years_old) = @_;
220 return $person->years_old > $required_years_old;
223 This would validate the following:
225 my $person = Person->new(age=>35);
226 PersonOverAge([18])->check($person);
228 You can then apply the following coercion
230 coerce PersonOverAge,
232 via {Person->new(%$_)},
234 via {Person->new(age=>$_)};
236 This coercion would then apply to all the following:
238 PersonOverAge([18])->check(30); ## via the Int coercion
239 PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
241 However, you are not allowed to place coercions on parameterizable types that have
242 had their constraining value filled, nor subtypes of such. For example:
244 coerce PersonOverAge[18],
248 That would generate a hard exception. This is a limitation for now until I can
249 devise a smarter way to cache the generated type constraints. However, I doubt
250 it will be a significant limitation, since the general use case is supported.
252 Lastly, the constraining value is available in the coercion in much the same way
253 it is available to the constraint.
255 ## Create a type constraint where a Person must be in the set
257 as Parameterizable[Person, PersonSet],
259 my ($person, $person_set) = @_;
260 $person_set->find($person);
266 my ($hashref, $person_set) = @_;
267 return $person_set->create($hash_ref);
272 TBD - Need more tests.
274 =head1 TYPE CONSTRAINTS
276 This type library defines the following constraints.
278 =head2 Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint]
280 Create a subtype of ParentTypeConstraint with a dependency on a value that can
281 pass the ParameterizableValueTypeConstraint. If ParameterizableValueTypeConstraint is empty
282 we default to the 'Any' type constraint (see L<Moose::Util::TypeConstraints>).
284 This creates a type constraint which must be further parameterized at later time
285 before it can be used to ->check or ->validate a value. Attempting to do so
286 will cause an exception.
290 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
291 MooseX::Meta::TypeConstraint::Parameterizable->new(
292 name => 'MooseX::Types::Parameterizable::Parameterizable',
293 parent => find_type_constraint('Any'),
294 constraint => sub {1},
300 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
302 =head1 COPYRIGHT & LICENSE
304 This program is free software; you can redistribute it and/or modify
305 it under the same terms as Perl itself.