doc updates to use the ArrayRef[Type] syntax
[gitmo/Moose.git] / lib / Moose / Cookbook / Recipe4.pod
CommitLineData
471c4f09 1
2=pod
3
4=head1 NAME
5
3824830b 6Moose::Cookbook::Recipe4 - Subtypes, and modeling a simple B<Company> class hierarchy
471c4f09 7
8=head1 SYNOPSIS
9
10 package Address;
471c4f09 11 use Moose;
05d9eaf6 12 use Moose::Util::TypeConstraints;
471c4f09 13
14 use Locale::US;
15 use Regexp::Common 'zip';
16
17 my $STATES = Locale::US->new;
18
19 subtype USState
20 => as Str
21 => where {
172e0738 22 (exists $STATES->{code2state}{uc($_)} ||
23 exists $STATES->{state2code}{uc($_)})
471c4f09 24 };
25
26 subtype USZipCode
27 => as Value
28 => where {
29 /^$RE{zip}{US}{-extended => 'allow'}$/
30 };
31
32 has 'street' => (is => 'rw', isa => 'Str');
33 has 'city' => (is => 'rw', isa => 'Str');
34 has 'state' => (is => 'rw', isa => 'USState');
35 has 'zip_code' => (is => 'rw', isa => 'USZipCode');
07cde929 36
471c4f09 37 package Person;
471c4f09 38 use Moose;
39
7c6cacb4 40 has 'first_name' => (is => 'rw', isa => 'Str', required => 1);
41 has 'last_name' => (is => 'rw', isa => 'Str', required => 1);
172e0738 42 has 'middle_initial' => (is => 'rw', isa => 'Str',
43 predicate => 'has_middle_initial');
471c4f09 44 has 'address' => (is => 'rw', isa => 'Address');
45
46 sub full_name {
47 my $self = shift;
48 return $self->first_name .
172e0738 49 ($self->has_middle_initial ?
50 ' ' . $self->middle_initial . '. '
51 :
52 ' ') .
471c4f09 53 $self->last_name;
54 }
55
56 package Employee;
471c4f09 57 use Moose;
58
59 extends 'Person';
60
7c6cacb4 61 has 'title' => (is => 'rw', isa => 'Str', required => 1);
471c4f09 62 has 'company' => (is => 'rw', isa => 'Company', weak_ref => 1);
63
64 override 'full_name' => sub {
65 my $self = shift;
66 super() . ', ' . $self->title
67 };
07cde929 68
69 package Company;
70 use Moose;
71
72 has 'name' => (is => 'rw', isa => 'Str', required => 1);
73 has 'address' => (is => 'rw', isa => 'Address');
74 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
75
76 sub BUILD {
77 my ($self, $params) = @_;
78 if ($params->{employees}) {
79 foreach my $employee (@{$params->{employees}}) {
80 $employee->company($self);
81 }
82 }
83 }
84
85 after 'employees' => sub {
86 my ($self, $employees) = @_;
87 if (defined $employees) {
88 foreach my $employee (@{$employees}) {
89 $employee->company($self);
90 }
91 }
92 };
93
2f04a0fc 94
471c4f09 95=head1 DESCRIPTION
96
172e0738 97In this recipe we introduce the C<subtype> keyword, and show
2f04a0fc 98how it can be useful for specifying type constraints
99without building an entire class to represent them. We
172e0738 100will also show how this feature can be used to leverage the
2f04a0fc 101usefulness of CPAN modules. In addition to this, we will
e08c54f5 102introduce another attribute option.
172e0738 103
2f04a0fc 104Let's first look at the C<subtype> feature. In the B<Address> class we have
105defined two subtypes. The first C<subtype> uses the L<Locale::US> module, which
106provides two hashes which can be used to perform existential checks for state
107names and their two letter state codes. It is a very simple and very useful
108module, and perfect for use in a C<subtype> constraint.
172e0738 109
110 my $STATES = Locale::US->new;
111 subtype USState
112 => as Str
113 => where {
114 (exists $STATES->{code2state}{uc($_)} ||
115 exists $STATES->{state2code}{uc($_)})
116 };
117
118Because we know that states will be passed to us as strings, we
119can make C<USState> a subtype of the built-in type constraint
2f04a0fc 120C<Str>. This will ensure that anything which is a C<USState> will
172e0738 121also pass as a C<Str>. Next, we create a constraint specializer
122using the C<where> keyword. The value being checked against in
123the C<where> clause can be found in the C<$_> variable (1). Our
2f04a0fc 124constraint specializer will then check whether the given string
172e0738 125is either a state name or a state code. If the string meets this
126criteria, then the constraint will pass, otherwise it will fail.
127We can now use this as we would any built-in constraint, like so:
128
129 has 'state' => (is => 'rw', isa => 'USState');
130
131The C<state> accessor will now check all values against the
132C<USState> constraint, thereby only allowing valid state names or
133state codes to be stored in the C<state> slot.
134
2f04a0fc 135The next C<subtype> does pretty much the same thing using the L<Regexp::Common>
136module, and is used as the constraint for the C<zip_code> slot.
172e0738 137
138 subtype USZipCode
139 => as Value
140 => where {
141 /^$RE{zip}{US}{-extended => 'allow'}$/
142 };
143
2f04a0fc 144Using subtypes can save a lot of unnecessary abstraction by not requiring you to
145create many small classes for these relatively simple values. They also allow
146you to reuse the same constraints in a number of classes (thereby avoiding
147duplication), since all type constraints are stored in a global registry and
148always accessible to C<has>.
172e0738 149
07cde929 150With these two subtypes and some attributes, we have defined as much as we
151need for a basic B<Address> class. Next comes our B<Person> class and its
152subclass, the B<Employee> class.
153
154The B<Person> class is pretty straightforward. We do introduce another attribute
155option, the C<required> option. This option tells Moose that the attribute is
156a required parameter in the constructor, and that the attribute's accessor cannot
157accept an undefined value for the slot. The result is that the attribute
158will always have a value.
159
160In B<Person>, the C<first_name> and C<last_name> attributes are C<required>, and
161the C<middle_initial> slot has an additional C<predicate> method (which we saw
162in the previous recipe with the B<BinaryTree> class).
163
164Next, the B<Employee> class. It requires a C<title>, and maintains a
165weakened reference to a B<Company> instance (which will be defined next).
166The only new item, which we have seen before in examples, but never in
167the recipe itself, is the C<override> method modifier:
168
169 override 'full_name' => sub {
170 my $self = shift;
171 super() . ', ' . $self->title
172 };
173
174This just tells Moose that I am intentionally overriding the superclass
175C<full_name> method here, and adding the value of the C<title> slot at
176the end of the employee's full name.
177
178Next, we define a basic B<Company> class, which itself has an address.
179As we saw in earlier recipes, we can use the C<Address> type constraint that
2f04a0fc 180Moose automatically created for us:
172e0738 181
182 has 'address' => (is => 'rw', isa => 'Address');
183
2f04a0fc 184A company also needs a name, so we define that as well:
172e0738 185
186 has 'name' => (is => 'rw', isa => 'Str', required => 1);
187
e08c54f5 188The next attribute option is not actually new, but a new variant
2f04a0fc 189of options we have already introduced:
ad5ed80c 190
07cde929 191 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
192
193Here we are creating a container type constraint. Container type constraints
194can be either C<ArrayRef> or C<HashRef> and have a second type which specifies
195the kind of values they contain. In this case, we are telling Moose that
196we expect an C<ArrayRef> of C<Employee> objects. This will ensure that our
197employees will all be of the correct type.
e08c54f5 198
07cde929 199It is important to note that container types B<must> be defined already,
200Moose will not create an anon-type for you as it will in other situations.
ad5ed80c 201
07cde929 202However, the B<Employee> object (which we will see in a moment) also maintains a
2f04a0fc 203reference to its associated B<Company>. In order to maintain this relationship
204(and preserve the referential integrity of our objects), we need to perform some
205processing of the employees over and above that of the type constraint check.
206This is accomplished in two places. First we need to be sure that any employees
207array passed to the constructor is properly initialized. For this we can use the
208C<BUILD> method (2):
ad5ed80c 209
210 sub BUILD {
211 my ($self, $params) = @_;
212 if ($params->{employees}) {
213 foreach my $employee (@{$params->{employees}}) {
214 $employee->company($self);
215 }
216 }
217 }
218
2f04a0fc 219The C<BUILD> method will be executed after the initial type constraint
220check, so we can simply perform a basic existential check on the C<employees>
ad5ed80c 221param here, and assume that if it does exist, it is both an ARRAY ref
2f04a0fc 222and contains I<only> instances of B<Employee>.
ad5ed80c 223
2f04a0fc 224The next aspect we need to address is the C<employees> read/write
ad5ed80c 225accessor (see the C<employees> attribute declaration above). This
2f04a0fc 226accessor will correctly check the type constraint, but we need to extend it
227with some additional processing. For this we use an C<after> method modifier,
ad5ed80c 228like so:
229
230 after 'employees' => sub {
231 my ($self, $employees) = @_;
232 if (defined $employees) {
233 foreach my $employee (@{$employees}) {
234 $employee->company($self);
235 }
236 }
237 };
238
239Again, as with the C<BUILD> method, we know that the type constraint
240check has already happened, so we can just check for defined-ness on the
241C<$employees> argument.
242
07cde929 243At this point, our B<Company> class is complete.
ad5ed80c 244
e08c54f5 245And that's about it.
ad5ed80c 246
247Once again, as with all the other recipes, you can go about using
248these classes like any other Perl 5 class. A more detailed example of
db1ab48d 249usage can be found in F<t/004_recipe.t>.
ad5ed80c 250
251=head1 CONCLUSION
252
253This recipe was intentionally longer and more complex to illustrate both
254how easily Moose classes can interact (using class type constraints, etc.)
2f04a0fc 255and the sheer density of information and behaviors which Moose can pack
ad5ed80c 256into a relatively small amount of typing. Ponder for a moment how much
257more code a non-Moose plain old Perl 5 version of this recipe would have
2f04a0fc 258been (including all the type constraint checks, weak references, and so on).
ad5ed80c 259
260And of course, this recipe also introduced the C<subtype> keyword, and
e08c54f5 261its usefulness within the Moose toolkit. In the next recipe we will
ad5ed80c 262focus more on subtypes, and introduce the idea of type coercion as well.
e08c54f5 263
172e0738 264=head1 FOOTNOTES
265
266=over 4
267
268=item (1)
269
270The value being checked is also passed as the first argument to
271the C<where> block as well, so it can also be accessed as C<$_[0]>
272as well.
273
ad5ed80c 274=item (2)
275
276The C<BUILD> method is called by C<Moose::Object::BUILDALL>, which is
277called by C<Moose::Object::new>. C<BUILDALL> will climb the object
4711f5f7 278inheritance graph and call the appropriate C<BUILD> methods in the
ad5ed80c 279correct order.
280
172e0738 281=back
282
471c4f09 283=head1 AUTHOR
284
285Stevan Little E<lt>stevan@iinteractive.comE<gt>
286
287=head1 COPYRIGHT AND LICENSE
288
b77fdbed 289Copyright 2006, 2007 by Infinity Interactive, Inc.
471c4f09 290
291L<http://www.iinteractive.com>
292
293This library is free software; you can redistribute it and/or modify
294it under the same terms as Perl itself.
295
e08c54f5 296=cut