Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure Spec Dependent fields

Tags:

clojure

I have a record and for this spec and I want to generate values but ensure that the current amount does not exceed the max amount. A simplified spec would be something like:

(s/def ::max-amt (s/and number? #(<= 0 % 1e30)))
(s/def ::cur-amt (s/and number? #(<= 0 % 1e30)))
(s/def ::loan (s/cat :max-amt ::max-amt
                     :cur-amt ::cur-amt))

I know that I can have s/and in the ::loan spec but I want something like:

(s/def :loan (s/cat :max-amt ::max-amt
                    ;; not a real line:
                    :cur-amt (s/and ::cur-amt #(< (:cur-amt %) (:max-amt %)))))

Is this type of constraint available in spec?

Note: I know I could replace the cur-amt with a number between 0 and 1 that represents the fractional portion but then I'm modifying the data to fit the code not the other way arround. I don't control the data source in the actual application here.

like image 924
Phil Cooper Avatar asked Dec 06 '16 17:12

Phil Cooper


1 Answers

This should work (adapted from this discussion):

(s/def ::loan (s/and (s/keys :req [::cur-amt ::max-amt])
                     (fn [{:keys [::cur-amt ::max-amt]}]
                       (< cur-amt max-amt))))

(s/conform ::loan {:cur-amt 50 :max-amt 100}) ; => {:cur-amt 50 :max-amt 100}
(s/conform ::loan {:cur-amt 100 :max-amt 50}) ; => :clojure.spec/invalid

Or, if you want to stick to s/cat:

(s/def ::loan (s/and (s/cat :cur-amt ::cur-amt
                            :max-amt ::max-amt)
                     (fn [{:keys [cur-amt max-amt]}]
                       (< cur-amt max-amt))))

(s/conform ::loan [50 100]) ; => [50 100]
(s/conform ::loan [100 50]) ; => :clojure.spec/invalid
like image 119
Aleph Aleph Avatar answered Nov 15 '22 12:11

Aleph Aleph