cleanup synopsis example and finished coercion fix
[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         ## 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
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 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
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
84
85     This throws a hard Moose exception. You'll need to capture it in an eval
86     or related exception catching system (see TryCatch or <Try::Tiny>.)
87
88         RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
89
90     If you can't accept a hard exception here, you'll need to test the
91     constraining values first, as in:
92
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         }
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
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!
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
116         TypeConstraint([$ob])->method;
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
130         subtype RangedInt,
131             as Parameterizable[Int, Range],
132             where {
133                 my ($value, $range) = @_;
134                 return ($value >= $range->{min} &&
135                  $value =< $range->{max});
136             };
137
138     Example subtype with additional constraints:
139
140         subtype PositiveRangedInt,
141             as RangedInt,
142             where {
143                 shift >= 0;              
144             };
145
146     Or you could have done the following instead:
147
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];
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
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             ];
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
197         subtype RequiredAgeInYears,
198           as Int;
199
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           }
206
207     This would validate the following:
208
209         my $person = Person->new(age=>35);
210         PersonOverAge([18])->check($person);
211
212     You can then apply the following coercion
213
214         coerce PersonOverAge,
215           from Dict[age=>int],
216           via {Person->new(%$_)},
217           from Int,
218           via {Person->new(age=>$_)};
219
220     This coercion would then apply to all the following:
221
222         PersonOverAge([18])->check(30); ## via the Int coercion
223         PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
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
229         coerce PersonOverAge[18],
230           from DateTime,
231           via {$_->years};
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
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             };
255
256   Recursion
257         TBD
258
259 TYPE 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
272 AUTHOR
273     John Napiorkowski, "<jjnapiork@cpan.org>"
274
275 COPYRIGHT & LICENSE
276     This program is free software; you can redistribute it and/or modify it
277     under the same terms as Perl itself.
278