(chapter-start 'validations "utilities to record and run validation routines") (let validations {} (def validate/reset () (= validations {})) (def validate/fns (thing context) (hash-get validations (list (type-of thing) context))) (def validate/fn+ (type context f) (hash-cons validations (list type context) f))) ;; returns a hash of error-name to list of error messages ;; ;; An empty return value signifies an error-free 'thing ;; ;; @thing@ the thing to validate ;; @context@ an identifier to select the subset of validations to apply ;; (def validate (thing context) (returnlet msgs {} (let msgf λem(hash-cons msgs e m) (eachl λv(v thing context msgf) (validate/fns thing context))))) ;; declare a validation routine for type 'type in context 'context ;; ;; @type@ must be a symbol ;; @context@ must be a symbol ;; @body@ is one or more nydp expressions. ;; ;; @body@ will be embedded in a function with access to the following variables : ;; ;; * the value of the 'type argument ;; * ctx ;; * mf ;; ;; @mf@ ("message function") is a function that takes two arguments and is used to store ;; the validation error message ;; example: (mf "Last name" "Last name must not be empty") ;; ;; example usage: ;; ;;
;; (validate/def invoice issue
;; (if (no invoice.account)
;; (mf "Account" "Account must be a client account"))
;; (if (!> invoice.total 0)
;; (mf "Amount" "Amount must be greater than zero"))
;; (if (any? !&group invoice.invoice-items)
;; (mf "Group" "Each line must be assigned to a group")))
;;
;;
;;
;; (let validations (validate invoice 'issue)
;; (if (empty? validations)
;; (invoice.issue)))
;;
;;
(mac validate/def (type context . body)
`(validate/fn+ ',type ',context (fn (,type ctx mf) ,@body)))