6 Moose::Cookbook::Recipe4 - Subtypes, and modeling a simple B<Company> class hierarchy
14 use Moose::Util::TypeConstraints;
17 use Regexp::Common 'zip';
19 my $STATES = Locale::US->new;
24 (exists $STATES->{code2state}{uc($_)} ||
25 exists $STATES->{state2code}{uc($_)})
31 /^$RE{zip}{US}{-extended => 'allow'}$/
34 has 'street' => (is => 'rw', isa => 'Str');
35 has 'city' => (is => 'rw', isa => 'Str');
36 has 'state' => (is => 'rw', isa => 'USState');
37 has 'zip_code' => (is => 'rw', isa => 'USZipCode');
43 use Moose::Util::TypeConstraints;
45 has 'name' => (is => 'rw', isa => 'Str', required => 1);
46 has 'address' => (is => 'rw', isa => 'Address');
47 has 'employees' => (is => 'rw', isa => subtype ArrayRef => where {
48 (blessed($_) && $_->isa('Employee') || return) for @$_; 1
52 my ($self, $params) = @_;
53 if ($params->{employees}) {
54 foreach my $employee (@{$params->{employees}}) {
55 $employee->company($self);
60 after 'employees' => sub {
61 my ($self, $employees) = @_;
62 if (defined $employees) {
63 foreach my $employee (@{$employees}) {
64 $employee->company($self);
74 has 'first_name' => (is => 'rw', isa => 'Str', required => 1);
75 has 'last_name' => (is => 'rw', isa => 'Str', required => 1);
76 has 'middle_initial' => (is => 'rw', isa => 'Str',
77 predicate => 'has_middle_initial');
78 has 'address' => (is => 'rw', isa => 'Address');
82 return $self->first_name .
83 ($self->has_middle_initial ?
84 ' ' . $self->middle_initial . '. '
97 has 'title' => (is => 'rw', isa => 'Str', required => 1);
98 has 'company' => (is => 'rw', isa => 'Company', weak_ref => 1);
100 override 'full_name' => sub {
102 super() . ', ' . $self->title
107 In this recipe we introduce the C<subtype> keyword, and show
108 how that can be useful for specifying specific type constraints
109 without having to build an entire class to represent them. We
110 will also show how this feature can be used to leverage the
111 usefulness of CPAN modules. In addition to this, we will also
112 introduce another attribute option.
114 Let's first get into the C<subtype> features. In the B<Address>
115 class we have defined two subtypes. The first C<subtype> uses
116 the L<Locale::US> module, which provides two hashes which can be
117 used to do existence checks for state names and their two letter
118 state codes. It is a very simple, and very useful module, and
119 perfect to use in a C<subtype> constraint.
121 my $STATES = Locale::US->new;
125 (exists $STATES->{code2state}{uc($_)} ||
126 exists $STATES->{state2code}{uc($_)})
129 Because we know that states will be passed to us as strings, we
130 can make C<USState> a subtype of the built-in type constraint
131 C<Str>. This will assure that anything which is a C<USState> will
132 also pass as a C<Str>. Next, we create a constraint specializer
133 using the C<where> keyword. The value being checked against in
134 the C<where> clause can be found in the C<$_> variable (1). Our
135 constraint specializer will then look to see if the string given
136 is either a state name or a state code. If the string meets this
137 criteria, then the constraint will pass, otherwise it will fail.
138 We can now use this as we would any built-in constraint, like so:
140 has 'state' => (is => 'rw', isa => 'USState');
142 The C<state> accessor will now check all values against the
143 C<USState> constraint, thereby only allowing valid state names or
144 state codes to be stored in the C<state> slot.
146 The next C<subtype>, does pretty much the same thing using the
147 L<Regexp::Common> module, and constrainting the C<zip_code> slot.
152 /^$RE{zip}{US}{-extended => 'allow'}$/
155 Using subtypes can save a lot of un-needed abstraction by not
156 requiring you to create many small classes for these relatively
157 simple values. It also allows you to define these constraints
158 and share them among many different classes (avoiding unneeded
159 duplication) because type constraints are stored by string in a
160 global registry and always accessible to C<has>.
162 With these two subtypes and some attributes, we have defined
163 as much as we need for a basic B<Address> class. Next we define
164 a basic B<Company> class, which itself has an address. As we saw in
165 earlier recipes, we can use the C<Address> type constraint that
166 Moose automatically created for us.
168 has 'address' => (is => 'rw', isa => 'Address');
170 A company also needs a name, so we define that too.
172 has 'name' => (is => 'rw', isa => 'Str', required => 1);
174 Here we introduce another attribute option, the C<required> option.
175 This option tells Moose that C<name> is a required parameter in
176 the B<Company> constructor, and that the C<name> accessor cannot
177 accept an undefined value for the slot. The result is that C<name>
178 should always have a value.
180 The next attribute option is not actually new, but a new variant
181 of options we have already introduced.
183 has 'employees' => (is => 'rw', isa => subtype ArrayRef => where {
184 (blessed($_) && $_->isa('Employee') || return) for @$_; 1
187 Here, instead of passing a string to the C<isa> option, we are passing
188 an anyonomous subtype of the C<ArrayRef> type constraint. This subtype
189 basically checks that all the values in the ARRAY ref are instance of
190 the B<Employee> class.
192 Now this will assure that our employee's will all be of the correct
193 type, however, the B<Employee> object (which we will see in a moment)
194 also maintains a reference back to it's associated B<Company>. In order
195 to maintain this relationship (and preserve the referential integrity
196 of our objects), we need to do some processing of the employees over
197 and above that of the type constraint check. This is accomplished in
198 two places. First we need to be sure that any employees array passed
199 to the constructor is properly initialized. For this we can use the
203 my ($self, $params) = @_;
204 if ($params->{employees}) {
205 foreach my $employee (@{$params->{employees}}) {
206 $employee->company($self);
211 The C<BUILD> method will have run after the intial type constraint
212 check, so we can do just a basic existence check on the C<employees>
213 param here, and assume that if it does exist, it is both an ARRAY ref
214 and full of I<only> instances of B<Employee>.
216 The next place we need to address is the C<employees> read/write
217 accessor (see the C<employees> attribute declaration above). This
218 accessor will properly check the type constraint, but we need to add
219 some additional behavior. For this we use an C<after> method modifier
222 after 'employees' => sub {
223 my ($self, $employees) = @_;
224 if (defined $employees) {
225 foreach my $employee (@{$employees}) {
226 $employee->company($self);
231 Again, as with the C<BUILD> method, we know that the type constraint
232 check has already happened, so we can just check for defined-ness on the
233 C<$employees> argument.
235 At this point, our B<Company> class is complete. Next comes our B<Person>
236 class and its subclass the previously mentioned B<Employee> class.
238 The B<Person> class should be obvious to you at this point. It has a few
239 C<required> attributes, and the C<middle_intial> slot has an additional
240 C<predicate> method (which we saw in the previous recipe with the
241 B<BinaryTree> class).
243 Next the B<Employee> class, this too should be pretty obvious at this
244 point. It requires a C<title>, and maintains a weakend reference to a
245 B<Company> instance. The only new item, which we have seen before in
246 examples, but never in the recipe itself, is the C<override> method
249 override 'full_name' => sub {
251 super() . ', ' . $self->title
254 This just tells Moose that I am intentionally overriding the superclass
255 C<full_name> method here, and adding the value of the C<title> slot at
256 the end of the employee's full name.
260 Once again, as with all the other recipes, you can go about using
261 these classes like any other Perl 5 class. A more detailed example of
262 usage can be found in F<t/004_recipe.t>.
266 This recipe was intentionally longer and more complex to illustrate both
267 how easily Moose classes can interact (using class type constraints, etc.)
268 and the shear density of information and behaviors which Moose can pack
269 into a relatively small amount of typing. Ponder for a moment how much
270 more code a non-Moose plain old Perl 5 version of this recipe would have
271 been (including all the type constraint checks, weak references, etc).
273 And of course, this recipe also introduced the C<subtype> keyword, and
274 its usefulness within the Moose toolkit. In the next recipe we will
275 focus more on subtypes, and introduce the idea of type coercion as well.
283 The value being checked is also passed as the first argument to
284 the C<where> block as well, so it can also be accessed as C<$_[0]>
289 The C<BUILD> method is called by C<Moose::Object::BUILDALL>, which is
290 called by C<Moose::Object::new>. C<BUILDALL> will climb the object
291 inheritence graph and call the approriate C<BUILD> methods in the
298 Stevan Little E<lt>stevan@iinteractive.comE<gt>
300 =head1 COPYRIGHT AND LICENSE
302 Copyright 2006 by Infinity Interactive, Inc.
304 L<http://www.iinteractive.com>
306 This library is free software; you can redistribute it and/or modify
307 it under the same terms as Perl itself.