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');
39 use Moose::Util::TypeConstraints;
41 has 'name' => (is => 'rw', isa => 'Str', required => 1);
42 has 'address' => (is => 'rw', isa => 'Address');
43 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
46 my ($self, $params) = @_;
47 if ($params->{employees}) {
48 foreach my $employee (@{$params->{employees}}) {
49 $employee->company($self);
54 after 'employees' => sub {
55 my ($self, $employees) = @_;
56 if (defined $employees) {
57 foreach my $employee (@{$employees}) {
58 $employee->company($self);
66 has 'first_name' => (is => 'rw', isa => 'Str', required => 1);
67 has 'last_name' => (is => 'rw', isa => 'Str', required => 1);
68 has 'middle_initial' => (is => 'rw', isa => 'Str',
69 predicate => 'has_middle_initial');
70 has 'address' => (is => 'rw', isa => 'Address');
74 return $self->first_name .
75 ($self->has_middle_initial ?
76 ' ' . $self->middle_initial . '. '
87 has 'title' => (is => 'rw', isa => 'Str', required => 1);
88 has 'company' => (is => 'rw', isa => 'Company', weak_ref => 1);
90 override 'full_name' => sub {
92 super() . ', ' . $self->title
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
151 as much as we need for a basic B<Address> class. Next, we define
152 a basic B<Company> class, which itself has an address. As we saw in
153 earlier recipes, we can use the C<Address> type constraint that
154 Moose automatically created for us:
156 has 'address' => (is => 'rw', isa => 'Address');
158 A company also needs a name, so we define that as well:
160 has 'name' => (is => 'rw', isa => 'Str', required => 1);
162 Here we introduce another attribute option, the C<required> option.
163 This option tells Moose that C<name> is a required parameter in
164 the B<Company> constructor, and that the C<name> accessor cannot
165 accept an undefined value for the slot. The result is that C<name>
166 will always have a value.
168 The next attribute option is not actually new, but a new variant
169 of options we have already introduced:
171 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
173 Here, we are passing a more complex string to the C<isa> option, we
174 are passing a container type constraint. Container type constraints
175 can either be C<ArrayRef> or C<HashRef> with a contained type given
176 inside the square brackets. This basically checks that all the values
177 in the ARRAY ref are instances of the B<Employee> class.
179 This will ensure that our employees will all be of the correct type. However,
180 the B<Employee> object (which we will see in a moment) also maintains a
181 reference to its associated B<Company>. In order to maintain this relationship
182 (and preserve the referential integrity of our objects), we need to perform some
183 processing of the employees over and above that of the type constraint check.
184 This is accomplished in two places. First we need to be sure that any employees
185 array passed to the constructor is properly initialized. For this we can use the
189 my ($self, $params) = @_;
190 if ($params->{employees}) {
191 foreach my $employee (@{$params->{employees}}) {
192 $employee->company($self);
197 The C<BUILD> method will be executed after the initial type constraint
198 check, so we can simply perform a basic existential check on the C<employees>
199 param here, and assume that if it does exist, it is both an ARRAY ref
200 and contains I<only> instances of B<Employee>.
202 The next aspect we need to address is the C<employees> read/write
203 accessor (see the C<employees> attribute declaration above). This
204 accessor will correctly check the type constraint, but we need to extend it
205 with some additional processing. For this we use an C<after> method modifier,
208 after 'employees' => sub {
209 my ($self, $employees) = @_;
210 if (defined $employees) {
211 foreach my $employee (@{$employees}) {
212 $employee->company($self);
217 Again, as with the C<BUILD> method, we know that the type constraint
218 check has already happened, so we can just check for defined-ness on the
219 C<$employees> argument.
221 At this point, our B<Company> class is complete. Next comes our B<Person>
222 class and its subclass, the previously mentioned B<Employee> class.
224 The B<Person> class should be obvious to you at this point. It has a few
225 C<required> attributes, and the C<middle_initial> slot has an additional
226 C<predicate> method (which we saw in the previous recipe with the
227 B<BinaryTree> class).
229 Next, the B<Employee> class, which should also be pretty obvious at this
230 point. It requires a C<title>, and maintains a weakened reference to a
231 B<Company> instance. The only new item, which we have seen before in
232 examples, but never in the recipe itself, is the C<override> method
235 override 'full_name' => sub {
237 super() . ', ' . $self->title
240 This just tells Moose that I am intentionally overriding the superclass
241 C<full_name> method here, and adding the value of the C<title> slot at
242 the end of the employee's full name.
246 Once again, as with all the other recipes, you can go about using
247 these classes like any other Perl 5 class. A more detailed example of
248 usage can be found in F<t/000_recipes/004_recipe.t>.
252 This recipe was intentionally longer and more complex to illustrate both
253 how easily Moose classes can interact (using class type constraints, etc.)
254 and the sheer density of information and behaviors which Moose can pack
255 into a relatively small amount of typing. Ponder for a moment how much
256 more code a non-Moose plain old Perl 5 version of this recipe would have
257 been (including all the type constraint checks, weak references, and so on).
259 And of course, this recipe also introduced the C<subtype> keyword, and
260 its usefulness within the Moose toolkit. In the next recipe we will
261 focus more on subtypes, and introduce the idea of type coercion as well.
269 The value being checked is also passed as the first argument to
270 the C<where> block as well, so it can also be accessed as C<$_[0]>
275 The C<BUILD> method is called by C<Moose::Object::BUILDALL>, which is
276 called by C<Moose::Object::new>. C<BUILDALL> will climb the object
277 inheritance graph and call the appropriate C<BUILD> methods in the
284 Stevan Little E<lt>stevan@iinteractive.comE<gt>
286 =head1 COPYRIGHT AND LICENSE
288 Copyright 2006-2008 by Infinity Interactive, Inc.
290 L<http://www.iinteractive.com>
292 This library is free software; you can redistribute it and/or modify
293 it under the same terms as Perl itself.