http://www.perlmonks.org?node_id=169976

I'm sick of writing HTML by hand. Fortunately, Perl comes to the rescue. A simple combination of make, HTML::Template, HTML::FromText, and an almost stupidly trivial "markup" format of my own have automated the generation of pages on http://infernus.net to a satisfying degree. The makefile especially is still in flux, but here's the Perl code that I call to generate each page:

#! /usr/bin/perl -w =head1 NAME to_html: Translate an atxt document to HTML =head1 SYNOPSIS to_html [-t template -o output] document.atxt =head1 DESCRIPTION to_html translates an augmented text (atxt) document to HTML using HTML::Template and HTML::FromText. to_html tries to use C<./template.tmpl> or a file specified by the C<-t> option as an HTML::Template template, and writes to C<./document.html> or a file specified by the C<-o> flag. =head1 ATXT FORMAT The atxt format is simple: a top block of headers (in key: value form), an empty line, and the rest of the document, just like an email. =cut use strict; use vars qw($opt_t $opt_o); use HTML::FromText; use HTML::Template; use Data::Dumper; # symbolic constants my $WEBROOT = "/var/www/infernus.net/"; my $doc = shift @ARGV or die "Usage: to_html [-t template -o output] document\n"; my $template = HTML::Template->new ( 'filename' => $opt_t || "template.tmpl" ) or die "Cannot create HTML::Template object: $!\n"; my $output = $opt_o || $doc; $output =~ s/\.\w+$/\.html/ unless $opt_o; my ($headers, $body) = &grab_doc($doc); my $html_body = &text2html ($body, 'bold' => 1, 'underline' => 1, 'urls' => 1, 'paras' => 1, 'blockparas' => 1, 'bullets' => 1, 'numbers' => 1, 'tables' => 1, ); my $lastmod = (-M $doc) * 86400 + time; # default template variables $template->param('lastmod_date' => &make_time_str($lastmod)); $template->param('content' => $html_body); $template->param('sitenav' => &make_sitenav()); # page-specific template variables $template->param('dirnav' => &make_dirnav()) unless ((defined $headers->{'dirnav'}) and (! $headers->{'dirnav'})); # set template variables based on headers $template->param(%$headers); &write_html($output, $template->output()); # make_sitenav # Make a site navigation link bar (top level) # Links to /main.html and each dir in $WEBROOT sub make_sitenav { my @links = (qq(<a href="/main.html">main</a>)); opendir WEBROOT, $WEBROOT or die "Can't opendir $WEBROOT: $!\n"; my @dirs = grep { -d $_ } readdir WEBROOT; foreach (@dirs) { next if /^\.{1,2}$/; push @links, qq(<a href="/$_/">$_</a>); } closedir WEBROOT or die "Can't closedir $WEBROOT: $!\n"; return join " | ", @links; } # make_dirnav # Make links to the HTML versions of any files in the current director +y. # Pretty up the filenames a bit for the link text -- this should chang +e # to document titles in later versions sub make_dirnav { my @links = (); opendir CURRENT, '.' or die "Can't opendir .: $!\n"; my @files = grep /\.atxt$/, readdir CURRENT; foreach (@files) { my ($file) = /^(.+)\.atxt$/; my $name = $file; $name =~ s/_/ /g; push @links, qq(<a href="$file.html">$name</a>); } closedir CURRENT or die "Can't closedir .: $!\n"; return join " | ", @links; } # grab_doc(doc) # Read in an atxt document, extract the headers, and grab the rest. # Returns a ref to a headers hash and a scalar of the body text. # Dies on file open error. sub grab_doc { my ($doc) = @_; open DOC, "<$doc" or die "Cannot open $doc: $!\n"; my $headers = {}; my $body = ""; # get headers while(<DOC>) { last if /^\s*$/; # would use split here, but only want to break on the first : my ($key, $value) = /^([^:]+):\s*(.*)$/; $headers->{$key} = $value; } # rest is body local $/ = undef; $body = <DOC>; close DOC or die "Cannot close $doc: $!\n"; return ($headers, $body); } # make_time_str(time) # Makes a pretty date string from an epoch time. # Probably horribly redundant. sub make_time_str { my ($time) = @_; my ($sec, $min, $hour, $day, $mth, $year, $wday, $yearday, $isdst ) = localtime($time); # CHANGE if you're in a different time zone my $timezone = $isdst ? "MDT" : "MST"; my @weekdays = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday); my $weekday = $weekdays[$wday]; my @months = qw(Jan. Feb. Mar. Apr. May June July Aug. Sep. Oct. Nov. Dec.); my $month = $months[$mth]; # bah to all of these. $year += 1900; $sec = "0$sec" if $sec < 10; $min = "0$min" if $min < 10; $hour = "0$hour" if $hour < 10; return "$weekday $day-$month-$year at $hour:$min:$sec $timezone"; } # write_html(outfile, content) # Writes content to a file named "outfile". Simple. sub write_html { my ($outfile, $content) = @_; open OUT, ">$outfile" or die "Cannot open output file $outfile: $!\n"; print OUT $content; close OUT or die "Cannot close output file $outfile: $!\n"; }

The make_time_str function is probably redundant, and all of the code is incredibly hackish, but for the hour or so that this took me (in total), it's well worth it.

--
The hell with paco, vote for Erudil!
:wq

Replies are listed 'Best First'.
(crazyinsomniac: webmake) Re: Automating a Static Website
by crazyinsomniac (Prior) on May 29, 2002 at 05:19 UTC
Re: Automating a Static Website
by webfiend (Vicar) on May 29, 2002 at 19:31 UTC

    This is a nice-looking bit of code (FoxtrotUniform++ for putting HTML::Template to more uses), but crazyinsomniac++ for pointing out WebMake. That is an awesome package that I've put to rather extensive use on my own site in the past. It has a lot of features that the average Perl geek would go googly over: a couple different levels of variable interpolation, metadata, and (of course) evaluation of Perl code in your templates. I'd say it's the best choice for generating a static site by hand.

    That said, I'm not using WebMake on my site right now, because ... well, because I'm fickle, really. I'm always testing new tools and toys under the subject of "automatic site generation". I miss it sometimes, though.


    "All you need is ignorance and confidence; then success is sure."-- Mark Twain