2015-05-12 18 views
5

Bir validator yuvası ile standart yuvaları geliştiren bir meta sınıfı tanımladığımda, :validator (clavier:valid-email "The email is invalid") ifadesini, ifadenin sonucunu kaydetmek yerine funcallable, ifadenin kendisini depolar. Standart yuvaları genişletirken bir adımı kaçırıyor muyum? İfadenin depolanmadan önce değerlendirildiğinden nasıl emin olurum? SBCL 1.2.11 btw kullanıyorum. Bir funcallable değildir: Burada söz (GEÇERLİ-EMAIL "e-posta geçersiz" clavier) olarak bir örneğini verirken kod başarısızÖzel yuva seçenekleri bağımsız değişkenine herhangi bir azaltma uygulamıyor

(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 

yılında kodudur.

(CLAVIER:VALID-EMAIL 
    "The email is invalid") fell through ETYPECASE expression. 
Wanted one of (FUNCTION SYMBOL). 
    [Condition of type SB-KERNEL:CASE-FAILURE] 
+0

'Defclass' bir şeyleri değerlendirmez, bu nedenle form yuva seçeneği olarak saklanır. En iyi çözümü düşünüyorum. Sbcl'de – Svante

+0

, canonicalize-defclass-yuvaları yuvaların işlenmesiyle ilgilenir ve tanımın enverine erişir, çevreyi kullanarak standart olmayan seçenekleri azaltmak için nasıl uygun hale getirilebileceğine dair herhangi bir işaretçiniz var mı? – PuercoPop

cevap

5

Yukarıdaki yorumun yaptığı gibi, defclass argümanları değerlendirmez (makrodur). Her zamanki tavsiyenin değerlendirmesi kaçınmak olsa da, bu durumda değerlendirmenin tam olarak istediğiniz gibi olabileceğini düşünüyorum. Genelde formu doğrudan bir makro gövdeye eklersiniz. Defclass ile cevabın, formun başlangıçta başlatılması ve değerlendirmenin saklanması (henüz değerlendirilmemişse) olduğunu düşünüyorum.

Bu muhtemelen oluşacak: Ayrıca, iki ayrı bağımsız değişkenleri olarak :validation-message ve :validation-fn depolayabilir İsteğe

(defmethod initialize-instance :after ((obj validation-slot) 
             &key &allow-other-keys) 
    #| ... |#) 

sonra çağrı:

(multiple-value-bind (validp msg) 
    (funcall (funcall (validator-fn slot) 
         (validator-message slot)) 
      new) 
    (unless validp 
    (error msg))) 

diğer alternatif değerlendirmesini deposu olacağını formu doldurup makroya yazınız:

(defvar *email-validator* (CLAVIER:VALID-EMAIL "The email is invalid")) 
(defun email-validator (val) 
    (funcall *email-validator* val)) 

Daha sonra defoluşmak için email-validator'u geçin.

Ayrıca, doğrulama işlevlerinizin tipi koşulları yerine slot-validation-error tipi koşullarını bildirmesini önerebilirim. Ardından durumunuz başarısız olan doğrulayıcıya, değere, yuvaya ve örneğe referanslar içerebilir. Bu, ham hatadan çok daha iyi kontrol sağlayabilir. Ayrıca bazı yeniden başlatmalar da ekleyebilirsiniz (farklı bir değer sağlamak için yuva değerini atlamak için iptal, kullanım değeri).

Kurulumunuza bağlı olarak, doğrulama işlevinizin sinyallere zorlanan birden çok değeri döndürmek yerine bunları doğrudan sinyal vermesi daha mantıklı olabilir.

+0

Slot doğrulama sınamasında işaretçi için teşekkürler. Eh, şu an için değerlendirme. Muhtemelen başka bir alternatifi görebilmek için, initformun tanımın gerçekleştiği zaman çevreyi nasıl izlediğine bakmalıyım. – PuercoPop

İlgili konular