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 => subtype ArrayRef => where {
44 (blessed($_) && $_->isa('Employee') || return) for @$_; 1
48 my ($self, $params) = @_;
49 if ($params->{employees}) {
50 foreach my $employee (@{$params->{employees}}) {
51 $employee->company($self);
56 after 'employees' => sub {
57 my ($self, $employees) = @_;
58 if (defined $employees) {
59 foreach my $employee (@{$employees}) {
60 $employee->company($self);
68 has 'first_name' => (is => 'rw', isa => 'Str', required => 1);
69 has 'last_name' => (is => 'rw', isa => 'Str', required => 1);
70 has 'middle_initial' => (is => 'rw', isa => 'Str',
71 predicate => 'has_middle_initial');
72 has 'address' => (is => 'rw', isa => 'Address');
76 return $self->first_name .
77 ($self->has_middle_initial ?
78 ' ' . $self->middle_initial . '. '
89 has 'title' => (is => 'rw', isa => 'Str', required => 1);
90 has 'company' => (is => 'rw', isa => 'Company', weak_ref => 1);
92 override 'full_name' => sub {
94 super() . ', ' . $self->title
99 In this recipe we introduce the C<subtype> keyword, and show
100 how it can be useful for specifying type constraints
101 without building an entire class to represent them. We
102 will also show how this feature can be used to leverage the
103 usefulness of CPAN modules. In addition to this, we will
104 introduce another attribute option.
106 Let's first look at the C<subtype> feature. In the B<Address> class we have
107 defined two subtypes. The first C<subtype> uses the L<Locale::US> module, which
108 provides two hashes which can be used to perform existential checks for state
109 names and their two letter state codes. It is a very simple and very useful
110 module, and perfect for use in a C<subtype> constraint.
112 my $STATES = Locale::US->new;
116 (exists $STATES->{code2state}{uc($_)} ||
117 exists $STATES->{state2code}{uc($_)})
120 Because we know that states will be passed to us as strings, we
121 can make C<USState> a subtype of the built-in type constraint
122 C<Str>. This will ensure that anything which is a C<USState> will
123 also pass as a C<Str>. Next, we create a constraint specializer
124 using the C<where> keyword. The value being checked against in
125 the C<where> clause can be found in the C<$_> variable (1). Our
126 constraint specializer will then check whether the given string
127 is either a state name or a state code. If the string meets this
128 criteria, then the constraint will pass, otherwise it will fail.
129 We can now use this as we would any built-in constraint, like so:
131 has 'state' => (is => 'rw', isa => 'USState');
133 The C<state> accessor will now check all values against the
134 C<USState> constraint, thereby only allowing valid state names or
135 state codes to be stored in the C<state> slot.
137 The next C<subtype> does pretty much the same thing using the L<Regexp::Common>
138 module, and is used as the constraint for the C<zip_code> slot.
143 /^$RE{zip}{US}{-extended => 'allow'}$/
146 Using subtypes can save a lot of unnecessary abstraction by not requiring you to
147 create many small classes for these relatively simple values. They also allow
148 you to reuse the same constraints in a number of classes (thereby avoiding
149 duplication), since all type constraints are stored in a global registry and
150 always accessible to C<has>.
152 With these two subtypes and some attributes, we have defined
153 as much as we need for a basic B<Address> class. Next, we define
154 a basic B<Company> class, which itself has an address. As we saw in
155 earlier recipes, we can use the C<Address> type constraint that
156 Moose automatically created for us:
158 has 'address' => (is => 'rw', isa => 'Address');
160 A company also needs a name, so we define that as well:
162 has 'name' => (is => 'rw', isa => 'Str', required => 1);
164 Here we introduce another attribute option, the C<required> option.
165 This option tells Moose that C<name> is a required parameter in
166 the B<Company> constructor, and that the C<name> accessor cannot
167 accept an undefined value for the slot. The result is that C<name>
168 will always have a value.
170 The next attribute option is not actually new, but a new variant
171 of options we have already introduced:
173 has 'employees' => (is => 'rw', isa => subtype ArrayRef => where {
174 (blessed($_) && $_->isa('Employee') || return) for @$_; 1
177 Here, instead of passing a string to the C<isa> option, we are passing
178 an anonymous subtype of the C<ArrayRef> type constraint. This subtype
179 basically checks that all the values in the ARRAY ref are instances of
180 the B<Employee> class.
182 This will ensure that our employees will all be of the correct type. However,
183 the B<Employee> object (which we will see in a moment) also maintains a
184 reference to its associated B<Company>. In order to maintain this relationship
185 (and preserve the referential integrity of our objects), we need to perform some
186 processing of the employees over and above that of the type constraint check.
187 This is accomplished in two places. First we need to be sure that any employees
188 array passed to the constructor is properly initialized. For this we can use the
192 my ($self, $params) = @_;
193 if ($params->{employees}) {
194 foreach my $employee (@{$params->{employees}}) {
195 $employee->company($self);
200 The C<BUILD> method will be executed after the initial type constraint
201 check, so we can simply perform a basic existential check on the C<employees>
202 param here, and assume that if it does exist, it is both an ARRAY ref
203 and contains I<only> instances of B<Employee>.
205 The next aspect we need to address is the C<employees> read/write
206 accessor (see the C<employees> attribute declaration above). This
207 accessor will correctly check the type constraint, but we need to extend it
208 with some additional processing. For this we use an C<after> method modifier,
211 after 'employees' => sub {
212 my ($self, $employees) = @_;
213 if (defined $employees) {
214 foreach my $employee (@{$employees}) {
215 $employee->company($self);
220 Again, as with the C<BUILD> method, we know that the type constraint
221 check has already happened, so we can just check for defined-ness on the
222 C<$employees> argument.
224 At this point, our B<Company> class is complete. Next comes our B<Person>
225 class and its subclass, the previously mentioned B<Employee> class.
227 The B<Person> class should be obvious to you at this point. It has a few
228 C<required> attributes, and the C<middle_initial> slot has an additional
229 C<predicate> method (which we saw in the previous recipe with the
230 B<BinaryTree> class).
232 Next, the B<Employee> class, which should also be pretty obvious at this
233 point. It requires a C<title>, and maintains a weakened reference to a
234 B<Company> instance. The only new item, which we have seen before in
235 examples, but never in the recipe itself, is the C<override> method
238 override 'full_name' => sub {
240 super() . ', ' . $self->title
243 This just tells Moose that I am intentionally overriding the superclass
244 C<full_name> method here, and adding the value of the C<title> slot at
245 the end of the employee's full name.
249 Once again, as with all the other recipes, you can go about using
250 these classes like any other Perl 5 class. A more detailed example of
251 usage can be found in F<t/004_recipe.t>.
255 This recipe was intentionally longer and more complex to illustrate both
256 how easily Moose classes can interact (using class type constraints, etc.)
257 and the sheer density of information and behaviors which Moose can pack
258 into a relatively small amount of typing. Ponder for a moment how much
259 more code a non-Moose plain old Perl 5 version of this recipe would have
260 been (including all the type constraint checks, weak references, and so on).
262 And of course, this recipe also introduced the C<subtype> keyword, and
263 its usefulness within the Moose toolkit. In the next recipe we will
264 focus more on subtypes, and introduce the idea of type coercion as well.
272 The value being checked is also passed as the first argument to
273 the C<where> block as well, so it can also be accessed as C<$_[0]>
278 The C<BUILD> method is called by C<Moose::Object::BUILDALL>, which is
279 called by C<Moose::Object::new>. C<BUILDALL> will climb the object
280 inheritance graph and call the appropriate C<BUILD> methods in the
287 Stevan Little E<lt>stevan@iinteractive.comE<gt>
289 =head1 COPYRIGHT AND LICENSE
291 Copyright 2006, 2007 by Infinity Interactive, Inc.
293 L<http://www.iinteractive.com>
295 This library is free software; you can redistribute it and/or modify
296 it under the same terms as Perl itself.