Reputation: 19938
I'm pretty sure I need a multi-spec, which works. But I am unsure how to say that a key value which is a vector can contain heterogeneous maps.
My is my source data I want to spec:
(def int-attr { :type "int" :validations [{ :min 0 } { :max 100 }] })
(def string-attr { :type "string" :validations [{ :presence true }] })
It is the validations
key I am having problems with, depending on the type
key, "int"
or "string"
, I want a different spec in the validations
key.
I'm pretty sure I have to use a multi-spec
. Here is what I have tried:
(defmulti attribute-type :type)
(defmethod attribute-type "string" [a] ::string-validations)
(defmethod attribute-type "int" [a] ::int-validations)
;; how to say this is a vector of int-min-validation, or int-max-validation etc.
;; (s/+ ...) and (s/or ...) maybe?
(s/def ::int-validations (...)
(s/def ::string-validations (...)
;; not sure how to incorporate these...
(s/def ::int-min-validation (s/keys :req-un [::min]))
(s/def ::int-max-validation (s/keys :req-un [::max]))
(s/def ::string-presence-validation (s/keys :req-un [::presence]))
(s/def ::attribute (s/multi-spec attribute-type ::type))
(s/explain ::attribute int-attr)
(s/explain ::attribute string-attr)
Upvotes: 1
Views: 670
Reputation: 1372
Use namespaced keywords to allow same key, but spec the value using two different specs, namely int/validations
and string/validations
. To allow a vector that contains maps, a good option is to use s/coll-of
.
(def int-attr { :type "int" :validations [{ :min 0 } { :max 100 }] })
(def string-attr { :type "string" :validations [{ :presence true }] })
(defmulti attribute-type :type)
(defmethod attribute-type "string" [_]
(s/keys :req-un [::type :string/validations]))
(defmethod attribute-type "int" [_]
(s/keys :req-un [::type :int/validations]))
(s/def :int/validations (s/coll-of :int/validation))
(s/def :string/validations (s/coll-of :string/presence))
(s/def :int/validation (s/keys :opt-un [:int/min :int/max]))
(s/def :int/min number?)
(s/def :int/max number?)
(s/def :string/presence (s/keys :req-un [::presence]))
(s/def ::attribute (s/multi-spec attribute-type ::type))
(s/explain ::attribute int-attr) ;; => Success!
(s/explain ::attribute string-attr) ;; => Success!
Upvotes: 5