Saturday, 24 November 2007

The all-elusive HTTP parameter bind-to-function-arguments

So last time, I mentioned that my defpage macro does not handle POST arguments. To recap, when writing the backend for a website, I'd like to treat my page definition as just a function call (sound familiar?) That is, if I was writing a page that returned the value of the submitted argument and added one to it, I'd really love to just write:

(defpage add-1 (a) #p"tmpl/add-one.tmpl"
(list :a-plus-1 (1+ a)))

Well now that dream is a reality. If you have been following along at home (I'm pretty sure I'm talking to myself just now), what we need to do is modify the defpage macro to iterate through each argument, look up the parameters using hunchentoot:get-parameter, setq them to something sensible, and call the body of our page.

Lets look at a macro expand of our macro now (yeah, thats right, I took a screenshot):

You can see in the above code that the macro generates some very bad code! But, it gets the job done for now. Here is the finished macro:

(defmacro defpage ((name args-list &key html-template)
&body body)
(unless html-template (error "Need html-template"))
`(defun ,name ()
(let (,@args-list)
(log-for (application info)
"Calling ~A with html-template ~A"
',name ,html-template)
,@(loop for arg in args-list collect
`(let ((arg-value (hunchentoot:get-parameter
(string-downcase (symbol-name ',arg)))))
(unless arg-value
(error (format nil
"Missing required value: ~A"
(string-downcase (symbol-name ',arg)))))
(setq ,arg arg-value)))
(let ((template-vars (progn ,@body))
(stream (make-string-output-stream)))
:stream stream)
(get-output-stream-string stream)))))

You must be saying: "You can't be finished Sohail, you don't handle non-string types in the arguments yet! Not to mention optional arguments! You are lazy!" You hurt my feelings by thinking that. But maybe, JUST MAYBE, I SHOULD JUST USE define-easy-handler. Believe me, I swore pretty loudly when I realized that existed. Oh well, atleast I learned something useful.

I'll just spend some time looking at lolcats. Ah, lolcats.

No comments: