6 Moose::Cookbook::Recipe4 - Subtypes, and modeling a simple B<Company> class hierarchy
12 use Moose::Util::TypeConstraints;
15 use Regexp::Common 'zip';
17 my $STATES = Locale::US->new;
22 (exists $STATES->{code2state}{uc($_)} ||
23 exists $STATES->{state2code}{uc($_)})
29 /^$RE{zip}{US}{-extended => 'allow'}$/
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');
40 has 'first_name' => (is => 'rw', isa => 'Str', required => 1);
41 has 'last_name' => (is => 'rw', isa => 'Str', required => 1);
42 has 'middle_initial' => (is => 'rw', isa => 'Str',
43 predicate => 'has_middle_initial');
44 has 'address' => (is => 'rw', isa => 'Address');
48 return $self->first_name .
49 ($self->has_middle_initial ?
50 ' ' . $self->middle_initial . '. '
61 has 'title' => (is => 'rw', isa => 'Str', required => 1);
62 has 'company' => (is => 'rw', isa => 'Company', weak_ref => 1);
64 override 'full_name' => sub {
66 super() . ', ' . $self->title
72 has 'name' => (is => 'rw', isa => 'Str', required => 1);
73 has 'address' => (is => 'rw', isa => 'Address');
74 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
77 my ($self, $params) = @_;
78 if ($params->{employees}) {
79 foreach my $employee (@{$params->{employees}}) {
80 $employee->company($self);
85 after 'employees' => sub {
86 my ($self, $employees) = @_;
87 if (defined $employees) {
88 foreach my $employee (@{$employees}) {
89 $employee->company($self);
97 In this recipe we introduce the C<subtype> keyword, and show
98 how it can be useful for specifying type constraints
99 without building an entire class to represent them. We
100 will also show how this feature can be used to leverage the
101 usefulness of CPAN modules. In addition to this, we will
102 introduce another attribute option.
104 Let's first look at the C<subtype> feature. In the B<Address> class we have
105 defined two subtypes. The first C<subtype> uses the L<Locale::US> module, which
106 provides two hashes which can be used to perform existential checks for state
107 names and their two letter state codes. It is a very simple and very useful
108 module, and perfect for use in a C<subtype> constraint.
110 my $STATES = Locale::US->new;
114 (exists $STATES->{code2state}{uc($_)} ||
115 exists $STATES->{state2code}{uc($_)})
118 Because we know that states will be passed to us as strings, we
119 can make C<USState> a subtype of the built-in type constraint
120 C<Str>. This will ensure that anything which is a C<USState> will
121 also pass as a C<Str>. Next, we create a constraint specializer
122 using the C<where> keyword. The value being checked against in
123 the C<where> clause can be found in the C<$_> variable (1). Our
124 constraint specializer will then check whether the given string
125 is either a state name or a state code. If the string meets this
126 criteria, then the constraint will pass, otherwise it will fail.
127 We can now use this as we would any built-in constraint, like so:
129 has 'state' => (is => 'rw', isa => 'USState');
131 The C<state> accessor will now check all values against the
132 C<USState> constraint, thereby only allowing valid state names or
133 state codes to be stored in the C<state> slot.
135 The next C<subtype> does pretty much the same thing using the L<Regexp::Common>
136 module, and is used as the constraint for the C<zip_code> slot.
141 /^$RE{zip}{US}{-extended => 'allow'}$/
144 Using subtypes can save a lot of unnecessary abstraction by not requiring you to
145 create many small classes for these relatively simple values. They also allow
146 you to reuse the same constraints in a number of classes (thereby avoiding
147 duplication), since all type constraints are stored in a global registry and
148 always accessible to C<has>.
150 With these two subtypes and some attributes, we have defined as much as we
151 need for a basic B<Address> class. Next comes our B<Person> class and its
152 subclass, the B<Employee> class.
154 The B<Person> class is pretty straightforward. We do introduce another attribute
155 option, the C<required> option. This option tells Moose that the attribute is
156 a required parameter in the constructor, and that the attribute's accessor cannot
157 accept an undefined value for the slot. The result is that the attribute
158 will always have a value.
160 In B<Person>, the C<first_name> and C<last_name> attributes are C<required>, and
161 the C<middle_initial> slot has an additional C<predicate> method (which we saw
162 in the previous recipe with the B<BinaryTree> class).
164 Next, the B<Employee> class. It requires a C<title>, and maintains a
165 weakened reference to a B<Company> instance (which will be defined next).
166 The only new item, which we have seen before in examples, but never in
167 the recipe itself, is the C<override> method modifier:
169 override 'full_name' => sub {
171 super() . ', ' . $self->title
174 This just tells Moose that I am intentionally overriding the superclass
175 C<full_name> method here, and adding the value of the C<title> slot at
176 the end of the employee's full name.
178 Next, we define a basic B<Company> class, which itself has an address.
179 As we saw in earlier recipes, we can use the C<Address> type constraint that
180 Moose automatically created for us:
182 has 'address' => (is => 'rw', isa => 'Address');
184 A company also needs a name, so we define that as well:
186 has 'name' => (is => 'rw', isa => 'Str', required => 1);
188 The next attribute option is not actually new, but a new variant
189 of options we have already introduced:
191 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
193 Here we are creating a container type constraint. Container type constraints
194 can be either C<ArrayRef> or C<HashRef> and have a second type which specifies
195 the kind of values they contain. In this case, we are telling Moose that
196 we expect an C<ArrayRef> of C<Employee> objects. This will ensure that our
197 employees will all be of the correct type.
199 It is important to note that container types B<must> be defined already,
200 Moose will not create an anon-type for you as it will in other situations.
202 However, the B<Employee> object (which we will see in a moment) also maintains a
203 reference 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
205 processing of the employees over and above that of the type constraint check.
206 This is accomplished in two places. First we need to be sure that any employees
207 array passed to the constructor is properly initialized. For this we can use the
211 my ($self, $params) = @_;
212 if ($params->{employees}) {
213 foreach my $employee (@{$params->{employees}}) {
214 $employee->company($self);
219 The C<BUILD> method will be executed after the initial type constraint
220 check, so we can simply perform a basic existential check on the C<employees>
221 param here, and assume that if it does exist, it is both an ARRAY ref
222 and contains I<only> instances of B<Employee>.
224 The next aspect we need to address is the C<employees> read/write
225 accessor (see the C<employees> attribute declaration above). This
226 accessor will correctly check the type constraint, but we need to extend it
227 with some additional processing. For this we use an C<after> method modifier,
230 after 'employees' => sub {
231 my ($self, $employees) = @_;
232 if (defined $employees) {
233 foreach my $employee (@{$employees}) {
234 $employee->company($self);
239 Again, as with the C<BUILD> method, we know that the type constraint
240 check has already happened, so we can just check for defined-ness on the
241 C<$employees> argument.
243 At this point, our B<Company> class is complete.
247 Once again, as with all the other recipes, you can go about using
248 these classes like any other Perl 5 class. A more detailed example of
249 usage can be found in F<t/004_recipe.t>.
253 This recipe was intentionally longer and more complex to illustrate both
254 how easily Moose classes can interact (using class type constraints, etc.)
255 and the sheer density of information and behaviors which Moose can pack
256 into a relatively small amount of typing. Ponder for a moment how much
257 more code a non-Moose plain old Perl 5 version of this recipe would have
258 been (including all the type constraint checks, weak references, and so on).
260 And of course, this recipe also introduced the C<subtype> keyword, and
261 its usefulness within the Moose toolkit. In the next recipe we will
262 focus more on subtypes, and introduce the idea of type coercion as well.
270 The value being checked is also passed as the first argument to
271 the C<where> block as well, so it can also be accessed as C<$_[0]>
276 The C<BUILD> method is called by C<Moose::Object::BUILDALL>, which is
277 called by C<Moose::Object::new>. C<BUILDALL> will climb the object
278 inheritance graph and call the appropriate C<BUILD> methods in the
285 Stevan Little E<lt>stevan@iinteractive.comE<gt>
287 =head1 COPYRIGHT AND LICENSE
289 Copyright 2006, 2007 by Infinity Interactive, Inc.
291 L<http://www.iinteractive.com>
293 This library is free software; you can redistribute it and/or modify
294 it under the same terms as Perl itself.