File Coverage

blib/lib/Catalyst/View/Download.pm
Criterion Covered Total %
statement 6 35 17.1
branch 0 8 0.0
condition 0 7 0.0
subroutine 2 4 50.0
pod 2 2 100.0
total 10 56 17.8


line stmt bran cond sub pod time code
1             package Catalyst::View::Download;
2              
3 2     2   1471729 use Moose;
  2         291974  
  2         13  
4 2     2   9082 use namespace::autoclean;
  2         3  
  2         16  
5              
6             extends 'Catalyst::View';
7              
8             =head1 NAME
9              
10             Catalyst::View::Download
11              
12             =head1 VERSION
13              
14             0.09
15              
16             =cut
17              
18             our $VERSION = '0.09';
19             $VERSION = eval $VERSION;
20              
21             __PACKAGE__->config(
22             'stash_key' => 'download',
23             'default' => 'text/plain',
24             'content_type' => {
25             'text/csv' => {
26             'outfile_ext' => 'csv',
27             'module' => '+Download::CSV',
28             },
29             'text/html' => {
30             'outfile_ext' => 'html',
31             'module' => '+Download::HTML',
32             },
33             'text/plain' => {
34             'outfile_ext' => 'txt',
35             'module' => '+Download::Plain',
36             },
37             'text/xml' => {
38             'outfile_ext' => 'xml',
39             'module' => '+Download::XML',
40             },
41             },
42             );
43              
44             sub process {
45 0     0 1   my $self = shift;
46 0           my ($c) = @_;
47              
48 0           my $content = $self->render( $c, $c->stash->{template}, $c->stash );
49              
50 0           $c->response->body($content);
51             }
52              
53             sub render {
54 0     0 1   my $self = shift;
55 0           my ( $c, $template, $args ) = @_;
56 0           my $content;
57              
58             my $content_type =
59             $args->{ $self->config->{'stash_key'} }
60             || $c->response->header('Content-Type')
61 0   0       || $self->config->{'default'};
62              
63 0   0       my $options = $self->config->{'content_type'}{$content_type}
64             || return $c->response->body;
65              
66 0   0       my $module = $options->{'module'} || return $c->response->body;
67 0 0         if ( $module =~ /^\+(.*)$/ ) {
68 0           my $part = $1;
69              
70             # First try a package in the app
71 0           $module = $c->config->{'name'} . '::View::' . $part;
72 0           my $file = $module . '.pm';
73 0           $file =~ s{::}{/}g;
74 0           eval { CORE::require($file) };
  0            
75              
76 0 0         if( $@ ) {
77             # Next try a module under Catalyst::View::
78 0           $module = 'Catalyst::View::' . $part;
79 0           my $file = $module . '.pm';
80 0           $file =~ s{::}{/}g;
81 0           eval { CORE::require($file) };
  0            
82              
83             # All attempts failed, so return body
84 0 0         return $c->response->body if( $@ );
85             }
86             } else {
87 0           Catalyst::Utils::ensure_class_loaded($module);
88             }
89              
90 0           $c->response->header( 'Content-Type' => $content_type );
91             $c->response->header( 'Content-Disposition' => 'attachment; filename='
92             . (
93             $c->stash->{'outfile_name'} ? $c->stash->{'outfile_name'} . '.' . $options->{'outfile_ext'}
94 0 0         : $c->action . '.' . $options->{'outfile_ext'}
95             )
96             );
97              
98 0           my $view = $module->new();
99              
100 0           return $view->render( @_ );
101             }
102              
103             __PACKAGE__->meta->make_immutable;
104              
105             1;
106              
107             __END__
108              
109             =head1 SYNOPSIS
110              
111             # Use the helper
112             > script/create.pl view Download Download
113              
114             # or just add your own...
115             # lib/MyApp/View/Download.pm
116             package MyApp::View::Download;
117             use Moose;
118             use namespace::autoclean;
119              
120             extends 'Catalyst::View::Download';
121              
122             1;
123              
124             # lib/MyApp/Controller/SomeController.pm
125             sub example_action : Local {
126             my ($self, $c, $content_type) = @_;
127              
128             my $data = [
129             ['00','01','02','03'],
130             ['10','11','12','13'],
131             ['20','21','22','23'],
132             ['30','31','32','33']
133             ];
134              
135             if( $content_type ) {
136             # For this example we are only using csv, html and plain.
137             # xml is also available and you can add any of your own
138             # modules under your MyApp::View::Download:: namespace.
139             $content_type = 'plain' unless
140             scalar(
141             grep { $content_type eq $_ }
142             qw(csv html plain)
143             );
144              
145             # Set the response header content type
146             $c->res->header('Content-Type' => 'text/' . $content_type);
147              
148             # OR set the content type in the stash variable 'download'
149             # to process it. (Note: this is configurable)
150             $c->stash->{'download'} = 'text/' . $content_type;
151              
152             # This is here just so I can do some quick data formatting
153             # for this example.
154             my $format = {
155             'html' => sub {
156             return "<!DOCTYPE html><html><head><title>Data</title></head><body>"
157             . join( "<br>", map { join( " ", @$_ ) } @$data )
158             . "</body></html>";
159             },
160             'plain' => sub {
161             return join( "\n", map { join( " ", @$_ ) } @$data );
162             },
163             'csv' => sub { return $data; }
164             };
165              
166             # Store the data in the appropriate stash key.
167             # 'csv' for csv, 'html' for html, etc.
168             $c->stash->{$content_type} = $format->{$content_type}();
169              
170             # You can optionally set the outfile_name or the current action name
171             # will be used
172             $c->stash->{'outfile_name'} = 'filename';
173              
174             # Use the Download View
175             $c->detach('MyApp::View::Download');
176             }
177              
178             # In this example if a content type isn't specified a page is then displayed
179             $c->res->body('Display page as normal.');
180             }
181              
182             =head1 DESCRIPTION
183              
184             A view module to help in the convenience of downloading data into many
185             supportable formats.
186              
187             =head1 SUBROUTINES
188              
189             =head2 process
190              
191             This method will be called by Catalyst if it is asked to forward to a component
192             without a specified action.
193              
194             =head2 render
195              
196             Allows others to use this view for much more fine-grained content generation.
197              
198             =head1 CONFIG
199              
200             =over
201              
202             =item stash_key
203              
204             Determines the key in the stash this view will look for when attempting to
205             retrieve the type of format to process. If this key isn't found it will search
206             for a Content-Type header for the format. Further if neither are found a
207             default format will be applied.
208              
209             $c->view('MyApp::View::Download')->config->{'stash_key'} = 'content_type';
210              
211             =item default
212              
213             Determines which Content-Type to use by default. Default: 'text/plain'
214              
215             $c->view('MyApp::View::Download')->config('default' => 'text/plain');
216              
217             =item content_type
218              
219             A hash ref of hash refs. Each key in content_type is Content-Type that is
220             handled by this view.
221              
222             $c->view('MyApp::View::Download')->config->{'content_type'}{'text/csv'} = {
223             outfile_ext => 'csv',
224             module => 'My::Module'
225             };
226              
227             The Content-Type key refers to it's own hash of parameters to determine the
228             actions thie view should take for that Content-Type.
229              
230             'outfile_ext' - The extenstion of the file that will downloaded.
231              
232             'module' - The name of the module that will handle data output. If there is
233             a plus symbol '+' at the beginning of the module name, this will indicate that
234             the module is either a MyApp::View module or a Catalyst::View module and
235             the appropriate namespace will be added to the beginning of the module name.
236              
237             # Module Loaded: Catalyst::View::Download::CSV
238             $c->view('MyApp::View::Download')
239             ->config
240             ->{'content_type'}{'text/csv'}{'module'} = '+Download::CSV';
241              
242             # Module Loaded: My::Module::CSV
243             $c->view('MyApp::View::Download')
244             ->config
245             ->{'content_type'}{'text/csv'}{'module'} = 'My::Module::CSV';
246              
247             =back
248              
249             =head1 Content-Type Module Requirements
250              
251             Any module set as 'the' module for a certain Content-Type needs to have a
252             subroutine named 'render' that returns the content to output with the
253             following parameters handled.
254              
255             =over
256              
257             =item $c
258              
259             The catalyst $c variable
260              
261             =item $template
262              
263             In case a template file is needed for the module. This view will pass
264             $c->stash->{template} as this value.
265              
266             =item $args
267              
268             A list of arguments the module will use to process the data into content.
269             This view will pass $c->stash as this value.
270              
271             =back
272              
273             =head1 INCLUDED CONTENT TYPES
274              
275             =head2 text/csv
276              
277             Catalyst::View::Download has the following default configuration for this
278             Content-Type.
279              
280             $c->view('MyApp::View::Download')->config->{'content_type'}{'text/csv'} = {
281             outfile_ext => 'csv',
282             module => '+Download::CSV'
283             };
284              
285             See L<Catalyst::View::Download::CSV> for more details.
286              
287             =head2 text/html
288              
289             Catalyst::View::Download has the following default configuration for this
290             Content-Type.
291              
292             $c->view('MyApp::View::Download')->config->{'content_type'}{'text/html'} = {
293             outfile_ext => 'html',
294             module => '+Download::HTML'
295             };
296              
297             See L<Catalyst::View::Download::HTML> for more details.
298              
299             =head2 text/plain
300              
301             Catalyst::View::Download has the following default configuration for this
302             Content-Type.
303              
304             $c->view('MyApp::View::Download')->config->{'default'} = 'text/plain';
305              
306             $c->view('MyApp::View::Download')->config->{'content_type'}{'text/plain'} = {
307             outfile_ext => 'txt',
308             module => '+Download::Plain'
309             };
310              
311             See L<Catalyst::View::Download::Plain> for more details.
312              
313             =head2 text/xml
314              
315             Catalyst::View::Download has the following default configuration for this
316             Content-Type.
317              
318             $c->view('MyApp::View::Download')->config->{'content_type'}{'text/xml'} = {
319             outfile_ext => 'xml',
320             module => '+Download::XML'
321             };
322              
323             See L<Catalyst::View::Download::XML> for more details.
324              
325             =head1 BUGS
326              
327             =head1 SEE ALSO
328              
329             L<Catalyst>, L<Catalyst::View>
330              
331             =head1 AUTHOR
332              
333             Travis Chase, C<< <gaudeon at cpan dot org> >>
334              
335             =head1 ACKNOWLEDGEMENTS
336              
337             Thanks to following people for their constructive comments and help:
338              
339             =over
340              
341             =item J. Shirley
342              
343             =item Jonathan Rockway
344              
345             =item Jon Schutz
346              
347             =item Kevin Frost
348              
349             =item Michele Beltrame
350              
351             =item Dave Lambley
352              
353             =back
354              
355             =head1 LICENSE
356              
357             This program is free software. You can redistribute it and/or modify it
358             under the same terms as Perl itself.
359              
360             =cut