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