2016-07-11 06:52:05 +00:00
|
|
|
(ns re-frame.fx
|
2016-07-26 06:53:50 +00:00
|
|
|
(:require
|
2016-07-27 05:43:47 +00:00
|
|
|
[re-frame.router :as router]
|
2016-07-26 06:53:50 +00:00
|
|
|
[re-frame.db :refer [app-db]]
|
|
|
|
[re-frame.events :as events]
|
|
|
|
[re-frame.interop :refer [ratom? set-timeout!]]
|
|
|
|
[re-frame.loggers :refer [console]]))
|
|
|
|
|
|
|
|
;; ---- Spec schema -----------------------------------------------------------
|
2016-07-27 05:43:47 +00:00
|
|
|
;; TODO use Spec to validate events for :dispatch and :dispatch-n when clj 1.9
|
2016-07-26 06:53:50 +00:00
|
|
|
;; e.g. (when (= :cljs.spec/invalid (s/conform ::event val))
|
|
|
|
;; (console :error (s/explain-str ::event val)))
|
|
|
|
;; (s/def ::event (s/and vector? (complement empty?)))
|
|
|
|
;; (s/def ::events-n (s/coll-off ::event))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
;; -- Registration ------------------------------------------------------------
|
2016-07-13 14:32:36 +00:00
|
|
|
|
2016-07-11 06:52:05 +00:00
|
|
|
(def ^:private id->handler-fn (atom {}))
|
|
|
|
|
|
|
|
(defn lookup-handler
|
2016-07-13 14:32:36 +00:00
|
|
|
[effect-id]
|
|
|
|
(get @id->handler-fn effect-id))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
|
2016-07-13 14:32:36 +00:00
|
|
|
(defn clear-all-handlers!
|
2016-07-11 06:52:05 +00:00
|
|
|
[]
|
|
|
|
(reset! id->handler-fn {}))
|
|
|
|
|
|
|
|
|
2016-07-13 14:32:36 +00:00
|
|
|
(defn clear-handler!
|
|
|
|
[effect-id]
|
2016-07-14 06:41:54 +00:00
|
|
|
(if (lookup-handler effect-id)
|
|
|
|
(swap! id->handler-fn dissoc effect-id)
|
|
|
|
(console :warn "re-frame: unable to clear effect handler for " effect-id ". Not defined.")))
|
2016-07-13 14:32:36 +00:00
|
|
|
|
|
|
|
|
2016-07-11 06:52:05 +00:00
|
|
|
(defn register
|
|
|
|
"register a handler fn for an effect."
|
2016-07-14 06:41:54 +00:00
|
|
|
[effect-id handler-fn]
|
|
|
|
(when (lookup-handler effect-id)
|
|
|
|
(console :warn "re-frame: overwriting an effects handler for: " effect-id)) ;; allow it, but warn.
|
|
|
|
(swap! id->handler-fn assoc effect-id handler-fn))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
|
2016-07-14 06:41:54 +00:00
|
|
|
;; -- Standard Builtin Effects Handlers --------------------------------------
|
2016-07-11 06:52:05 +00:00
|
|
|
|
2016-07-27 05:43:47 +00:00
|
|
|
;; Dispatch event(s) after some time.
|
2016-07-11 06:52:05 +00:00
|
|
|
;; Example:
|
2016-07-27 05:43:47 +00:00
|
|
|
;; {:dispatch-later [{:ms 200 :dispatch [:event-id "param"]} ;; in 200ms do this: (dispatch [:event-id "param"])
|
|
|
|
;; {:ms 100 :dispatch [:also :this :in :100ms]}
|
|
|
|
;; {:ms 250 :dispatch-n (list [:do ] [:all ] [:three ])}]}
|
2016-07-11 06:52:05 +00:00
|
|
|
;;
|
|
|
|
(register
|
|
|
|
:dispatch-later
|
2016-07-27 05:43:47 +00:00
|
|
|
(fn [effects-v]
|
|
|
|
;TODO: use Spec to verify vector and elements when clj 1.9.0 is rel.
|
|
|
|
(doseq [effect effects-v]
|
|
|
|
(let [{:keys [ms dispatch dispatch-n]} effect
|
|
|
|
tasks (cond dispatch (list dispatch)
|
|
|
|
dispatch-n dispatch-n
|
|
|
|
:else (console :error "re-frame: dispatch-later fx must have one of :dispatch or :dispatch-n"))]
|
|
|
|
(when (and (some? dispatch) (some? dispatch-n))
|
|
|
|
(console :warn "re-frame: dispatch-later fx must have one of :dispatch or :dispatch-n Got: " effect " Ignoring: :dispatch-n"))
|
|
|
|
(if (or (empty? tasks) (-> ms number? not))
|
|
|
|
(console :warn "re-frame: dispatch-later fx :ms not number or missing :dispatch/:dispatch-n Got:" effect " Ignored.")
|
|
|
|
(set-timeout! #(doseq [event tasks]
|
|
|
|
(router/dispatch event)) ms))))))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
2016-07-14 06:41:54 +00:00
|
|
|
|
2016-07-26 06:53:50 +00:00
|
|
|
;; Supply a vector. For example:
|
2016-07-14 06:41:54 +00:00
|
|
|
;;
|
|
|
|
;; {:dispatch [:event-id "param"] }
|
2016-07-26 06:53:50 +00:00
|
|
|
|
|
|
|
(register
|
|
|
|
:dispatch
|
|
|
|
(fn [val]
|
|
|
|
(when-not (vector? val)
|
|
|
|
(console :warn "re-frame: :dispatch fx val expected vector got:" val))
|
2016-07-27 05:43:47 +00:00
|
|
|
(router/dispatch val)))
|
2016-07-26 06:53:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
;; Supply sequencial coll. For example:
|
2016-07-14 06:41:54 +00:00
|
|
|
;;
|
2016-07-26 06:53:50 +00:00
|
|
|
;; {:dispatch-n (list [:do :all] [:three :of] [:these])}
|
2016-07-14 06:41:54 +00:00
|
|
|
;;
|
2016-07-26 06:53:50 +00:00
|
|
|
;; Coll can be anything that returns true to sequential? but should not be a map
|
|
|
|
;; NOTE: this does not include Set
|
2016-07-11 06:52:05 +00:00
|
|
|
(register
|
2016-07-26 06:53:50 +00:00
|
|
|
:dispatch-n
|
2016-07-14 06:41:54 +00:00
|
|
|
(fn [val]
|
2016-07-26 06:53:50 +00:00
|
|
|
(when (or (-> val sequential? not) (map? val))
|
|
|
|
(console :warn "re-frame: :dispatch-n fx val expected sequential not map got:" val))
|
2016-07-27 05:43:47 +00:00
|
|
|
(doseq [event val] (router/dispatch event))))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
|
2016-07-13 14:32:36 +00:00
|
|
|
(register
|
|
|
|
:deregister-event-handler
|
|
|
|
(fn [val]
|
2016-07-20 07:16:01 +00:00
|
|
|
(if (sequential? val)
|
2016-07-26 06:53:50 +00:00
|
|
|
(doall (map events/clear-handler! val))
|
|
|
|
(events/clear-handler! val))))
|
2016-07-13 14:32:36 +00:00
|
|
|
|
|
|
|
|
2016-07-11 06:52:05 +00:00
|
|
|
(register
|
|
|
|
:db
|
2016-07-14 06:41:54 +00:00
|
|
|
(fn [val]
|
|
|
|
(reset! app-db val)))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
;; -- Middleware --------------------------------------------------------------
|
|
|
|
|
2016-07-24 07:08:09 +00:00
|
|
|
;; XXX a coeffect for jsDate or random number
|
2016-07-11 06:52:05 +00:00
|
|
|
;; XXX add metadata saying it is fx.
|
|
|
|
;; XXX ordering
|
|
|
|
;; XXX think about an undo effect
|
|
|
|
|
|
|
|
|
|
|
|
(defn fx
|
|
|
|
[handler]
|
|
|
|
(fn fx-handler
|
|
|
|
[app-db event-vec]
|
2016-07-20 08:57:46 +00:00
|
|
|
(if-not (ratom? app-db)
|
2016-07-15 01:33:35 +00:00
|
|
|
(if (map? app-db)
|
2016-07-24 07:08:09 +00:00
|
|
|
(console :warn "re-frame: Did you use \"fx\" middleware with \"reg-event\"? Use \"reg-event-fx\" instead (and don't directly use \"fx\")")
|
2016-07-15 01:40:07 +00:00
|
|
|
(console :warn "re-frame: \"fx\" middleware not given a Ratom. Got: " app-db)))
|
2016-07-15 08:46:16 +00:00
|
|
|
(let [run-effect (fn [[key val]]
|
|
|
|
(if-let [effect-fn (lookup-handler key)]
|
|
|
|
(effect-fn val)
|
|
|
|
(console :error "re-frame: no effects handler registered for: " key ". Ignoring")))
|
|
|
|
world {:db @app-db}]
|
|
|
|
(->> (handler world event-vec) ;; is expected to return a map of effects
|
|
|
|
(mapv run-effect))))) ;; use mapv to process the returned effects (because it isn't lazy)
|