1 (in-package :lisp-on-lines
)
3 (defstruct invalid-attribute-value
4 (initial-value nil
:read-only t
)
5 (invalid-value nil
:read-only t
)
6 (conditions nil
:read-only t
))
8 (deflayer validate
(editor))
10 (defattribute base-attribute
()
16 :required-test
'validate-true
))
18 (defparameter *invalid-attribute-renderer
*
19 #'(lambda (invalid-attribute-value next-method
)
21 :class
"lol-invalid-attribute"
23 :class
"lol-invalid-attribute-message"
24 (dolist (c (invalid-attribute-value-conditions invalid-attribute-value
))
25 (<:li
(<:as-html
(message c
)))))
26 (funcall next-method
))))
28 (defdisplay :in-layer validate
29 :around
((attribute base-attribute
) object
)
30 "Set the callback to perform validation
31 and create invalid-attribute-values when things don't validate. duh "
34 (ucw::make-new-callback
36 (setf (attribute-value object attribute
) val
)))))
37 ;;;; We need to lexically bind some slots here
38 ;;;; As by the time the validator runs we'll be in
39 ;;;; a totally different dynamic scope.
40 (validators (validate-using attribute
))
41 (label (label attribute
)))
42 (when (requiredp attribute
)
43 (push (required-test attribute
) validators
))
44 (dletf (((callback attribute
)
45 (ucw::make-new-callback
47 (flet ((setter (value)
49 (ucw::context.current-frame
*context
*)
53 ;; We have to do DLETF ,as we will no longer be
54 ;; in the validation layer at callback-application time.
55 (dletf (((validate-using attribute
) validators
)
56 ((slot-value attribute
'label
) label
)
58 (multiple-value-bind (validp conditions
)
59 (validate-attribute object attribute val
)
63 (make-invalid-attribute-value
64 :initial-value
(attribute-value object attribute
)
66 :conditions conditions
))))))))))
69 ;;;; Ok, so if the attribute-value holds an
70 ;;;; invalid-attribute-value struct, we take the appropriate action
71 (let ((value (attribute-value object attribute
)))
72 (if (invalid-attribute-value-p value
)
74 ;;;; set the value back the the previous
75 ;;;; TODO: does not handle unbound slots
77 (ucw::context.current-frame
*context
*)
79 (invalid-attribute-value-initial-value value
))
80 (funcall *invalid-attribute-renderer
*
84 (call-next-method))))))