Friday, 7 December 2007

Update: (Weblocks) Doing first-time setup for a web-app

Just some updates to the earlier post.

As I mentioned, if you want to do model validation with Weblocks, you currently have to write it yourself. But the choice of method to specialize was sub-optimal. Ideally you would want your model to be validated no matter how you were modifying it, say with a gridedit or a dataform. So instead of hijacking dataform-submit-action, I should really have created an around method for update-object-from-request. This is below:


(defmethod weblocks:update-object-from-request :around ((data create-login)
&rest args)
(multiple-value-bind (success failed-slots)
(call-next-method)
(if success
(validate-new-login data)
(values success failed-slots))))

validate-new-login will return multiple values in the same manner as the generic update-object-from-request: a boolean value indicating success and a list of errors, if failure was indicated. It looks like this:

(defun validate-new-login (new-login)
"If the Weblocks validation succeeds, then all required values
are already there, so we only need to check the consistency. Returns
(values success errors) where success is t if there were no errors otherwise
success is nil and errors is an association list of (slot . error)"
(let (errors)
(validate-slot "password"
(equal (password new-login)
(verify-password new-login))
"Both passwords must match!")
(values (null errors) errors)))

And validate-slot is an extremely unhygienic macro that looks like:

(defmacro validate-slot (slot-name
predicate
message)
`(unless ,predicate
(push (cons (attributize-name ,slot-name)
,message)
errors)))

Pretend you didn't see it.

Which brings me to my next update. Weblocks nicely renders all the errors for you. Unfortunately, I made that part really difficult. Previously, my use of continuations would always create a new dataform which would then not show any errors that needed to be displayed. Since the continuations are stored in the widgets themselves, I should have just yielded the dataform widget itself and be done with it:

(defun setup-admin-password ()
(make-instance 'dataform
:name "create-login"
:data (make-instance 'create-login)
:ui-state :form
:allow-close-p nil
:on-success
(lambda (self)
(answer self))))

Have a good weekend!

No comments: