Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move validateur to a separate repository
- Loading branch information
0 parents
commit c1c0d57
Showing
5 changed files
with
354 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
pom.xml | ||
*jar | ||
/lib/ | ||
/classes/ | ||
.lein-failures | ||
.lein-deps-sum | ||
TAGS |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"]]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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} }))) |