6d04c87cb6b78d91cb89010510ee7ff1166d3df6
[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 Configuration file example:
85
86   <View::TT>
87       subinclude_plugin   ESI
88   </View::TT>
89
90 =head2 C<set_subinclude_plugin( $plugin )>
91
92 This method changes the current active subinclude plugin in runtime. It expects
93 the plugin suffix (e.g. C<ESI> or C<SubRequest>) or a fully-qualified class
94 name in the C<Catalyst::View::Component::SubInclude> namespace.
95
96 =head2 Writing plugins
97
98 If writing your own plugin, keep in kind plugins are required to implement a
99 class method C<generate_subinclude> with the following signature:
100
101   sub generate_subinclude {
102       my ($class, $c, @args) = @_;
103   }
104
105 The default plugin is stored in the C<subinclude_plugin> which can be changed
106 in runtime. It expects a fully qualified class name.
107
108 =cut
109
110 has 'subinclude_plugin' => (
111     is => 'rw',
112     isa => Str,
113 );
114
115 has subinclude => (
116     is => 'ro',
117     isa => HashRef,
118     default => sub { {} },
119 );
120
121 around 'new' => sub {
122     my $next = shift;
123     my $class = shift;
124
125     my $self = $class->$next( @_ );
126
127     my $subinclude_plugin = $self->config->{subinclude_plugin} || 'SubRequest';
128     $self->set_subinclude_plugin( $subinclude_plugin );
129
130     $self;
131 };
132
133 before 'render' => sub {
134     my ($self, $c, @args) = @_;
135
136     $c->stash->{subinclude}       = $self->make_context_closure(sub { $self->_subinclude( @_ ) }, $c);
137     $c->stash->{subinclude_using} = $self->make_context_closure(sub { $self->_subinclude_using( @_ ) }, $c);
138 };
139
140 sub set_subinclude_plugin {
141     my ($self, $plugin) = @_;
142
143     my $subinclude_class = blessed $self->_subinclude_plugin_class_instance( $plugin );
144     $self->subinclude_plugin( $subinclude_class );
145 }
146
147 sub _subinclude {
148     my ($self, $c, @args) = @_;
149     $self->_subinclude_using( $c, $self->subinclude_plugin, @args );
150 }
151
152 sub _subinclude_using {
153     my ($self, $c, $plugin, @args) = @_;
154     $plugin = $self->_subinclude_plugin_class_instance($plugin);
155     $plugin->generate_subinclude( $c, @args );
156 }
157
158 has _subinclude_plugin_class_instance_cache => (
159     isa => HashRef,
160     is => 'ro',
161     default => sub { {} },
162 );
163
164 sub _subinclude_plugin_class_instance {
165     my ($self, $plugin) = @_;
166
167     my $cache = $self->_subinclude_plugin_class_instance_cache;
168     return $cache->{$plugin} if exists $cache->{$plugin};
169
170     my $plugin_config = Catalyst::Utils::merge_hashes(
171         $self->subinclude->{ALL}||{},
172         $self->subinclude->{$plugin}||{}
173     );
174     my $short_class = $plugin_config->{'class'} ?
175         delete $plugin_config->{'class'}
176         : $plugin;
177     my $class = $short_class =~ /::/ ?
178         $short_class
179         : __PACKAGE__ . '::' . $short_class;
180
181     Class::MOP::load_class($class);
182
183     return $cache->{$class} = $class->new($plugin_config);
184 }
185
186 =head1 SEE ALSO
187
188 L<Catalyst::Plugin::SubRequest|Catalyst::Plugin::SubRequest>,
189 L<Moose::Role|Moose::Role>, L<Moose|Moose>,
190 L<http://www.catalystframework.org/calendar/2008/17>
191
192 =head1 BUGS
193
194 Please report any bugs or feature requests to
195 C<bug-catalyst-view-component-subinclude at rt.cpan.org>, or through the web interface at
196 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-View-Component-SubInclude>.
197 I will be notified, and then you'll automatically be notified of progress on
198 your bug as I make changes.
199
200 =head1 AUTHOR
201
202 Nilson Santos Figueiredo Junior, C<< <nilsonsfj at cpan.org> >>
203
204 =head1 CONTRIBUTORS
205
206 Tomas Doran (t0m) C<< <bobtfish@bobtfish.net >>.
207
208 Vladimir Timofeev, C<< <vovkasm at gmail.com> >>.
209
210 Wallace Reis (wreis) C<< <wreis@cpan.org> >>.
211
212 =head1 SPONSORSHIP
213
214 Development sponsored by Ionzero LLC L<http://www.ionzero.com/>.
215
216 =head1 COPYRIGHT & LICENSE
217
218 Copyright (C) 2010 Nilson Santos Figueiredo Junior and the above contributors.
219
220 Copyright (C) 2009 Nilson Santos Figueiredo Junior.
221
222 Copyright (C) 2009 Ionzero LLC.
223
224 This program is free software; you can redistribute it and/or modify it
225 under the same terms as Perl itself.
226
227 =cut
228
229 1;