diff --git a/src/validateur/validation.cljc b/src/validateur/validation.cljc index bf1f48f..9f32e01 100644 --- a/src/validateur/validation.cljc +++ b/src/validateur/validation.cljc @@ -461,7 +461,6 @@ [true {}] [false {attr #{message}}]))))) - (defn nest "Takes an attribute (either a single key or a vector of keys) and a validation set and prefixes the keys in the validation set's @@ -497,6 +496,39 @@ [(subvec k attrcount (count k)) messages]) (into {})))) +(defn validate-nested + "Returns a function that, when given a map, will validate that the + value of at key attr in that map passes validation using the given + validator (a function as returned by validation-set). + + Accepted options: + + :message (default: \"is invalid\"): an error message for invalid values + :message-fn (default:nil): function to retrieve message with signature (fn [map]) + + Example: + + (require '[validateur.validation :refer :all]) + + (def foo-validator (validation-set (presence-of :foo))) + + (validation-set (validate-nested :bar foo-validator))" + [attr validator & {:keys [message message-fn]}] + (let [get-fn (if (vector? attr) get-in get) + validate-fn (if (or message message-fn) + (let [msg-fn (or message-fn (constantly message))] + (fn [m] + (reduce-kv (fn [out k v] + (assoc out k #{(msg-fn m)})) + {} + (validator m)))) + validator)] + (fn [m] + (let [value (get-fn m attr) + result (validate-fn value)] + (if (seq result) + [false (nest attr result)] + [true {}]))))) (defn validate-with-predicate "Returns a function that, when given a map, will validate that the predicate returns diff --git a/test/validateur/test/validation_test.cljc b/test/validateur/test/validation_test.cljc index 5a0594d..3954595 100644 --- a/test/validateur/test/validation_test.cljc +++ b/test/validateur/test/validation_test.cljc @@ -786,6 +786,67 @@ (vr/inclusion-of :status :in #{:active :inactive})))] (is (= {} (v {:person {:name "Michał" :status :active}}))))) +;; +;; validate-nested +;; + +(deftest test-validate-nested-validation-fails + (let [person-v (vr/validation-set + (vr/presence-of :name) + (vr/format-of :name :format #"[A-Za-z]+")) + v (vr/validate-nested :person person-v)] + (is (= [false {[:person :name] #{"can't be blank"}}] + (v {}))) + (is (= [false {[:person :name] #{"can't be blank"}}] + (v {:person {}}))) + (is (= [false {[:person :name] #{"can't be blank"}}] + (v {:person {:name nil}}))) + (is (= [false {[:person :name] #{"can't be blank"}}] + (v {:person {:name ""}}))) + (is (= [false {[:person :name] #{"has incorrect format"}}] + (v {:person {:name "123"}}))))) + +(deftest test-validate-nested-validation-succeeds + (let [person-v (vr/validation-set + (vr/presence-of :name) + (vr/format-of :name :format #"[A-Za-z]+")) + v (vr/validate-nested :person person-v)] + (is (= [true {}] + (v {:person {:name "Michał"}}))))) + +(deftest test-validate-nested-validation-fails-with-custom-message + (let [person-v (vr/validation-set + (vr/presence-of :name) + (vr/format-of :name :format #"[A-Za-z]+")) + v (vr/validate-nested :person person-v :message "test")] + (is (= [false {[:person :name] #{"test"}}] + (v {}))) + (is (= [false {[:person :name] #{"test"}}] + (v {:person {}}))) + (is (= [false {[:person :name] #{"test"}}] + (v {:person {:name nil}}))) + (is (= [false {[:person :name] #{"test"}}] + (v {:person {:name ""}}))) + (is (= [false {[:person :name] #{"test"}}] + (v {:person {:name "123"}}))))) + +(deftest test-validate-nested-validation-fails-with-custom-message-fn + (let [person-v (vr/validation-set + (vr/presence-of :name) + (vr/format-of :name :format #"[A-Za-z]+")) + v (vr/validate-nested :person person-v + :message-fn (fn [m] + (str "test" (+ (count m) (count (:person m))))))] + (is (= [false {[:person :name] #{"test0"}}] + (v {}))) + (is (= [false {[:person :name] #{"test0"}}] + (v {:person {}}))) + (is (= [false {[:person :name] #{"test1"}}] + (v {:person {:name nil}}))) + (is (= [false {[:person :name] #{"test1"}}] + (v {:person {:name ""}}))) + (is (= [false {[:person :name] #{"test1"}}] + (v {:person {:name "123"}}))))) ;; ;; Error Reporting