Dire se definisco un metaclasse che migliora gli slot standard con uno slot di validatore, quando passo il :validator (clavier:valid-email "The email is invalid")
come opzione, invece di memorizzare il risultato dell'espressione, che è un funzionale, memorizza l'espressione stessa. Mi manca un passaggio quando estendi gli slot standard? Come faccio a garantire che l'espressione venga valutata prima di essere archiviata? Sto usando SBCL 1.2.11 btw. Ecco il codice in questioneLe opzioni di slot personalizzate non applicano alcuna riduzione al suo argomento
(unless (find-package 'clavier)
(ql:quickload :clavier))
(unless (find-package 'c2mop)
(ql:quickload :c2mop))
(defpackage #:clos2web/validation
(:use #:cl)
(:import-from #:c2mop
#:standard-class
#:standard-direct-slot-definition
#:standard-effective-slot-definition
#:validate-superclass
#:direct-slot-definition-class
#:effective-slot-definition-class
#:compute-effective-slot-definition
#:slot-value-using-class))
(in-package #:clos2web/validation)
(defun true (value)
"Always return true."
(declare (ignore value))
t)
(defclass validation-class (standard-class)
()
(:documentation "Meta-class for objects whose slots know how to validate
their values."))
(defmethod validate-superclass
((class validation-class) (super standard-class))
t)
(defmethod validate-superclass
((class standard-class) (super validation-class))
t)
(defclass validation-slot (c2mop:standard-slot-definition)
((validator :initarg :validator :accessor validator :initform #'true
:documentation "The function to determine if the value is
valid. It takes as a parameter the value.")))
(defclass validation-direct-slot (validation-slot
standard-direct-slot-definition)
())
(defclass validation-effective-slot (validation-slot
standard-effective-slot-definition)
())
(defmethod direct-slot-definition-class ((class validation-class) &rest initargs)
(declare (ignore initargs))
(find-class 'validation-direct-slot))
(defmethod effective-slot-definition-class ((class validation-class) &rest initargs)
(declare (ignore initargs))
(find-class 'validation-effective-slot))
(defmethod compute-effective-slot-definition
((class validation-class) slot-name direct-slot-definitions)
(let ((effective-slot-definition (call-next-method)))
(setf (validator effective-slot-definition)
(some #'validator direct-slot-definitions))
effective-slot-definition))
(defmethod (setf slot-value-using-class) :before
(new (class validation-class) object (slot validation-effective-slot))
(when (slot-boundp slot 'validator)
(multiple-value-bind (validp msg)
(funcall (validator slot) new)
(unless validp
(error msg)))))
;; Example usage
(defclass user()
((name :initarg :name)
(email :initarg :email :validator (clavier:valid-email "The email is invalid") :accessor email))
(:metaclass validation-class))
(let ((pepe (make-instance 'user :name "Pepe" :email "[email protected]")))
(setf (email pepe) "FU!")) ;; should throw
Il codice non riesce quando si effettua un'istanza come (CLAVIER: VALIDO-MAIL "L'e-mail non è valido") non è una funcallable.
(CLAVIER:VALID-EMAIL
"The email is invalid") fell through ETYPECASE expression.
Wanted one of (FUNCTION SYMBOL).
[Condition of type SB-KERNEL:CASE-FAILURE]
'Defclass' non valuta le cose, quindi il modulo viene memorizzato come opzione di slot. Sto riflettendo sulla migliore soluzione. – Svante
in sbcl le canonicalize-defclass-slots si occupano dell'elaborazione degli slot e hanno accesso all'env della definizione, avete qualche indicazione su come renderlo suscettibile di ridurre le opzioni non standard utilizzando l'ambiente? – PuercoPop