From 8b1288e20b5c15f71be49c265736abfacd1a7d8f Mon Sep 17 00:00:00 2001 From: Eric Dvorsak Date: Wed, 28 Feb 2018 16:38:46 +0100 Subject: [PATCH] add merge-fx macro this macro allows safe merging of cofx, making complex events easier to understand while avoiding the risk to overwrite the db effect when threading multiple effects creating functions Signed-off-by: Eric Dvorsak --- src/status_im/utils/handlers.clj | 53 +++++++++++++++++++++++++++++++ src/status_im/utils/handlers.cljs | 14 ++++++++ 2 files changed, 67 insertions(+) diff --git a/src/status_im/utils/handlers.clj b/src/status_im/utils/handlers.clj index de6515fd6f..79b0b109a9 100644 --- a/src/status_im/utils/handlers.clj +++ b/src/status_im/utils/handlers.clj @@ -16,3 +16,56 @@ ~new-db ~db))) forms))] ~db)))) + +(defmacro merge-fx* + "This macro is called recursively from merge-fx + It wraps each form in a let binding that captures + - the new co-effect after updating the db key if a db effect was in the map returned + by the previous form + - the new fx map after merging previous fx map with the map returned by the previous form + We use safe-merge for that which is returning a map with a :merging-fx-with-common-keys effect + in case at least one effect that is not a :db effect was overwritten by merge" + {:added "1.0"} + [fx cofx & forms] + (if forms + (let [form (first forms) + temp-cofx (gensym 'temp-cofx)] + `(let [~temp-cofx (update-db ~cofx ~fx) + fx# (safe-merge ~fx ~(with-meta `(~(first form) ~@(next form) ~temp-cofx) (meta form)))] + (merge-fx* fx# ~temp-cofx ~@(next forms)))) + fx)) + +(defmacro merge-fx + "Takes a map of co-effects and forms as argument. + The first optional form can be map of effects/ + The next forms are functions applying effects and returning a map of effects. + The macro ensures that updates to db are passed from function to function within the cofx :db key and + that only a :merging-fx-with-common-keys effect is returned if some functions are trying + to produce the same effects (excepted :db effect)" + {:added "1.0"} + [cofx & forms] + (let [form (first forms)] + (if (or (symbol? form) + (map? form)) + `(merge-fx* ~form ~cofx ~@(next forms)) + `(merge-fx* {} ~cofx ~@forms)))) + +(comment (defn fn1 [{:keys [db]} ] + {:db (assoc db :a 0) + :a "1"}) + + (defn fn2 [ a {:keys [db]}] + {:db (update db :a + a) + }) + + (defn fn3 [ a {:keys [db u]}] + {:db (update db :a + u)}) + + (let [a 1 + b 2 + cofx {:db {} :u 1}] + (merge-fx cofx + {:db {:hello 2}} + (fn1) + (fn2 a) + (fn3 b)))) diff --git a/src/status_im/utils/handlers.cljs b/src/status_im/utils/handlers.cljs index 9dde6099a1..987be1f4de 100644 --- a/src/status_im/utils/handlers.cljs +++ b/src/status_im/utils/handlers.cljs @@ -107,3 +107,17 @@ (remove (fn [{:keys [dapp? pending?]}] (or pending? dapp?))) (map :whisper-identity))) + +(defn update-db [cofx fx] + (if-let [db (:db fx)] + (assoc cofx :db db) + cofx)) + +(defn safe-merge [fx new-fx] + (if (:merging-fx-with-common-keys fx) + fx + (let [common-keys (clojure.set/intersection (into #{} (keys fx)) + (into #{} (keys new-fx)))] + (if (empty? (disj common-keys :db)) + (merge fx new-fx) + {:merging-fx-with-common-keys common-keys}))))