Commit | Line | Data |
471c4f09 |
1 | |
2 | =pod |
3 | |
4 | =head1 NAME |
5 | |
021b8139 |
6 | Moose::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] ) }, |
471c4f09 |
28 | ); |
c765b254 |
29 | |
471c4f09 |
30 | has 'right' => ( |
c765b254 |
31 | is => 'rw', |
32 | isa => 'BinaryTree', |
33 | predicate => 'has_right', |
34 | lazy => 1, |
35 | default => sub { BinaryTree->new( parent => $_[0] ) }, |
471c4f09 |
36 | ); |
c765b254 |
37 | |
471c4f09 |
38 | before 'right', 'left' => sub { |
c765b254 |
39 | my ( $self, $tree ) = @_; |
40 | $tree->parent($self) if defined $tree; |
471c4f09 |
41 | }; |
42 | |
43 | =head1 DESCRIPTION |
44 | |
f6f9ec6a |
45 | This recipe shows how various advanced attribute features can be used |
46 | to create complex and powerful behaviors. |
8597d950 |
47 | |
f6f9ec6a |
48 | The example class is a classic binary tree. Each node in the tree is |
49 | itself an instance of C<BinaryTree>. It has a C<node>, which holds |
50 | some arbitrary value. It has C<right> and C<left> attributes, which |
51 | refer to its child trees, and a C<parent>. |
8597d950 |
52 | |
f6f9ec6a |
53 | Let's take a look at the C<node> attribute: |
8597d950 |
54 | |
c765b254 |
55 | has 'node' => ( is => 'rw', isa => 'Any' ); |
8597d950 |
56 | |
f6f9ec6a |
57 | Moose generates a read-write accessor for this attribute. The type |
58 | constraint is C<Any>, which means literally means it can contain |
59 | anything. |
455ca293 |
60 | |
f6f9ec6a |
61 | We could have left out the C<isa> option, but in this case, we are |
62 | including ir for the benefit of other programmers, not the computer. |
63 | |
64 | Next, let's move on to the C<parent> attribute: |
8597d950 |
65 | |
66 | has 'parent' => ( |
67 | is => 'rw', |
c765b254 |
68 | isa => 'BinaryTree', |
8597d950 |
69 | predicate => 'has_parent', |
70 | weak_ref => 1, |
71 | ); |
72 | |
f6f9ec6a |
73 | Again, we have a read-write accessor. This time, the C<isa> option |
74 | says that this attribute must always be an instance of |
75 | C<BinaryTree>. In the second recipe, we saw that every time we create |
76 | a Moose-based class, we also get a corresponding class type |
77 | constraint. |
8597d950 |
78 | |
f6f9ec6a |
79 | The C<predicate> option is new. It creates a method which can be used |
80 | to check whether or not a given attribute has been initialized. In |
81 | this case, the method is named C<has_parent>. |
8597d950 |
82 | |
f6f9ec6a |
83 | This brings us to our last attribute option, C<weak_ref>. Since |
84 | C<parent> is a circular reference (the tree in C<parent> should |
85 | already have a reference to this one, in its C<left> or C<right> |
86 | attribute), we want to make sure that we weaken the reference to avoid |
87 | memory leaks. If C<weak_ref> is true, it alters the accessor function |
88 | so that the reference is weakened when it is set. |
8597d950 |
89 | |
f6f9ec6a |
90 | Finally, we have the the C<left> and C<right> attributes. They are |
91 | essentially identical except for their names, so we'll just look at |
92 | C<left>: |
8597d950 |
93 | |
94 | has 'left' => ( |
c765b254 |
95 | is => 'rw', |
96 | isa => 'BinaryTree', |
97 | predicate => 'has_left', |
8597d950 |
98 | lazy => 1, |
c765b254 |
99 | default => sub { BinaryTree->new( parent => $_[0] ) }, |
8597d950 |
100 | ); |
101 | |
f6f9ec6a |
102 | There are two new options here, C<lazy> and C<default>. These two |
103 | options are linked, and in fact you cannot have a C<lazy> attribute |
104 | unless it has a C<default> (or a C<builder>, but we'll cover that |
105 | later). If you try to make an attribute lazy without a default, class |
106 | creation will fail with an exception. (2) |
107 | |
108 | In the second recipe the B<BankAccount>'s C<balance> attribute had a |
109 | default value of C<0>. Given a non-reference, Perl copies the |
110 | I<value>. However, given a reference, it does not do a deep clone, |
111 | instead simply copying the reference. If you just specified a simply |
112 | reference for a default, Perl would create it once and it would be |
113 | shared by all objects with that attribute. |
8597d950 |
114 | |
f6f9ec6a |
115 | As a workaround, we use an anonymous subroutine to generate a new |
116 | reference every time the default is called. |
8597d950 |
117 | |
f6f9ec6a |
118 | has 'foo' => ( is => 'rw', default => sub { [] } ); |
119 | |
120 | In fact, using a non-subroutine reference as a default is illegal in Moose. |
8597d950 |
121 | |
c765b254 |
122 | has 'foo' => ( is => 'rw', default => [] ); |
8597d950 |
123 | |
f6f9ec6a |
124 | This will blow up, so don't do it. |
8597d950 |
125 | |
f6f9ec6a |
126 | You'll notice that we use C<$_[0]> in our default sub. When the |
127 | default subroutine is executed, it is called as a method on the |
128 | object. |
129 | |
130 | In our case, we're making a new C<BinaryTree> object in our default, |
131 | with the current tree as the parent. |
132 | |
133 | Normally, when an object is instantiated, any defaults are evaluted |
134 | immediately. With our C<BinaryTree> class, this would be a big |
135 | problem! We'd create the first object, which would immediately try to |
136 | populate its C<left> and C<right> attributes, which would create a new |
137 | C<BinaryTree>, which would populate I<its> C<left> and C<right> |
138 | slots. Kaboom! |
139 | |
140 | By making our C<left> and C<right> attributes C<lazy>, we avoid this |
141 | problem. If the attribute has a value when it is read, the default is |
142 | never executed at all. |
143 | |
144 | We still have one last bit of behavior to add. The autogenerated |
145 | C<right> and C<left> accessors are not quite correct. When one of |
146 | these is set, we want to make sure that we update the parent of the |
147 | C<left> or C<right> attribute's tree. |
8597d950 |
148 | |
f6f9ec6a |
149 | We could write our own accessors, but then why use Moose at all? |
150 | Instead, we use method modifiers: |
c765b254 |
151 | |
8597d950 |
152 | before 'right', 'left' => sub { |
c765b254 |
153 | my ( $self, $tree ) = @_; |
154 | $tree->parent($self) if defined $tree; |
8597d950 |
155 | }; |
156 | |
f6f9ec6a |
157 | This is a C<before> modifier, just like we saw in the second recipe, |
158 | but with two slight differences. First, we are applying the modifier |
159 | to more than one method at a time, because both C<left> and C<right> |
160 | attributes need the same behavior. The other difference is that we are |
161 | not wrapping an inherited method, but rather a method from our own |
162 | local class. Wrapping local methods is no different, the only |
163 | requirement is that the wrappee must exist before the wrapper is |
164 | defined (after all, you cannot wrap something which doesn't exist, |
455ca293 |
165 | right?). |
8597d950 |
166 | |
f6f9ec6a |
167 | As with all the other recipes, B<BinaryTree> can be used just like any |
168 | other Perl 5 class. A more detailed example of its usage can be found |
169 | in F<t/000_recipes/003_recipe.t>. |
8597d950 |
170 | |
171 | =head1 CONCLUSION |
172 | |
f6f9ec6a |
173 | This recipe introduced several of Moose's advanced features. We hope |
174 | that this inspires you to think of other ways these features can be |
175 | used to simplify your code. |
e08c54f5 |
176 | |
8597d950 |
177 | =head1 FOOTNOTES |
178 | |
179 | =over 4 |
180 | |
181 | =item (1) |
182 | |
f6f9ec6a |
183 | Weak references are tricky things, and should be used sparingly and |
184 | appropriately (such as in the case of circular refs). If you are not |
185 | careful, attribute values could disappear "mysteriously" because |
186 | Perl's reference counting garbage collector has gone and removed the |
187 | item you are weak-referencing. |
8597d950 |
188 | |
189 | In short, don't use them unless you know what you are doing :) |
190 | |
191 | =item (2) |
192 | |
f6f9ec6a |
193 | You I<can> use the C<default> option without the C<lazy> option if you |
194 | like, as we showed in the second recipe. |
8597d950 |
195 | |
f6f9ec6a |
196 | Also, you can use C<builder> instead of C<default>. See |
021b8139 |
197 | L<Moose::Cookbook::Basics::Recipe9> for details. |
ceb8945d |
198 | |
8597d950 |
199 | =back |
200 | |
471c4f09 |
201 | =head1 AUTHOR |
202 | |
203 | Stevan Little E<lt>stevan@iinteractive.comE<gt> |
204 | |
205 | =head1 COPYRIGHT AND LICENSE |
206 | |
2840a3b2 |
207 | Copyright 2006-2009 by Infinity Interactive, Inc. |
471c4f09 |
208 | |
209 | L<http://www.iinteractive.com> |
210 | |
211 | This library is free software; you can redistribute it and/or modify |
212 | it under the same terms as Perl itself. |
213 | |
f7522f24 |
214 | =cut |