2017-10-13 23 views
2

J'ai une carte de clojure.specs que je veux utiliser pour valider mes demandes dans l'exécution comme ceci:enregistrer clojure.specs d'une carte

{::num-id int? 
:project-spec/id ::num-id 
:project-spec/name (s/and string? #((< (count %) 24))) 
:project-spec/project (s/keys :req-un [:project-spec/id :project-spec/name])} 

je peux utiliser l'une de ces spécifications dans l'exécution pour valider en utilisant s/valid?, sauf le project-spec/project un. Celui-là exige que tous les autres soient enregistrés dans le registre global des spécifications pour fonctionner correctement. Lorsque j'essaie d'enregistrer spec en utilisant la fonction simple doseq si échoue depuis que je passe les variables locales à s/def macros et il ne résout pas les variables à la valeur avant d'étendre les macros.

(doseq [[name spec] spec-map] 
    (s/def name spec)) 

J'ai essayé de créer des macros à des variables eval avant de les transmettre à s/def macros, mais que l'on échoue avec CompilerException java.lang.UnsupportedOperationException: Can't eval locals.

(defmacro reg-spec 
    [name spec] 
    `(s/def ~(eval name) ~(eval spec))) 

(doseq [[name spec] spec-map] 
    (reg-spec name spec)) 

La dernière chose que j'ai essayé est eval des variables lors du passage à s/def mais échoue la validation spec.

(s/def (eval spec-name) (eval spec-spec)) 
CompilerException java.lang.AssertionError: Assert failed: k must be 
namespaced keyword or resolvable symbol (c/and (ident? k) (namespace k)) 

Y at-il un moyen de réaliser ce que je veux faire? Ou est-ce que j'ai mal compris quelque chose d'évident? Toute aide est appréciée!

Répondre

3

Y at-il une raison pour laquelle vous ne voulez pas donner les spécifications nom via s/def? Un aspect important de spec est les noms forts/namespaced. Votre exemple leur donne des noms dans un sens, mais seulement comme des clés sur cette carte. Je les ferais tous. J'ai corrigé quelques erreurs dans l'exemple ci-dessus. Vos clés de carte sont nommées, donc s/keys doit utiliser :req au lieu de :req-un. Vous pouvez toujours créer votre carte de spécifications si vous le souhaitez, mais les clés/valeurs seraient identiques.

(s/conform :project-spec/project 
      {:project-spec/id 1, :project-spec/name "123"}) 
;;=> {:project-spec/id 1, :project-spec/name "123"} 
+0

Le but que je suis en train de réaliser est de réutiliser les spécifications à la fois en partie ClojureScript et Clojure d'une application et je veux avoir accès à l'exécution en, donc je peux faire respecter et de validation. Je veux aussi les réutiliser pour des fonctions qui fonctionneront avec ces données. Donc, les avoir comme une carte rend cela pratique, mais j'ai du mal à les enregistrer dans le registre mondial à partir de la carte. –

+0

De même, même si vous placez des clés 'req-un' qui ne sont pas espacées, vous devez toujours fournir des clés namespaced, de sorte que spec peut valider les valeurs correspondant aux clés. –

+3

@SergeyShvets vérifier [cet article] (http://blog.cognitect.com/blog/2017/3/24/3xeif9bxaom78qyzwssgwz1leuorh4#sharing-specs-across-the-front-and-back-end). Je pense qu'il décrit exactement ce que vous essayez de faire pour réutiliser les spécifications à travers CLJ/CLJS. En outre, je ne suis pas sûr que stocker les spécifications dans une carte a des avantages sur juste 's/def'ing dans un espace de noms. –