File: Recipe9.pod

package info (click to toggle)
libmoose-perl 0.54-1
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 1,496 kB
  • ctags: 448
  • sloc: perl: 15,125; makefile: 10
file content (210 lines) | stat: -rw-r--r-- 5,490 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

=pod

=head1 NAME

Moose::Cookbook::Recipe9 - Builder methods and lazy_build

=head1 SYNOPSIS

  package BinaryTree;
  use Moose;

  has 'node' => (is => 'rw', isa => 'Any');

  has 'parent' => (
      is        => 'rw',
      isa       => 'BinaryTree',
      predicate => 'has_parent',
      weak_ref  => 1,
  );

  has 'left' => (
      is        => 'rw',
      isa       => 'BinaryTree',
      predicate => 'has_left',
      lazy      => 1,
      builder   => '_build_child_tree',
  );

  has 'right' => (
      is        => 'rw',
      isa       => 'BinaryTree',
      predicate => 'has_right',
      lazy      => 1,
      builder   => '_build_child_tree',
  );

  before 'right', 'left' => sub {
      my ($self, $tree) = @_;
      $tree->parent($self) if defined $tree;
  };

  sub _build_child_tree {
      my $self = shift;

      return BinaryTree->new( parent => $self );
  }

=head1 DESCRIPTION

If you've already read L<Moose::Cookbook::Recipe3>, then this example
should look awfully familiar. In fact, all we've done here is replace
the attribute C<default> with a C<builder> method.

In this particular case, the C<default> and C<builder> options act in
exactly the same way. When the C<left> or C<right> attribute get
method is called, Moose will call the builder method to initialize the
attribute.

Note that Moose calls the builder method I<on the object which has the
attribute>. Here's an example in code:

  my $tree = BinaryTree->new();

  my $left = $tree->left();

At this point, Moose will call C<< $tree->_build_child_tree() >> in
order to populate the C<left> attribute. If we had passed C<left> to
the original constructor, the builer would not be called.

=head2 Subclassable

There are some differences between C<default> and C<builder>. Because
C<builder> is called I<by name>, it goes through Perl's normal
inheritance system. This means that builder methods are both
inheritable and overrideable.

For example, we might make a C<BinaryTree> subclass:

  package TrinaryTree;
  use Moose;

  extends 'BinaryTree';

  has 'middle' => (
      is        => 'rw',
      isa       => 'BinaryTree',
      predicate => 'has_middle',
      lazy      => 1,
      builder   => '_build_child_tree',
  );

This doesn't quite work though. If you look closely at the
C<_build_child_tree> method defined in C<BinaryTree>, you'll notice
that it hard-codes a class name. Naughty us!

Also, as a bonus, we'll pass C<@_> through, so subclasses can override
the method to pass additional options to the constructor.

Good object-oriented code should allow itself to be subclassed
gracefully. Let's tweak C<_build_child_tree>:

  sub _build_child_tree {
      my $self = shift;

      return (ref $self)->new( parent => $self, @_ );
  }

Now C<_build_child_tree> can be gracefully inherited and overridden.

=head2 Composable

There's more to builders than just subclassing, though. The fact that
builders are called by name also makes them suitable for use in a
role.

  package HasAnimal;
  use Moose::Role;

  requires '_build_animal';

  has 'animal' => (
      is      => 'ro',
      isa     => 'Animal',
      lazy    => 1,
      builder => '_build_animal',
  );

This role provides an animal attribute, but requires that the consumer
of the role provide a builder method it.

  package CatLover;
  use Moose;

  with 'HasAnimal';

  sub _build_animal {
      return Cat->new();
  }

=head2 The lazy_build shortcut

The C<lazy_build> attribute parameter can be used as sugar to specify
a whole bunch of options at once.

  has 'animal' => (
      is         => 'ro',
      isa        => 'Animal',
      lazy_build => 1,
  );

This is a shorthand for this:

  has 'animal' => (
      is        => 'ro',
      isa       => 'Animal',
      required  => 1,
      lazy      => 1,
      builder   => '_build_animal',
      predicate => 'has_animal',
      clearer   => 'clear_animal',
  );

If your attribute starts with an underscore, Moose is smart and will
do the right thing with the C<predicate> and C<clearer>, making them
both start with an underscore. The C<builder> method I<always> starts
with an underscore, since you will want this to be private the vast
majority of the time.

Note that the C<builder> method name is created by simply taking
"_build_" and appending the attribute name. This means that attributes
with a leading underscore like C<_animal> end up with a builder named
C<_build__animal>.

=head1 CONCLUSION

The C<builder> option is a more OO-friendly version of the C<default>
functionality. It also has the property of separating out the code
into a separate well-defined method. This alone makes it valuable. It
is quite ugly to jam a long default code reference into your attribute
definition.

Here are some good rules for determining when to use C<builder> vs
C<default>.

If the default value is a simple scalar that only needs to be
calculated once (or a constant), use C<default>.

If the default value is an empty reference that needs to be wrapped in
a coderef like C<sub { [] }>, use C<default>.

Otherwise, use C<builder>.

This ensures that your classes are easily subclassable, and also helps
keep crufty code out of your attribute definition blocks.

=head1 AUTHOR

Dave Rolsky E<lt>autarch@urth.orgE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright 2006-2008 by Infinity Interactive, Inc.

L<http://www.iinteractive.com>

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut