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

Hello Fellow Monks:

I'm writing a small web app to handle DNS requests. Everything works fine, except I'm tripping up on some data extraction for my "summary". In order to print out an ongoing summary for the user, I've created a structure similar to that found in the HTML::Template HOWTO that is an array of hash references. Primarily, this data is passed on to the main handler routine as an array reference, which is then passed onto HTML::Template so that a table can be printed out via TMPL_LOOP.

While this works fine for it's primary purpose, I'd also like to make use of this data for another sub which mails out a notice to the client. Unfortunately, I've run into difficulty while trying to deref and extract the data. I've used Data::Dumper to verify that the data is in the format I expect (an array of hashes). Here's what the data structure looks like...
$VAR1 = { 'target' => '1.1.1.1', 'id' => '1025116394', 'session' => '1025116375.79921', 'domain' => 'test.com', 'type' => 'A', 'priority' => undef, 'action' => 'add' }; $VAR2 = { 'target' => 'test.com', 'id' => '1025118001', 'session' => '1025116375.79921', 'domain' => 'www', 'type' => 'CNAME', 'priority' => undef, 'action' => 'add' };
And here are the relevant subs that create and attempt to extract the data for mailing. Can anyone tell me what I'm doing wrong here? In it's current form (I've attempted all sorts of dereferencing), I get the error "Bad index while coercing array into hash".

Thanks!
sub db_retrieve_records { my $select_query = "SELECT * from records where session=$sessi +on"; my $sth = $dbh->prepare($select_query); $sth->execute(); my %sessions; while (@records_results = $sth->fetchrow_array) { my $id = $records_results[0]; $sessions{$id}{id} = $id; $sessions{$id}{action} = $records_results[2]; $sessions{$id}{domain} = $records_results[3]; $sessions{$id}{priority} = $records_results[4]; $sessions{$id}{target} = $records_results[5]; $sessions{$id}{type} = $records_results[6]; $sessions{$id}{session} = $session; } foreach my $key (sort keys %sessions) { push(@loop,\%{$sessions{$key}}); } return \@loop; }
sub mail { local $self = shift; local $dbh = $self->param('dbh'); my $q = $self->query(); local $session = $q->param('session'); my %master_info = db_retrieve_master_info(); my @loop = db_retrieve_records(); my $head = Mail::Header->new; $head->add(From => 'somebody@localhost'); $head->add(To => $master_info{$session}{email}); $head->add(Subject => "Summary of DNS Request"); my $body .= "\n"; foreach (qw( account domain )) { $body .= "$_ $master_info{$session}{$_}\n"; } $body .= "\n"; for my $i (0..@loop) { foreach (qw( action type domain priority target )) { $body .= $loop[$i]{$_}; } $body .= "\n"; } # $body .= Data::Dumper->Dump(@loop); $body .= "Other Instructions: \n" if $master_info{$session}{ot +her}; $body .= "$master_info{$session}{other}\n"; my $mail; $mail = Mail::Internet->new(Header => $head, Body => [$body], Modify => 1, ); my $success = $mail->send('sendmail'); }

Replies are listed 'Best First'.
Re: Dereferencing an array of hash references
by kvale (Monsignor) on Jun 26, 2002 at 19:39 UTC
    Two things come to mind. First, in building @loop, there is no need to dereference, and then rereference your anon hash. Just use
    foreach my $key (sort keys %sessions) { push @loop, $sessions{$key}; }
    Second, db_retrieve_records returns an array reference \@loop, but you assign it to an array:
    my @loop = db_retrieve_records();
    which is probably the cause of the error. A simple solution is to return the array:
    return @loop;
    -Mark
      In response to your first, I'm going strictly by the example provided by the author of HTML::Template. If it's good enough (or required) by them, it's good enough for me.

      Second, attempts to return it NOT in the form of an array ref result in errors. As I mentioned, this data structure works fine for my other subroutine. The array is initiated and assigned *exactly* like it is in mail(), and then passed to the template object in the form of an array. No problems whatsoever.

      -fuzzyping
        In that case, you need to properly dereference what you're getting back from db_retrieve_records.

        my @loop = db_retrieve_records();

        should be

        my @loop = @{ db_retrieve_records() };

        Also, you need to learn the difference between my and local.

        UPDATE
        Fixed a typo.

        Perhaps there is a confusion. Your subroutine db_retrieve_records returns a reference to the @loop array, not the @loop array itself. If that is the return value you want for db_retrieve_records, then the mail routine needs correcting.

        First, save the reference to @loop:
        my $loop = db_retrieve_records();
        Then access loop information with the -> dereferencing op:
        for my $i (0..@$loop-1) { foreach (qw( action type domain priority target )) { $body .= $loop->[$i]{$_}; } $body .= "\n"; }
        Note also that I used @$loop-1 for the last element of the outer loop.

        UPDATE: Mr. Freidman's is a much simpler fix. Consider mine as an alternative way to do it :)

        -Mark