mirror of
https://github.com/status-im/status-react.git
synced 2025-02-04 07:06:18 +00:00
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
42
src/status_im/utils/fx.clj
Normal file
42
src/status_im/utils/fx.clj
Normal file
@ -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))))))))
|
51
src/status_im/utils/fx.cljs
Normal file
51
src/status_im/utils/fx.cljs
Normal file
@ -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).
|
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
|
: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). "
|
(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]
|
[cofx & forms]
|
||||||
(let [form (first forms)]
|
(let [form (first forms)]
|
||||||
(if (or (symbol? form)
|
(if (or (symbol? form)
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
[status-im.test.utils.http]
|
[status-im.test.utils.http]
|
||||||
[status-im.test.init.core]
|
[status-im.test.init.core]
|
||||||
[status-im.test.ui.screens.add-new.models]
|
[status-im.test.ui.screens.add-new.models]
|
||||||
|
[status-im.test.utils.fx]
|
||||||
[status-im.test.accounts.recover.core]
|
[status-im.test.accounts.recover.core]
|
||||||
[status-im.test.hardwallet.core]
|
[status-im.test.hardwallet.core]
|
||||||
[status-im.test.ui.screens.currency-settings.models]
|
[status-im.test.ui.screens.currency-settings.models]
|
||||||
@ -114,6 +114,7 @@
|
|||||||
'status-im.test.utils.keychain.core
|
'status-im.test.utils.keychain.core
|
||||||
'status-im.test.utils.universal-links.core
|
'status-im.test.utils.universal-links.core
|
||||||
'status-im.test.utils.http
|
'status-im.test.utils.http
|
||||||
|
'status-im.test.utils.fx
|
||||||
'status-im.test.ui.screens.add-new.models
|
'status-im.test.ui.screens.add-new.models
|
||||||
'status-im.test.accounts.recover.core
|
'status-im.test.accounts.recover.core
|
||||||
'status-im.test.hardwallet.core
|
'status-im.test.hardwallet.core
|
||||||
|
67
test/cljs/status_im/test/utils/fx.cljs
Normal file
67
test/cljs/status_im/test/utils/fx.cljs
Normal file
@ -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…
x
Reference in New Issue
Block a user