Thursday, 22 November 2007

Separating content generation from presentation generation

So where I left off last time, Bopinder and I had just created a sample page where (as a web designer) he was able to work on the application's front end without futzing around in Lisp. However, to get him there, I had to write a bunch of messy of code. See the end of the above linked article for the gory details.

In this article, there was the ever-present "business logic" intertwined with the code to generate the dynamic page (calls to html-template). Yuck. I would have to do this for every page! No good. So after writing the second page in this manner, I decided it was time to write a macro. Ideally, I would like to write:


(defpage add-one (a) "The value of a+1 is: <!-- tmpl_var a-and-one -->"
(list :a-and-one (1+ a))

That is, my page definition would amount to returning some named values. A formidable task! But a good separation in my opinion as then I could easily turn the results into a JSON (or XMSmell) service without blinking too much.

Since I have not yet written the macro to grab the values of the HTTP POST parameters that are posted to the page, lets pretend that functionality works as I expect, that is:

http://mywebsite.com/add-one?a=1

Would eventually call the function add-one where a is 1. Then the code inside the add-one function just has to concern itself with doing the actual calculations, not yucky HTML. I do have a nagging feeling that I will eventually reinvent WebBlocks badly, but that is how I learn!

The most important thing is that I want to be able to give hunchentoot a function called add-one which pulls out the required POST parameters, executes the body which returns our list with named values and passes this list to whichever template I specify. So here is the macro which now allows me to separate content generation from presentation generation (yay):

(defmacro defpage (name args-list template &body body)
; define a function
`(defun ,name ,args-list
; execute the function body
(let ((template-vars (progn ,@body))
(stream (make-string-output-stream)))
; make the call to html-template
(html-template:fill-and-print-template
,template
template-vars
:stream stream)
(get-output-stream-string stream))))

So given the above macro definition:

CL-USER> (defpage add-one (a) "a+1=<!-- tmpl_var a -->"
(list :a (1+ a)))
CL-USER> (add-one 1)
"a+1=2"

I figure that next, I will modify the defpage macro to pull out the POST parameters.

No comments: