Validateur is a functional validations library inspired by Ruby’s ActiveModel.
Validateur 2.3 is a minor feature release.
Changes Between 2.2.0 and 2.3.0
unnest
unnest is a helper function useful for building UIs that validate on
the fly. Here’s a basic example. Let’s write some code to render a UI
off of a nested map and build up live validation for that map off of
component validators. Here are the components:
Next are the “rendering” functions. Imagine that these are input
components responsible for validating their input and displaying
errors when present. Our “render” phase will just print.
12345678910111213
(defn render-profile[profileerrors](prn "Profile: "profile)(prn "Profile Errors: "errors))(defn render-secrets[secretserrors](prn "Secrets: "secrets)(prn "Secret Errors: "errors))(defn submit-button"Renders a submit button that can only submit when no errors are present."[errors](prn "All Errors: "errors))
The render-account function renders all subcomponents, performs
global validation and routes the errors and data where each needs to
go:
12345678910111213
(defn render-account"This function accepts an account object, validates the entire thing using the subvalidators defined above, then uses unnested to pull out specific errors for each component. The entire validation error map is passed into submit-button, which might only allow a server POST on click of the full error map is empty."[{:keys[secretsprofile]:asaccount}](let [errors(account-validatoraccount)](render-profileprofile(vr/unnest:profileerrors))(render-secretssecrets(vr/unnest:secretserrors))(submit-buttonerrors)))
Let’s see this function in action. Calling render-account with an
invalid map triggers a render that shows off a bunch of errors:
1234567891011
(render-account{:secrets{:password"face":phone"703555555512323"}:profile{:first-name"Queequeg"}})"Profile: "{:first-name"Queequeg"}"Errors: "{[:last-name]#{"can't be blank"}}"Secrets: "{:password"face", :phone"703555555512323"}"Errors: "{[:phone]#{"must be 10 characters long"}, [:password]#{"must be from 5 to 14 characters long"}}"All Errors: "{[:profile:last-name]#{"can't be blank"}, [:secrets:phone]#{"must be 10 characters long"}, [:secrets:password]#{"must be from 5 to 14 characters long"}}
Calling render-account with a valid map prints only the data:
nest is a helper function that makes it easy to validate dynamic
data that’s not part of the actual map you pass into the
validator. For example, say you wanted to validate all user accounts,
then build up a map of userid –> validation errors:
12345678
(for [account(get-all-accounts)](vr/nest(:idaccount)(account-validatoraccount))){[100:profile:first-name]"can't be blank"[200:profile:last-name]"can't be blank";; etc}