more docs, test fixes and prep for release
[gitmo/MooseX-Dependent.git] / README
CommitLineData
c08b66ad 1NAME
2 MooseX::Types::Parameterizable - Create your own Parameterizable Types.
3
4SYNOPSIS
afdaaf52 5 The follow is example usage.
c08b66ad 6
afdaaf52 7 package Test::MooseX::Types::Parameterizable::Synopsis;
8
9 use Moose;
c08b66ad 10 use MooseX::Types::Parameterizable qw(Parameterizable);
afdaaf52 11 use MooseX::Types::Moose qw(Str Int ArrayRef);
12 use MooseX::Types -declare=>[qw(Varchar)];
13
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 ## an SQL Varchar type.
17
18 subtype Varchar,
19 as Parameterizable[Str,Int],
20 where {
21 my($string, $int) = @_;
22 $int >= length($string) ? 1:0;
23 },
24 message { "'$_' is too long" };
25
26 coerce Varchar,
27 from ArrayRef,
28 via {
29 my ($arrayref, $int) = @_;
30 join('', @$arrayref);
31 };
32
33 has 'varchar_five' => (isa=>Varchar[5], is=>'ro', coerce=>1);
34 has 'varchar_ten' => (isa=>Varchar[10], is=>'ro');
35
36 ## Object created since attributes are valid
37 my $object1 = __PACKAGE__->new(
38 varchar_five => '1234',
39 varchar_ten => '123456789',
40 );
41
42 ## Dies with an invalid constraint for 'varchar_five'
43 my $object2 = __PACKAGE__->new(
44 varchar_five => '12345678',
45 varchar_ten => '123456789',
46 );
47
48 ## varchar_five coerces as expected
49 my $object3 = __PACKAGE__->new(
50 varchar_five => [qw/aa bb/],
51 varchar_ten => '123456789',
52 );
53
54 See t/05-pod-examples.t for runnable versions of all POD code
c08b66ad 55
56DESCRIPTION
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 a secondary type parameter which is available
60 to constraint callbacks (such as inside the 'where' clause) or in the
61 coercions.
62
63 This allows you to create a type that has additional runtime advice,
64 such as a set of numbers within which another number must be unique, or
65 allowable ranges for a integer, such as in:
66
6c0e3459 67 subtype Range,
68 as Dict[max=>Int, min=>Int],
69 where {
70 my ($range) = @_;
71 return $range->{max} > $range->{min};
72 };
73
74 subtype RangedInt,
75 as Parameterizable[Int, Range],
76 where {
77 my ($value, $range) = @_;
78 return ($value >= $range->{min} &&
79 $value <= $range->{max});
80 };
81
82 RangedInt([{min=>10,max=>100}])->check(50); ## OK
83 RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
c08b66ad 84
85 This throws a hard Moose exception. You'll need to capture it in an eval
6c0e3459 86 or related exception catching system (see TryCatch or <Try::Tiny>.)
c08b66ad 87
6c0e3459 88 RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
c08b66ad 89
90 If you can't accept a hard exception here, you'll need to test the
91 constraining values first, as in:
92
6c0e3459 93 my $range = {min=>99, max=>10};
94 if(my $err = Range->validate($range)) {
95 ## Handle #$err
96 } else {
97 RangedInt($range)->check(99);
98 }
c08b66ad 99
100 Please note that for ArrayRef or HashRef parameterizable type
101 constraints, as in the example above, as a convenience we automatically
102 ref the incoming type parameters, so that the above could also be
103 written as:
104
6c0e3459 105 RangedInt([min=>10,max=>100])->check(50); ## OK
106 RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
107 RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
c08b66ad 108
109 This is the preferred syntax, as it improve readability and adds to the
110 conciseness of your type constraint declarations. An exception wil be
111 thrown if your type parameters don't match the required reference type.
112
113 Also not that if you 'chain' parameterization results with a method call
114 like:
115
6c0e3459 116 TypeConstraint([$ob])->method;
c08b66ad 117
118 You need to have the "(...)" around the ArrayRef in the Type Constraint
119 parameters. This seems to have something to do with the precendent level
120 of "->". Patches or thoughts welcomed. You only need to do this in the
121 above case which I imagine is not a very common case.
122
123 ==head2 Subtyping a Parameterizable type constraints
124
125 When subclassing a parameterizable type you must be careful to match
126 either the required type parameter type constraint, or if
127 re-parameterizing, the new type constraints are a subtype of the parent.
128 For example:
129
6c0e3459 130 subtype RangedInt,
131 as Parameterizable[Int, Range],
132 where {
133 my ($value, $range) = @_;
134 return ($value >= $range->{min} &&
135 $value =< $range->{max});
136 };
c08b66ad 137
138 Example subtype with additional constraints:
139
6c0e3459 140 subtype PositiveRangedInt,
141 as RangedInt,
142 where {
143 shift >= 0;
144 };
c08b66ad 145
146 Or you could have done the following instead:
147
6c0e3459 148 ## Subtype of Int for positive numbers
149 subtype PositiveInt,
150 as Int,
151 where {
152 my ($value, $range) = @_;
153 return $value >= 0;
154 };
155
156 ## subtype Range to re-parameterize Range with subtypes
157 subtype PositiveRange,
158 as Range[max=>PositiveInt, min=>PositiveInt];
159
160 ## create subtype via reparameterizing
161 subtype PositiveRangedInt,
162 as RangedInt[PositiveRange];
c08b66ad 163
164 Notice how re-parameterizing the parameterizable type 'RangedInt' works
165 slightly differently from re-parameterizing 'PositiveRange' Although it
166 initially takes two type constraint values to declare a parameterizable
167 type, should you wish to later re-parameterize it, you only use a
168 subtype of the second type parameter (the parameterizable type
169 constraint) since the first type constraint sets the parent type for the
170 parameterizable type. In other words, given the example above, a type
171 constraint of 'RangedInt' would have a parent of 'Int', not
172 'Parameterizable' and for all intends and uses you could stick it
173 wherever you'd need an Int.
174
6c0e3459 175 subtype NameAge,
176 as Tuple[Str, Int];
177
178 ## re-parameterized subtypes of NameAge containing a Parameterizable Int
179 subtype NameBetween18and35Age,
180 as NameAge[
181 Str,
182 PositiveRangedInt[min=>18,max=>35],
183 ];
c08b66ad 184
185 One caveat is that you can't stick an unparameterized parameterizable
186 type inside a structure, such as MooseX::Types::Structured since that
187 would require the ability to convert a 'containing' type constraint into
188 a parameterizable type, which is a capacity we current don't have.
189
190 Coercions
191 Parameterizable types have some limited support for coercions. Several
192 things must be kept in mind. The first is that the coercion targets the
193 type constraint which is being made parameterizable, Not the
194 parameterizable type. So for example if you create a Parameterizable
195 type like:
196
6c0e3459 197 subtype RequiredAgeInYears,
198 as Int;
c08b66ad 199
6c0e3459 200 subtype PersonOverAge,
201 as Parameterizable[Person, RequiredAgeInYears]
202 where {
203 my ($person, $required_years_old) = @_;
204 return $person->years_old > $required_years_old;
205 }
c08b66ad 206
207 This would validate the following:
208
6c0e3459 209 my $person = Person->new(age=>35);
210 PersonOverAge([18])->check($person);
c08b66ad 211
212 You can then apply the following coercion
213
6c0e3459 214 coerce PersonOverAge,
215 from Dict[age=>int],
216 via {Person->new(%$_)},
217 from Int,
218 via {Person->new(age=>$_)};
c08b66ad 219
220 This coercion would then apply to all the following:
221
6c0e3459 222 PersonOverAge([18])->check(30); ## via the Int coercion
223 PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
c08b66ad 224
225 However, you are not allowed to place coercions on parameterizable types
226 that have had their constraining value filled, nor subtypes of such. For
227 example:
228
6c0e3459 229 coerce PersonOverAge[18],
230 from DateTime,
231 via {$_->years};
c08b66ad 232
233 That would generate a hard exception. This is a limitation for now until
234 I can devise a smarter way to cache the generated type constraints.
235 However, I doubt it will be a significant limitation, since the general
236 use case is supported.
237
238 Lastly, the constraining value is available in the coercion in much the
239 same way it is available to the constraint.
240
6c0e3459 241 ## Create a type constraint where a Person must be in the set
242 subtype PersonInSet,
243 as Parameterizable[Person, PersonSet],
244 where {
245 my ($person, $person_set) = @_;
246 $person_set->find($person);
247 }
248
249 coerce PersonInSet,
250 from HashRef,
251 via {
252 my ($hashref, $person_set) = @_;
253 return $person_set->create($hash_ref);
254 };
c08b66ad 255
256 Recursion
6c0e3459 257 TBD
c08b66ad 258
259TYPE CONSTRAINTS
260 This type library defines the following constraints.
261
262 Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint]
263 Create a subtype of ParentTypeConstraint with a dependency on a value
264 that can pass the ParameterizableValueTypeConstraint. If
265 ParameterizableValueTypeConstraint is empty we default to the 'Any' type
266 constraint (see Moose::Util::TypeConstraints).
267
268 This creates a type constraint which must be further parameterized at
269 later time before it can be used to ->check or ->validate a value.
270 Attempting to do so will cause an exception.
271
272AUTHOR
273 John Napiorkowski, "<jjnapiork@cpan.org>"
274
275COPYRIGHT & LICENSE
276 This program is free software; you can redistribute it and/or modify it
277 under the same terms as Perl itself.
278