clojure.spec cheatsheet
clojure.spec cheat sheet
Specs
Require
(ns my.ns
(:require [clojure.spec.alpha :as s]))
Register
(s/def ::even even?)
(s/valid? ::even 4)
Use a registered spec from another namespace
(require '[my.namespace :as mn])
(s/valid? ::mn/even 4)
Validate
(s/conform even? 4) ; returns the value or :clojure.spec.alpha/invalid
(s/valid? even? 4) ; returns true or false
Get validation errors
(s/explain-data even? 4) ; => nil
(s/explain-data even? 5)
; => #:clojure.spec.alpha{:problems [{:path [], :pred clojure.core/even?, :val 5, :via [], :in []}],
; :spec #function[clojure.core/even?],
; :value 5}
Compose
(s/def ::even-and-above-10 (s/and even? #(> % 10)))
(s/valid? ::even-and-above-10 12) ; => true
(s/valid? ::even-and-above-10 8) ; => false
Entity maps
(s/def ::name string?)
(s/def ::nickname string?)
(s/def ::age int?)
(s/def ::person (s/keys :req-un [::name ::age] :opt-un [::nickname]))
(s/explain-data ::person {:name "Pesho" :age 30 :nickname "10"}) ; => nil
(s/explain-data ::person {:name "Pesho" :age 30 :nickname 10})
; #:clojure.spec.alpha{:problems
; ({:path [:nickname],
; :pred clojure.core/string?,
; :val 10,
; :via [:user/person :user/nickname],
; :in [:nickname]}),
; :spec :user/person,
; :value {:name "Pesho", :age 30, :nickname 10}}
Generators
Require
(require '[clojure.spec.gen.alpha :as gen])
Generator
(s/gen pos-int?)
Compound generator
(s/gen (s/and pos-int? even?))
Generate a value
(gen/generate (s/gen pos-int?)) ; => 14
Custom generators
from spec
This works by clojure.spec
generating values from the base spec (in this case string?
) and then applying the subsequent predicates as filters. Since it only generates 100 initial values, this will probably not work for complex specs.
(def uuid-regex #"(?i)^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$")
(s/def ::uuid-string (s/and string? #(some? (re-matches uuid-regex %))))
(s/valid? ::uuid-string "c278820c-9734-4ac2-99f6-23dd959ee73c") ; => true
(gen/generate (s/gen ::uuid-string))
; =>
; Error:
; Execution error (ExceptionInfo) at clojure.test.check.generators/fn (generators.cljc:435).
; Couldn't satisfy such-that predicate after 100 tries.
with-gen
Takes a spec and a function, returning a generator, and returns a spec, which uses the generator to generate values.
(s/def ::my-even (s/with-gen
(s/and pos-int? even?)
#(s/gen #{2 100 980})))
(gen/generate (s/gen ::my-even)) ; => one of 2, 100 or 980
fmap
Takes a function and a generator and returns a generator, whose values are transformed by the function
(gen/generate (s/gen uuid?)) ; => #uuid "a06baf1e-3d77-49b4-8279-bceb5cd74ecd"
(gen/generate (gen/fmap str (s/gen uuid?))) ; => "a06baf1e-3d77-49b4-8279-bceb5cd74ecd"
Spec with custom generator
(def uuid-regex #"(?i)^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$")
(s/def ::uuid-string
(s/with-gen (s/and string? #(some? (re-matches uuid-regex %)))
#(gen/fmap str (s/gen ::uuid))))
(gen/generate (s/gen ::uuid-string)) ; => "a06baf1e-3d77-49b4-8279-bceb5cd74ecd"
Written by Alex Popov
Related protips
Have a fresh tip? Share with Coderwall community!
Post
Post a tip
Best
#Clojure
Authors
Sponsored by #native_company# — Learn More
#native_title#
#native_desc#