Angenommen, ich definiere eine Metaklasse, die Standard-Slots mit einem Validator-Slot erweitert, wenn ich :validator (clavier:valid-email "The email is invalid")
als Option übergebe, anstatt das Ergebnis des Ausdrucks zu speichern ein Funcallable, speichert es den Ausdruck selbst. Fehle ich bei der Erweiterung der Standard-Slots einen Schritt? Wie stelle ich sicher, dass der Ausdruck vor dem Speichern ausgewertet wird? Ich benutze SBCL 1.2.11 btw. Hier ist der Code in FrageBenutzerdefinierte Slot-Optionen wenden keine Reduktion auf ihr Argument an
(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
Der Code schlägt fehl, wenn eine Instanz als (CLAVIER: VALID-EMAIL "Die E-Mail ist ungültig") zu machen ist kein funcallable.
'Defclass' wertet Dinge nicht aus, daher wird das Formular als Slot-Option gespeichert. Ich denke über die beste Lösung nach. – Svante
in sbcl die canonicalize-defclass-slots kümmert sich um die Verarbeitung der Slots und hat Zugriff auf die env der Definition, haben Sie irgendwelche Hinweise, wie es möglich, nicht-Standard-Optionen mit der Umgebung zu reduzieren? – PuercoPop