I have several Clojure predicates which validate the application input against different business rules. I'm wondering what is the best strategy to walk through this pipe of predicates considering that:
->some
would not work because it would get only the first nil
occurrence. And I need to push all violations to a collection of violations.I considered the following approach but I have doubts about it.
(defn validate [arg]
(cond
(logic/check-one? arg) (add-violation-one)
(logic/check-two? arg) (add-violation-two)))
One way to do this is with cond->
. It runs all the checks and it
pases the result of a branch down. E.g.
(let [arg 42
add-error #(update %1 :errors conj %2)]
(cond-> {:errors []}
(even? arg) (add-error "must not be even")
(odd? arg) (add-error "must not be odd")
(zero? arg) (add-error "must not be zero")
(pos? arg) (add-error "must not be positive")))
; → {:errors ["must not be even" "must not be positive"]}
This approach makes most sense, if you really want to write out those things by hand. It's maybe more flexible to not directly rely on something from core and write your own function. E.g. you could reduce over tuples of predicate and error handler.
(defn validate
[checks errors arg]
(reduce
(fn validation-step [errors [pred handle-error]]
(if-let [result (pred arg)]
(update errors :errors conj (handle-error result))
errors))
errors
checks))
(prn
(validate
[[even? (constantly "must not be even")]
[odd? (constantly "must not be odd")]
[zero? (constantly "must not be zero")]
[pos? (constantly "must not be positive")]]
{:errors []}
42))
; → {:errors ["must not be even" "must not be positive"]}