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