Scoop

Simple templating system

Sadly, going to New York, banging one's face against CSS and web design issues, and being snowed in a few days before Christmas doesn't lead to a lot of posting for some reason. However, the horrible stuff with CSS and web design stuff got me thinking about better ways to handle templating in Scoop.

Right now, when Scoop builds a page, it takes a template out of the database, substitutes a bunch of keys, and returns the page. Unfortunately, at least in some circumstances, the substitutions can get stuck and just sit spinning, eating up CPU and RAM. Also, the current box mechanism could be more flexible. Finally, putting a new page design in can involve a lot of delicate surgery which gets pretty old.

I looked at stuff like HTML::Embperl, but none of the perl things out there really did what I wanted. I wanted something lighter-weight that I could use to build pages inside of Scoop, not something to do all the Apache handling. After working on it for a while, I wrote a parser that can generate code from a page with <% %>, <%= %>, and <%# %> tags in it. I haven't integrated it into Scoop yet, but here's what the code looks like:

sub parse_text {
       my $T = shift;
       my $string = shift;
       $string .= "<% ; %>"; # helps parsing
       my $code;
       while($string =~ /(.*?)<%(.*?)%>/sg){
               my $t = $1;
               my $snip = $2;
               $t =~ s/\\/\\\\/g;
               $t =~ s/'/\\'/g;
               $code .= qq|\$pageRR .= '$t';| if $t;
               if ($snip =~ s/^=//){
                       $code .= qq|\$pageRR .= $snip;|;
                       }
               elsif ($snip =~ /#/){
                       $code .= $snip . "\n";
                       }
               else {
                       $code .= $snip . ";";
                       }
               }
       return $code;
       }

Using this text as an example:

       my $string = shift;
<%= "florp" %>
       This is some text. And \tmore text. And "text\".
       <% my $foo = "feep"; %> A perl value: <%= $foo %>
<% my $k = 0; %> More text to be had, testing a for loop:
       <% foreach my $j (qw(a b c d)){ %>
               Item: <%= $j %> glop
       <% } %>
       A little more. 'text' \\\'? ''''''\'
       <% # a comment %>
       Finish it up.
       hmm. <% my $i = 0;
               while ($i < 5){ # Huh?
                       $i++;
                       }
               %>
      <%# feep %> i?<%= $i %>
Now finished for real.
|

And running it through that perl function above, it spits this perl out:

$pageRR .=  "florp" ;$pageRR .= '
       This is some text. And \\tmore text. And "text\\".
       '; my $foo = "feep"; ;$pageRR .= ' A perl value: ';$pageRR .=  $foo ;$pageRR .= '
'; my $k = 0; ;$pageRR .= ' More text to be had, testing a for loop:
       '; foreach my $j (qw(a b c d)){ ;$pageRR .= '
               Item: ';$pageRR .=  $j ;$pageRR .= ' glop
       '; } ;$pageRR .= '
       A little more. \'text\' \\\\\\\'? \'\'\'\'\'\'\\\'
       '; # a comment
$pageRR .= '
       Finish it up.
       hmm. '; my $i = 0;
               while ($i < 5){ # Huh?
                       $i++;
                       }
               
$pageRR .= '
      ';# feep
$pageRR .= ' i? ';$pageRR .=  $i ;$pageRR .= '
Now finished for real.
|
'; ; ;

As you can see, it generates perfectly good, although kind of hideous, perl, which is suitable for evaling and putting into a namespace for later use, like so (NB: this is stripped down from what I'm actually doing with it so far):

my $code = 'package Foobaricus; sub foo { my $pageRR; ' . $code . '}';
eval ($code);

After that, you can call Foobaricus::foo() and get the formatted page back. This code also does its best to preserve line numbers, although inline comments may lead to extra lines.

Moving forward, Scoop integration is the next step. After that, if there seems to be a need or interest, I'll look at generalizing this code for use elsewhere. Note that this code is not designed to do the heavy lifting of Apache handling, like Apache::ASP or HTML::Embperl do -- this is just an attempt to render pages better, leaving the rest of it to other code. The syntax of the <% %> tags is pretty common, but here's now the tags work:

  • <% %> - used for perl code you don’t want to return a value to the page.
  • <%= %> - inserts the value of the perl variable in that code. If you want to have more than one statement in a set of <%= %> tags, either move most of the code to <% %> tags and only have the value you want in the page in the <%= %> tags, or wrap your code in a do { }; statement.
  • <%# %> - comments.

I'll provide more code and examples later, once this is integrated and I have a better idea of real world usage.

Totally quiet note

I've been meaning to merge all the DK Scoop changes back into mainline Scoop for a while now, but somehow never found the time. I'd like to get those changes out there, though, so right now I'm leaning towards making a clean copy of the database, doing yet another audit of the code for vulnerabilities, and just releasing it that way. A script to allow importing an existing Scoop site into the DK version of Scoop might be handy too. I'll need to run another diff between the two branches, but I know from when I've tried to do it before that merging would be an absolutely beastly endeavour. We'll see what happens.