Last active
December 29, 2022 19:23
-
-
Save mbezjak/1112a321d12c7aaf41a2d7140f2a535a to your computer and use it in GitHub Desktop.
Additional functions for the error model
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
(ns my.company.errors | |
"Errors is a collection of `error`." | |
(:require | |
[my.company.coll :as coll] | |
[my.company.error :as error])) | |
;; Functions in addition to: | |
;; https://gist.github.com/mbezjak/c1baeece563b8ed734692938e6d1a36f | |
(defn validate | |
"Collect validation errors from `results`. | |
This is a way to collect many errors at once. E.g. | |
(errors/validate | |
(when something-1 {:code ...}) | |
(when something-2 {:code ...}) | |
(task/validate ...) | |
(for [x xs] | |
{:code ...})) | |
`results` can be (as seen above): | |
- a collection of nested results | |
- nil | |
- error | |
- errors | |
On successful validation returns `nil`." | |
[& results] | |
(->> results | |
(flatten) | |
(remove nil?) | |
(make) | |
(not-empty))) | |
(defn lazy-validate-fns | |
"Evaluate `fns` lazily, moving on in case of success." | |
[& fns] | |
(loop [[f & rst] fns | |
val nil] | |
(if (or (some? val) (not f)) | |
val | |
(recur rst (validate (f)))))) | |
(def continue-if-above-success | |
"This value is not used. It's just here to satisfy the linter and make the macro below a bit easier to use.") | |
(defmacro validate-groups | |
"Breaks code into multiple groups, lazily evaluates one and moves on in case of success." | |
[& form] | |
(let [split-fn #(= 'errors/continue-if-above-success %) | |
into-fn (fn [validation-group] `(fn [] ~(vec validation-group))) | |
fns (->> form (coll/splits-by split-fn) (map into-fn))] | |
`(lazy-validate-fns ~@fns))) | |
(defn throw! [es] | |
(when (seq es) | |
(throw (ex-info "Validation failure" | |
{::validation? true | |
::errors es})))) | |
(defn validation-exception? [exception] | |
(-> exception ex-data ::validation? true?)) | |
(defn unwrap-exception [exception] | |
(-> exception ex-data ::errors)) |
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
(ns my.company.errors-test | |
(:require | |
[clojure.test :refer [deftest is]] | |
[my.company.errors :as sut])) | |
(deftest validate | |
(is (nil? (sut/validate))) | |
(is (nil? (sut/validate nil))) | |
(is (nil? (sut/validate (sut/validate nil)))) | |
(is (= [:error/a] (sut/codes (sut/validate {:code :error/a})))) | |
(is (= [:error/a :error/b] | |
(sut/codes (sut/validate | |
{:code :error/a} | |
{:code :error/b})))) | |
(is (= [:error/a :error/b] | |
(sut/codes (sut/validate | |
nil | |
{:code :error/a} | |
nil | |
{:code :error/b} | |
nil)))) | |
(is (= [:error/a :error/b] | |
(sut/codes (sut/validate | |
nil | |
[{:code :error/a}] | |
nil | |
[[[{:code :error/b}]]] | |
nil)))) | |
(is (= [:error/a] | |
(sut/codes (sut/validate (sut/validate {:code :error/a}))))) | |
(is (= [:error/a :error/b] | |
(sut/codes (sut/validate | |
(sut/validate {:code :error/a}) | |
{:code :error/b}))))) | |
(deftest lazy-validate-fns | |
(is (nil? (sut/lazy-validate-fns))) | |
(is (nil? (sut/lazy-validate-fns (constantly nil)))) | |
(is (nil? (sut/lazy-validate-fns (constantly nil) (constantly nil)))) | |
(is (nil? (sut/lazy-validate-fns (constantly [])))) | |
(is (nil? (sut/lazy-validate-fns (constantly [nil nil])))) | |
(is (= [:error/a] | |
(sut/codes | |
(sut/lazy-validate-fns (constantly nil) (constantly {:code :error/a}))))) | |
(is (= [:error/a] | |
(sut/codes | |
(sut/lazy-validate-fns (constantly {:code :error/a}) | |
(constantly {:code :error/b}))))) | |
(is (= [:error/a :error/b] | |
(sut/codes | |
(sut/lazy-validate-fns (constantly [{:code :error/a} {:code :error/b}])))))) | |
(deftest validate-groups | |
(is (nil? (sut/validate-groups))) | |
(is (nil? (sut/validate-groups nil))) | |
(is (nil? (sut/validate-groups [nil]))) | |
(is (= [:error/a] (sut/codes (sut/validate-groups {:code :error/a})))) | |
(is (= [:error/a :error/b] | |
(sut/codes (sut/validate-groups | |
{:code :error/a} | |
{:code :error/b})))) | |
(is (= [:error/a :error/b] | |
(sut/codes (sut/validate-groups | |
{:code :error/a} | |
{:code :error/b} | |
errors/continue-if-above-success | |
{:code :error/c})))) | |
(is (= [:error/c] | |
(sut/codes (sut/validate-groups | |
nil | |
errors/continue-if-above-success | |
{:code :error/c}))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment