Skip to content

Instantly share code, notes, and snippets.

@kirang89
Last active November 18, 2015 19:30
Show Gist options
  • Save kirang89/2db402af178f57288d1a to your computer and use it in GitHub Desktop.
Save kirang89/2db402af178f57288d1a to your computer and use it in GitHub Desktop.
Custom implementation of an if-valid and when-valid macro based on Chapter 8 of Clojure for the Brave and True
;;
;; Custom implementation based on http://www.braveclojure.com/writing-macros
;;
(ns clojurist.core
(:gen-class))
(def order-details-valid
{:name "Kiran Gangadharan"
:email "kiran.daredevil@gmail.com"})
(def order-details-invalid-1
{:name "Kiran Gangadharan"
:email "kiran.daredevilgmail.com"})
(def order-details-invalid-2
{:name ""
:email "kiran.daredevil@gmail.com"})
(def order-validations
"Validations for an order"
{:name ["Name cannot be empty" not-empty]
:email ["Email cannot be empty" not-empty
"Invalid email address" #(re-seq #"@" %)]})
(defn error-messages-for
"Return a seq of error messages"
[field validator]
(filter #(not (nil? %))
(map #(let [message (first %)
fn (second %)
result (fn field)]
(if (true? (not result)) message nil))
(partition 2 validator))))
(defn validate
"Validate a map and return error for each key if any"
[to-validate validations]
(reduce (fn [errors [field value]]
(let [field-validators (validations field)
err (error-messages-for value field-validators)]
(if (empty? err)
errors
(assoc errors field err))))
{} to-validate))
(defmacro if-valid [order-details validations errors & then-else]
`(let [~errors (validate ~order-details ~validations)]
(if (empty? ~errors)
~(first then-else)
~(second then-else))))
(defmacro when-valid [order-details validations & body]
`(if (empty? (validate ~order-details ~validations))
(do ~@body)
nil))
;; Test if-valid
(if-valid order-details-invalid-2 order-validations data-errors
(println :success)
(println :failure data-errors)) ;; => :failure {:name (Name cannot be empty)}
(if-valid order-details-invalid-1 order-validations data-errors
(println :success)
(println :failure data-errors)) ;; => :failure {:email (Invalid email address)}
(if-valid order-details-valid order-validations data-errors
(println :success)
(println :failure data-errors)) ;; => :success
;; Test when-valid
(when-valid order-details-valid order-validations
(println "It's a success!")
(println :success))
;; It's a success!
;; :success
(when-valid order-details-invalid-1 order-validations
(println "It's a success!")
(println :success)) ;; => nil
(when-valid order-details-invalid-2 order-validations
(println "It's a success!")
(println :success)) ;; => nil
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment