6 Moose::Cookbook::Recipe4 - Modeling a simple B<Company> class
16 use Regexp::Common 'zip';
18 my $STATES = Locale::US->new;
23 (exists $STATES->{code2state}{uc($_)} ||
24 exists $STATES->{state2code}{uc($_)})
30 /^$RE{zip}{US}{-extended => 'allow'}$/
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');
43 has 'name' => (is => 'rw', isa => 'Str', required => 1);
44 has 'address' => (is => 'rw', isa => 'Address');
45 has 'employees' => (is => 'rw', isa => subtype ArrayRef => where {
46 (blessed($_) && $_->isa('Employee') || return) for @$_; 1
50 my ($self, $params) = @_;
51 if ($params->{employees}) {
52 foreach my $employee (@{$params->{employees}}) {
53 $employee->company($self);
58 after 'employees' => sub {
59 my ($self, $employees) = @_;
60 if (defined $employees) {
61 foreach my $employee (@{$employees}) {
62 $employee->company($self);
72 has 'first_name' => (is => 'rw', isa => 'Str', required => 1);
73 has 'last_name' => (is => 'rw', isa => 'Str', required => 1);
74 has 'middle_initial' => (is => 'rw', isa => 'Str',
75 predicate => 'has_middle_initial');
76 has 'address' => (is => 'rw', isa => 'Address');
80 return $self->first_name .
81 ($self->has_middle_initial ?
82 ' ' . $self->middle_initial . '. '
95 has 'title' => (is => 'rw', isa => 'Str', required => 1);
96 has 'company' => (is => 'rw', isa => 'Company', weak_ref => 1);
98 override 'full_name' => sub {
100 super() . ', ' . $self->title
105 In this recipe we introduce the C<subtype> keyword, and show
106 how that can be useful for specifying specific type constraints
107 without having to build an entire class to represent them. We
108 will also show how this feature can be used to leverage the
109 usefulness of CPAN modules. In addition to this, we will also
110 introduce another attribute option as well.
112 Lets first get into the C<subtype> features. In the B<Address>
113 class we have defined two subtypes. The first C<subtype> uses
114 the L<Locale::US> module, which provides two hashes which can be
115 used to do existence checks for state names and their two letter
116 state codes. It is a very simple, and very useful module, and
117 perfect to use in a C<subtype> constraint.
119 my $STATES = Locale::US->new;
123 (exists $STATES->{code2state}{uc($_)} ||
124 exists $STATES->{state2code}{uc($_)})
127 Because we know that states will be passed to us as strings, we
128 can make C<USState> a subtype of the built-in type constraint
129 C<Str>. This will assure that anything which is a C<USState> will
130 also pass as a C<Str>. Next, we create a constraint specializer
131 using the C<where> keyword. The value being checked against in
132 the C<where> clause can be found in the C<$_> variable (1). Our
133 constraint specializer will then look to see if the string given
134 is either a state name or a state code. If the string meets this
135 criteria, then the constraint will pass, otherwise it will fail.
136 We can now use this as we would any built-in constraint, like so:
138 has 'state' => (is => 'rw', isa => 'USState');
140 The C<state> accessor will now check all values against the
141 C<USState> constraint, thereby only allowing valid state names or
142 state codes to be stored in the C<state> slot.
144 The next C<subtype>, does pretty much the same thing using the
145 L<Regexp::Common> module, and constrainting the C<zip_code> slot.
150 /^$RE{zip}{US}{-extended => 'allow'}$/
153 Using subtypes can save a lot of un-needed abstraction by not
154 requiring you to create many small classes for these relatively
155 simple values. It also allows you to define these constraints
156 and share them among many different classes (avoiding unneeded
157 duplication) because type constraints are stored by string in a
158 global registry and always accessible to C<has>.
160 With these two subtypes and some attributes, we pretty much define
161 as much as we need for a basic B<Address> class. Next we define
162 a basic B<Company> class, which itself has an address. As we saw in
163 earlier recipes, we can use the C<Address> type constraint that
164 Moose automatically created for us.
166 has 'address' => (is => 'rw', isa => 'Address');
168 A company also needs a name, so we define that too.
170 has 'name' => (is => 'rw', isa => 'Str', required => 1);
172 Here we introduce another attribute option, the C<required> option.
173 This option tells Moose that C<name> is a required parameter in
174 the B<Company> constructor, and that the C<name> accessor cannot
175 accept an undefined value for the slot. The result is that C<name>
176 should always have a value.
178 The next attribute option is not actually a new one, but a new varient
179 of options we have already introduced.
181 has 'employees' => (is => 'rw', isa => subtype ArrayRef => where {
182 (blessed($_) && $_->isa('Employee') || return) for @$_; 1
185 Here, instead of passing a string to the C<isa> option, we are passing
186 an anyonomous subtype of the C<ArrayRef> type constraint. This subtype
187 basically checks that all the values in the ARRAY ref are instance of
188 the B<Employee> class.
190 Now this will assure that our employee's will all be of the correct
191 type, however, the B<Employee> object (which we will see in a moment)
192 also maintains a reference back to it's associated B<Company>. In order
193 to maintain this relationship (and preserve the referential integrity
194 of our objects), we need to do some processing of the employees over
195 and above that of the type constraint check. This is accomplished in
196 two places. First we need to be sure that any employees array passed
197 to the constructor is properly initialized. For this we can use the
201 my ($self, $params) = @_;
202 if ($params->{employees}) {
203 foreach my $employee (@{$params->{employees}}) {
204 $employee->company($self);
209 The C<BUILD> method will have run after the intial type constraint
210 check, so we can do just a basic existence check on the C<employees>
211 param here, and assume that if it does exist, it is both an ARRAY ref
212 and full of I<only> instances of B<Employee>.
214 The next place we need to address is the C<employees> read/write
215 accessor (see the C<employees> attribute declaration above). This
216 accessor will properly check the type constraint, but we need to add
217 so additional behavior. For this we use an C<after> method modifier
220 after 'employees' => sub {
221 my ($self, $employees) = @_;
222 if (defined $employees) {
223 foreach my $employee (@{$employees}) {
224 $employee->company($self);
229 Again, as with the C<BUILD> method, we know that the type constraint
230 check has already happened, so we can just check for defined-ness on the
231 C<$employees> argument.
233 At this point, our B<Company> class is complete. Next comes our B<Person>
234 class and it's subclass the previously mentioned B<Employee> class.
236 The B<Person> class should be obvious to you at this point. It has a few
237 C<required> attributes, and the C<middle_intial> slot has an additional
238 C<predicate> method (which we saw in the previous recipe with the
239 B<BinaryTree> class).
241 Next the B<Employee> class, this too should be pretty obvious at this
242 point. It requires a C<title>, and maintains a weakend reference to a
243 B<Company> instance. The only new item, which we have seen before in
244 examples, but never in the recipe itself, is the C<override> method
247 override 'full_name' => sub {
249 super() . ', ' . $self->title
252 This just tells Moose that I am intetionally overriding the superclass
253 C<full_name> method here, and adding the value of the C<title> slot at
254 the end of the employee's full name.
258 Once again, as with all the other recipes, you can go about using
259 these classes like any other Perl 5 class. A more detailed example of
260 usage can be found in F<t/004_basic.t>.
264 This recipe was intentionally longer and more complex to illustrate both
265 how easily Moose classes can interact (using class type constraints, etc.)
266 and the shear density of information and behaviors which Moose can pack
267 into a relatively small amount of typing. Ponder for a moment how much
268 more code a non-Moose plain old Perl 5 version of this recipe would have
269 been (including all the type constraint checks, weak references, etc).
271 And of course, this recipe also introduced the C<subtype> keyword, and
272 it's usefulness within the Moose toolkit. In the next recipe we will
273 focus more on subtypes, and introduce the idea of type coercion as well.
281 The value being checked is also passed as the first argument to
282 the C<where> block as well, so it can also be accessed as C<$_[0]>
287 The C<BUILD> method is called by C<Moose::Object::BUILDALL>, which is
288 called by C<Moose::Object::new>. C<BUILDALL> will climb the object
289 inheritence graph and call the approriate C<BUILD> methods in the
296 Stevan Little E<lt>stevan@iinteractive.comE<gt>
298 =head1 COPYRIGHT AND LICENSE
300 Copyright 2006 by Infinity Interactive, Inc.
302 L<http://www.iinteractive.com>
304 This library is free software; you can redistribute it and/or modify
305 it under the same terms as Perl itself.