c98981b1e4ccfbaa8c4d9fa7461c03feeae1d341
[gitmo/Moose.git] / lib / Moose / Cookbook / Basics / Recipe6.pod
1
2 =pod
3
4 =head1 NAME
5
6 Moose::Cookbook::Basics::Recipe6 - The augment/inner example
7
8 =head1 SYNOPSIS
9
10   package Document::Page;
11   use Moose;
12
13   has 'body' => ( is => 'rw', isa => 'Str', default => sub {''} );
14
15   sub create {
16       my $self = shift;
17       $self->open_page;
18       inner();
19       $self->close_page;
20   }
21
22   sub append_body {
23       my ( $self, $appendage ) = @_;
24       $self->body( $self->body . $appendage );
25   }
26
27   sub open_page  { (shift)->append_body('<page>') }
28   sub close_page { (shift)->append_body('</page>') }
29
30   package Document::PageWithHeadersAndFooters;
31   use Moose;
32
33   extends 'Document::Page';
34
35   augment 'create' => sub {
36       my $self = shift;
37       $self->create_header;
38       inner();
39       $self->create_footer;
40   };
41
42   sub create_header { (shift)->append_body('<header/>') }
43   sub create_footer { (shift)->append_body('<footer/>') }
44
45   package TPSReport;
46   use Moose;
47
48   extends 'Document::PageWithHeadersAndFooters';
49
50   augment 'create' => sub {
51       my $self = shift;
52       $self->create_tps_report;
53       inner();
54   };
55
56   sub create_tps_report {
57       (shift)->append_body('<report type="tps"/>');
58   }
59
60   # <page><header/><report type="tps"/><footer/></page>
61   my $report_xml = TPSReport->new->create;
62
63 =head1 DESCRIPTION
64
65 This recipe shows how the C<augment> method modifier works. This
66 modifier reverses the normal subclass to parent method resolution
67 order. With an C<augment> modifier the I<least> specific method is
68 called first. Each successive call to C<inner> descends the
69 inheritance tree, ending at the most specific subclass.
70
71 The C<augment> modifier lets you design a parent class that can be
72 extended in a specific way. The parent provides generic wrapper
73 functionality, and the subclasses fill in the details.
74
75 In the example above, we've created a set of document classes, with
76 the most specific being the C<TPSReport> class.
77
78 We start with the least specific class, C<Document::Page>. Its create
79 method contains a call to C<inner()>:
80
81   sub create {
82       my $self = shift;
83       $self->open_page;
84       inner();
85       $self->close_page;
86   }
87
88 The C<inner> function is exported by C<Moose>, and is like C<super>
89 for augmented methods. When C<inner> is called, Moose finds the next
90 method in the chain, which is the C<augment> modifier in
91 C<Document::PageWithHeadersAndFooters>. You'll note that we can call
92 C<inner> in our modifier:
93
94   augment 'create' => sub {
95       my $self = shift;
96       $self->create_header;
97       inner();
98       $self->create_footer;
99   };
100
101 This finds the next most specific modifier, in the C<TPSReport> class.
102
103 Finally, in the C<TPSReport> class, the chain comes to an end:
104
105   augment 'create' => sub {
106       my $self = shift;
107       $self->create_tps_report;
108       inner();
109   };
110
111 We do call the C<inner> function one more time, but since there is no
112 more specific subclass, this is a no-op. Making this call means we can
113 easily subclass C<TPSReport> in the future.
114
115 =head1 CONCLUSION
116
117 The C<augment> modifier is a powerful tool for creating a set of
118 nested wrappers. It's not something you will need often, but when you
119 do, it is very handy.
120
121 =head1 AUTHOR
122
123 Stevan Little E<lt>stevan@iinteractive.comE<gt>
124
125 Dave Rolsky E<lt>autarch@urth.orgE<gt>
126
127 =head1 COPYRIGHT AND LICENSE
128
129 Copyright 2007-2009 by Infinity Interactive, Inc.
130
131 L<http://www.iinteractive.com>
132
133 This library is free software; you can redistribute it and/or modify
134 it under the same terms as Perl itself.
135
136 =begin testing
137
138 my $tps_report = TPSReport->new;
139 isa_ok( $tps_report, 'TPSReport' );
140
141 is(
142     $tps_report->create,
143     q{<page><header/><report type="tps"/><footer/></page>},
144     '... got the right TPS report'
145 );
146
147 =end testing
148
149 =cut