Validating data with Valip

Validating data happens so often that it's good to have an EDSL to express the validation rules that our data has to pass. This makes the rules easier to create, understand, and maintain.

Valip (https://github.com/weavejester/valip) provides this. It's aimed at validating input from web forms, so it expects to validate maps with string values. We'll need to work around this expectation a time or two, but it isn't difficult.

Getting ready

We need to make sure that the Valip library is in our Leiningen project.clj file:

(defproject cleaning-data "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [org.clojure/data.xml "0.0.8"]
                 [valip "0.2.0"]])

Also, we need to load it into our script or REPL:

(use 'valip.core
     'valip.predicates)

How to do it…

To validate some data, we have to define predicates to test the data fields against, then define the fields and predicates to validate, plus validate error messages.

  1. First, we need data to validate:
    (def user
      {:given-name "Fox"
       :surname "Mulder"
       :age 51
       :badge "JTT047101111"})
  2. We also need to define a predicate to determine whether a number is present or not. The present? predicate defined by Valip fails if its input isn't a string:
    (defn number-present? [x]
      (and (present? (str x))
           (or (instance? Integer x)
               (instance? Long x))))
  3. We'd also like to validate the badge numbers. Taking this one as a template, let's say they begin with three uppercase letters followed by one or more digits. We can express this using this predicate:
    (defn valid-badge [n]
       (not (nil? (re-find #"[A-Z]{3}d+" n))))
  4. Now, we can start defining some validation rules. Rules are vector triples, each listing a field, a predicate, and an error message:
    (defn validate-user [user]
      (validate user
        [:given-name present? "Given name required."]
        [:surname present? "Surname required."]
        [:age number-present? "Age required."]
        [:age (over 0) "Age should be positive."]
        [:age (under 150) "Age should be under 150."]
        [:badge present?
         "The badge number is required."]
        [:badge valid-badge
         "The badge number is invalid."]))

Now, we can easily validate data against this set of rules:

user=> (validate-user (assoc user :age -42))
{:age ["Age should be positive."]}
user=> (validate-user (assoc user :age -42 :surname nil))
{:age ["Age should be positive."],
 :surname ["Surname required."]}

How it works…

Valip provides an easy-to-use DSL to define validation rules. It then breaks the incoming map structures and validates each field against the rules given. Finally, it returns error messages for any problem data. This system is simple to integrate into a data processing workflow.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.31.165