In this code:
(defmacro require-member-id [member-id & body]
`(if ~member-id
(do ~@body)
[false "member-id missing"]))
(def ^:dynamic handle-prevayler!
"Can be replaced in unit tests using `binding` to inject exceptions, etc."
(fn [p cmd args]
(prevayler/handle! p [cmd args])))
(defn- pfn [p]
(fn [cmd args on-success]
(try
(handle-prevayler! p cmd args)
on-success
(catch Throwable t [false (ex-message t)]))))
(defmacro defopn
"This macro defines a prevayler operation function. The first 2 args
NOT tagged with `^:out` must be the prevayler instance and the member-id,
respectively. The first parameter tagged with `^:out` is not passed into
the generated function, but is instead bound to the result of invoking `pfn`
on the given prevayler instance."
[n args & body]
(let [v (map (juxt meta identity) args)
[p member-id :as in]
(into [] (->> v (filter (comp not :out first)) (map second)))
[p!] (into [] (->> v (filter (comp :out first)) (map second)))]
`(defn ~n [~@in]
(require-member-id ~member-id
(let [~p! (pfn ~p)]
~@body)))))
(defopn perform-save! [_p member-id checklist checklist-vars, ^:out p!]
(let [checklist-id (*new-checklist-id*)]
(p!
"save"
{:id checklist-id
:member-id member-id
:checklist checklist
:checklist-vars checklist-vars}
[true checklist-id])))
(defopn perform-delete! [_p member-id checklist-id, ^:out p!]
(p! "del" {:id checklist-id, :member-id member-id} [true]))
This essentially introduces an "around aspect" around each of the functions defined using defopn. The namespace I pulled this from uses this function 18 times, which saves quite a lot of code I think?