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