Robert Robert - 4 months ago 36
Perl Question

Passing a Tie::IxHash object to the Template module

I am using Perl v5.18.2.

I want hash elements printed in order with Perl's

module, but the hash seems to be ordered only until it gets passed to
Template
.

use strict;
use warnings;

use Tie::IxHash;
use Template;
use Test::More;

tie my %hash, 'Tie::IxHash';
%hash = ('one' => 1, 'two' => 2, 'three' => 3);
my %vars = ('hash' => \%hash);

is_deeply(\[keys $vars{'hash'}], \['one', 'two', 'three'], 'key order');
is_deeply(\[values $vars{'hash'}], \[1, 2, 3], 'value order');

my $t = Template->new(STRICT => 1) || die(Template->error());
my $template = '[% FOREACH k IN hash %][% k.value %][% END %]';
my $output = '';

$t->process(\$template, \%vars, \$output) || die $t->error();

is($output, '123', 'templatized hash order');

done_testing(3);


This always fails:

ok 1 - key order
ok 2 - value order
not ok 3 - templatized hash order
# Failed test 'templatized hash order'
# at t/wtf.t line 18.
# got: '132'
# expected: '123'
1..3
# Looks like you failed 1 test of 3.


What am I missing here? And how can I preserve hash element order in the template?

Answer

The tied hash loses its magic when it is passed to Template Toolkit which doesn't use the tied hash, but makes plain copy of it instead. To get around this, you can just store and pass in your key order and use it to pull out your hash elements in order.

#!/usr/bin/env perl 
use strict;
use warnings;
use Tie::IxHash;
use Template;
use Test::More;

tie my %hash, 'Tie::IxHash';
%hash = ('one' => 1, 'two' => 2, 'three' => 3);
is_deeply([keys %hash], ['one', 'two', 'three'], 'key order');
is_deeply([values %hash], [1, 2, 3], 'value order');
my @key_order = keys %hash;
my %vars = ( 'hash' => \%hash, key_order => \@key_order );

my $t = Template->new(STRICT => 1) || die(Template->error());
my $template = '[% FOREACH k IN key_order %][% hash.$k %][% END %]';
my $output = '';
$t->process(\$template, \%vars, \$output) || die $t->error();
is($output, '123', 'templatized hash order');
done_testing(3);

Output

ok 1 - key order
ok 2 - value order
ok 3 - templatized hash order
1..3