[Catalyst-commits] r13909 - in Catalyst-Action-Serialize-SimpleExcel/1.000/trunk: . lib/Catalyst/Action/Serialize t t/lib/TestApp/Controller

caelum at dev.catalyst.perl.org caelum at dev.catalyst.perl.org
Wed Jan 5 09:39:55 GMT 2011


Author: caelum
Date: 2011-01-05 09:39:55 +0000 (Wed, 05 Jan 2011)
New Revision: 13909

Removed:
   Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/META.yml
Modified:
   Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/Changes
   Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/Makefile.PL
   Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/lib/Catalyst/Action/Serialize/SimpleExcel.pm
   Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/excel.t
   Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/lib/TestApp/Controller/REST.pm
   Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/pod.t
Log:
multiple worksheet support, release 0.014

Modified: Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/Changes
===================================================================
--- Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/Changes	2011-01-04 13:22:54 UTC (rev 13908)
+++ Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/Changes	2011-01-05 09:39:55 UTC (rev 13909)
@@ -1,5 +1,8 @@
 Revision history for Catalyst-Action-Serialize-SimpleExcel
 
+0.014  2011-01-05 09:37:55
+    - Multiple worksheet support.
+
 0.013  2009-09-24 10:22:22
     Added $worksheet->keep_leading_zeros(1)
 

Deleted: Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/META.yml
===================================================================
--- Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/META.yml	2011-01-04 13:22:54 UTC (rev 13908)
+++ Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/META.yml	2011-01-05 09:39:55 UTC (rev 13909)
@@ -1,28 +0,0 @@
----
-abstract: 'Serialize tables to Excel files'
-author:
-  - 'Rafael Kitover <rkitover at cpan.org>'
-build_requires:
-  Spreadsheet::ParseExcel: 0
-  Test::Deep: 0
-  Test::More: 0
-distribution_type: module
-generated_by: 'Module::Install version 0.77'
-license: perl
-meta-spec:
-  url: http://module-build.sourceforge.net/META-spec-v1.4.html
-  version: 1.4
-name: Catalyst-Action-Serialize-SimpleExcel
-no_index:
-  directory:
-    - inc
-    - t
-requires:
-  Catalyst::Controller::REST: 0
-  Scalar::Util: 0
-  Spreadsheet::WriteExcel: 0
-  namespace::clean: 0
-  parent: 0
-resources:
-  license: http://dev.perl.org/licenses/
-version: 0.012

Modified: Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/Makefile.PL
===================================================================
--- Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/Makefile.PL	2011-01-04 13:22:54 UTC (rev 13908)
+++ Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/Makefile.PL	2011-01-05 09:39:55 UTC (rev 13909)
@@ -9,7 +9,6 @@
 requires 'Spreadsheet::WriteExcel';                                   
 requires 'parent';                                                    
 requires 'namespace::clean';                                          
-requires 'Scalar::Util';                                              
                                                                       
 test_requires 'Test::More';                                           
 test_requires 'Spreadsheet::ParseExcel';                              

Modified: Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/lib/Catalyst/Action/Serialize/SimpleExcel.pm
===================================================================
--- Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/lib/Catalyst/Action/Serialize/SimpleExcel.pm	2011-01-04 13:22:54 UTC (rev 13908)
+++ Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/lib/Catalyst/Action/Serialize/SimpleExcel.pm	2011-01-05 09:39:55 UTC (rev 13909)
@@ -4,21 +4,17 @@
 use warnings;
 no warnings 'uninitialized';
 use parent 'Catalyst::Action';
-use Spreadsheet::WriteExcel;
-use Scalar::Util 'reftype';
+use Spreadsheet::WriteExcel ();
+use Catalyst::Exception ();
 use namespace::clean;
 
 =head1 NAME
 
-Catalyst::Action::Serialize::SimpleExcel - Serialize tables to Excel files
+Catalyst::Action::Serialize::SimpleExcel - Serialize to Excel files
 
-=head1 VERSION
-
-Version 0.013
-
 =cut
 
-our $VERSION = '0.013';
+our $VERSION = '0.014';
 
 =head1 SYNOPSIS
 
@@ -40,20 +36,40 @@
     sub books_GET {
         my ($self, $c) = @_;
 
-        my $rs = $c->model('MyDB::Book')->search({}, {
+        my $books_rs = $c->model('MyDB::Book')->search({}, {
             order_by => 'author,title'
         });
 
-        $rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
+        $books_rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
 
-        my @t = map {
+        my @books = map {
             [ @{$_}{qw/author title/} ]
-        } $rs->all;
+        } $books_rs->all;
 
+        my $authors_rs = $c->model('MyDB::Author')->search({}, {
+            order_by => 'last_name,middle_name,last_name'
+        });
+
+        $authors_rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
+
+        my @authors = map {
+            [ @{$_}{qw/first_name middle_name last_name/} ]
+        } $authors_rs->all;
+
         my $entity = {
-            header => ['Author', 'Title'], # will be bold
-            rows => \@t,
-    # the part before .xls, which is automatically appended
+            sheets => [
+                {
+                    name => 'Books',
+                    header => ['Author', 'Title'], # will be bold
+                    rows => \@books,
+                },
+                {
+                    name => 'Authors',
+                    header => ['First Name', 'Middle Name', 'Last Name'],
+                    rows => \@authors,
+                },
+            ],
+            # the part before .xls, which is automatically appended
             filename => 'myapp-books-'.strftime('%m-%d-%Y', localtime)
         };
 
@@ -78,11 +94,26 @@
 
 =head1 DESCRIPTION
 
-Your entity should be either an array of arrays or a hash with the keys as
-described below and in the L</SYNOPSIS>.
+Your entity should be either an array of arrays, an array of arrays of arrays,
+or a hash with the keys as described below and in the L</SYNOPSIS>.
 
 If entity is a hashref, keys should be:
 
+=head2 sheets
+
+An array of worksheets. Either sheets or a worksheet specification at the top
+level is required.
+
+=head2 filename
+
+Optional. The name of the file before .xls. Defaults to "data".
+
+Each sheet should be an array of arrays, or a hashref with the following fields:
+
+=head2 name
+
+Optional. The name of the worksheet.
+
 =head2 rows
 
 Required. The array of arrays of rows.
@@ -96,10 +127,8 @@
 Optional, the widths in characters of the columns. Otherwise the widths are
 calculated automatically from the data and header.
 
-=head2 filename
+If you only have one sheet, you can put it in the top level hash.
 
-The name of the file before .xls. Defaults to "data".
-
 =cut
 
 sub execute {
@@ -114,12 +143,89 @@
 
     my $data = $c->stash->{$stash_key};
 
-    $data = { rows => $data } if reftype $data eq 'ARRAY';
-
     open my $fh, '>', \my $buf;
     my $workbook = Spreadsheet::WriteExcel->new($fh);
-    my $worksheet = $workbook->add_worksheet;
 
+    my ($filename, $sheets) = $self->_parse_entity($data);
+
+    for my $sheet (@$sheets) {
+        $self->_add_sheet($workbook, $sheet);
+    }
+
+    $workbook->close;
+
+    $self->_write_file($c, $filename, $buf);
+
+    return 1;
+}
+
+sub _write_file {
+    my ($self, $c, $filename, $data) = @_;
+
+    $c->res->content_type('application/vnd.ms-excel');
+    $c->res->header('Content-Disposition' =>
+     "attachment; filename=${filename}.xls");
+    $c->res->output($data);
+}
+
+sub _parse_entity {
+    my ($self, $data) = @_;
+
+    my @sheets;
+    my $filename = 'data'; # default
+
+    if (ref $data eq 'ARRAY') {
+        if (not ref $data->[0][0]) {
+            $sheets[0] = { rows => $data };
+        }
+        else {
+            @sheets = map 
+                ref $_ eq 'HASH' ? $_ 
+              : ref $_ eq 'ARRAY' ? { rows => $_ }
+              : Catalyst::Exception->throw(
+                  'Unsupported sheet reference type: '.ref($_)), @{ $data };
+        }
+    }
+    elsif (ref $data eq 'HASH') {
+        $filename = $data->{filename} if $data->{filename};
+
+        my $sheets = $data->{sheets};
+        my $rows   = $data->{rows};
+
+        if ($sheets && $rows) {
+            Catalyst::Exception->throw('Use either sheets or rows, not both.');
+        }
+
+        if ($sheets) {
+            @sheets = map 
+                ref $_ eq 'HASH' ? $_ 
+              : ref $_ eq 'ARRAY' ? { rows => $_ }
+              : Catalyst::Exception->throw(
+                  'Unsupported sheet reference type: '.ref($_)), @{ $sheets };
+        }
+        elsif ($rows) {
+            $sheets[0] = $data;
+        }
+        else {
+            Catalyst::Exception->throw('Must supply either sheets or rows.');
+        }
+    }
+    else {
+        Catalyst::Exception->throw(
+            'Unsupported workbook reference type: '.ref($data)
+        );
+    }
+
+    return ($filename, \@sheets);
+}
+
+sub _add_sheet {
+    my ($self, $workbook, $sheet) = @_;
+
+    my $worksheet = $workbook->add_worksheet(
+        $sheet->{name} ? $sheet->{name} : ()
+    );
+
     $worksheet->keep_leading_zeros(1);
 
     my ($row, $col) = (0,0);
@@ -127,10 +233,10 @@
     my @auto_widths;
 
 # Write Header
-    if (exists $data->{header}) {
+    if (exists $sheet->{header}) {
         my $header_format = $workbook->add_format;
         $header_format->set_bold;
-        for my $header (@{ $data->{header} }) {
+        for my $header (@{ $sheet->{header} }) {
             $auto_widths[$col] = length $header
                 if $auto_widths[$col] < length $header;
 
@@ -141,7 +247,7 @@
     }
 
 # Write data
-    for my $the_row (@{ $data->{rows} }) {
+    for my $the_row (@{ $sheet->{rows} }) {
         for my $the_col (@$the_row) {
             $auto_widths[$col] = length $the_col
                 if $auto_widths[$col] < length $the_col;
@@ -153,27 +259,17 @@
     }
 
 # Set column widths
-    $data->{column_widths} = \@auto_widths
-        unless exists $data->{column_widths};
+    $sheet->{column_widths} = \@auto_widths
+        unless exists $sheet->{column_widths};
 
-    for my $width (@{ $data->{column_widths} }) {
+    for my $width (@{ $sheet->{column_widths} }) {
         $worksheet->set_column($col, $col++, $width);
     }
 # Have to set the width of column 0 again, otherwise Excel loses it!
 # I don't know why...
-    $worksheet->set_column(0, 0, $data->{column_widths}[0]);
-    $col = 0;
+    $worksheet->set_column(0, 0, $sheet->{column_widths}[0]);
 
-# Write the file
-    my $filename = $data->{filename} || 'data';
-
-    $workbook->close;
-    $c->res->content_type('application/vnd.ms-excel');
-    $c->res->header('Content-Disposition' =>
-     "attachment; filename=${filename}.xls");
-    $c->res->output($buf);
-
-    1;
+    return $worksheet;
 }
 
 =head1 AUTHOR
@@ -192,16 +288,6 @@
 L<Catalyst::View::Excel::Template::Plus>, L<Spreadsheet::WriteExcel>,
 L<Spreadsheet::ParseExcel>
 
-=head1 TODO
-
-=over 4
-
-=item * Split into mutliple overridable methods.
-
-=item * Multiple sheet support.
-
-=back
-
 =head1 SUPPORT
 
 You can find documentation for this module with the perldoc command.
@@ -232,7 +318,7 @@
 
 =head1 COPYRIGHT & LICENSE
 
-Copyright (c) 2008 Rafael Kitover
+Copyright (c) 2008-2011 Rafael Kitover
 
 This program is free software; you can redistribute it and/or modify it
 under the same terms as Perl itself.

Modified: Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/excel.t
===================================================================
--- Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/excel.t	2011-01-04 13:22:54 UTC (rev 13908)
+++ Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/excel.t	2011-01-05 09:39:55 UTC (rev 13909)
@@ -7,13 +7,14 @@
 use Catalyst::Test 'TestApp';
 use Spreadsheet::ParseExcel ();
 
-use Test::More tests => 17;
+use Test::More tests => 31;
 use Test::Deep;
 
 # Test array of array
 
 ok((my $file  = get '/rest/a_o_a?content-type=application%2Fvnd.ms-excel'),
     'received file');
+
 ok((my $excel = Spreadsheet::ParseExcel::Workbook->Parse(\$file)),
     'parsed file');
 my $sheet = $excel->{Worksheet}[0];
@@ -25,6 +26,7 @@
 );
 
 # test that number-like data does not get numified
+
 ok(($file  = get '/rest/no_numify?content-type=application%2Fvnd.ms-excel'),
     'received file');
 ok(($excel = Spreadsheet::ParseExcel::Workbook->Parse(\$file)),
@@ -59,8 +61,29 @@
     'auto_widths -> sheet'
 );
 
-# Test everything else
+# Test multiple worksheets as array of array of array
 
+ok(($file  = get '/rest/multi_worksheet_a_o_a_o_a?content-type=application%2Fvnd.ms-excel'),
+    'received file');
+ok(($excel = Spreadsheet::ParseExcel::Workbook->Parse(\$file)),
+    'parsed file');
+my $sheet1 = $excel->{Worksheet}[0];
+my $sheet2 = $excel->{Worksheet}[1];
+
+cmp_deeply(
+    read_sheet($sheet1),
+    [[1,2,3],[4,5,6]],
+    'multi worksheet array_of_array_of_array -> sheet1'
+);
+
+cmp_deeply(
+    read_sheet($sheet2),
+    [[7,8,9],[10,11,12]],
+    'multi worksheet array_of_array_of_array -> sheet2'
+);
+
+# Test hashref with options
+
 ok((my $resp = request '/rest/fancy?content-type=application%2Fvnd.ms-excel'),
     'received response');
 
@@ -86,6 +109,45 @@
     'with options -> sheet'
 );
 
+# Test multiple worksheets as hash
+
+ok(($resp = request '/rest/multi_worksheet_hash?content-type=application%2Fvnd.ms-excel'),
+    'received response');
+
+is($resp->header('Content-Type'), 'application/vnd.ms-excel', 'Content-Type');
+
+is($resp->header('Content-Disposition'), 'attachment; filename=mtfnpy.xls', 'Content-Disposition');
+
+ok(($file = $resp->content), 'received file');
+
+ok(($excel = Spreadsheet::ParseExcel::Workbook->Parse(\$file)), 'parsed file');
+
+$sheet1 = $excel->{Worksheet}[0];
+$sheet2 = $excel->{Worksheet}[1];
+my $sheet3 = $excel->{Worksheet}[2];
+
+is eval { $sheet1->get_name }, 'MySheet1', 'multi sheets hash -> sheet1 name';
+
+cmp_deeply(
+    read_sheet($sheet1),
+    [ [qw/Foo Bar/], [1,2], [3,4] ],
+    'multi sheets hash -> sheet1'
+);
+
+is eval { $sheet2->get_name }, 'MySheet2', 'multi sheets hash -> sheet2 name';
+
+cmp_deeply(
+    read_sheet($sheet2),
+    [ [qw/Baz Quux/], [5,6], [7,8] ],
+    'multi sheets hash -> sheet2'
+);
+
+cmp_deeply(
+    read_sheet($sheet3),
+    [ [9,10], [11,12] ],
+    'multi sheets hash -> sheet3 (as array)'
+);
+
 sub read_sheet {
     my $sheet = shift;
     my $res;

Modified: Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/lib/TestApp/Controller/REST.pm
===================================================================
--- Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/lib/TestApp/Controller/REST.pm	2011-01-04 13:22:54 UTC (rev 13908)
+++ Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/lib/TestApp/Controller/REST.pm	2011-01-05 09:39:55 UTC (rev 13909)
@@ -15,11 +15,31 @@
         $c,
         entity => [
             [1,2,3],
-            [4,5,6]
+            [4,5,6],
         ]
     );
 }
 
+sub multi_worksheet_a_o_a_o_a : Local ActionClass('REST') {}
+
+sub multi_worksheet_a_o_a_o_a_GET {
+    my ($self, $c) = @_;
+
+    $self->status_ok(
+        $c,
+        entity => [
+            [
+                [1,2,3],
+                [4,5,6],
+            ],
+            [
+                [7,8,9],
+                [10,11,12],
+            ],
+        ],
+    );
+}
+
 sub no_numify : Local ActionClass('REST') {}
 
 # test that strings that parse as numbers pass through unmolested
@@ -30,7 +50,7 @@
         $c,
         entity => [
             ['01',' 2',3],
-            [4,5,'006']
+            [4,5,'006'],
         ]
     );
 }
@@ -47,13 +67,48 @@
             column_widths => [10, 20],
             rows => [
                 [1,2],
-                [3,4]
+                [3,4],
             ],
             filename => 'mtfnpy'
         }
     );
 }
 
+sub multi_worksheet_hash : Local ActionClass('REST') {}
+
+sub multi_worksheet_hash_GET {
+    my ($self, $c) = @_;
+
+    $self->status_ok(
+        $c,
+        entity => {
+            sheets => [
+                {
+                    name => 'MySheet1',
+                    header => [qw/Foo Bar/],
+                    rows => [
+                        [1,2],
+                        [3,4],
+                    ],
+                },
+                {
+                    name => 'MySheet2',
+                    header => [qw/Baz Quux/],
+                    rows => [
+                        [5,6],
+                        [7,8],
+                    ],
+                },
+                [
+                    [9,10],
+                    [11,12],
+                ],
+            ],
+            filename => 'mtfnpy'
+        },
+    );
+}
+
 sub auto_widths : Local ActionClass('REST') {}
 
 sub auto_widths_GET {
@@ -65,7 +120,7 @@
             header => [qw/Foo Bar/],
             rows => [
                 [1,2],
-                [3,999999]
+                [3,999999],
             ],
             filename => 'mtfnpy'
         }

Modified: Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/pod.t
===================================================================
--- Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/pod.t	2011-01-04 13:22:54 UTC (rev 13908)
+++ Catalyst-Action-Serialize-SimpleExcel/1.000/trunk/t/pod.t	2011-01-05 09:39:55 UTC (rev 13909)
@@ -1,5 +1,3 @@
-#!perl -T
-
 use strict;
 use warnings;
 use Test::More;




More information about the Catalyst-commits mailing list