Recherche…


Syntaxe

  • :: est un raccourci un mot-clé qualifié par un espace de noms. Par exemple, si nous sommes dans l'espace de noms user: :: foo est un raccourci pour: user / foo
  • #: ou # - syntaxe littérale de la carte pour qualifier les clés d'une carte par un espace de noms

Remarques

Clojure spec est une nouvelle bibliothèque de spécifications / contrats pour clojure disponible à partir de la version 1.9.

Les spécifications sont exploitées de diverses manières, notamment en étant incluses dans la documentation, la validation des données, la génération de données pour les tests et bien plus encore.

Utiliser un prédicat comme spécification

Toute fonction de prédicat peut être utilisée en tant que spécification. Voici un exemple simple:

(clojure.spec/valid? odd? 1)
;;=> true

(clojure.spec/valid? odd? 2)
;;=> false

le valid? function prendra une spec et une valeur et retournera true si la valeur est conforme à la spécification et false sinon.

Un autre prédicat intéressant est défini pour l'appartenance:

(s/valid? #{:red :green :blue} :red) 
;;=> true

fdef: écrire une spécification pour une fonction

Disons que nous avons la fonction suivante:

(defn nat-num-count [nums] (count (remove neg? nums)))

Nous pouvons écrire une spécification pour cette fonction en définissant une spécification de fonction du même nom:

(clojure.spec/fdef nat-num-count
        :args (s/cat :nums (s/coll-of number?))
        :ret integer?
        :fn #(<= (:ret %) (-> % :args :nums count)))

:args prend une spécification de regex qui décrit la séquence des arguments par une étiquette de mot-clé correspondant au nom de l'argument et à une spécification correspondante. La raison pour laquelle la spécification requise par :args est une spécification de regex est de prendre en charge plusieurs arités pour une fonction. :ret spécifie une spécification pour la valeur de retour de la fonction.

:fn est une spécification qui contraint la relation entre les :args et :ret . Il est utilisé comme propriété lorsqu'il est exécuté via test.check. Il est appelé avec un seul argument: une carte avec deux clés:: :args (les arguments conformes à la fonction) et :ret (la valeur de retour de la fonction).

Enregistrement d'une spec

En plus des prédicats fonctionnant en tant que spécifications, vous pouvez enregistrer une spécification globalement en utilisant clojure.spec/def . def exige qu'une spécification enregistrée soit nommée par un mot clé qualifié par un espace de nommage:

(clojure.spec/def ::odd-nums odd?)
;;=> :user/odd-nums

(clojure.spec/valid? ::odd-nums 1)
;;=> true
(clojure.spec/valid? ::odd-nums 2)
;;=> false

Une fois enregistrée, une spécification peut être référencée globalement n'importe où dans un programme Clojure.

La syntaxe ::odd-nums est un raccourci pour :user/odd-nums , en supposant que nous sommes dans l'espace de noms de l' user . :: qualifiera le symbole qu'il précède avec le nom actuel.

Plutôt que de passer le prédicat, nous pouvons passer le nom de la spécification à valid? , et cela fonctionnera de la même manière.

clojure.spec / et & clojure.spec / ou

clojure.spec/and & clojure.spec/or peuvent être utilisés pour créer des spécifications plus complexes, en utilisant plusieurs spécifications ou prédicats:

(clojure.spec/def ::pos-odd (clojure.spec/and odd? pos?))

(clojure.spec/valid? ::pos-odd 1)
;;=> true

(clojure.spec/valid? ::pos-odd -3)
;;=> false

or fonctionne de manière similaire, avec une différence significative. Lors de la définition d'une or une spécification, vous devez marquer chaque branche possible avec un mot-clé. Ceci est utilisé pour fournir des branches spécifiques qui échouent dans les messages d'erreur:

(clojure.spec/def ::big-or-small (clojure.spec/or :small #(< % 10) :big #(> % 100)))

(clojure.spec/valid? ::big-or-small 1)
;;=> true

(clojure.spec/valid? ::big-or-small 150)
;;=> true

(clojure.spec/valid? ::big-or-small 20)
;;=> false

Lors de la conformité d'une spécification à l'aide de or , la spécification applicable sera renvoyée, ce qui a rendu la valeur conforme:

(clojure.spec/conform ::big-or-small 5)
;; => [:small 5]

Fiche technique

Vous pouvez spécifier un enregistrement comme suit:

(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)

(defrecord Person [name age occupation])

(clojure.spec/def ::person (clojure.spec/keys :req-un [::name ::age ::occupation]))

(clojure.spec/valid? ::person (->Person "john doe" 25 "programmer"))
;;=> true

(clojure.spec/valid? ::person (->Person "john doe" "25" "programmer"))
;;=> false

À un moment donné, une syntaxe de lecteur ou une prise en charge intégrée pour la qualification des clés d'enregistrement par l'espace de nom des enregistrements peut être introduite. Ce support existe déjà pour les cartes.

Spécifications de la carte

Vous pouvez spécifier une carte en spécifiant les clés qui doivent être présentes sur la carte:

(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)

(clojure.spec/def ::person (clojure.spec/keys :req [::name ::age ::occupation]))

(clojure.spec/valid? ::person {::name "john" ::age 25 ::occupation "programmer"})
;; => true

:req est un vecteur de clés devant être présent dans la carte. Vous pouvez spécifier des options supplémentaires telles que :opt , un vecteur de clés facultatif.

Les exemples jusqu'à présent exigent que les clés du nom soient qualifiées par des espaces de noms. Mais il est courant que les clés de carte ne soient pas qualifiées. Pour ce cas, clojure.spec fournit: req et: opt équivalents pour les clés non qualifiées :req-un et :opt-un . Voici le même exemple, avec des clés non qualifiées:

(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)

(clojure.spec/def ::person (clojure.spec/keys :req-un [::name ::age ::occupation]))

(clojure.spec/valid? ::person {:name "john" :age 25 :occupation "programmer"})
;; => true

Notez que les spécifications fournies dans le vecteur :req-un sont toujours qualifiées. clojure.spec, confirmera automatiquement les versions non qualifiées dans la carte lors de la conformité des valeurs.

La syntaxe littérale de la carte d'espace de noms vous permet de qualifier toutes les clés d'une carte par un seul espace de noms succinct. Par exemple:

(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)

(clojure.spec/def ::person (clojure.spec/keys :req [::name ::age ::occupation]))

(clojure.spec/valid? ::person #:user{:name "john" :age 25 :occupation "programmer"})
;;=> true

Notez la syntaxe spéciale #: reader. Nous suivons cela avec l'espace de noms que nous souhaitons qualifier par toutes les clés de la carte. Celles-ci seront ensuite vérifiées par rapport aux spécifications correspondant à l'espace de noms fourni.

Collections

Vous pouvez spécifier des collections de différentes manières. coll-of vous permet de spécifier des collections et de fournir des contraintes supplémentaires. Voici un exemple simple:

(clojure.spec/valid? (clojure.spec/coll-of int?) [1 2 3])
;; => true

(clojure.spec/valid? (clojure.spec/coll-of int?) '(1 2 3))
;; => true

Les options de contrainte suivent les spécifications / prédicats principaux de la collection. Vous pouvez contraindre le type de collection avec :kind comme ceci:

(clojure.spec/valid? (clojure.spec/coll-of int? :kind vector?) [1 2 3])
;; => true

(clojure.spec/valid? (clojure.spec/coll-of int? :kind vector?) '(1 2 3))
;; => false

Ce qui précède est faux car la collection transmise n'est pas un vecteur.

(clojure.spec/valid? (clojure.spec/coll-of int? :kind list?) '(1 2 3))
;; => true

(clojure.spec/valid? (clojure.spec/coll-of int? :kind set?) #{1 2 3})
;; => true

(clojure.spec/valid? (clojure.spec/coll-of int? :kind set?) #{1 "2" 3})
;; => false

Ce qui précède est faux car tous les éléments de l'ensemble ne sont pas ints.

Vous pouvez également limiter la taille de la collection de plusieurs manières:

(clojure.spec/valid? (clojure.spec/coll-of int? :kind vector? :count 3) [1 2 3])
;; => true

    (clojure.spec/valid? (clojure.spec/coll-of int? :kind vector? :count 3) [1 2])
;; => false

(clojure.spec/valid? (clojure.spec/coll-of int? :min-count 3 :max-count 5) [1 2 3])
;; => true

    (clojure.spec/valid? (clojure.spec/coll-of int? :min-count 3 :max-count 5) [1 2])
;; => false

Vous pouvez également imposer l'unicité des éléments de la collection avec :distinct :

(clojure.spec/valid? (clojure.spec/coll-of int? :distinct true) [1 2])
;; => true

(clojure.spec/valid? (clojure.spec/coll-of int? :distinct true) [2 2])
;; => false

coll-of s'assure que tous les éléments d'une séquence sont vérifiés. Pour les grandes collections, cela peut être très inefficace. every se comporte exactement comme coll-of , sauf qu'il échantillonne seulement un nombre relativement petit d'éléments de la séquence pour la conformité. Cela fonctionne bien pour les grandes collections. Voici un exemple:

(clojure.spec/valid? (clojure.spec/every int? :distinct true) [1 2 3 4 5])
;; => true

map-of est similaire à coll-of , mais pour les cartes. Comme les cartes ont à la fois des clés et des valeurs, vous devez fournir une spécification pour la clé et une spécification pour la valeur:

(clojure.spec/valid? (clojure.spec/map-of keyword? string?) {:red "red" :green "green"})
;; => true

Comme coll-of , la conformité de toutes les clés / valeurs coll-of map-of contrôles. Pour les grandes cartes, cela sera inefficace. A l'instar de coll-of , une map-of fournitures à every-kv pour échantillonner efficacement un nombre relativement petit de valeurs à partir d'une grande carte:

(clojure.spec/valid? (clojure.spec/every-kv keyword? string?) {:red "red" :green "green"})
;; => true

Séquences

spec peut décrire et être utilisé avec des séquences arbitraires. Il prend en charge cela via un certain nombre d'opérations de spécification regex.

(clojure.spec/valid? (clojure.spec/cat :text string? :int int?) ["test" 1])
;;=> true

cat nécessite des étiquettes pour chaque spécification utilisée pour décrire la séquence. cat décrit une séquence d'éléments et une spécification pour chacun.

alt est utilisé pour choisir parmi un certain nombre de spécifications possibles pour un élément donné dans une séquence. Par exemple:

(clojure.spec/valid? (clojure.spec/cat :text-or-int (clojure.spec/alt :text string? :int int?)) ["test"])
;;=> true

alt exige également que chaque spécification soit étiquetée par un mot-clé.

Les séquences de regex peuvent être composées de manières très intéressantes et puissantes pour créer des spécifications de description de séquence arbitrairement complexes. Voici un exemple légèrement plus complexe:

(clojure.spec/def ::complex-seq (clojure.spec/+ (clojure.spec/cat :num int? :foo-map (clojure.spec/map-of keyword? int?))))
(clojure.spec/valid? ::complex-seq [0 {:foo 3 :baz 1} 4 {:foo 4}])
;;=> true

Ici ::complex-seq validera une séquence d'une ou plusieurs paires d'éléments, la première étant un int et la seconde étant une carte de mot-clé à int.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow