ag4ve has asked for the wisdom of the Perl Monks concerning the following question:

Perl is returning this error when I try to run this code:

Not a HASH reference at /usr/share/perl5/Spreadsheet/WriteExcel/Worksh +eet.pm line 1798.

And I'm at a loss for where it might be. Also, as I've been faced with this before, what is the best way to track down issues when the error comes from the module itself? I looked at the code there (for the heck of it) - I'm about 99.9% sure I messed up somewhere. My 'messed up' code is below.

#!/usr/bin/perl -T use strict; use warnings; use Carp::Assert; use Spreadsheet::ParseExcel; use Spreadsheet::WriteExcel; use lib '/home/shawn/test/ais'; use DBConnect; my( $infile ) = $ARGV[ 0 ] =~ m/^([\ A-Z0-9_.-]+)$/ig; my( $date ) = $ARGV[ 1 ] =~ m/^([0-9-]+)$/ig; print "infile = $infile\n"; my $sth = $dbh->prepare( 'SELECT * FROM table WHERE field = ? AND YEAR(time) = ?' ) or die "QUERY FAIL: $DBI::errstr\n$!\n"; my @header = ([ "Some", "header", "fields" ]); my $parser = Spreadsheet::ParseExcel->new(); my $workbookin = $parser->parse( $infile ); if ( !defined $workbookin ) { die "Can\'t read spreadsheet: ", $parser->error(), ".\n"; } for( my $sheetcount = 0; $sheetcount < $workbookin->{SheetCount}; $she +etcount++ ) { my($name, $indata, $width) = readws( $sheetcount ); my $outfile = $name =~ /^([\ A-Z0-9_-]+)$/; my $workbookout = Spreadsheet::WriteExcel->new( $outfile . ".xls" ) +; my $worksheetout = $workbookout->add_worksheet( 'Data' ); $worksheetout->write_col( 0, 0, @$indata ); foreach my $row ( 1 .. @$indata) { my $sqldata = sqlget( $indata->[ $row ]->[ 3 ] ); if( $sqldata->[ 0 ] ) { my $worksheetout = $workbookout->add_worksheet( $indata->[ $r +ow ]->[ 0 ] ); foreach my $col ( @$width ) { $worksheetout->set_column( $col, $col, $width->[ $col ] ); } $worksheetout->write_col( 0, 0, \@header ); $worksheetout->write_col( 1, 0, @$sqldata ); } } } sub readws { # Read worksheet - return worksheet n +ame, data, and max cell width my $wsnum = $_[ 0 ]; # Count of input worksheet my $worksheetin = $workbookin->{Worksheet}[ $wsnum ]; my( @xldata, @width ); my( $row_min, $row_max ) = $worksheetin->row_range(); my( $col_min, $col_max ) = $worksheetin->col_range(); my $wsname = $worksheetin->{Name}; # Name of worksheet for my $row ( $row_min .. $row_max ) { for my $col ( $col_min .. $col_max ) { $width[ $col ] = 0 unless $width[ $col ]; my $cell = $worksheetin->get_cell( $row, $col ); next unless $cell; $xldata[ $row ][ $col ] = $cell->unformatted() ; if( length( $xldata[ $row ][ $col ] ) > $width[ $col ] ) { $width[ $col ] = length( $xldata[ $row ][ $col ] ); } } } return( $wsname, \@xldata, \@width ); } sub sqlget { # Return sql data my $sqlin = $_[ 0 ]; $sth->execute( $sqlin, $date ); my $sqlout = $sth->selectall_arrayref; if( $sth->rows > 0) { return $sqlout; } else { return undef; } }

Replies are listed 'Best First'.
Re: Spreadsheet::WriteExcel error
by jmcnamara (Monsignor) on Dec 13, 2010 at 11:03 UTC
    I think the error is at the following line:
    $worksheetout->write_col( 0, 0, @$indata ); # Should be: $worksheetout->write_col( 0, 0, $indata );
    The write_col() method takes an array_ref as the third argument.

    Update: In relation to your question "what is the best way to track down issues when the error comes from the module itself?".

    In your particular case you were a little unlucky. WriteExcel checks that the required parameter is an array_ref and croaks an error from the point of view of the calling program. Something like this:

    Not an array ref in call to write_col() at /tmp/pm876841.pl line 4 +1
    Unfortunately, once you had (accidentally) flattened the first level of the array ref the next element was also an array ref and the check was validated so the module didn't report the error back at the right place.

    The way that I tracked this down was the way I always do it. With the perl debugger running under Emacs. I commented out the includes that I didn't have and replaced your DB call with a function returning a simple array ref.

    Once the program compiled I ran through it in the debugger until I hit the write_col() line where the error was thrown.

    P.S.: you probably have the same error here.

    $worksheetout->write_col( 1, 0, @$sqldata );

    --
    John.

Re: Spreadsheet::WriteExcel error
by ELISHEVA (Prior) on Dec 13, 2010 at 12:05 UTC

    Figuring out exactly how and where an error message is coming from can be a challenge if the error gets triggered deep inside code that isn't your own, or even deep inside your own code, if you have reusable subroutines that get called in several places.

    If your code is dying when you get this error, you might try placing the following line at the top of your script just below your use Carp::Assert. It forces a nice stack trace so you can see exactly what was calling what when your error got generated.

    $SIG{__DIE__} = sub { Carp::confess unless $^S };

    I find it helpful sometimes to comment and uncomment it. The Carp stack trace has a nasty habit of truncating messages so you can't always see the full error message. I'll run the code once with the above line commented to see the full message and then once again with it uncommented to see the stack trace that lead to the message.

    And many thanks to the kind monk who suggested using Carp::confess in $SIG{__DIE__} to me a year or so ago in the cb - I wish I remember who you are because you saved me countless hours of debugging time since.

Re: Spreadsheet::WriteExcel error
by Anonyrnous Monk (Hermit) on Dec 13, 2010 at 10:06 UTC
    what is the best way to track down issues when the error comes from the module itself?

    A first step would be to look at the line mentioned in the error message, in order to get an idea what that line and the surrounding code is trying to accomplish, what data is being processed, and how it tracks back to how you're using the module. This might give you some idea as to whether you're maybe passing incorrect parameters to a method, or some such.

    Being able to do this is the big advantage of open source software...