theleoborges / bouncer

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

Return a seq/list of validation errors from a validation function #38

Closed talios closed 8 years ago

talios commented 9 years ago

Is it possible to have a validator function that returns a list of its errors/error messages at all?

Currently, I'm calling (b/validate) with a fairly deep data structure applying several validation functions against it - functions such as "model doesn't contain duplicate entries", it looks as tho the validation function can only return a boolean "yes I passed, no I failed", and the metadata can provide a default message.

Is there a way to return a custom message from the validation execution, such as "no, I failed with these specific instances..." ( either as a single string, or a list of strings )?

Cheers

theleoborges commented 9 years ago

Could you post a simple example? I will have a look at it later today.

Thanks On 9 Sep 2015 7:59 am, "Mark Derricutt" notifications@github.com wrote:

Is it possible to have a validator function that returns a list of its errors/error messages at all?

Currently, I'm calling (b/validate) with a fairly deep data structure applying several validation functions against it - functions such as "model doesn't contain duplicate entries", it looks as tho the validation function can only return a boolean "yes I passed, no I failed", and the metadata can provide a default message.

Is there a way to return a custom message from the validation execution, such as "no, I failed with these specific instances..." ( either as a single string, or a list of strings )?

Cheers

— Reply to this email directly or view it on GitHub https://github.com/leonardoborges/bouncer/issues/38.

talios commented 9 years ago

Re-reading the docs, I see theres [v/every] which would work for some instances I think.

One example, given a model that happenes to contain:

{:domains [{:domain "example.com"} {:domain "example.com"}]}

And a validation function:

(v/defvalidator duplicate-customer-domains?
  {:default-message-format "Customer %s has duplicate domains used."}
  [value]
  (let [domains (map :domain (:domains value))]
    (if (empty? domains)
      true
      (contains-unique? domains))))

This is checking that, if the customer in our model ( the match key being some form of identifier for a customer ), if they happen to have :domain entries, check that they don't contain duplicates.

Ideally, I'd like to be able to return something like:

{:result false :message "Customer %s has duplicate domains used: [example.com]"}

or:

{:result false :messages [
  "Customer %s has duplicate domain used: example.com"
  "Customer %s has duplicate domain used: foo.example.com"]}
theleoborges commented 9 years ago

Thanks for the example. That gave me a clear picture of what you want.

Currently there is no way of doing this elegantly. You would have to identify the duplicates post validation and then present that to the user.

One possible way to address this is turning the default-message-format into a function that returns the error message instead. It would look like this:

(v/defvalidator duplicate-customer-domains?
    {:default-message-fn (fn [key domains]
                           (format "Customer %s has duplicate domains used: %s" key (get-duplicates domains)))}
    [domains]
    (if (empty? domains)
      true
      (contains-unique? domains)))

Personally I'm not happy with this approach as it would force you to re-identify the duplicates.

However I thought I'd post this here to start the discussion. Interested in feedback about how people might want to use it.

theleoborges commented 8 years ago

Haven't heard anything on this issue for a while so I'm closing it. Feel free to re-open and restart the discussion.