[Zope-dev] ANN: ZScript 0.5 - DTML without the punctuation

Phillip J. Eby pje@telecommunity.com
Sun, 20 Jun 1999 14:26:21 -0500


Tired of typing <!--#foo--><!--#/foo--> until your fingers bleed?  Eyes
going numb trying to read unindented (and unindentable because it would
change the output) DTML code?  Ever wished you could return something other
than a string from a DTML method, so you wouldn't have to write an
ExternalMethod?  Waiting with bated breath for a "safe Python method"
product?  Or perhaps you'd like to easily do streaming output from Zope
without dropping to Python?  

Well, guess what...  now you can fix 80-90% of the above, with the new,
free (and absolutely without warranty), ZScript classes.  In a ZScript, you
can write DTML like this:

================================================
if spam
    with "foo.bar"
        let goats="fish and not fowl"  #embedded comment \
            pigs="green.eggs and ham" \
            fool="'on the hill'"

            # Make sure we use the widget!
            call "manage_editProperties(...)"

        /let
    /with
else
    return "['red','herring']"
end if

call "RESPONSE.redirect('/wazoo')"
================================================


Notice that each tag *must* begin on a seperate line, with no exceptions.
\ is used to continue tag arguments on subsequent lines.  # begins a
comment which runs until the end of the line.  There is no "content" here -
ZScripts always return an empty string unless a var or similar tag was
executed in the script body.  Also notice that you can use "/tag" or "end
tag" to close blocks as suits your fancy.  Indentation is ignored unless
it's part of a quoted argument to a tag (in which case it's passed through
to the tag's parser), so you have to close all of your blocks.

The ZScript classes also add a 'return' tag which can take a name or expr,
just like var.  Unlike var, however, the result is not converted into a
string, but instead is returned to the ZScript's caller, breaking out of
the entire method in the process.  With this addition to the existing DTML
vocabulary, ZScript can do pretty much all the "safe" things Python can.
(The only significant item remaining is setting local variables, but
that'll have to wait till I upgrade DT_Let to support a 'set' block
continuation.  Arguably, a "finally" clause for the try tag would be the
only other ommission to speak of.)


Because ZScripts can't contain any "content", you must use var or similar
tags to insert any constant text.  If you're doing significant content, you
should put it in regular DTML methods and call them from your ZScript.  The
benefit of not generating any text by default, is that it makes 'return'
more useful, and also allows you to do streaming output using:

call "RESPONSE.write('text')"

and

call "RESPONSE.flush()"

Thus, you can display in-progress data as a complex method executes.  Of
course, if an error occurs in your script once a RESPONSE.write() has taken
place, no error message will be displayed (although any side-effects of
standard_error_message or other error handlers would still apply).  This is
because there's no way to "take back" streamed output, and the current
HTTPResponse class doesn't check for streamed output when outputting
exception data.  Still, if you can accept this limitation, streamed output
can easily be yours.

Obligatory disclaimers: I wrote this code today, because I just had the
idea and I couldn't get it to go away.  It has received only minimal
testing, which attempted to exercise all the code paths through the parser.
 That doesn't mean that all the paths actually got tested, that it doesn't
have bugs, or that it will work in your application.  Also, it is an
extension of DocumentTemplate, not a Zope Product, so some Productmeister
(perhaps the folks at DC?) will need to package it up as a Product if you
want to be able to drop ZScript Methods into a Zopelication.  Finally, it
installs the 'return' tag globally, which means return can be called from
regular DocumentTemplates.  This will throw an exception unless the
template was called from inside a ZScript, in which case the ZScript will
act as though the return came from within itself.

But anyway, if you like living dangerously, can implement a Product with
one hand behind your back, and don't mind tinkering with alpha software,
then copy this file into your DocumentTemplate package directory:

http://telecommunity.com/DT_Script.py

Please note that if you want to use the 'let' tag as shown in the examples,
you will also need to import:

http://telecommunity.com/DT_Let.py

Finally, to use a ZScript once the package(s):

from DocumentTemplate.DT_Script import ZScript, ZScriptFile, ZScriptDefault

And then use any of the three classes as you would the corresponding
DocumentTemplate.HTML* classes.  Don't forget to import DT_Let if you want
the 'let' tag as well.