A Clojure validation library with emphasis on human readable error messages that goes beyond "just a type". As opposed to the validation provided by libraries like Schema and core.typed it validates an entire map as a whole. This allows for differentiating validation of one valued based on the value of another. Inspiration for this library came from jkk/verily but this lib encourage nested data structures.
The intended use case is to validate a data structure behind a form in ie. when using Om or something like it.
Examples of not "just a type" validation:
The error (message) is central in proven
validation, and a validation error
can reference one or multiple keys (paths) in the map that caused the validation
error. This also means that a single key in a map can have multiple different
errors attached to it. The examples below will show this.
Proven returns human readable errors that can be shipped back to end users!
Add the following dependency to your project.clj
file:
[proven "0.1.1"]
The validation function takes a collection of rules and the hash-map to be validated. A rule is a function that takes a hash-map and returns an empty list when data within it is valid and list of validation errors when not.
The namespace proven.rule
contains helper functions to build rules for most
common cases. But see "Built in validations" further down for a complete list.
A very basic example to get started:
(ns example.core
(:require [proven.rule :as rule]
[proven.validator :refer [validate]]))
(def rules
[(rule/required :last-name)
(rule/not-blank :first-name)])
(validate rules {:last-name "Smith"})
;; Return an empty list which means there is no validation errors
()
(validate rules {:first-name ""})
;; Return a list with Err records describing all validation errors
(#proven.core.Err{:paths #{[:last-name]},
:msg "must not be blank or missing"}
#proven.core.Err{:paths #{[:first-name]},
:msg "must not be blank"})
The list of errors is the biggest difference from jkk/verily because
instead of referencing keys the Err
record references a path (in a nested
structure).
Now for some conditional rules:
(ns example.core
(:require [proven.rule :as rule]
[proven.validator :refer [validate upon]]))
(def private-rules
[(rule/required :last-name)
(rule/not-blank :first-name)])
(def company-rules
[(rule/required [:company-name])])
(def rules
[(upon #(= (:type %) "private")
private-rules
company-rules)])
(validate rules {:type "private" :last-name "Smith"})
;; No validation errors
()
(validate rules {:type "company" :last-name "Smith"})
;; When type is "company" (or at least not "private")
;; Then `:company-name` is a reuqired key
(#proven.core.Err{:paths #{[:company-name]},
:msg "must not be blank or missing"})
(ns example.core
(:require [proven.rule :as rule]
[proven.validator :refer [validate in-coll]]))
(def person-rules
[(rule/required :last-name)])
(def rules
[(rule/min-length 1 :persons)
(in-coll [:persons] person-rules)])
(def data
{:persons [{:last-name "Smith"}
{:first-name "John"}
{:last-name "Doe"}]})
(validate rules data)
;; The path can now be used to reference the exact value The validation path now points exactly
(#proven.core.Err{:paths #{[:persons 1 :last-name]},
:msg "must not be blank or missing"})
(def corrected-data
(assoc-in data [:persons 1 :last-name] "Smith"))
(validate rules corrected-data)
;; No validation errors
()
When representing a nested data structure in a graphical user interface ie. a web form, the path can be used to highlight the form and/or specific form fields which has led to the validation error.
The built-in rules aren't rules pr. say but rather rule builders (functions that
return the actual validation function). This fact might become more clear when
looking at "Custom rules" below. For now just know that all proven.rule
buildes
takes the key (or keys) and an optional error message as the last arguments.
required
- key(s) must be present in the map and be not blank (blank being nil or empty string)not-blank
- key(s) if is present they must be not blankcontains
- key(s) must be present but can have whatever value (even nil)exact value
- key(s) if present must contain excatly the specified valuematches re-pattern
- key(s) if present must match the regular expression or be blankmin-length length
- key(s) if present must have a value with the specified minimum length (can be both strings and collections - everything that count
works on)max-length length
- kinda like min-length
except it isn't :-Pexact-length length
- kinda like min-length
except it isn't :-Pbetween-length [lower upper]
- a convinience to avoid having to make both a min-length
and max-length
in some situationsSome examples usages of rule building:
(ns example.core
(:require [proven.rule :as rule]))
(def rules
[(rule/required :last-name) ; apply rule to single key
(rule/not-blank [:first-name :middle-name]) ; apply rule to multiple keys
(rule/min-length 6 :password) ; rule builder with an argument other than key(s)
(rule/max-length 160 :tweet "Tweets cannot exceed 160 characters") ; rule builder with an optional error message
(rule/exact "secretcode" [:code :repeat-code] "must match \"secretcode\"")
(matches #"[0-9]{5}" :zip "must contain excatly 5 digits")
])
Copyright © 2016
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close