File Coverage

blib/lib/Mojolicious/Plugin/ReplyTable.pm
Criterion Covered Total %
statement 68 68 100.0
branch 16 20 80.0
condition 6 7 85.7
subroutine 13 13 100.0
pod 1 2 50.0
total 104 110 94.5


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::ReplyTable;
2              
3 1     1   1108 use Mojo::Base 'Mojolicious::Plugin';
  1         2  
  1         5  
4              
5             our $VERSION = '0.08';
6             $VERSION = eval $VERSION;
7              
8 1     1   206 use Mojo::Util;
  1         1  
  1         938  
9              
10             sub register {
11 1     1 1 36 my ($plugin, $app, $config) = @_;
12 1         2 $plugin->setup_types($app);
13 1         8 push @{$app->renderer->classes}, __PACKAGE__;
  1         4  
14 1         17 $app->helper( 'reply.table' => \&_reply_table );
15             }
16              
17             sub _reply_table {
18 17     17   388881 my $c = shift;
19 17 100       54 my $default = ref $_[0] ? undef : shift;
20 17   50     64 my $data = shift || die 'table data is required';
21             my %respond = (
22             json => { json => $data },
23             html => { template => 'reply_table', 'reply_table.table' => $data },
24 2     2   656 csv => sub { $_[0]->render(text => _to_csv($_[0], $data)) },
25 3     3   944 txt => sub { $_[0]->render(text => _to_txt($_[0], $data)) },
26 2     2   618 xls => sub { $_[0]->render(data => _to_xls($_[0], $data)) },
27 2     2   870 xlsx => sub { $_[0]->render(data => _to_xlsx($_[0], $data)) },
28             @_
29 17         310 );
30 17 100       48 if ($default) {
31 3 100       5 $c->stash(format => $default) unless @{$c->accepts};
  3         15  
32             }
33 17         1133 $c->respond_to(%respond);
34             }
35              
36             sub _to_csv {
37 2     2   4 my ($c, $data) = @_;
38 2         16 require Text::CSV;
39 2   100     5 my $csv_options = $c->stash('reply_table.csv_options') || {};
40 2 50       26 $csv_options->{binary} = 1 unless exists $csv_options->{binary};
41 2         17 my $csv = Text::CSV->new($csv_options);
42 2         241 my $string = '';
43 2         6 for my $row (@$data) {
44 6 50       34 $csv->combine(@$row) || die $csv->error_diag;
45 6         117 $string .= $csv->string . "\n";
46             }
47 2         27 return $string;
48             }
49              
50             sub _to_txt {
51 3     3   6 my ($c, $data) = @_;
52 3 100 100     9 if (!$c->stash('reply_table.tablify') && eval{ require Text::Table::Tiny; 1 }) {
  2         45  
  1         6  
53 1         5 return Text::Table::Tiny::table(
54             rows => $data,
55             header_row => $c->stash('reply_table.header_row'),
56             separate_rows => $c->stash('reply_table.separate_rows'),
57             );
58             } else {
59 2         1343 return Mojo::Util::tablify($data);
60             }
61             }
62              
63             sub _to_xls {
64 2     2   5 my ($c, $data) = @_;
65 2 100       4 unless (eval{ require Spreadsheet::WriteExcel; 1 }) {
  2         36  
  1         3  
66 1         1216 $c->rendered(406);
67 1         204 return '';
68             }
69 1 50       33 open my $xfh, '>', \my $fdata or die "Failed to open filehandle: $!";
70 1         11 my $workbook = Spreadsheet::WriteExcel->new( $xfh );
71 1         6520 my $worksheet = $workbook->add_worksheet();
72 1         509 $worksheet->write_col('A1', $data);
73 1         3275 $workbook->close();
74 1         3531 return $fdata;
75             };
76              
77             sub _to_xlsx {
78 2     2   6 my ($c, $data) = @_;
79 2 100       5 unless (eval{ require Excel::Writer::XLSX; 1 }) {
  2         31  
  1         4  
80 1         1342 $c->rendered(406);
81 1         161 return '';
82             }
83 1 50       28 open my $xfh, '>', \my $fdata or die "Failed to open filehandle: $!";
84 1         10 my $workbook = Excel::Writer::XLSX->new( $xfh );
85 1         255 my $worksheet = $workbook->add_worksheet();
86 1         190 $worksheet->write_col('A1', $data);
87 1         601 $workbook->close();
88 1         19989 return $fdata;
89             };
90              
91             sub setup_types {
92 1     1 0 1 my ($plugin, $app) = @_;
93 1         5 my $types = $app->types;
94 1         21 $types->type(csv => [qw{text/csv application/csv}]);
95 1         65 $types->type(xls => [qw{
96             application/vnd.ms-excel application/msexcel application/x-msexcel application/x-ms-excel
97             application/x-excel application/x-dos_ms_excel application/xls
98             }]);
99 1         13 $types->type(xlsx => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
100             }
101              
102             1;
103              
104             =encoding utf8
105              
106             =head1 NAME
107              
108             Mojolicious::Plugin::ReplyTable - Easily render rectangular data in many formats using Mojolicious
109              
110             =head1 SYNOPSIS
111              
112             use Mojolicious::Lite;
113             plugin 'ReplyTable';
114              
115             any '/table' => sub {
116             my $c = shift;
117             my $data = [
118             [qw/a b c d/],
119             [qw/e f g h/],
120             ];
121             $c->reply->table($data);
122             };
123              
124             app->start;
125              
126             =head1 DESCRIPTION
127              
128             L adds the C<< reply->table >> helper which can render a table of data in one of several user-selected formats.
129             The format is selected by the client via the usual Mojolicious L mechanisms.
130              
131             Loading the plugin also sets up several MIME types (using L, see L), and appends the module to the list of rendering classes (See L).
132              
133             =head1 HELPERS
134              
135             =head2 reply->table
136              
137             $c->reply->table([[...], [...], ... ]]);
138             $c->reply->table($default => $data, html => sub { ... });
139              
140             Renders an arrayref of arrayrefs (the inner arrayref being a row) in one of several formats listed below.
141             An optional leading argument is used as the default format when one is not otherwise requested.
142             Optional trailing key-value pairs are merged into the arguments to L.
143              
144             Any additional options, particularly those governing formatting details, are via stash keys prefixed by C.
145             Note that the prefix C is reserved for internal use.
146              
147             The formats currently include:
148              
149             =head3 csv
150              
151             Implemented via L using the default values with C enabled.
152             The ability to modify the formatter is planned and will likely utilize stash keys.
153              
154             =head3 html
155              
156             Implemented via the standard L rendering functionality and a template named C.
157             Setting the stash key C to a true value will cause the default template to use the first row as header values.
158             This default template may be overloaded to change the formatting, the table is available to the template via the stash key C.
159              
160             =head3 json
161              
162             Implemented via the standard L handling.
163              
164             =head3 txt
165              
166             A textual representation of the table.
167             This format is intended for human consumption and the specific formatting should not be relied upon.
168              
169             If L is available, it will be used to format the data (can be overridden with C).
170             It can be controlled via the stash keys C and C as noted in that module's documentation.
171             Otherwise it is generated via L.
172              
173             =head3 xls
174              
175             Binary Microsoft Excel format (for older editions of Excel), provided by optional module L.
176             If that module is not installed, the client will receive an error status 406.
177              
178             =head3 xlsx
179              
180             XML Microsoft Excel format (for newer editions of Excel), provided by optional module L.
181             If that module is not installed, the client will receive an error status 406.
182              
183             =head1 METHODS
184              
185             This module inherits all the methods from L and implements the following new ones
186              
187             =head2 register
188              
189             The typical mechanism of loading a L.
190             No pass-in options are currently available.
191              
192             =head1 FUTURE WORK
193              
194             Beyond what is mentioned in the specifc formats above, the following work is planned.
195             If any of it tickles your fancy, pull-requests are always welcome.
196              
197             =over
198              
199             =item *
200              
201             Better tests for generated Excel documents.
202              
203             =item *
204              
205             Exposing the formatters so that they can be used directly.
206              
207             =item *
208              
209             Add additional formats, like OpenOffice/LibreOffice.
210             If needed these can be appended via additional handlers to the helper.
211              
212             =back
213              
214             =head1 SEE ALSO
215              
216             =over
217              
218             =item L
219              
220             =item L
221              
222             =back
223              
224             =head1 SOURCE REPOSITORY
225              
226             L
227              
228             =head1 SPECIAL THANKS
229              
230             Pharmetika Software, L
231              
232             =head1 AUTHOR
233              
234             Joel Berger, Ejoel.a.berger@gmail.comE
235              
236             =head1 CONTRIBUTORS
237              
238             =over
239              
240             Nils Diewald (Akron)
241              
242             Красимир Беров (kberov)
243              
244             Ryan Perry
245              
246             =back
247              
248             =head1 COPYRIGHT AND LICENSE
249              
250             Copyright (C) 2015 by L and L.
251             This library is free software; you can redistribute it and/or modify
252             it under the same terms as Perl itself.
253              
254             =cut
255              
256             __DATA__