DerekR
DerekR

Reputation: 3986

How to specify that two keys in a map should have the same value with Clojure.Spec?

Say for a minimal example, I've got a map with the following fields.

{:name
 :password
 :confirm-password}

and I've written the following specs for this shape.

(s/def ::name string?)
;; password is a string and between 8 - 255 characters
(s/def ::password (s/and string? #(<= 8 (count %) 255))
;; How to write (s/def ::confirm-password)

(s/def ::sign-up-form (s/keys :req-un [::name
                                       ::password
                                       ::confirm-password])

How would I go about writing a ::confirm-password spec to check whether the two values are equal? i.e. I need access to that other field (password) to get to it.

One thing I tried was to write the spec on the sign-up-form to get access to the keys to make sure they were the same and that kind of works but the problem with that is I lose the path specificity. Basically the spec/problem that get's generated points towards the sign-up form rather than the ::confirm-password which I would like ideally.

Upvotes: 4

Views: 1069

Answers (1)

Taylor Wood
Taylor Wood

Reputation: 16194

You can s/and another predicate with your s/keys spec to check equality between the two keys' values:

(s/def ::sign-up-form
  (s/and
    (s/keys :req-un [::name
                     ::password
                     ::confirm-password])
    #(= (:password %) (:confirm-password %))))

This anonymous function predicate receives the entire conformed map output of the s/keys spec.

(s/explain ::sign-up-form
  {:name "Taylor"
   :password "weak pass"
   :confirm-password "weak pass!"})
;; val: {:name "Taylor", :password "weak pass", :confirm-password "weak pass!"} 
;;    fails spec: :sandbox.so/sign-up-form predicate: 
;;    (= (:password %) (:confirm-password %))

Upvotes: 6

Related Questions