fixed readme
[gitmo/MooseX-Dependent.git] / README
1 NAME
2     MooseX::Types::Parameterizable - Create your own Parameterizable Types.
3
4 SYNOPSIS
5     The follow is example usage.
6
7         package Test::MooseX::Types::Parameterizable::Synopsis;
8
9         use Moose;
10         use MooseX::Types::Parameterizable qw(Parameterizable);
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         ## a SQL Varchar database 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',  ## too long!
45             varchar_ten => '123456789',
46         );
47
48         ## varchar_five coerces as expected
49         my $object3 = __PACKAGE__->new(
50             varchar_five => [qw/aa bb/],  ## coerces to "aabb"
51             varchar_ten => '123456789',
52         );
53
54     See t/05-pod-examples.t for runnable versions of all POD code
55
56 DESCRIPTION
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.
62
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
67     more.
68
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:
72
73         subtype Range,
74             as Dict[max=>Int, min=>Int],
75             where {
76                 my ($range) = @_;
77                 return $range->{max} > $range->{min};
78             };
79
80         subtype RangedInt,
81             as Parameterizable[Int, Range],
82             where {
83                 my ($value, $range) = @_;
84                 return ($value >= $range->{min} &&
85                  $value <= $range->{max});
86             };
87         
88         RangedInt([{min=>10,max=>100}])->check(50); ## OK
89         RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
90
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)
96
97         RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
98
99     If you can't accept a hard exception here, you'll need to test the
100     constraining values first, as in:
101
102         my $range = {min=>99, max=>10};
103         if(my $err = Range->validate($range)) {
104             ## Handle #$err
105         } else {
106             RangedInt($range)->check(99);
107         }
108
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
112     written as:
113
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!
117
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.
121
122     Also not that if you 'chain' parameterization results with a method call
123     like:
124
125         TypeConstraint([$ob])->method;
126
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.
131
132     ==head2 Subtyping a Parameterizable type constraints
133
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.
137     For example:
138
139         subtype RangedInt,
140             as Parameterizable[Int, Range],
141             where {
142                 my ($value, $range) = @_;
143                 return ($value >= $range->{min} &&
144                  $value =< $range->{max});
145             };
146
147     Example subtype with additional constraints:
148
149         subtype PositiveRangedInt,
150             as RangedInt,
151             where {
152                 shift >= 0;              
153             };
154
155     Or you could have done the following instead:
156
157         ## Subtype of Int for positive numbers
158         subtype PositiveInt,
159             as Int,
160             where {
161                 my ($value, $range) = @_;
162                 return $value >= 0;
163             };
164
165         ## subtype Range to re-parameterize Range with subtypes
166         subtype PositiveRange,
167             as Range[max=>PositiveInt, min=>PositiveInt];
168     
169         ## create subtype via reparameterizing
170         subtype PositiveRangedInt,
171             as RangedInt[PositiveRange];
172
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.
183
184         subtype NameAge,
185             as Tuple[Str, Int];
186     
187         ## re-parameterized subtypes of NameAge containing a Parameterizable Int    
188         subtype NameBetween18and35Age,
189             as NameAge[
190                 Str,
191                 PositiveRangedInt[min=>18,max=>35],
192             ];
193
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.
198
199   Coercions
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
204     type like:
205
206         subtype RequiredAgeInYears,
207           as Int;
208
209         subtype PersonOverAge,
210           as Parameterizable[Person, RequiredAgeInYears]
211           where {
212             my ($person, $required_years_old) = @_;
213             return $person->years_old > $required_years_old;
214           }
215
216     This would validate the following:
217
218         my $person = Person->new(age=>35);
219         PersonOverAge([18])->check($person);
220
221     You can then apply the following coercion
222
223         coerce PersonOverAge,
224           from Dict[age=>int],
225           via {Person->new(%$_)},
226           from Int,
227           via {Person->new(age=>$_)};
228
229     This coercion would then apply to all the following:
230
231         PersonOverAge([18])->check(30); ## via the Int coercion
232         PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
233
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
236     example:
237
238         coerce PersonOverAge[18],
239           from DateTime,
240           via {$_->years};
241
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.
246
247     Lastly, the constraining value is available in the coercion in much the
248     same way it is available to the constraint.
249
250         ## Create a type constraint where a Person must be in the set
251         subtype PersonInSet,
252             as Parameterizable[Person, PersonSet],
253             where {
254                 my ($person, $person_set) = @_;
255                 $person_set->find($person);
256             }
257
258         coerce PersonInSet,
259             from HashRef,
260             via {
261                 my ($hashref, $person_set) = @_;
262                 return $person_set->create($hash_ref);
263             };
264
265   Recursion
266         TBD - Need more tests.
267
268 TYPE CONSTRAINTS
269     This type library defines the following constraints.
270
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).
276
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.
280
281 AUTHOR
282     John Napiorkowski, "<jjnapiork@cpan.org>"
283
284 COPYRIGHT & LICENSE
285     This program is free software; you can redistribute it and/or modify it
286     under the same terms as Perl itself.
287