(require '[clojure.spec :as s])
consider the following data:
(def data {:names [["Anna" :lucky]
["Peter"]
["Jon" :lucky]
["Andre" :lucky]]})
It's a hash-map of with one key :names having a value of a vector of vectors. The inner vectors must contain a string as the first element and can optionally contain a the keyword :lucky as the second element.
The previous two sentences should be described with clojure.spec
- Let's start with the items in the vector:
(s/def ::item (s/cat :name string? :lucky (s/? #(= :lucky %))))
(s/conform ::item ["Tom"])
;; {:name "Tom"}
(s/conform ::item ["Tom" :lucky])
;; {:name "Tom", :lucky :lucky}
(s/conform ::item ["Tom" :sad])
;; :clojure.spec/invalid
This works. However, if there is only one option. Wouldn't a parsed result look be better like this:
`{:name "Tom", :lucky true}` or `{:name "Tom", :lucky false}`
Can this be done in clojure.spec?
With this, one can carry on:
(s/def ::items (s/coll-of ::item '()))
(s/conform ::items [["Tom" :lucky] ["Tim"]])
[["Tom" :lucky] ["Tim"]]
However, it looks like it passes the test but why the items are not parsed anymore?
Edit: This could be solved by switching from alpha7 to alpha10 release, where coll-of only takes one argument
Finally my spec looks like, having the previously described caveats:
(s/def ::my-spec (s/keys req-un [::items]))
spec is not designed to provide arbitrary data transformation (Clojure's core library can be used for that).
It is possible to achieve this using s/conformer
but it is not recommended to use that feature for arbitrary transformations like this (it is better suited to things like building custom specs).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With