How I'm writing pages out
[ Previous: Keyword/permission management ]
[ Top: To-do manager ]
[ Next: Datasheet interface ]
The todomgr_pageout
function is pretty much the same as all the template
output functions I use in my daily work. It looks for tags of the form [##tag##]
in a base HTML file, and replaces them with hash lookups in a tags
variable
in its caller (in Perl I do the same by passing a hash reference in, but in Tcl I can just
look up into the stack frame of my caller, which is so incredibly arcane it gives me a little
frisson every time I do it.)
I've been using this ungainly hack for a long time, in several different languages now. And
by golly I'm never going to stop!
Note that the HTTP return status is passed in as an optional parameter. The default is, of
course, 200, but a useful alternative is 401 for user authentication, for instance.
|
proc todomgr_pageout {conn file {status 200}} {
upvar tags tags
global todomgr_home
set fn $todomgr_home/$file
if {![file exists $fn]} {
See Handling 404 returns
}
set fil [open $fn]
while {[gets $fil line] >= 0} {
set hit [regexp -nocase {\[##([-a-z_ 0-9!/?]*)##\]} $line match tag]
while {$hit} {
regsub -all -nocase \\\[##$tag##\\\] $line [escape_ampersand [todomgr_pageout_tag_value $tag]] line
set hit [regexp -nocase {\[##([-a-z_ 0-9!/?]*)##\]} $line match tag]
}
append pg $line "\n"
}
close $fil
ns_return $conn $status text/html $pg
}
|
If you look away from the truly horrible things we have to do to get regsub
to
work with those square brackets, the whole thing is pretty obvious: you open the file, read in
a line at a time, and find tags of the form [##tag##]
. Then you pass the text
from the tag into todomgr_pageout_tag_value
, which returns the value. There's one
little irritating bit about that, though. Since regexp
has one oh-so-helpful
"feature" that I would remove given the chance: it replaces all occurences of '&' with the
match string. So the result of todomgr_pageout_tag_value
has to
be processed in order to escape ampersands. But since
both regsub
and Tcl itself have to be escaped, we end up with a triple-escape.
It's just so lovely. Here's escape_ampersand
:
|
proc escape_ampersand {str} {
regsub -all "&" $str \\\\\\& retval
return $retval
}
|
And of course actual retrieval of values from the tags
array is simple. A tag
is just an arbitrary string, so this is a dandy place to define "special" tags or even
functional tags. I'm defining two special tags here; flagopen
and
flagclose
. These are functional tags, so that [##flagopen flag##]
resolves to <!--
if flag
is equal to an empty string or zero, and
resolves to an empty string otherwise. The corresponding [##flagclose flag##]
resolves to -->
, of course. This means that we can display parts of a page
conditionally depending on whether a particular flag is true or not; it allows much greater
flexibility in page design. (And allows us to push much more of the page design into the
template so that code changes aren't necessary.)
A more complex implementation of todomgr_pageout
could simply omit that portion
of the template enclosed in the flagopen/flagclose pair, but I'm not going to go that deep
right now.
|
proc todomgr_pageout_tag_value {tag} {
upvar tags tags
if [string match "flagopen *" $tag] {
set tag [string range $tag 9 end]
if [info exists tags($tag)] {
if {$tags($tag) != 0 && $tags($tag) != ""} { return "" }
}
return "<!--"
}
if [string match "flagclose *" $tag] {
set tag [string range $tag 10 end]
if [info exists tags($tag)] {
if {$tags($tag) != 0 && $tags($tag) != ""} { return "" }
}
return "-->"
}
if [info exists tags($tag)] { return $tags($tag) }
return ""
}
|
The only thing left to do is to define how we handle non-existent pages.
Handling 404 returns
The simplest way to do this is simply to return a standard apology and be done with it. And
since I'm in a hurry, that's what I'll do:
|
return [ns_return $conn 404 text/html "
<h1>404</h1>
<hr>
<blockquote>
<i>You step in the stream<br>
the water has moved on<br>
page not found
</i>
</blockquote>
Sorry, <code>[ns_conn url $conn]</code> can't be found.
"]
|
Sorry for the cheesy haiku but I just love that one. If you've ever encountered a missing link
on the rest of my site, you'll see I use it there, too. And that's all the apology you're going
to get.
[ Previous: Keyword/permission management ]
[ Top: To-do manager ]
[ Next: Datasheet interface ]
This code and documentation are released under the terms of the GNU license. They are
additionally copyright (c) 2000, Vivtek. All rights reserved except those explicitly
granted under the terms of the GNU license.
|