introduce fx/defn macro and fx/merge function
intended to deprecate `handlers-macro/merge-fx` Signed-off-by: yenda <eric@status.im>
This commit is contained in:
parent
bf2e51f856
commit
ed2abf9101
|
@ -0,0 +1,42 @@
|
|||
(ns status-im.utils.fx
|
||||
(:refer-clojure :exclude [defn]))
|
||||
|
||||
(defmacro defn
|
||||
"Defines an fx producing function
|
||||
Takes the same arguments as the defn macro
|
||||
Produces a 2 arity function:
|
||||
- first arity takes the declared parameters and returns a function that takes cofx as
|
||||
single argument, for use in composition of effects
|
||||
- second arity takes cofx as first arguments and declared parameters as next arguments,
|
||||
for use in repl or direct call
|
||||
Notes:
|
||||
- destructuring of cofx is possible
|
||||
- supports docstring
|
||||
- supports attr-map
|
||||
- TODO: add suport for `prepost-map?` (don't forget to add it to arglist)
|
||||
- TODO: add validation of macro parameters"
|
||||
{:arglists '([name doc-string? attr-map? [params*] body])}
|
||||
[name & fdecl]
|
||||
(let [m (if (string? (first fdecl))
|
||||
{:doc (first fdecl)}
|
||||
{})
|
||||
fdecl (if (string? (first fdecl))
|
||||
(next fdecl)
|
||||
fdecl)
|
||||
m (if (map? (first fdecl))
|
||||
(conj m (first fdecl))
|
||||
m)
|
||||
fdecl (if (map? (first fdecl))
|
||||
(next fdecl)
|
||||
fdecl)
|
||||
[cofx & args] (first fdecl)
|
||||
fdecl (next fdecl)
|
||||
argsyms (take (count args) (repeatedly #(gensym "arg")))]
|
||||
`(clojure.core/defn ~(with-meta name m)
|
||||
([~@argsyms] (fn [cofx#] (~name cofx# ~@argsyms)))
|
||||
([cofx# ~@args]
|
||||
(if (and (map? cofx#)
|
||||
(not (nil? (:db cofx#))))
|
||||
(let [~cofx cofx#]
|
||||
~@fdecl)
|
||||
(throw (js/Error. (str "fx/defn expects a map of cofx as first argument got " cofx# " in function " ~name))))))))
|
|
@ -0,0 +1,51 @@
|
|||
(ns status-im.utils.fx
|
||||
(:require-macros status-im.utils.fx
|
||||
[taoensso.timbre :as log])
|
||||
(:require [clojure.set :as set])
|
||||
(:refer-clojure :exclude [merge]))
|
||||
|
||||
(defn- update-db [cofx fx]
|
||||
(if-let [db (:db fx)]
|
||||
(assoc cofx :db db)
|
||||
cofx))
|
||||
|
||||
(def ^:private mergable-keys
|
||||
#{:data-store/tx :data-store/base-tx :chat-received-message/add-fx
|
||||
:shh/add-new-sym-keys :shh/get-new-sym-keys :shh/post
|
||||
:shh/generate-sym-key-from-password :confirm-messages-processed
|
||||
:utils/dispatch-later})
|
||||
|
||||
(defn- safe-merge [fx new-fx]
|
||||
(if (:merging-fx-with-common-keys fx)
|
||||
fx
|
||||
(let [common-keys (set/intersection (into #{} (keys fx))
|
||||
(into #{} (keys new-fx)))]
|
||||
(if (empty? (set/difference common-keys (conj mergable-keys :db)))
|
||||
(clojure.core/merge (apply dissoc fx mergable-keys)
|
||||
(apply dissoc new-fx mergable-keys)
|
||||
(merge-with into
|
||||
(select-keys fx mergable-keys)
|
||||
(select-keys new-fx mergable-keys)))
|
||||
(do (log/error "Merging fx with common-keys: " common-keys)
|
||||
{:merging-fx-with-common-keys common-keys})))))
|
||||
|
||||
(defn merge
|
||||
"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 fn 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, :data-source/tx and :data-source/base-tx effects).
|
||||
:data-source/tx and :data-source/base-tx effects are handled specially and their results
|
||||
(list of transactions) are compacted to one transactions list (for each effect). "
|
||||
[{:keys [db] :as cofx} & args]
|
||||
(let [[first-arg & rest-args] args
|
||||
initial-fxs? (map? first-arg)
|
||||
fx-fns (if initial-fxs? rest-args args)]
|
||||
(reduce (fn [fxs fx-fn]
|
||||
(let [updated-cofx (update-db cofx fxs)]
|
||||
(if fx-fn
|
||||
(safe-merge fxs (fx-fn updated-cofx))
|
||||
fxs)))
|
||||
(if initial-fxs? first-arg {:db db})
|
||||
fx-fns)))
|
|
@ -27,7 +27,8 @@
|
|||
to produce the same effects (excepted :db, :data-source/tx and :data-source/base-tx effects).
|
||||
:data-source/tx and :data-source/base-tx effects are handled specially and their results
|
||||
(list of transactions) are compacted to one transactions list (for each effect). "
|
||||
{:added "1.0"}
|
||||
{:added "1.0"
|
||||
:deprecated "Please use utils.fx/merge function instead"}
|
||||
[cofx & forms]
|
||||
(let [form (first forms)]
|
||||
(if (or (symbol? form)
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
[status-im.test.utils.http]
|
||||
[status-im.test.init.core]
|
||||
[status-im.test.ui.screens.add-new.models]
|
||||
|
||||
[status-im.test.utils.fx]
|
||||
[status-im.test.accounts.recover.core]
|
||||
[status-im.test.hardwallet.core]
|
||||
[status-im.test.ui.screens.currency-settings.models]
|
||||
|
@ -114,6 +114,7 @@
|
|||
'status-im.test.utils.keychain.core
|
||||
'status-im.test.utils.universal-links.core
|
||||
'status-im.test.utils.http
|
||||
'status-im.test.utils.fx
|
||||
'status-im.test.ui.screens.add-new.models
|
||||
'status-im.test.accounts.recover.core
|
||||
'status-im.test.hardwallet.core
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
(ns status-im.test.utils.fx
|
||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||
[status-im.utils.fx :as fx]))
|
||||
|
||||
(fx/defn hello
|
||||
"this is a very nice useless function"
|
||||
[{:keys [db]} a]
|
||||
{:db (assoc db :a a)})
|
||||
|
||||
(fx/defn hello2
|
||||
{:doc "this function is useless as well"}
|
||||
[{:keys [db]} b]
|
||||
{:db (assoc db :a b) :b (:a db)})
|
||||
|
||||
(fx/defn hello3
|
||||
{:doc "lol lazy function does nothing"}
|
||||
[{:keys [db]} b]
|
||||
(identity nil))
|
||||
|
||||
(deftest merge-fxs-test
|
||||
(testing "merge function for fxs"
|
||||
(let [cofx {:db {:c 2}}]
|
||||
(is (= (fx/merge cofx
|
||||
(hello "a")
|
||||
(hello2 "b"))
|
||||
{:db {:c 2
|
||||
:a "b"}
|
||||
:b "a"}))
|
||||
(testing "with initial fxs map"
|
||||
(is (= (fx/merge cofx
|
||||
{:potatoe :potating}
|
||||
(hello "a")
|
||||
(hello2 "b"))
|
||||
{:db {:c 2
|
||||
:a "b"}
|
||||
:b "a"
|
||||
:potatoe :potating})
|
||||
"initial fxs map should be merged in the result"))
|
||||
(testing "with a nil producing function"
|
||||
(is (= (fx/merge cofx
|
||||
(hello "a")
|
||||
(hello3 "c")
|
||||
(hello2 "b"))
|
||||
{:db {:c 2
|
||||
:a "b"}
|
||||
:b "a"})))
|
||||
(testing "with condition statement"
|
||||
(testing "false"
|
||||
(is (= (let [do-hello? false]
|
||||
(fx/merge cofx
|
||||
(when do-hello?
|
||||
(hello "a"))
|
||||
(hello2 "b")))
|
||||
{:db {:c 2
|
||||
:a "b"}
|
||||
:b nil})
|
||||
"the conditional statement should not apply"))
|
||||
(testing "true"
|
||||
(is (= (let [do-hello? true]
|
||||
(fx/merge cofx
|
||||
(when do-hello?
|
||||
(hello "a"))
|
||||
(hello2 "b")))
|
||||
{:db {:c 2
|
||||
:a "b"}
|
||||
:b "a"})
|
||||
"the conditional statement should apply"))))))
|
Loading…
Reference in New Issue