1Moose::Cookbook::BasicsU:s:eCromCpoanntyr_iSbuuMbtoteoydspeeP:se:(rC3lo)oDkobcouomke:n:tBaatsiiocns::Company_Subtypes(3)
2
3
4
6 Moose::Cookbook::Basics::Company_Subtypes - Demonstrates the use of
7 subtypes and how to model classes related to companies, people,
8 employees, etc.
9
11 version 2.2015
12
14 package Address;
15 use Moose;
16 use Moose::Util::TypeConstraints;
17
18 use Locale::US;
19 use Regexp::Common 'zip';
20
21 my $STATES = Locale::US->new;
22 subtype 'USState'
23 => as Str
24 => where {
25 ( exists $STATES->{code2state}{ uc($_) }
26 || exists $STATES->{state2code}{ uc($_) } );
27 };
28
29 subtype 'USZipCode'
30 => as Value
31 => where {
32 /^$RE{zip}{US}{-extended => 'allow'}$/;
33 };
34
35 has 'street' => ( is => 'rw', isa => 'Str' );
36 has 'city' => ( is => 'rw', isa => 'Str' );
37 has 'state' => ( is => 'rw', isa => 'USState' );
38 has 'zip_code' => ( is => 'rw', isa => 'USZipCode' );
39
40 package Company;
41 use Moose;
42 use Moose::Util::TypeConstraints;
43
44 has 'name' => ( is => 'rw', isa => 'Str', required => 1 );
45 has 'address' => ( is => 'rw', isa => 'Address' );
46 has 'employees' => (
47 is => 'rw',
48 isa => 'ArrayRef[Employee]',
49 default => sub { [] },
50 );
51
52 sub BUILD {
53 my ( $self, $params ) = @_;
54 foreach my $employee ( @{ $self->employees } ) {
55 $employee->employer($self);
56 }
57 }
58
59 after 'employees' => sub {
60 my ( $self, $employees ) = @_;
61 return unless $employees;
62 foreach my $employee ( @$employees ) {
63 $employee->employer($self);
64 }
65 };
66
67 package Person;
68 use Moose;
69
70 has 'first_name' => ( is => 'rw', isa => 'Str', required => 1 );
71 has 'last_name' => ( is => 'rw', isa => 'Str', required => 1 );
72 has 'middle_initial' => (
73 is => 'rw', isa => 'Str',
74 predicate => 'has_middle_initial'
75 );
76 has 'address' => ( is => 'rw', isa => 'Address' );
77
78 sub full_name {
79 my $self = shift;
80 return $self->first_name
81 . (
82 $self->has_middle_initial
83 ? ' ' . $self->middle_initial . '. '
84 : ' '
85 ) . $self->last_name;
86 }
87
88 package Employee;
89 use Moose;
90
91 extends 'Person';
92
93 has 'title' => ( is => 'rw', isa => 'Str', required => 1 );
94 has 'employer' => ( is => 'rw', isa => 'Company', weak_ref => 1 );
95
96 override 'full_name' => sub {
97 my $self = shift;
98 super() . ', ' . $self->title;
99 };
100
102 This recipe introduces the "subtype" sugar function from
103 Moose::Util::TypeConstraints. The "subtype" function lets you
104 declaratively create type constraints without building an entire class.
105
106 In the recipe we also make use of Locale::US and Regexp::Common to
107 build constraints, showing how constraints can make use of existing
108 CPAN tools for data validation.
109
110 Finally, we introduce the "required" attribute option.
111
112 In the "Address" class we define two subtypes. The first uses the
113 Locale::US module to check the validity of a state. It accepts either a
114 state abbreviation or full name.
115
116 A state will be passed in as a string, so we make our "USState" type a
117 subtype of Moose's builtin "Str" type. This is done using the "as"
118 sugar. The actual constraint is defined using "where". This function
119 accepts a single subroutine reference. That subroutine will be called
120 with the value to be checked in $_ (1). It is expected to return a true
121 or false value indicating whether the value is valid for the type.
122
123 We can now use the "USState" type just like Moose's builtin types:
124
125 has 'state' => ( is => 'rw', isa => 'USState' );
126
127 When the "state" attribute is set, the value is checked against the
128 "USState" constraint. If the value is not valid, an exception will be
129 thrown.
130
131 The next "subtype", "USZipCode", uses Regexp::Common. Regexp::Common
132 includes a regex for validating US zip codes. We use this constraint
133 for the "zip_code" attribute.
134
135 subtype 'USZipCode'
136 => as Value
137 => where {
138 /^$RE{zip}{US}{-extended => 'allow'}$/;
139 };
140
141 Using a subtype instead of requiring a class for each type greatly
142 simplifies the code. We don't really need a class for these types, as
143 they're just strings, but we do want to ensure that they're valid.
144
145 The type constraints we created are reusable. Type constraints are
146 stored by name in a global registry, which means that we can refer to
147 them in other classes. Because the registry is global, we do recommend
148 that you use some sort of namespacing in real applications, like
149 "MyApp::Type::USState" (just as you would do with class names).
150
151 These two subtypes allow us to define a simple "Address" class.
152
153 Then we define our "Company" class, which has an address. As we saw in
154 earlier recipes, Moose automatically creates a type constraint for each
155 our classes, so we can use that for the "Company" class's "address"
156 attribute:
157
158 has 'address' => ( is => 'rw', isa => 'Address' );
159
160 A company also needs a name:
161
162 has 'name' => ( is => 'rw', isa => 'Str', required => 1 );
163
164 This introduces a new attribute option, "required". If an attribute is
165 required, then it must be passed to the class's constructor, or an
166 exception will be thrown. It's important to understand that a
167 "required" attribute can still be false or "undef", if its type
168 constraint allows that.
169
170 The next attribute, "employees", uses a parameterized type constraint:
171
172 has 'employees' => (
173 is => 'rw',
174 isa => 'ArrayRef[Employee]'
175 default => sub { [] },
176 );
177
178 This constraint says that "employees" must be an array reference where
179 each element of the array is an "Employee" object. It's worth noting
180 that an empty array reference also satisfies this constraint, such as
181 the value given as the default here.
182
183 Parameterizable type constraints (or "container types"), such as
184 "ArrayRef[`a]", can be made more specific with a type parameter. In
185 fact, we can arbitrarily nest these types, producing something like
186 "HashRef[ArrayRef[Int]]". However, you can also just use the type by
187 itself, so "ArrayRef" is legal. (2)
188
189 If you jump down to the definition of the "Employee" class, you will
190 see that it has an "employer" attribute.
191
192 When we set the "employees" for a "Company" we want to make sure that
193 each of these employee objects refers back to the right "Company" in
194 its "employer" attribute.
195
196 To do that, we need to hook into object construction. Moose lets us do
197 this by writing a "BUILD" method in our class. When your class defines
198 a "BUILD" method, it will be called by the constructor immediately
199 after object construction, but before the object is returned to the
200 caller. Note that all "BUILD" methods in your class hierarchy will be
201 called automatically; there is no need to (and you should not) call the
202 superclass "BUILD" method.
203
204 The "Company" class uses the "BUILD" method to ensure that each
205 employee of a company has the proper "Company" object in its "employer"
206 attribute:
207
208 sub BUILD {
209 my ( $self, $params ) = @_;
210 foreach my $employee ( @{ $self->employees } ) {
211 $employee->employer($self);
212 }
213 }
214
215 The "BUILD" method is executed after type constraints are checked, so
216 it is safe to assume that if "$self->employees" has a value, it will be
217 an array reference, and that the elements of that array reference will
218 be "Employee" objects.
219
220 We also want to make sure that whenever the "employees" attribute for a
221 "Company" is changed, we also update the "employer" for each employee.
222
223 To do this we can use an "after" modifier:
224
225 after 'employees' => sub {
226 my ( $self, $employees ) = @_;
227 return unless $employees;
228 foreach my $employee ( @$employees ) {
229 $employee->employer($self);
230 }
231 };
232
233 Again, as with the "BUILD" method, we know that the type constraint
234 check has already happened, so we know that if $employees is defined it
235 will contain an array reference of "Employee" objects.
236
237 Note that "employees" is a read/write accessor, so we must return early
238 if it's called as a reader.
239
240 The Person class does not really demonstrate anything new. It has
241 several "required" attributes. It also has a "predicate" method, which
242 we first used in Moose::Cookbook::Basics::BinaryTree_AttributeFeatures.
243
244 The only new feature in the "Employee" class is the "override" method
245 modifier:
246
247 override 'full_name' => sub {
248 my $self = shift;
249 super() . ', ' . $self->title;
250 };
251
252 This is just a sugary alternative to Perl's built in "SUPER::" feature.
253 However, there is one difference. You cannot pass any arguments to
254 "super". Instead, Moose simply passes the same parameters that were
255 passed to the method.
256
257 A more detailed example of usage can be found in
258 t/recipes/basics_company_subtypes.t.
259
261 This recipe was intentionally longer and more complex. It illustrates
262 how Moose classes can be used together with type constraints, as well
263 as the density of information that you can get out of a small amount of
264 typing when using Moose.
265
266 This recipe also introduced the "subtype" function, the "required"
267 attribute, and the "override" method modifier.
268
269 We will revisit type constraints in future recipes, and cover type
270 coercion as well.
271
273 (1) The value being checked is also passed as the first argument to the
274 "where" block, so it can be accessed as $_[0].
275
276 (2) Note that "ArrayRef[]" will not work. Moose will not parse this as
277 a container type, and instead you will have a new type named
278 "ArrayRef[]", which doesn't make any sense.
279
281 • Stevan Little <stevan@cpan.org>
282
283 • Dave Rolsky <autarch@urth.org>
284
285 • Jesse Luehrs <doy@cpan.org>
286
287 • Shawn M Moore <sartak@cpan.org>
288
289 • יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
290
291 • Karen Etheridge <ether@cpan.org>
292
293 • Florian Ragwitz <rafl@debian.org>
294
295 • Hans Dieter Pearcey <hdp@cpan.org>
296
297 • Chris Prather <chris@prather.org>
298
299 • Matt S Trout <mstrout@cpan.org>
300
302 This software is copyright (c) 2006 by Infinity Interactive, Inc.
303
304 This is free software; you can redistribute it and/or modify it under
305 the same terms as the Perl 5 programming language system itself.
306
307
308
309perl v5.34.0 2M0o2o1s-e0:7:-C2o2okbook::Basics::Company_Subtypes(3)