Скажите, если я определяю метакласс, который улучшает стандартные слоты с слотом для проверки, когда я передаю :validator (clavier:valid-email "The email is invalid")
в качестве опции вместо сохранения результата выражения, которое является funcallable, он хранит само выражение. Я пропустил шаг при расширении стандартных слотов? Как обеспечить, чтобы выражение оценивалось перед сохранением? Я использую SBCL 1.2.11 кстати. Вот код, о котором идет речьПользовательские варианты слотов не применяют никакого сокращения к его аргументу
(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
код не удается при создании экземпляра в качестве (клавир: ДЕЙСТВИТЕЛЬНАЯ-ПОЧТА «Электронная почта является недействительной») не является 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' не оценивает вещи, поэтому форма сохраняется как опция слота. Я обдумываю лучшее решение. – Svante
В sbcl слоты canonicalize-defclass заботятся о обработке слотов и имеют доступ к env определения, есть ли у вас какие-либо указатели на то, как сделать его доступным для уменьшения нестандартных параметров с использованием среды? – PuercoPop