Add built local::lib
[catagits/Gitalist.git] / local-lib5 / lib / perl5 / Moose / Cookbook / Basics / Recipe3.pod
CommitLineData
3fea05b9 1
2=pod
3
4=head1 NAME
5
6Moose::Cookbook::Basics::Recipe3 - A lazy B<BinaryTree> example
7
8=head1 SYNOPSIS
9
10 package BinaryTree;
11 use Moose;
12
13 has 'node' => ( is => 'rw', isa => 'Any' );
14
15 has 'parent' => (
16 is => 'rw',
17 isa => 'BinaryTree',
18 predicate => 'has_parent',
19 weak_ref => 1,
20 );
21
22 has 'left' => (
23 is => 'rw',
24 isa => 'BinaryTree',
25 predicate => 'has_left',
26 lazy => 1,
27 default => sub { BinaryTree->new( parent => $_[0] ) },
28 trigger => \&_set_parent_for_child
29 );
30
31 has 'right' => (
32 is => 'rw',
33 isa => 'BinaryTree',
34 predicate => 'has_right',
35 lazy => 1,
36 default => sub { BinaryTree->new( parent => $_[0] ) },
37 trigger => \&_set_parent_for_child
38 );
39
40 sub _set_parent_for_child {
41 my ( $self, $child ) = @_;
42
43 confess "You cannot insert a tree which already has a parent"
44 if $child->has_parent;
45
46 $child->parent($self);
47 }
48
49=head1 DESCRIPTION
50
51This recipe shows how various advanced attribute features can be used
52to create complex and powerful behaviors. In particular, we introduce
53a number of new attribute options, including C<predicate>, C<lazy>,
54and C<trigger>.
55
56The example class is a classic binary tree. Each node in the tree is
57itself an instance of C<BinaryTree>. It has a C<node>, which holds
58some arbitrary value. It has C<right> and C<left> attributes, which
59refer to its child trees, and a C<parent>.
60
61Let's take a look at the C<node> attribute:
62
63 has 'node' => ( is => 'rw', isa => 'Any' );
64
65Moose generates a read-write accessor for this attribute. The type
66constraint is C<Any>, which literally means it can contain anything.
67
68We could have left out the C<isa> option, but in this case, we are
69including it for the benefit of other programmers, not the computer.
70
71Next, let's move on to the C<parent> attribute:
72
73 has 'parent' => (
74 is => 'rw',
75 isa => 'BinaryTree',
76 predicate => 'has_parent',
77 weak_ref => 1,
78 );
79
80Again, we have a read-write accessor. This time, the C<isa> option
81says that this attribute must always be an instance of
82C<BinaryTree>. In the second recipe, we saw that every time we create
83a Moose-based class, we also get a corresponding class type
84constraint.
85
86The C<predicate> option is new. It creates a method which can be used
87to check whether or not a given attribute has been initialized. In
88this case, the method is named C<has_parent>.
89
90This brings us to our last attribute option, C<weak_ref>. Since
91C<parent> is a circular reference (the tree in C<parent> should
92already have a reference to this one, in its C<left> or C<right>
93attribute), we want to make sure that we weaken the reference to avoid
94memory leaks. If C<weak_ref> is true, it alters the accessor function
95so that the reference is weakened when it is set.
96
97Finally, we have the the C<left> and C<right> attributes. They are
98essentially identical except for their names, so we'll just look at
99C<left>:
100
101 has 'left' => (
102 is => 'rw',
103 isa => 'BinaryTree',
104 predicate => 'has_left',
105 lazy => 1,
106 default => sub { BinaryTree->new( parent => $_[0] ) },
107 trigger => \&_set_parent_for_child
108 );
109
110There are three new options here, C<lazy>, C<default>, and
111C<trigger>. The C<lazy> and C<default> options options are linked. In
112fact, you cannot have a C<lazy> attribute unless it has a C<default>
113(or a C<builder>, but we'll cover that later). If you try to make an
114attribute lazy without a default, class creation will fail with an
115exception. (2)
116
117In the second recipe the B<BankAccount>'s C<balance> attribute had a
118default value of C<0>. Given a non-reference, Perl copies the
119I<value>. However, given a reference, it does not do a deep clone,
120instead simply copying the reference. If you just specified a simply
121reference for a default, Perl would create it once and it would be
122shared by all objects with that attribute.
123
124As a workaround, we use an anonymous subroutine to generate a new
125reference every time the default is called.
126
127 has 'foo' => ( is => 'rw', default => sub { [] } );
128
129In fact, using a non-subroutine reference as a default is illegal in Moose.
130
131 # will fail
132 has 'foo' => ( is => 'rw', default => [] );
133
134This will blow up, so don't do it.
135
136You'll notice that we use C<$_[0]> in our default sub. When the
137default subroutine is executed, it is called as a method on the
138object.
139
140In our case, we're making a new C<BinaryTree> object in our default,
141with the current tree as the parent.
142
143Normally, when an object is instantiated, any defaults are evaluated
144immediately. With our C<BinaryTree> class, this would be a big
145problem! We'd create the first object, which would immediately try to
146populate its C<left> and C<right> attributes, which would create a new
147C<BinaryTree>, which would populate I<its> C<left> and C<right>
148slots. Kaboom!
149
150By making our C<left> and C<right> attributes C<lazy>, we avoid this
151problem. If the attribute has a value when it is read, the default is
152never executed at all.
153
154We still have one last bit of behavior to add. The autogenerated
155C<right> and C<left> accessors are not quite correct. When one of
156these is set, we want to make sure that we update the parent of the
157C<left> or C<right> attribute's tree.
158
159We could write our own accessors, but then why use Moose at all?
160Instead, we use a C<trigger>. A C<trigger> accepts a subroutine
161reference, which will be called as a method whenever the attribute is
162set. This can happen both during object construction or later by
163passing a new object to the attribute's accessor method. However, it
164is not called when a value is provided by a C<default> or C<builder>.
165
166 sub _set_parent_for_child {
167 my ( $self, $child ) = @_;
168
169 confess "You cannot insert a tree which already has a parent"
170 if $child->has_parent;
171
172 $child->parent($self);
173 }
174
175This trigger does two things. First, it ensures that the new child
176node does not already have a parent. This is done for the sake of
177simplifying the example. If we wanted to be more clever, we would
178remove the child from its old parent tree and add it to the new one.
179
180If the child has no parent, we will add it to the current tree, and we
181ensure that is has the correct value for its C<parent> attribute.
182
183As with all the other recipes, B<BinaryTree> can be used just like any
184other Perl 5 class. A more detailed example of its usage can be found
185in F<t/000_recipes/moose_cookbook_basics_recipe3.t>.
186
187=head1 CONCLUSION
188
189This recipe introduced several of Moose's advanced features. We hope
190that this inspires you to think of other ways these features can be
191used to simplify your code.
192
193=head1 FOOTNOTES
194
195=over 4
196
197=item (1)
198
199Weak references are tricky things, and should be used sparingly and
200appropriately (such as in the case of circular refs). If you are not
201careful, attribute values could disappear "mysteriously" because
202Perl's reference counting garbage collector has gone and removed the
203item you are weak-referencing.
204
205In short, don't use them unless you know what you are doing :)
206
207=item (2)
208
209You I<can> use the C<default> option without the C<lazy> option if you
210like, as we showed in the second recipe.
211
212Also, you can use C<builder> instead of C<default>. See
213L<Moose::Cookbook::Basics::Recipe8> for details.
214
215=back
216
217=head1 AUTHORS
218
219Stevan Little E<lt>stevan@iinteractive.comE<gt>
220
221Dave Rolsky E<lt>autarch@urth.orgE<gt>
222
223=head1 COPYRIGHT AND LICENSE
224
225Copyright 2006-2009 by Infinity Interactive, Inc.
226
227L<http://www.iinteractive.com>
228
229This library is free software; you can redistribute it and/or modify
230it under the same terms as Perl itself.
231
232=begin testing
233
234use Scalar::Util 'isweak';
235
236my $root = BinaryTree->new(node => 'root');
237isa_ok($root, 'BinaryTree');
238
239is($root->node, 'root', '... got the right node value');
240
241ok(!$root->has_left, '... no left node yet');
242ok(!$root->has_right, '... no right node yet');
243
244ok(!$root->has_parent, '... no parent for root node');
245
246# make a left node
247
248my $left = $root->left;
249isa_ok($left, 'BinaryTree');
250
251is($root->left, $left, '... got the same node (and it is $left)');
252ok($root->has_left, '... we have a left node now');
253
254ok($left->has_parent, '... lefts has a parent');
255is($left->parent, $root, '... lefts parent is the root');
256
257ok(isweak($left->{parent}), '... parent is a weakened ref');
258
259ok(!$left->has_left, '... $left no left node yet');
260ok(!$left->has_right, '... $left no right node yet');
261
262is($left->node, undef, '... left has got no node value');
263
264lives_ok {
265 $left->node('left')
266} '... assign to lefts node';
267
268is($left->node, 'left', '... left now has a node value');
269
270# make a right node
271
272ok(!$root->has_right, '... still no right node yet');
273
274is($root->right->node, undef, '... right has got no node value');
275
276ok($root->has_right, '... now we have a right node');
277
278my $right = $root->right;
279isa_ok($right, 'BinaryTree');
280
281lives_ok {
282 $right->node('right')
283} '... assign to rights node';
284
285is($right->node, 'right', '... left now has a node value');
286
287is($root->right, $right, '... got the same node (and it is $right)');
288ok($root->has_right, '... we have a right node now');
289
290ok($right->has_parent, '... rights has a parent');
291is($right->parent, $root, '... rights parent is the root');
292
293ok(isweak($right->{parent}), '... parent is a weakened ref');
294
295# make a left node of the left node
296
297my $left_left = $left->left;
298isa_ok($left_left, 'BinaryTree');
299
300ok($left_left->has_parent, '... left does have a parent');
301
302is($left_left->parent, $left, '... got a parent node (and it is $left)');
303ok($left->has_left, '... we have a left node now');
304is($left->left, $left_left, '... got a left node (and it is $left_left)');
305
306ok(isweak($left_left->{parent}), '... parent is a weakened ref');
307
308# make a right node of the left node
309
310my $left_right = BinaryTree->new;
311isa_ok($left_right, 'BinaryTree');
312
313lives_ok {
314 $left->right($left_right)
315} '... assign to rights node';
316
317ok($left_right->has_parent, '... left does have a parent');
318
319is($left_right->parent, $left, '... got a parent node (and it is $left)');
320ok($left->has_right, '... we have a left node now');
321is($left->right, $left_right, '... got a left node (and it is $left_left)');
322
323ok(isweak($left_right->{parent}), '... parent is a weakened ref');
324
325# and check the error
326
327dies_ok {
328 $left_right->right($left_left)
329} '... cant assign a node which already has a parent';
330
331=end testing
332
333=cut