How to dereference hashes in Template Toolkit?

1.5k Views Asked by At

I have the following problem: I have an array of references to hashes, which i want to render.

$VAR1 = \{
        'nice_key' => undef,
        'nicer_key' => '0',
        'nicest_key' => 'Miller'
      };
$VAR2 = \{
        'nice_key' => undef,
        'nicer_key' => '0',
        'nicest_key' => 'Farns'
      };
$VAR3 = \{
        'nice_key' => undef,
        'nicer_key' => '0',
        'nicest_key' => 'Woodstock'
      };
...

I pass this as \@tablerows to the template. Inside the template I do:

[% FOREACH row = tablerows %]
    <tr>
        <td>[% row %]</td>
        <td>[% row.nicer_key %]</td>
        <td>[% row.nicest_key %]</td>
    </tr>
[% END %]

The [% row %]-line outputs something like REF(0x74a0160), but the other two lines are just blank.

As far as I understand this, the row variable in the template would have to be dereferenced in order to call row.nicer_key, but working with -> or {} results in a parser error.

Is this even possible or what am I getting wrong?

Edit: Background of the datastructure: The program does the following:

  1. Parse an HTML file with a table in it
  2. While parsing, read every row of the table into a hash (the nice_keys are the cells of the table) and store these hashes into a hash of hashes (lets call it tabledata)
  3. Do some database queries and add these to the inner hashes (e.g. nicest_key was not present on the original HTML file)
  4. Output a HTML table with the same order as before.

In order to preserve the order of the original table I filled the tablerows array in step 2 with references to the inner hashes.

Edit2: What I have intended: enter image description here

The arrows symbolize references to the hashes.

How I filled these data

my %tabledata = ();
my @tablerows = ();
foreach (... parsing ...) {
  ...
  $tabledata{$current_no} = ();
  push @tablerows, \$tabledata{$current_no};

  $tabledata{$current_no}{$row} = $value;
}

When I dump each of them %tabledata and @tablerows the content seems right to me.

2

There are 2 best solutions below

0
On

Okay I have found the problem:

$tabledata{$current_no} = ();
push @tablerows, \$tabledata{$current_no};

was in my code, now I have

$tabledata{$current_no} = {};
push @tablerows, $tabledata{$current_no};

Which means, I have a hash reference instead of a list in hash context to work with, and this turned out to be what I wanted.

Which results in the following dump (note, no references to references) and the template is parsed correctly.

$VAR1 = {
    'nice_key' => undef,
    'nicer_key' => '0',
    'nicest_key' => 'Miller'
  };
$VAR2 = {
    'nice_key' => undef,
    'nicer_key' => '0',
    'nicest_key' => 'Farns'
  };
$VAR3 = {
    'nice_key' => undef,
    'nicer_key' => '0',
    'nicest_key' => 'Woodstock'
  };
...
0
On

As the comments say (and you found out on your own while I was writing this), your core problem is that your hashes are buried one superfluous level of references too deep. You should strive to fix that first.

Now to answer the actual question in the post title, AFAIK the Template Toolkit doesn't natively provide a facility to force hash dereferencing, but it's feasible to add one:

my @array = ( \{ nice_key => undef, nicer_key => '0', nicest_key => 'Miller'} );

Template->new->process( \<<EOT, { deref => sub{${+shift}}, tablerows => \@array } );
[% FOREACH row = tablerows %]
    <tr>
        <td>[% row %]</td>
        <td>[% deref(row) %]</td>
        <td>[% deref(row).nicer_key %]</td>
        <td>[% deref(row).nicest_key %]</td>
    </tr>
[% END %]
EOT

I actually tried to implement this as a scalar vmethod and failed. Any pointers?