Skip to content

Commit

Permalink
Move validateur to a separate repository
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelklishin committed Sep 11, 2011
0 parents commit c1c0d57
Show file tree
Hide file tree
Showing 5 changed files with 354 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
@@ -0,0 +1,7 @@
pom.xml
*jar
/lib/
/classes/
.lein-failures
.lein-deps-sum
TAGS
28 changes: 28 additions & 0 deletions README.md
@@ -0,0 +1,28 @@
# Validateur

Validateur is a validation library inspired by Ruby's ActiveModel. Validateur is functional: validators are
functions, validation sets are higher-order functions, validation results are returned as values.


## Usage

Since these are very early days of the library, it is fair to say that it is *completely unusable* to anyone
other than the author.


## This is a Work In Progress

Validateur is very much a work in progress and right now, there is nothing to
see here, really.


## Supported Clojure versions

Validateur is built from the ground up for Clojure 1.3 and up.


## License

Copyright (C) 2011 Michael S. Klishin

Distributed under the Eclipse Public License, the same as Clojure.
3 changes: 3 additions & 0 deletions project.clj
@@ -0,0 +1,3 @@
(defproject com.novemberain/validateur "0.1.0-SNAPSHOT"
:description "Functional validations inspired by Ruby's ActiveModel"
:dependencies [[org.clojure/clojure "1.3.0-beta3"]])
122 changes: 122 additions & 0 deletions src/validateur/validation.clj
@@ -0,0 +1,122 @@
(ns validateur.validation
(:use [clojure.set :as set]))

;;
;; API
;;

(declare as-vec)
(declare assoc-with)
(declare concat-with-separator)

(defn presence-of
[attribute]
(fn [m]
(let [errors (if (attribute m) {} { attribute #{"can't be blank"} })]
[(empty? errors) errors])))


(defn numericality-of
[attribute & { :keys [allow-nil only-integer gt gte lt lte equal-to odd even] :or { allow-nil false, only-integer false, odd false, event false }}]
(fn [m]
(let [v (attribute m)
errors (atom {})]
(if (and (nil? v) (not allow-nil))
(reset! errors (assoc @errors attribute #{"can't be blank"})))
(when (and v (not (number? v)))
(reset! errors (assoc-with set/union @errors attribute #{"should be a number"})))
(when (and v only-integer (not (integer? v)))
(reset! errors (assoc-with set/union @errors attribute #{"should be an integer"})))
(when (and v (number? v) odd (not (odd? v)))
(reset! errors (assoc-with set/union @errors attribute #{"should be odd"})))
(when (and v (number? v) even (not (even? v)))
(reset! errors (assoc-with set/union @errors attribute #{"should be even"})))
(when (and v (number? v) equal-to (not (= equal-to v)))
(reset! errors (assoc-with set/union @errors attribute #{(str "should be equal to " equal-to)})))
(when (and v (number? v) gt (not (> v gt)))
(reset! errors (assoc-with set/union @errors attribute #{(str "should be greater than " gt)})))
(when (and v (number? v) gte (not (>= v gte)))
(reset! errors (assoc-with set/union @errors attribute #{(str "should be greater than or equal to " gte)})))
(when (and v (number? v) lt (not (< v lt)))
(reset! errors (assoc-with set/union @errors attribute #{(str "should be less than " lt)})))
(when (and v (number? v) lte (not (<= v lte)))
(reset! errors (assoc-with set/union @errors attribute #{(str "should be less than or equal to " lte)})))
[(empty? @errors) @errors])))


(defn acceptance-of
[attribute & { :keys [allow-nil accept] :or { allow-nil false, accept #{true, "true", "1"} }}]
(fn [m]
(let [v (attribute m)]
(if (and (nil? v) (not allow-nil))
[false, { attribute #{"can't be blank"} }]
(if (accept v)
[true, {}]
[false, { attribute #{"must be accepted"} }])))))


(defn inclusion-of
[attribute & { :keys [allow-nil in] :or { allow-nil false }}]
(fn [m]
(let [v (attribute m)]
(if (and (nil? v) (not allow-nil))
[false, { attribute #{"can't be blank"} }]
(if (in v)
[true, {}]
[false, { attribute #{(str "must be one of: " (concat-with-separator in ", "))} }])))))


(defn exclusion-of
[attribute & { :keys [allow-nil in] :or { allow-nil false }}]
(fn [m]
(let [v (attribute m)]
(if (and (nil? v) (not allow-nil))
[false, { attribute #{"can't be blank"} }]
(if-not (in v)
[true, {}]
[false, { attribute #{(str "must not be one of: " (concat-with-separator in ", "))} }])))))



(defn format-of
[attribute & { :keys [allow-nil format] :or { allow-nil false }}]
(fn [m]
(let [v (attribute m)]
(if (and (nil? v) (not allow-nil))
[false, { attribute #{"can't be blank"} }]
(if (re-find format v)
[true, {}]
[false, { attribute #{"has incorrect format"} }])))))



(defmacro validation-set
[& clauses]
)



;;
;; Implementation
;;

(defn assoc-with
([f m k v]
(let [ov (k m)
nv (apply f [ov v])]
(assoc m k nv)))
([f m k v & kvs]
(let [ret (assoc-with f m k v)]
(if kvs
(recur f ret (first kvs) (second kvs) (nnext kvs))
ret))))

(defn as-vec
[arg]
(if (sequential? arg)
(vec arg)
(vec [arg])))

(defn- concat-with-separator
[v, s]
(apply str (interpose s v)))
194 changes: 194 additions & 0 deletions test/validateur/test/validation.clj
@@ -0,0 +1,194 @@
(ns validateur.test.validation
(:use [clojure.test] [validateur.validation :as validation]))

;;
;; validation-set
;;


;; (deftest presence-validation
;; (def v (validation/validation-set
;; (presence-of :name)
;; (presence-of :age)))
;; (is (validation/valid? v { :name "Joe", :age 28 }))
;; (is-not (validation/valid? v { :name "Joe" }))
;; (is-not (validation/valid? v { :age 30 })))



;;
;; presence-of
;;

(deftest test-presence-validator-with-one-attribute
(def v (validation/presence-of :name))
(is (fn? v))
(is (= [true, {}] (apply v [{ :name "Michael" }])))
(is (= [false, { :name #{"can't be blank"} }] (apply v [{ :age 28 }]))))



;;
;; numericality-of
;;


(deftest test-numerical-integer-only-validator-with-one-attribute
(def v (validation/numericality-of :age :only-integer true))
(is (fn? v))
(is (= [true, {}] (apply v [{ :age 26 }])))
(is (= [false, { :age #{"should be a number" "should be an integer"} }] (apply v [{ :age "Twenty six" }])))
(is (= [false, { :age #{"should be an integer"} }] (apply v [{ :age 26.6 }]))))


(deftest test-numerical-validator-with-one-attribute-that-is-gt-than-a-value
(def v (validation/numericality-of :age :gt 40))
(is (fn? v))
(is (= [true, {}] (apply v [{ :age 46 }])))
(is (= [false, { :age #{"can't be blank"} }] (apply v [{ :age nil }])))
(is (= [false, { :age #{"should be a number"} }] (apply v [{ :age "Twenty six" }])))
(is (= [false, { :age #{"should be greater than 40"} }] (apply v [{ :age 26.6 }]))))


(deftest test-numerical-validator-with-one-attribute-that-is-lt-than-a-value
(def v (validation/numericality-of :age :lt 40))
(is (fn? v))
(is (= [true, {}] (apply v [{ :age 36 }])))
(is (= [false, { :age #{"can't be blank"} }] (apply v [{ :age nil }])))
(is (= [false, { :age #{"should be a number"} }] (apply v [{ :age "Twenty six" }])))
(is (= [false, { :age #{"should be less than 40"} }] (apply v [{ :age 46.6 }]))))


(deftest test-numerical-validator-with-one-attribute-that-is-gte-than-a-value
(def v (validation/numericality-of :age :gte 40))
(is (fn? v))
(is (= [true, {}] (apply v [{ :age 46 }])))
(is (= [true, {}] (apply v [{ :age 40 }])))
(is (= [false, { :age #{"can't be blank"} }] (apply v [{ :age nil }])))
(is (= [false, { :age #{"should be a number"} }] (apply v [{ :age "Twenty six" }])))
(is (= [false, { :age #{"should be greater than or equal to 40"} }] (apply v [{ :age 26.6 }]))))


(deftest test-numerical-validator-with-one-attribute-that-is-lte-than-a-value
(def v (validation/numericality-of :age :lte 40))
(is (fn? v))
(is (= [true, {}] (apply v [{ :age 36 }])))
(is (= [true, {}] (apply v [{ :age 40 }])))
(is (= [false, { :age #{"can't be blank"} }] (apply v [{ :age nil }])))
(is (= [false, { :age #{"should be a number"} }] (apply v [{ :age "Twenty six" }])))
(is (= [false, { :age #{"should be less than or equal to 40"} }] (apply v [{ :age 46.6 }]))))



(deftest test-numerical-validator-with-one-attribute-that-is-equal-to-a-value
(def v (validation/numericality-of :age :equal-to 40))
(is (fn? v))
(is (= [true, {}] (apply v [{ :age 40 }])))
(is (= [false, { :age #{"can't be blank"} }] (apply v [{ :age nil }])))
(is (= [false, { :age #{"should be a number"} }] (apply v [{ :age "Twenty six" }])))
(is (= [false, { :age #{"should be equal to 40"} }] (apply v [{ :age 46.6 }])))
(is (= [false, { :age #{"should be equal to 40"} }] (apply v [{ :age 100 }]))))



(deftest test-numerical-validator-with-one-attribute-that-is-odd
(def v (validation/numericality-of :age :odd true))
(is (fn? v))
(is (= [true, {}] (apply v [{ :age 41 }])))
(is (= [false, { :age #{"can't be blank"} }] (apply v [{ :age nil }])))
(is (= [false, { :age #{"should be a number"} }] (apply v [{ :age "Twenty six" }])))
(is (= [false, { :age #{"should be odd"} }] (apply v [{ :age 20 }]))))


(deftest test-numerical-validator-with-one-attribute-that-is-even
(def v (validation/numericality-of :age :even true))
(is (fn? v))
(is (= [true, {}] (apply v [{ :age 40 }])))
(is (= [false, { :age #{"can't be blank"} }] (apply v [{ :age nil }])))
(is (= [false, { :age #{"should be a number"} }] (apply v [{ :age "Twenty six" }])))
(is (= [false, { :age #{"should be even"} }] (apply v [{ :age 21 }]))))



;;
;; acceptance-of
;;

(deftest test-acceptance-validator-with-one-attribute
(def v (validation/acceptance-of :terms-and-conditions))
(is (fn? v))
(is (= [true, {}] (apply v [{ :terms-and-conditions true }])))
(is (= [false, { :terms-and-conditions #{"must be accepted"} }] (apply v [{ :terms-and-conditions "I do not approve it" }]))))


(deftest test-acceptance-validator-with-one-attribute-and-custom-accepted-values-list
(def v (validation/acceptance-of :terms-and-conditions :accept #{"yes", "hell yes"}))
(is (fn? v))
(is (= [true, {}] (apply v [{ :terms-and-conditions "yes" }])))
(is (= [true, {}] (apply v [{ :terms-and-conditions "hell yes" }])))
(is (= [false, { :terms-and-conditions #{"must be accepted"} }] (apply v [{ :terms-and-conditions true }])))
(is (= [false, { :terms-and-conditions #{"must be accepted"} }] (apply v [{ :terms-and-conditions "I do not approve it" }])))
(is (= [false, { :terms-and-conditions #{"must be accepted"} }] (apply v [{ :terms-and-conditions "1" }])))
(is (= [false, { :terms-and-conditions #{"must be accepted"} }] (apply v [{ :terms-and-conditions "jeez no" }]))))


;;
;; inclusion-of
;;

(deftest test-inclusion-validator
(def v (validation/inclusion-of :genre :in #{"trance", "dnb"}))
(is (fn? v))
(is (= [false, { :genre #{"can't be blank"} }] (apply v [{ :genre nil }])))
(is (= [true, {}] (apply v [{ :genre "trance" }])))
(is (= [true, {}] (apply v [{ :genre "dnb" }])))
(is (= [false, { :genre #{"must be one of: trance, dnb"} }] (apply v [{ :genre true }])))
(is (= [false, { :genre #{"must be one of: trance, dnb"} }] (apply v [{ :genre "I do not approve it" }])))
(is (= [false, { :genre #{"must be one of: trance, dnb"} }] (apply v [{ :genre "1" }]))))



;;
;; exclusion-of
;;

(deftest test-exclusion-validator
(def v (validation/exclusion-of :genre :in #{"trance", "dnb"}))
(is (fn? v))
(is (= [false, { :genre #{"can't be blank"} }] (apply v [{ :genre nil }])))
(is (= [true, {}] (apply v [{ :genre "rock" }])))
(is (= [true, {}] (apply v [{ :genre "power metal" }])))
(is (= [false, { :genre #{"must not be one of: trance, dnb"} }] (apply v [{ :genre "trance" }])))
(is (= [false, { :genre #{"must not be one of: trance, dnb"} }] (apply v [{ :genre "dnb" }]))))


;;
;; format-of
;;

(deftest test-format-of-validator
(def v (validation/format-of :id :format #"abc-\d\d\d"))
(is (fn? v))
(is (= [false, { :id #{"can't be blank"} }] (apply v [{ :id nil }])))
(is (= [true, {}] (apply v [{ :id "abc-123" }])))
(is (= [false, { :id #{"has incorrect format"} }] (apply v [{ :id "123-abc" }]))))



;;
;; Implementation functions
;;


(deftest test-as-vec
(is (= [1 2 3] (validation/as-vec [1 2 3])))
(is (= [1 2 3] (validation/as-vec '(1 2 3))))
(is (= [10] (validation/as-vec 10)))
(is (= [{ :a 1, :b 2 }] (validation/as-vec { :a 1, :b 2 }))))


(deftest test-assoc-with
(is (= (validation/assoc-with clojure.set/union {} :a #{"should not be nil"}) { :a #{"should not be nil"} }))
(is (= (validation/assoc-with clojure.set/union { :a #{1} } :a #{2}) { :a #{1 2} }))
(is (= (validation/assoc-with clojure.set/union { :a #{1} } :b #{2}) { :a #{1}, :b #{2} }))
(is (= (validation/assoc-with clojure.set/union { :a #{1} } :a #{2}, :b #{3}) { :a #{1 2}, :b #{3} })))

0 comments on commit c1c0d57

Please sign in to comment.