de3b20ac82553940a5131e42026a4fdd56628249
[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.07
19
20 =cut
21
22 our $VERSION = '0.07';
23
24 =head1 SYNOPSIS
25
26   package MyApp::View::TT;
27   use Moose;
28
29   extends 'Catalyst::View::TT';
30   with 'Catalyst::View::Component::SubInclude';
31
32   __PACKAGE__->config( subinclude_plugin => 'SubRequest' );
33
34 Then, somewhere in your templates:
35
36   [% subinclude('/my/widget') %]
37   [% subinclude_using('SubRequest', '/page/footer') %]
38
39 =head1 DESCRIPTION
40
41 C<Catalyst::View::Component::SubInclude> allows you to include content in your
42 templates (or, more generally, somewhere in your view's C<render> processing)
43 which comes from another action in your application. It's implemented as a 
44 L<Moose::Role|Moose::Role>, so using L<Moose|Moose> in your view is required.
45
46 Simply put, it's a way to include the output of a Catalyst sub-request somewhere
47 in your page. 
48
49 It's built in an extensible way so that you're free to use sub-requests, Varnish 
50 ESI (L<http://www.catalystframework.org/calendar/2008/17>) or any other 
51 sub-include plugin you might want to implement. An LWP plugin seems useful and
52 might be developed in the future.
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 around 'new' => sub {
116     my $next = shift;
117     my $class = shift;
118     
119     my $self = $class->$next( @_ );
120     
121     my $subinclude_plugin = $self->config->{subinclude_plugin} || 'SubRequest';
122     $self->set_subinclude_plugin( $subinclude_plugin );
123     
124     $self;
125 };
126
127 before 'render' => sub {
128     my ($self, $c, @args) = @_;
129
130     $c->stash->{subinclude}       = $self->make_context_closure(sub { $self->_subinclude( @_ ) }, $c);
131     $c->stash->{subinclude_using} = $self->make_context_closure(sub { $self->_subinclude_using( @_ ) }, $c);
132 };
133
134 sub set_subinclude_plugin {
135     my ($self, $plugin) = @_;
136
137     my $subinclude_class = blessed $self->_subinclude_plugin_class_instance( $plugin );
138     $self->subinclude_plugin( $subinclude_class );
139 }
140
141 sub _subinclude {
142     my ($self, $c, @args) = @_;
143     $self->_subinclude_using( $c, $self->subinclude_plugin, @args );
144 }
145
146 sub _subinclude_using {
147     my ($self, $c, $plugin, @args) = @_;
148     $plugin = $self->_subinclude_plugin_class_instance($plugin);
149     $plugin->generate_subinclude( $c, @args );
150 }
151
152 has _subinclude_plugin_class_instance_cache => (
153     isa => HashRef,
154     is => 'ro',
155     default => sub { {} },
156 );
157
158 sub _subinclude_plugin_class_instance {
159     my ($self, $plugin) = @_;
160     
161     my $class = $plugin =~ /::/ ? $plugin : __PACKAGE__ . '::' . $plugin;
162
163     my $cache = $self->_subinclude_plugin_class_instance_cache;
164     return $cache->{$plugin} if exists $cache->{$plugin};
165
166     my $plugin_config = Catalyst::Utils::merge_hashes(
167         $self->config->{subinclude}->{ALL}||{},
168         $self->config->{subinclude}->{$plugin}||{}
169     );
170     
171     Class::MOP::load_class($class);
172
173     return $cache->{$plugin} = $class->new($plugin_config);
174 }
175
176 =head1 SEE ALSO
177
178 L<Catalyst::Plugin::SubRequest|Catalyst::Plugin::SubRequest>, 
179 L<Moose::Role|Moose::Role>, L<Moose|Moose>,
180 L<http://www.catalystframework.org/calendar/2008/17>
181
182 =head1 BUGS
183
184 Please report any bugs or feature requests to
185 C<bug-catalyst-view-component-subinclude at rt.cpan.org>, or through the web interface at
186 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-View-Component-SubInclude>.
187 I will be notified, and then you'll automatically be notified of progress on
188 your bug as I make changes.
189
190 =head1 AUTHOR
191
192 Nilson Santos Figueiredo Junior, C<< <nilsonsfj at cpan.org> >>
193
194 =head1 SPONSORSHIP
195
196 Development sponsored by Ionzero LLC L<http://www.ionzero.com/>.
197
198 =head1 COPYRIGHT & LICENSE
199
200 Copyright (C) 2009 Nilson Santos Figueiredo Junior.
201
202 Copyright (C) 2009 Ionzero LLC.
203
204 This program is free software; you can redistribute it and/or modify it
205 under the same terms as Perl itself.
206
207 =cut
208
209 1;