Map validation library for Clojure and ClojureScript
Why
Most other validation libraries (such as valip) assume validation is done per key, rather than on a map as a whole. This makes it difficult to write multi-key validations. Validation functions in Verily take an entire map, making multi-key (or single-key) validations easy.
Verily also provides a declarative, data-oriented API in addition to a conventional, function-oriented API. Using data to describe validations has benefits in certain use cases. For instance, you can share validation specifications between Clojure and ClojureScript.
Installation
Leiningen:
[jkkramer/verily "0.6.0"]Usage
(ns example.core (:require [jkkramer.verily :as v]))
There are two ways to validate a map:
- With a validation specification - i.e., data, which Verily turns into a validation function for you
- With a validation function
To validate using a validation specification:
(def validations [[:required [:foo :bar :password]] [:equal [:password :confirm-password] "Passwords don't match, dummy"] [:min-length 8 :password]]) (v/validate {:foo "foo" :password "foobarbaz" :password-confirm "foobarba"} validations) ;; Returns: ({:keys (:bar), :msg "must not be blank"} {:keys [:password :confirm-password], :msg "Passwords don't match, dummy"})
To turn a validation specification into a function yourself:
(def validator (v/validations->fn validations))
You can use validation functions instead of data if you prefer. This makes it easier to write your own:
;; Custom validator (defn validate-password [m] (when (#{"12345" "password" "hunter2"} (:password m)) {:keys [:password] :msg "You can't use that password"})) ;; Combine several built-in validators and our own custom one (def validator (v/combine (v/required [:foo :bar :password]) (v/equal [:password :password-confirm]) (v/min-length 8 :password) validate-password)) (validator {:foo "foo" :password "foobarbaz" :password-confirm "foobarba"}) ;; Returns: ({:keys (:bar), :msg "must not be blank"} {:keys [:password :password-confirm], :msg "must be equal"})
Validation Function Contract
The contract for a validation function is:
- Take a map as an argument
- If validation succeeded, return nil or an empty collection. If there was a problem, return a problem map or collection of problem maps. Each problem is a map with the following keys:
:keys(optional) - relevant map keys:msg- description of the problem
Built-in Validations
All validation specifications accept a key or sequence of keys. The message is always optional.
Keys can be dotted keywords such as :foo.bar, which will be interpreted as a nested path (if the :foo.bar key does not exist in the top-level map). Strings such as "foo[bar]" are also allowed, and have the same semantics.
Unless :required is used, all validations allow the keys to be absent from the map, or have a nil value (or blank if a string-based type).
:required <keys> [msg]- must not be absent, blank, or nil:contains <keys> [msg]- must not be absent, but can be blank or nil:not-blank <keys> [msg]- may be absent but not blank or nil:exact <value> <keys> [msg]- must be a particular value:equal <keys> [msg]- all keys must be equal:email <keys> [msg]- must be a valid email:url <keys> [msg]- must be a valid URL:web-url <keys> [msg]- must be a valid website URL (http or https):link-url <keys> [msg]- must be a valid link URL (can be relative, http: or https: may be omitted):matches <regex> <keys> [msg]- must match a regular expression:min-length <len> <keys> [msg]- must be a certain length (for strings or collections):max-length <len> <keys> [msg]- must not exceed a certain length (for strings or collections):complete <keys> [msg]- must be a collection with no blank or nil values:min-val <min> <keys> [msg]- must be at least a certain value:max-val <max> <keys> [msg]- must be at most a certain value:within <min> <max> <keys> [msg]- must be within a certain range (inclusive):positive <keys> [msg]- must be a positive number:negative <keys> [msg]- must be a negative number:after <date> <keys> [msg]- must be after a certain date:before <date> <keys> [msg]- must be before a certain date:in <coll> <keys> [msg]- must be contained within a collection:every-in <coll> <keys> [msg]- each value must be within a collection (for values that are themselves collections):us-zip <keys> [msg]- must be a valid US zip code:luhn <keys> [msg]- must be pass the Luhn check (e.g., for credit card numbers)- Datatype validations:
:string,:boolean,:integer,:float,:decimal,:date(plus aliases) - Datatype collection validations:
:strings,:booleans,:integers,:floats,:decimals,:dates(plus aliases)
All validation specifications have corresponding validator functions in the jkkramer.verily namespace, if you prefer to use those directly.
License
Copyright © 2012-2013 Justin Kramer
Distributed under the Eclipse Public License, the same as Clojure.