r13930@rob-kinyons-powerbook58: rob | 2006-06-02 15:20:16 -0400
[p5sagit/Excel-Template.git] / lib / Excel / Template.pm
index d157cdb..453ec74 100644 (file)
@@ -6,7 +6,7 @@ BEGIN {
     use Excel::Template::Base;
     use vars qw ($VERSION @ISA);
 
-    $VERSION  = '0.20';
+    $VERSION  = '0.26';
     @ISA      = qw( Excel::Template::Base );
 }
 
@@ -14,23 +14,44 @@ use File::Basename;
 use XML::Parser;
 use IO::Scalar;
 
+use constant RENDER_NML => 'normal';
+use constant RENDER_BIG => 'big';
+use constant RENDER_XML => 'xml';
+
+my %renderers = (
+    RENDER_NML, 'Spreadsheet::WriteExcel',
+    RENDER_BIG, 'Spreadsheet::WriteExcel::Big',
+    RENDER_XML, 'Spreadsheet::WriteExcelXML',
+);
+
 sub new
 {
     my $class = shift;
     my $self = $class->SUPER::new(@_);
 
-    $self->parse_xml($self->{FILENAME})
-        if defined $self->{FILENAME};
+    $self->{FILE} = $self->{FILENAME}
+        if !defined $self->{FILE} && defined $self->{FILENAME};
+
+    $self->parse_xml($self->{FILE})
+        if defined $self->{FILE};
 
     my @renderer_classes = ( 'Spreadsheet::WriteExcel' );
-    if (exists $self->{BIG_FILE} && $self->{BIG_FILE})
+
+    if (exists $self->{RENDERER} && $self->{RENDERER})
     {
-        unshift @renderer_classes, 'Spreadsheet::WriteExcel::Big';
+        if (exists $renderers{ lc $self->{RENDERER} })
+        {
+            unshift @renderer_classes, $renderers{ lc $self->{RENDERER} };
+        }
+        elsif ($^W)
+        {
+            warn "'$self->{RENDERER}' is not recognized\n";
+        }
     }
-
-    if (exists $self->{XML} && $self->{XML})
+    elsif (exists $self->{BIG_FILE} && $self->{BIG_FILE})
     {
-        unshift @renderer_classes, 'Spreadsheet::WriteExcelXML';
+        warn "Use of BIG_FILE is deprecated.\n";
+        unshift @renderer_classes, 'Spreadsheet::WriteExcel::Big';
     }
 
     $self->{RENDERER} = undef;
@@ -65,7 +86,7 @@ sub param
     my $self = shift;
 
     # Allow an arbitrary number of hashrefs, so long as they're the first things    # into param(). Put each one onto the end, de-referenced.
-    push @_, %{shift @_} while UNIVERSAL::isa($_[0], 'HASH');
+    push @_, %{shift @_} while ref $_[0] eq 'HASH';
 
     (@_ % 2)
         && die __PACKAGE__, "->param() : Odd number of parameters to param()\n";
@@ -85,10 +106,15 @@ sub write_file
     my $xls = $self->{RENDERER}->new($filename)
         || die "Cannot create XLS in '$filename': $!\n";
 
-    $self->_prepare_output($xls);
+    eval {
+        $self->_prepare_output($xls);
+    };
+print $@ if $@;
 
     $xls->close;
 
+    return if $@;
+
     return ~~1;
 }
 
@@ -99,7 +125,8 @@ sub output
     my $output;
     tie *XLS, 'IO::Scalar', \$output;
 
-    $self->write_file(\*XLS);
+    $self->write_file(\*XLS)
+        or return;
 
     return $output;
 }
@@ -107,25 +134,22 @@ sub output
 sub parse_xml
 {
     my $self = shift;
-    my ($fname) = @_;
+    my ($file) = @_;
 
-    my ($filename, $dirname) = fileparse($fname);
     my @stack;
-    my $parser = XML::Parser->new(
-        Base => $dirname,
+    my @parms = (
         Handlers => {
             Start => sub {
                 shift;
 
                 my $name = uc shift;
 
-                my $node = Excel::Template::Factory->create_node($name, @_);
+                my $node = Excel::Template::Factory->_create_node($name, @_);
                 die "'$name' (@_) didn't make a node!\n" unless defined $node;
 
                 if ( $node->isa( 'WORKBOOK' ) )
                 {
-                    push @{$self->{WORKBOOKS}}, $node;
+                    $self->{WORKBOOK} = $node;
                 }
                 elsif ( $node->is_embedded )
                 {
@@ -168,25 +192,47 @@ sub parse_xml
         },
     );
 
+    if ( ref $file )
+    {
+        *INFILE = $file;
+    }
+    else
     {
-        open( INFILE, "<$fname" )
-            || die "Cannot open '$fname' for reading: $!\n";
+        my ($filename, $dirname) = fileparse($file);
+        push @parms, Base => $dirname;
+
+        eval q{
+            open( INFILE, '<', $file )
+                || die "Cannot open '$file' for reading: $!\n";
+        }; if ( $@ ) {
+           if ( $@ =~ /Too many arguments for open/ ) {
+                open( INFILE, "< $file" )
+                    || die "Cannot open '$file' for reading: $!\n";
+            } else {
+                die $@;
+            }
+        }
+    }
 
-        $parser->parse(do { local $/ = undef; <INFILE> });
+    my $parser = XML::Parser->new( @parms );
+    $parser->parse(do { local $/ = undef; <INFILE> });
 
-        close INFILE;
-    }
+    close INFILE
+        unless ref $file;
 
     return ~~1;
 }
-*parse = \&parse_xml;
+*parse = *parse = \&parse_xml;
 
 sub _prepare_output
 {
     my $self = shift;
+    return unless $self->{WORKBOOK};
+
     my ($xls) = @_;
 
-    my $context = Excel::Template::Factory->create(
+    my $context = Excel::Template::Factory->_create(
         'CONTEXT',
 
         XLS       => $xls,
@@ -194,12 +240,12 @@ sub _prepare_output
         UNICODE   => $self->{UNICODE},
     );
 
-    $_->render($context) for @{$self->{WORKBOOKS}};
+    $self->{WORKBOOK}->render($context);
 
     return ~~1;
 }
 
-sub register { shift; Excel::Template::Factory::register(@_) }
+sub register { shift; Excel::Template::Factory->register(@_) }
 
 1;
 __END__
@@ -217,14 +263,17 @@ For example, test.xml:
 
   <workbook>
       <worksheet name="tester">
-          <cell text="$HOME"/>
-          <cell text="$PATH"/>
+          <cell text="$HOME" />
+          <cell text="$PATH" />
       </worksheet>
   </workbook>
 
 Now, create a small program to use it:
 
   #!/usr/bin/perl -w
+
+  use strict;
+
   use Excel::Template;
 
   # Create the Excel template
@@ -240,8 +289,7 @@ Now, create a small program to use it:
 
   $template->write_file('test.xls');
 
-If everything worked, then you should have a spreadsheet in your work directory
-that looks something like:
+If everything worked, then you should have a spreadsheet called text.xls in your working directory that looks something like:
 
              A                B                C
     +----------------+----------------+----------------
@@ -253,42 +301,69 @@ that looks something like:
 
 =head1 DESCRIPTION
 
-This is a module used for templating Excel files. Its genesis came from the
-need to use the same datastructure as HTML::Template, but provide Excel files
-instead. The existing modules don't do the trick, as they require replication
-of logic that's already been done within HTML::Template.
+This is a module used for templating Excel files. Its genesis came from the need to use the same datastructure as L<HTML::Template>, but provide Excel files instead. The existing modules don't do the trick, as they require replication of logic that's already been done within L<HTML::Template>.
 
 =head1 MOTIVATION
 
-I do a lot of Perl/CGI for reporting purposes. In nearly every place I've been,
-I've been asked for HTML, PDF, and Excel. HTML::Template provides the first, and
-PDF::Template does the second pretty well. But, generating Excel was the
-sticking point. I already had the data structure for the other templating
-modules, but I just didn't have an easy mechanism to get that data structure
-into an XLS file.
+I do a lot of Perl/CGI for reporting purposes. In nearly every place I've been, I've been asked for HTML, PDF, and Excel. L<HTML::Template> provides the first, and L<PDF::Template> does the second pretty well. But, generating Excel was the sticking point. I already had the data structure for the other templating modules, but I just didn't have an easy mechanism to get that data structure into an XLS file.
 
 =head1 USAGE
 
 =head2 new()
 
-This creates a Excel::Template object. If passed a FILENAME parameter, it will
-parse the template in the given file. (You can also use the parse() method,
-described below.)
+This creates a Excel::Template object.
+
+=head3 Parameters
+
+=over 4
+
+=item * FILE / FILENAME
 
-new() accepts an optional BIG_FILE parameter. This will attempt to change the
-renderer from L<Spreadsheet::WriteExcel> to L<Spreadsheet::WriteExcel::Big>. You
-must already have L<OLE::Storage_Lite> (required by Spreadsheet::WriteExcel::Big) installed on your system.
+Excel::Template will parse the template in the given file or filehandle automatically. (You can also use the parse() method, described below.)
 
-new() also accepts an optional USE_UNICODE parameter. This will use
-L<Unicode::String> to represent strings instead of Perl's internal string
-handling. You must already have L<Unicode::String> installed on your system.
+If you want to use the __DATA__ section, you can do so by passing
 
-The USE_UNICODE parameter will be ignored if you are using Perl 5.8 or higher as
-Perl's internal string handling is unicode-aware.
+  FILE => \*DATA
 
-NOTE: Certain older versions of L<OLE::Storage_Lite> and mod_perl clash for some
-reason. Upgrading to the latest version of L<OLE::Storage_Lite> should fix the
-problem.
+=item * RENDERER
+
+The default rendering engine is L<Spreadsheet::WriteExcel>. You may, if you choose, change that to another choice. The legal values are:
+
+=over 4
+
+=item * Excel::Template->RENDER_NML
+
+This is the default of L<Spreadsheet::WriteExcel>.
+
+=item * Excel::Template->RENDER_BIG
+
+This attempts to load L<Spreadsheet::WriteExcel::Big>.
+
+=item * Excel::Template->RENDER_XML
+
+This attempts to load L<Spreadsheet::WriteExcelXML>.
+
+=back
+
+=item * USE_UNICODE
+
+This will use L<Unicode::String> to represent strings instead of Perl's internal string handling. You must already have L<Unicode::String> installed on your system.
+
+The USE_UNICODE parameter will be ignored if you are using Perl 5.8 or higher as Perl's internal string handling is unicode-aware.
+
+NOTE: Certain older versions of L<OLE::Storage_Lite> and mod_perl clash for some reason. Upgrading to the latest version of L<OLE::Storage_Lite> should fix the problem.
+
+=back
+
+=head3 Deprecated
+
+=over 4
+
+=item * BIG_FILE
+
+Instead, use RENDERER => Excel::Template->RENDER_BIG
+
+=back
 
 =head2 param()
 
@@ -296,35 +371,33 @@ This method is exactly like L<HTML::Template>'s param() method.
 
 =head2 parse() / parse_xml()
 
-This method actually parses the template file. It can either be called
-separately or through the new() call. It will die() if it runs into a situation
-it cannot handle.
+This method actually parses the template file. It can either be called separately or through the new() call. It will die() if it runs into a situation it cannot handle.
+
+If a filename is passed in (vs. a filehandle), the directory name will be passed in to L<XML::Parser> as the I<Base> parameter. This will allow for XML directives to work as expected.
 
 =head2 write_file()
 
-Create the Excel file and write it to the specified filename, if possible. (This
-is when the actual merging of the template and the parameters occurs.)
+Create the Excel file and write it to the specified filename, if possible. (This is when the actual merging of the template and the parameters occurs.)
 
 =head2 output()
 
-It will act just like HTML::Template's output() method, returning the resultant
-file as a stream, usually for output to the web. (This is when the actual
-merging of the template and the parameters occurs.)
+It will act just like L<HTML::Template>'s output() method, returning the resultant file as a stream, usually for output to the web. (This is when the actual merging of the template and the parameters occurs.)
+
+=head2 register()
+
+This allows you to register a class as handling a node. q.v. L<Excel::Template::Factory> for more info.
 
 =head1 SUPPORTED NODES
 
-This is a partial list of nodes. See the other classes in this distro for more
-details on specific parameters and the like.
+This is a partial list of nodes. See the other classes in this distro for more details on specific parameters and the like.
 
-Every node can set the ROW and COL parameters. These are the actual ROW/COL
-values that the next CELL-type tag will write into.
+Every node can set the ROW and COL parameters. These are the actual ROW/COL values that the next CELL-type tag will write into.
 
 =over 4
 
 =item * L<WORKBOOK|Excel::Template::Container::Workbook>
 
-This is the node representing the workbook. It is the parent for all other
-nodes.
+This is the node representing the workbook. It is the parent for all other nodes.
 
 =item * L<WORKSHEET|Excel::Template::Container::Worksheet>
 
@@ -332,8 +405,7 @@ This is the node representing a given worksheet.
 
 =item * L<IF|Excel::Template::Container::Conditional>
 
-This node represents a conditional expression. Its children may or may not be
-rendered. It behaves just like L<HTML::Template>'s TMPL_IF.
+This node represents a conditional expression. Its children may or may not be rendered. It behaves just like L<HTML::Template>'s TMPL_IF.
 
 =item * L<LOOP|Excel::Template::Container::Loop>
 
@@ -341,14 +413,11 @@ This node represents a loop. It behaves just like L<HTML::Template>'s TMPL_LOOP.
 
 =item * L<ROW|Excel::Template::Container::Row>
 
-This node represents a row of data. This is the A in A1.
+This node represents a row of data. This is the 1 in A1. There is no COLUMN node, as of yet.
 
 =item * L<FORMAT|Excel::Template::Container::Format>
 
-This node varies the format for its children. All formatting options supported
-in L<Spreadsheet::WriteExcel> are supported here. There are also a number of
-formatting shortcuts, such as L<BOLD|Excel::Template::Container::Bold> and
-L<ITALIC|Excel::Template::Container::Italic>.
+This node varies the format for its children. All formatting options supported in L<Spreadsheet::WriteExcel> are supported here. There are also a number of formatting shortcuts, such as L<BOLD|Excel::Template::Container::Bold> and L<ITALIC|Excel::Template::Container::Italic>.
 
 =item * L<BACKREF|Excel::Template::Element::Backref>
 
@@ -371,7 +440,7 @@ This is a BACKREF for a number of identically-named cells.
 This is a variable. It is generally used when the 'text' attribute isn't
 sufficient.
 
-=back 4
+=back
 
 =head1 BUGS
 
@@ -379,8 +448,7 @@ None, that I know of.
 
 =head1 SUPPORT
 
-This is production quality software, used in several production web
-applications.
+This is production quality software, used in several production web applications.
 
 =head1 AUTHOR
 
@@ -388,9 +456,9 @@ applications.
 
 =head1 CONTRIBUTORS
 
-There is a mailing list at http://groups.google.com/group/ExcelTemplate
+There is a mailing list at http://groups.google.com/group/ExcelTemplate or exceltemplate@googlegroups.com
 
-Robert Graff -
+=head2 Robert Graff
 
 =over 4
 
@@ -398,18 +466,59 @@ Robert Graff -
 
 =item * Fixing several bugs in worksheet naming
 
-=back 4
+=back
+
+=head1 TEST COVERAGE
+
+I use L<Devel::Cover> to test the coverage of my tests. Every release, I intend to improve these numbers.
+
+Excel::Template is also part of the CPAN Kwalitee initiative, being one of the top 100 non-core modules downloaded from CPAN. If you wish to help out, please feel free to contribute tests, patches, and/or suggestions.
+
+  ---------------------------- ------ ------ ------ ------ ------ ------ ------
+  File                           stmt   bran   cond    sub    pod   time  total
+  ---------------------------- ------ ------ ------ ------ ------ ------ ------
+  blib/lib/Excel/Template.pm     93.8   60.0   58.8  100.0  100.0   24.6   83.3
+  ...ib/Excel/Template/Base.pm   94.4   50.0    n/a  100.0    0.0    7.0   80.0
+  ...cel/Template/Container.pm  100.0   50.0   33.3  100.0    0.0    4.1   83.3
+  ...emplate/Container/Bold.pm  100.0    n/a    n/a  100.0    0.0    0.3   95.0
+  .../Container/Conditional.pm   95.9   90.0   66.7  100.0    0.0    1.2   91.0
+  ...plate/Container/Format.pm  100.0    n/a    n/a  100.0    0.0    0.5   96.6
+  ...plate/Container/Hidden.pm  100.0    n/a    n/a  100.0    0.0    0.0   95.0
+  ...plate/Container/Italic.pm  100.0    n/a    n/a  100.0    0.0    0.1   95.0
+  ...ainer/KeepLeadingZeros.pm  100.0  100.0    n/a  100.0    0.0    0.1   96.3
+  ...plate/Container/Locked.pm  100.0    n/a    n/a  100.0    0.0    0.0   95.0
+  ...emplate/Container/Loop.pm   96.8   50.0   50.0  100.0    0.0    0.3   82.7
+  ...late/Container/Outline.pm  100.0    n/a    n/a  100.0    0.0    0.0   95.0
+  ...Template/Container/Row.pm  100.0   75.0    n/a  100.0    0.0    0.2   90.6
+  ...mplate/Container/Scope.pm  100.0    n/a    n/a  100.0    n/a    0.0  100.0
+  ...plate/Container/Shadow.pm  100.0    n/a    n/a  100.0    0.0    0.0   95.0
+  ...te/Container/Strikeout.pm  100.0    n/a    n/a  100.0    0.0    0.0   95.0
+  ...ate/Container/Workbook.pm  100.0    n/a    n/a  100.0    n/a    2.6  100.0
+  ...te/Container/Worksheet.pm   94.7   75.0    n/a  100.0    0.0    1.0   87.1
+  ...Excel/Template/Context.pm   98.0   80.0   75.0  100.0   73.3   21.4   90.7
+  ...Excel/Template/Element.pm  100.0    n/a    n/a  100.0    n/a    0.3  100.0
+  ...mplate/Element/Backref.pm  100.0   50.0   33.3  100.0    0.0    0.4   87.1
+  .../Template/Element/Cell.pm   97.9   75.0   80.0  100.0    0.0    3.5   88.6
+  ...mplate/Element/Formula.pm  100.0    n/a    n/a  100.0    0.0    0.2   94.1
+  ...te/Element/FreezePanes.pm  100.0    n/a    n/a  100.0    0.0    0.0   95.5
+  ...Template/Element/Image.pm  100.0  100.0    n/a  100.0    0.0    0.1   94.3
+  ...Template/Element/Range.pm  100.0   66.7    n/a  100.0    0.0    0.1   88.9
+  ...l/Template/Element/Var.pm  100.0    n/a    n/a  100.0    0.0    0.0   94.1
+  ...Excel/Template/Factory.pm  100.0   73.1   66.7  100.0  100.0   23.5   91.4
+  .../Excel/Template/Format.pm   98.4   75.0   33.3  100.0   66.7    5.4   90.5
+  ...xcel/Template/Iterator.pm   98.6   80.0   70.6  100.0   50.0    1.1   88.8
+  ...el/Template/TextObject.pm   92.9   62.5   33.3  100.0    0.0    1.8   80.9
+  Total                          97.8   74.3   63.5  100.0   35.7  100.0   89.4
+  ---------------------------- ------ ------ ------ ------ ------ ------ ------
 
 =head1 COPYRIGHT
 
-This program is free software; you can redistribute
-it and/or modify it under the same terms as Perl itself.
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
 
-The full text of the license can be found in the
-LICENSE file included with this module.
+The full text of the license can be found in the LICENSE file included with this module.
 
 =head1 SEE ALSO
 
-perl(1), HTML::Template, Spreadsheet::WriteExcel.
+perl(1), L<HTML::Template>, L<Spreadsheet::WriteExcel>
 
 =cut