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