Last Updated:

Document Templates and Perl

Whenever we need to write a CGI script in Perl we are faced with the fact that the script must return some HTML code to the user. Typically, this code is inserted directly into the code of the script itself. This approach is not entirely convenient in the sense that when changing the design of the site, as a rule, you have to change the text of the script. You probably noticed that very often the "script" part of the server is somewhat different from the rest of the site. This happens precisely because it is quite difficult to change HTML fragments in the script code itself. Somewhere you will miscalculate.

The second, most popular, option is to move variables with HTML to a separate file. This, of course, makes life easier, but does not give complete freedom. The script below consists of only 30 lines, but it solves all the problems described above.

So, to begin with, let's decide that all our HTML data will be stored in a template file. Let's call it html.dot the file format will be as follows:

#! Fragment_name
# Comment
Any text
#! Title_of_second_fragment
More text

In addition, the following constructions can be used in the text:

  • @Name@ - substitute the value of the variable "Name"
  • @#Name@ - substitute a fragment with the name "Name". Note that substitution is recursive and in the substituted blocks, you can also use substitutions.

Now let's write a function that reads this file, converts it (makes substitutions of fragments) and saves fragments in the hash. In this case, the names of the hash elements will correspond to the names of the fragments.

 sub read_from_dot{
1    my (@lines, $line, $curname);
2    open (F, "$_[0]") || die "Can't open file $_[0]";
3    while ($line=){
4        chomp($line);
5        if (( $line !~ /^#[^!]/ ) && ( $line ne "#" )){
6          if ($line =~ /^#!\s+/){
7              $curname=$';}
9        else{
10            $DOT{$curname}.="$line\n";
          }
        }
     }
14   close(F);

16  foreach (keys %DOT){
17        $DOT{$_} =~ s/@#(.[^@]*)@/$DOT{"$1"}/g;
    }
}
  • 2 - Open the file. The file name must be passed as a function argument.
  • 3 - Run through all the lines of the file.
  • 4 - Get rid of the end-of-line character.
  • 5 - If the line begins with "#" (and after it is not "!"), then we work with it. Otherwise, we lose. Because it's a comment.
  • 6 - If the line begins with "#!", then this is the name of the next fragment.
  • 7 - Remember the name of the current fragment.
  • 9-10 - If it is just a string, add it to the hash element with a name equal to the name of the current fragment.
  • 14 - Close the file.

Recursive substitution procedure

  • 16 - Run through all elements of the %DOT hash.
  • 17 - Replace all constructs of the form @#NAME@ with the text of the fragment named "NAME".

That's it. We have a global hash %DOT that contains all of our templates.

Let's take this template file as an example:

#! MAIN
###########################################
@#TITLE@
This is the main page

#! STYLE
###########################################
<STYLE>
H1 {color: red}
</STYLE>

#! TITLE
#
# @STRING@ - name
###########################################
<HTML>
<HEAD>
@#STYLE@
<TITLE>@STRING@</TITLE>
</HEAD>

After processing, we will get a hash of three elements MAIN, STYLE, TITLE.

And finally, let's write a function that will substitute the values of variables. So that the variables do not intersect with the variables used in the script, we will store them in the hash %DOT_VAL.

 sub insert_values{
1    if ($_[0]){
2        $_[0] =~ s/@(.[^@]*)@/$DOT_VAL{$1}/g;
     }
     else{
5        foreach (keys %DOT){
6            $DOT{$_} =~ s/@(.[^@]*)@/$DOT_VAL{$1}/g;
         }
     }
 }
  • 1 - If the function is called with an argument, then the replacement of variables occurs in this argument. Otherwise, the replacement of variables occurs in the entire hash $DOT.
  • 2 - Substitution of variables.
  • 5 - Run on all elements of the hash %DOT
  • 6 - Substitute the values of variables.

And finally, an example of using this code.

read_from_dot("html.dot");
$DOT_VAL{STRING}="Welcome to our site!!!";
insert_values();
print $DOT{"MAIN"};