template-toolkit: passing array variables into tpage from command line?

I have this template working correctly:

$ cat matrix.tt 
[% DEFAULT
    ncols = 3
    elems = [1,2,3,4,5,6,7,8,9]
%]
\left[\begin{matrix}
[% WHILE elems.size %]
    [% elems.splice(0,ncols).join(' & ') %]
    [% IF elems.size %]\\[% END %]
[% END %]
\end{matrix}\right]
$ tpage --pre_chomp --post_chomp matrix.tt 
\left[\begin{matrix}1 & 2 & 3\\4 & 5 & 6\\7 & 8 & 9\end{matrix}\right]

But this does not work:

$ tpage --define ncols=2 --define elems=[1,2,3,4] matrix.tt 
undef error - WHILE loop terminated (> 1000 iterations) 

I found using the following code that it is not trivial to pass arrays using the --define option to tpage.

$ cat pk.tt 
[% DEFAULT 
    ho = [1,2]
-%]
ho is [% ho %]
po is [% po %]
$ tpage --define po=[1,3] pk.tt #I want po to be interpreted as an array
ho is ARRAY(0x1a3fd00)
po is [1,3]

The output shows that po is a scalar. I want to pass arrays from command line is there any way to do that?

Answers


Depending on how complex the values in your array are, you might be able to get away with something like:

[%- el = elems.split(',') -%]
\left[\begin{matrix}
[% WHILE el.size %]
    [% el.splice(0,ncols).join(' & ') %]
... #etc

$ tpage --define ncols=2 --define elems="1,2,3,4" matrix.tt 

But of course if elems is uncontrolled, or has embedded metacharacters or commas that might lead to pain. But otherwise it's simple enough to promote a scalar to an array using the .split() VMethod.


Here is a way to pass any data into tpage from command line without having to tweak tpage source or the template.

$ cat matrix.tt 
[% DEFAULT
    ncols = 3
    elems = [1,2,3,4,5,6,7,8,9,10,11,12]
%]
\left[\begin{matrix}
[% WHILE elems.size %]
    [% elems.splice(0,ncols).join(' & ') %]
    [% IF elems.size %]\\[% END %]
[% END %]
\end{matrix}\right]
$ cat <(echo '[% elems=["x","y","z",1,2,3] %]') matrix.tt |tpage --pre_chomp --post_chomp
\left[\begin{matrix}x & y & z\\1 & 2 & 3\end{matrix}\right]
$ 

or if you wish, you can type in a directive to set variables and press ^D:

$ rlwrap cat - matrix.tt |tpage --pre_chomp --post_chomp
[%
  ncols=4
  elems=[4,5,6,7,"x","y","z","t"]
%]
\left[\begin{matrix}4 & 5 & 6 & 7\\x & y & z & t\end{matrix}\right]
$

rlwrap saves the lines you type in, and makes them available later by pressing the up key. You can remove rlwrap if you do not need this.

The method works with any program that can process stdin given the shell supports this kind of redirection. I hope it is pretty portable.


Command line arguments to tpage are not evaluated as Perl code, so you can only get string values. If you want to support arbitrary Perl code, you have to hack the tpage source.


Find the following line in the source:

my %ttopts   = $config->varlist('^template_(?!directive_)', 1);

On the next line, add the following code:

foreach my $var (keys %{ $ttopts{variables} }) {
    $ttopts{variables}{$var} = eval $ttopts{variables}{$var};
    die $@ if $@;
}

This loops through all of the arguments to --define and calls eval on them, converting them from strings to Perl code.

When you run this, make sure to quote your arguments to protect them from expansion by the shell:

$ ./tpage --define 'array=["foo","bar"]' --define 'hash={baz=>"qux"}' foo.tt

$VAR1 = [
          'foo',
          'bar'
        ];

$VAR1 = {
          'baz' => 'qux'
        };

$ cat foo.tt
[% USE Dumper %]
[% Dumper.dump(array) %]
[% Dumper.dump(hash) %]

To pass a literal string, you have to quote it with q{} or qq{} because AppConfig strips regular quotes (tpage uses AppConfig behind the scenes to read command line arguments). For example:

$ ./tpage --define 'string=q{foo}' foo.tt 

$VAR1 = 'foo';

$ cat foo.tt
[% USE Dumper %]
[% Dumper.dump(string) %]

Note that although Template Toolkit supports variables of just about any Perl type, my modified version of tpage doesn't. Numbers, strings, arrays, and hashes should all work; subroutines don't.


Need Your Help

javascript split('\n') and not ('\n\n')

javascript split vue.js

I am trying to make a filter that splits all \n and rejoins them as \n\n. Is there a way to bypass all \n\n combinations and only target the single \n? The issue now is that everytime i unfocus the...

Facebook Login Button Appears Over Navigation Drawer

android android-layout android-fragments facebook-android-sdk navigation-drawer

I'm trying to implement the facebook login tutorial, along with a navigation drawer. I managed to create the drawer successfully, but when I tried it on the emulator, the facebook login button appe...