theleoborges / bouncer

A validation DSL for Clojure & Clojurescript applications
364 stars 38 forks source link

Cannot validate nested map before validating keys in the nested map #16

Closed casperc closed 10 years ago

casperc commented 11 years ago

The following validation fails:

(b/validate {:kundeid "612345678"} :kundeid v/required :info v/required [:info :klient-bruger] v/required) => ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Associative clojure.lang.RT.assoc (RT.java:702)

The following validation succeeds:

(b/validate {:kundeid "612345678"} :kundeid v/required [:info :klient-bruger] v/required :info v/required)

Seems like a bug or at least unexpected behaviour.

theleoborges commented 11 years ago

Yes this seems odd. I'll have a look at it. Thanks. On 02/10/2013 12:24 AM, "Casper Clausen" notifications@github.com wrote:

The following validation fails:

(b/validate {:kundeid "612345678"} :kundeid v/required :info v/required [:info :klient-bruger] v/required) => ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Associative clojure.lang.RT.assoc (RT.java:702)

The following validation succeeds:

(b/validate {:kundeid "612345678"} :kundeid v/required [:info :klient-bruger] v/required :info v/required)

Seems like a bug or at least unexpected behaviour.

— Reply to this email directly or view it on GitHubhttps://github.com/leonardoborges/bouncer/issues/16 .

theleoborges commented 10 years ago

Hi @casperc ,

Sorry it took me so long to get back to you. This issue somehow disappeared from my todo list.

Anyway, the error you see is due to how bouncer performs validations. By the time it tries to validate [:info :klient-bruger], the key :info in the resulting map is set to: '("info must be present"), therefore, trying to update the map with an extra error message fails.

Essentially, this is what's happening:

(update-in {:a '("an error message")} 
           [:a :b]
           (fn [a]
             (conj a 1)))
;; ClassCastException clojure.lang.PersistentList$EmptyList cannot be cast to clojure.lang.Associative  clojure.lang.RT.assoc (RT.java:702)

Personally I would remove the validation of [:info] altogether as it would seem you're interested in validating it's contents.

However if you're really set into validating the presence of the :info key first, I recommend you use pre-conditions like so:

(core/validate {:kundeid "612345678"} 
               :kundeid v/required
               :info v/required
               [:info :klient-bruger] [[ v/required :pre (comp not nil? :info)]])

;; [{:info ("info must be present")}
;; {:bouncer.core/errors {:info ("info must be present")},
;;  :kundeid "612345678"}]

This causes the validation for [:info :klient-bruger] to only run if :info is present, as shown below:

(core/validate {:kundeid "612345678" :info {}} 
               :kundeid v/required
               :info v/required
               [:info :klient-bruger] [[ v/required :pre (comp not nil? :info)]])

;; [{:info {:klient-bruger ("klient-bruger must be present")}}
;;  {:bouncer.core/errors
;;   {:info {:klient-bruger ("klient-bruger must be present")}},
;;   :kundeid "612345678",
;;   :info {}}]

As this is the current way of achieving the desired behaviour, I'm closing this issue.

Thanks.