f63a05850a0994ec021f295006dd19db18c5e32d
[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.09
19
20 =cut
21
22 our $VERSION = '0.10';
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. An LWP plugin seems useful and
53 might be developed in the future. If you need to address a resource by it's
54 public path (i.e. the path part trailing C<http://example.com/myapp> then you
55 will need to use L<Catalyst::Plugin::SubRequest> directly, and not this
56 component.
57
58 =head1 STASH FUNCTIONS
59
60 This component does its magic by exporting a C<subinclude> coderef entry to the
61 stash. This way, it's easily accessible by the templates (which is the most
62 common use-case).
63
64 =head2 C<subinclude( $path, @args )>
65
66 This will render and return the body of the included resource (as specified by
67 C<$path>) using the default subinclude plugin.
68
69 =head2 C<subinclude_using( $plugin, $path, @args )>
70
71 This will render and return the body of the included resource (as specified by
72 C<$path>) using the specified subinclude plugin.
73
74 The C<subinclude> function above is implemented basically as a shortcut which
75 calls this function using the default plugin as the first parameter.
76
77 =head1 SUBINCLUDE PLUGINS
78
79 The module comes with two subinclude plugins:
80 L<SubRequest|Catalyst::Plugin::View::Component::SubRequest>,
81 L<Visit|Catalyst::Plugin::View::Component::Visit> and
82 L<ESI|Catalyst::Plugin::View::Component::ESI>.
83
84 By default, the C<SubRequest> plugin will be used. This can be changed in the
85 view's configuration options (either in the config file or in the view module
86 itself).
87
88 Configuration file example:
89
90   <View::TT>
91       subinclude_plugin   ESI
92   </View::TT>
93
94 =head2 C<set_subinclude_plugin( $plugin )>
95
96 This method changes the current active subinclude plugin in runtime. It expects
97 the plugin suffix (e.g. C<ESI> or C<SubRequest>) or a fully-qualified class
98 name in the C<Catalyst::View::Component::SubInclude> namespace.
99
100 =head2 Writing plugins
101
102 If writing your own plugin, keep in kind plugins are required to implement a
103 class method C<generate_subinclude> with the following signature:
104
105   sub generate_subinclude {
106       my ($class, $c, @args) = @_;
107   }
108
109 The default plugin is stored in the C<subinclude_plugin> which can be changed
110 in runtime. It expects a fully qualified class name.
111
112 =cut
113
114 has 'subinclude_plugin' => (
115     is => 'rw',
116     isa => Str,
117 );
118
119 has subinclude => (
120     is => 'ro',
121     isa => HashRef,
122     default => sub { {} },
123 );
124
125 around 'new' => sub {
126     my $next = shift;
127     my $class = shift;
128
129     my $self = $class->$next( @_ );
130
131     my $subinclude_plugin = $self->config->{subinclude_plugin} || 'SubRequest';
132     $self->set_subinclude_plugin( $subinclude_plugin );
133
134     $self;
135 };
136
137 before 'render' => sub {
138     my ($self, $c, @args) = @_;
139
140     $c->stash->{subinclude}       = $self->make_context_closure(sub { $self->_subinclude( @_ ) }, $c);
141     $c->stash->{subinclude_using} = $self->make_context_closure(sub { $self->_subinclude_using( @_ ) }, $c);
142 };
143
144 sub set_subinclude_plugin {
145     my ($self, $plugin) = @_;
146
147     my $subinclude_class = blessed $self->_subinclude_plugin_class_instance( $plugin );
148     $self->subinclude_plugin( $subinclude_class );
149 }
150
151 sub _subinclude {
152     my ($self, $c, @args) = @_;
153     $self->_subinclude_using( $c, $self->subinclude_plugin, @args );
154 }
155
156 sub _subinclude_using {
157     my ($self, $c, $plugin, @args) = @_;
158     $plugin = $self->_subinclude_plugin_class_instance($plugin);
159     $plugin->generate_subinclude( $c, @args );
160 }
161
162 has _subinclude_plugin_class_instance_cache => (
163     isa => HashRef,
164     is => 'ro',
165     default => sub { {} },
166 );
167
168 sub _subinclude_plugin_class_instance {
169     my ($self, $plugin) = @_;
170
171     my $cache = $self->_subinclude_plugin_class_instance_cache;
172     return $cache->{$plugin} if exists $cache->{$plugin};
173
174     my $plugin_config = Catalyst::Utils::merge_hashes(
175         $self->subinclude->{ALL}||{},
176         $self->subinclude->{$plugin}||{}
177     );
178     my $short_class = $plugin_config->{'class'} ?
179         delete $plugin_config->{'class'}
180         : $plugin;
181     my $class = $short_class =~ /::/ ?
182         $short_class
183         : __PACKAGE__ . '::' . $short_class;
184
185     Class::MOP::load_class($class);
186
187     return $cache->{$class} = $class->new($plugin_config);
188 }
189
190 =head1 SEE ALSO
191
192 L<Catalyst::Plugin::SubRequest|Catalyst::Plugin::SubRequest>,
193 L<Moose::Role|Moose::Role>, L<Moose|Moose>,
194 L<http://www.catalystframework.org/calendar/2008/17>
195
196 =head1 BUGS
197
198 Please report any bugs or feature requests to
199 C<bug-catalyst-view-component-subinclude at rt.cpan.org>, or through the web interface at
200 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-View-Component-SubInclude>.
201 I will be notified, and then you'll automatically be notified of progress on
202 your bug as I make changes.
203
204 =head1 AUTHOR
205
206 Nilson Santos Figueiredo Junior, C<< <nilsonsfj at cpan.org> >>
207
208 =head1 CONTRIBUTORS
209
210 Tomas Doran (t0m) C<< <bobtfish@bobtfish.net >>.
211
212 Vladimir Timofeev, C<< <vovkasm at gmail.com> >>.
213
214 Wallace Reis (wreis) C<< <wreis@cpan.org> >>.
215
216 =head1 SPONSORSHIP
217
218 Development sponsored by Ionzero LLC L<http://www.ionzero.com/>.
219
220 =head1 COPYRIGHT & LICENSE
221
222 Copyright (C) 2010 Nilson Santos Figueiredo Junior and the above contributors.
223
224 Copyright (C) 2009 Nilson Santos Figueiredo Junior.
225
226 Copyright (C) 2009 Ionzero LLC.
227
228 This program is free software; you can redistribute it and/or modify it
229 under the same terms as Perl itself.
230
231 =cut
232
233 1;