in reply to Templating suggestions for code generation

I agree with the suggestion to use Template::Toolkit. I have worked with merlyn on using Template Toolkit to generate httpd.conf files for a series of servers (dev configuration, then qa, then prod). This allows us to leave key directives the same and test them through our environment and be sure they will work in produciton. So it works well for all types of templating.

Your example would look something like:

[%- items = ['one', 'two', 'three'] -%] # Output an array data structure in perl. @array = ( [%- FOREACH item IN items -%] [%- IF loop.last -%] '[%- item -%]' [%- ELSE -%] '[%- item -%]', [%- END -%] [%- END -%]);

You can run this from the command line after you install Template using the tpage command. The extra '-' gets rid of whitespace from inside the template directives. If you want the code to look nice, you'll have to play with the spacing a bit.

Replies are listed 'Best First'.
Re^2: Templating suggestions for code generation
by bart (Canon) on Feb 17, 2005 at 14:17 UTC
    Thank you. I don't know how I missed the "loop.last" property for the FOREACH directive, I clearly was looking at the wrong spot in the docs. It obviously is the right property to use to put text between items.

    So, to answer my own question, for Template::Toolkit, this is a good way to do what I wanted in Template::Toolkit — though you're still free to suggest improvements :):

    // BEGIN [% IF list -%] switch(x) { [% FOREACH item IN list -%] case [% item %]: // code for [% item %] [% UNLESS loop.last -%] break; [% END -%] [% END -%] } [% END -%] // END

    Run together with the following code:

    #! perl -w use Template; my $tt = Template->new(); foreach my $data ([undef => undef], [ empty => []], ['("foo", "bar", "baz")' => [qw(foo bar baz)]]) { print "// List is $data->[0]:\n"; $tt->process('tt2.tmpl', { list => $data->[1] }); print "\n\n"; }

    Oh, and I feel that I had to tweak it quite a bit just to get the proper amount of whitespace, adding the hyphens just at the right spots (those -%] thingies). Either I'm missing something, or Template::Toolkit could use a touchup.

    I'd also like to indent the loop control directives, without that the added leading whitespace shows up in the output. Can it be done? It seems like I have to choose between stripping all whitespace (including newlines) just in front or right after the directives, or no stripping at all. Can't I strip leading whitespace just up to, but excluding, the newline?

      // BEGIN [% IF list -%] switch(x) { [% FOREACH item IN list -%] case [% item %]: // code for [% item %] [% UNLESS loop.last -%] break; [% END -%] [% END -%] } [% END -%] // END
      With loop.first and loop.last (which I sometimes wish Perl had), you can simplify this even further by getting rid of the outer "if":
      [% FOREACH item IN list -%] [% IF loop.first -%] // BEGIN switch(x) { [% END %] case [% item %]: // code for [% item %] [% UNLESS loop.last -%] break; [% ELSE -%] } // END [% END -%]
      The loop won't be run if there are no elements, so you don't need to test if elements exist or not. Then, once in the loop, you put "ahead of first time" and "after last time" things inside the loop.first and loop.last conditionals. Nice pattern. In the abstract:
      [% FOR item IN list %] [% IF loop.first %] ... before first item .. [% END %] ... each item ... [% UNLESS loop.last %] ... between items ... [% ELSE %] ... after last item ... [% END; END %]

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.

      Oh, and I feel that I had to tweak it quite a bit just to get the proper amount of whitespace, adding the hyphens just at the right spots (those -%] thingies). Either I'm missing something, or Template::Toolkit could use a touchup.

      You can control this behavior in a more global manner with the chomping options PRE_CHOMP and POST_CHOMP which you can set when you create your Template object. You can find some details on the TT web site in the section on config options.

        I find in general that POST_CHOMP nearly always "does the right thing" for line-oriented output. I just have to remember the occasional "+" for those items that appear at the end of a line where I really do want to keep the newline.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.