Document the keep_stash option for each plugin
[catagits/Catalyst-View-Component-SubInclude.git] / lib / Catalyst / View / Component / SubInclude.pm
1 package Catalyst::View::Component::SubInclude;
2 use Moose::Role;
3
4 use Carp qw/croak/;
5 use Catalyst::Utils ();
6 use Class::MOP ();
7 use MooseX::Types::Moose qw/Str HashRef/;
8 use namespace::clean -except => 'meta';
9
10 with 'Catalyst::Component::ContextClosure';
11
12 =head1 NAME
13
14 Catalyst::View::Component::SubInclude - Use subincludes in your Catalyst views
15
16 =head1 VERSION
17
18 Version 0.11
19
20 =cut
21
22 our $VERSION = '0.11';
23 $VERSION = eval $VERSION;
24
25 =head1 SYNOPSIS
26
27   package MyApp::View::TT;
28   use Moose;
29
30   extends 'Catalyst::View::TT';
31   with 'Catalyst::View::Component::SubInclude';
32
33   __PACKAGE__->config( subinclude_plugin => 'SubRequest' );
34
35 Then, somewhere in your templates:
36
37   [% subinclude('/my/widget') %]
38   [% subinclude_using('SubRequest', '/page/footer') %]
39
40 =head1 DESCRIPTION
41
42 C<Catalyst::View::Component::SubInclude> allows you to include content in your
43 templates (or, more generally, somewhere in your view's C<render> processing)
44 which comes from another action in your application. It's implemented as a
45 L<Moose::Role|Moose::Role>, so using L<Moose|Moose> in your view is required.
46
47 Simply put, it's a way to include the output of a Catalyst sub-request somewhere
48 in your page.
49
50 It's built in an extensible way so that you're free to use sub-requests,
51 Varnish ESI (L<http://www.catalystframework.org/calendar/2008/17>) or any other
52 sub-include plugin you might want to implement.
53
54 =head1 STASH FUNCTIONS
55
56 This component does its magic by exporting a C<subinclude> coderef entry to the
57 stash. This way, it's easily accessible by the templates (which is the most
58 common use-case).
59
60 =head2 C<subinclude( $path, @args )>
61
62 This will render and return the body of the included resource (as specified by
63 C<$path>) using the default subinclude plugin.
64
65 =head2 C<subinclude_using( $plugin, $path, @args )>
66
67 This will render and return the body of the included resource (as specified by
68 C<$path>) using the specified subinclude plugin.
69
70 The C<subinclude> function above is implemented basically as a shortcut which
71 calls this function using the default plugin as the first parameter.
72
73 =head1 SUBINCLUDE PLUGINS
74
75 The module comes with two subinclude plugins:
76 L<SubRequest|Catalyst::Plugin::View::Component::SubRequest>,
77 L<Visit|Catalyst::Plugin::View::Component::Visit> and
78 L<ESI|Catalyst::Plugin::View::Component::ESI>.
79
80 By default, the C<SubRequest> plugin will be used. This can be changed in the
81 view's configuration options (either in the config file or in the view module
82 itself).
83
84     __PACKAGE__->config(
85         subinclude_plugin => 'ESI',
86         subinclude => {
87             'SubRequest' => {
88                 keep_stash => 1,
89             },
90             'HTTP::POST' => {
91                 class => 'HTTP',
92                 http_method => 'POST',
93                 ua_timeout => '10',
94                 uri_map => {
95                     '/foo/' => 'http://www.foo.com/',
96                 },
97             },
98         },
99     );
100
101 You can change each plugins' configuration through the keys in the 'subinclude'
102 config key (example above)
103
104 =head2 C<set_subinclude_plugin( $plugin )>
105
106 This method changes the current active subinclude plugin in runtime. It expects
107 the plugin suffix (e.g. C<ESI> or C<SubRequest>) or a fully-qualified class
108 name in the C<Catalyst::View::Component::SubInclude> namespace.
109
110 =head2 Writing plugins
111
112 If writing your own plugin, keep in kind plugins are required to implement a
113 class method C<generate_subinclude> with the following signature:
114
115   sub generate_subinclude {
116       my ($class, $c, @args) = @_;
117   }
118
119 The default plugin is stored in the C<subinclude_plugin> which can be changed
120 in runtime. It expects a fully qualified class name.
121
122 =cut
123
124 has 'subinclude_plugin' => (
125     is => 'rw',
126     isa => Str,
127 );
128
129 has subinclude => (
130     is => 'ro',
131     isa => HashRef,
132     default => sub { {} },
133 );
134
135 around 'new' => sub {
136     my $next = shift;
137     my $class = shift;
138
139     my $self = $class->$next( @_ );
140
141     my $subinclude_plugin = $self->config->{subinclude_plugin} || 'SubRequest';
142     $self->set_subinclude_plugin( $subinclude_plugin );
143
144     $self;
145 };
146
147 before 'render' => sub {
148     my ($self, $c, @args) = @_;
149
150     $c->stash->{subinclude}       = $self->make_context_closure(sub { $self->_subinclude( @_ ) }, $c);
151     $c->stash->{subinclude_using} = $self->make_context_closure(sub { $self->_subinclude_using( @_ ) }, $c);
152 };
153
154 sub set_subinclude_plugin {
155     my ($self, $plugin) = @_;
156
157     my $subinclude_class = blessed $self->_subinclude_plugin_class_instance( $plugin );
158     $self->subinclude_plugin( $subinclude_class );
159 }
160
161 sub _subinclude {
162     my ($self, $c, @args) = @_;
163     $self->_subinclude_using( $c, $self->subinclude_plugin, @args );
164 }
165
166 sub _subinclude_using {
167     my ($self, $c, $plugin, @args) = @_;
168     $plugin = $self->_subinclude_plugin_class_instance($plugin);
169     $plugin->generate_subinclude( $c, @args );
170 }
171
172 has _subinclude_plugin_class_instance_cache => (
173     isa => HashRef,
174     is => 'ro',
175     default => sub { {} },
176 );
177
178 sub _subinclude_plugin_class_instance {
179     my ($self, $plugin) = @_;
180
181     my $cache = $self->_subinclude_plugin_class_instance_cache;
182     return $cache->{$plugin} if exists $cache->{$plugin};
183
184     my $plugin_config = Catalyst::Utils::merge_hashes(
185         $self->subinclude->{ALL}||{},
186         $self->subinclude->{$plugin}||{}
187     );
188     my $short_class = $plugin_config->{'class'} ?
189         delete $plugin_config->{'class'}
190         : $plugin;
191     my $class = $short_class =~ /::/ ?
192         $short_class
193         : __PACKAGE__ . '::' . $short_class;
194
195     Class::MOP::load_class($class);
196
197     return $cache->{$class} = $class->new($plugin_config);
198 }
199
200 =head1 SEE ALSO
201
202 L<Catalyst::Plugin::SubRequest|Catalyst::Plugin::SubRequest>,
203 L<Moose::Role|Moose::Role>, L<Moose|Moose>,
204 L<http://www.catalystframework.org/calendar/2008/17>
205
206 =head1 BUGS
207
208 Please report any bugs or feature requests to
209 C<bug-catalyst-view-component-subinclude at rt.cpan.org>, or through the web interface at
210 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-View-Component-SubInclude>.
211 I will be notified, and then you'll automatically be notified of progress on
212 your bug as I make changes.
213
214 =head1 AUTHOR
215
216 Nilson Santos Figueiredo Junior, C<< <nilsonsfj at cpan.org> >>
217
218 =head1 CONTRIBUTORS
219
220 Tomas Doran (t0m) C<< <bobtfish@bobtfish.net >>.
221
222 Vladimir Timofeev, C<< <vovkasm at gmail.com> >>.
223
224 Wallace Reis (wreis) C<< <wreis@cpan.org> >>.
225
226 =head1 SPONSORSHIP
227
228 Development sponsored by Ionzero LLC L<http://www.ionzero.com/>.
229
230 =head1 COPYRIGHT & LICENSE
231
232 Copyright (C) 2010 Nilson Santos Figueiredo Junior and the above contributors.
233
234 Copyright (C) 2009 Nilson Santos Figueiredo Junior.
235
236 Copyright (C) 2009 Ionzero LLC.
237
238 This program is free software; you can redistribute it and/or modify it
239 under the same terms as Perl itself.
240
241 =cut
242
243 1;