swinging-needle-meter/resources/public/js/compiled/out/re_frame/subs.cljc
2020-10-20 14:44:11 +01:00

230 lines
10 KiB
Clojure

(ns re-frame.subs
(:require
[re-frame.db :refer [app-db]]
[re-frame.interop :refer [add-on-dispose! debug-enabled? make-reaction ratom? deref? dispose! reagent-id]]
[re-frame.loggers :refer [console]]
[re-frame.utils :refer [first-in-vector]]
[re-frame.registrar :refer [get-handler clear-handlers register-handler]]
[re-frame.trace :as trace :include-macros true]))
(def kind :sub)
(assert (re-frame.registrar/kinds kind))
;; -- cache -------------------------------------------------------------------
;;
;; De-duplicate subscriptions. If two or more equal subscriptions
;; are concurrently active, we want only one handler running.
;; Two subscriptions are "equal" if their query vectors test "=".
(def query->reaction (atom {}))
(defn clear-subscription-cache!
"Runs on-dispose for all subscriptions we have in the subscription cache.
Used to force recreation of new subscriptions. Should only be necessary
in development.
The on-dispose functions for the subscriptions will remove themselves from the
cache.
Useful when reloading Figwheel code after a React exception, as React components
aren't cleaned up properly. This means a subscription's on-dispose function isn't
run when the components are destroyed. If a bad subscription caused your exception,
then you can't fix it without reloading your browser."
[]
(doseq [[k rxn] @query->reaction]
(dispose! rxn))
(if (not-empty @query->reaction)
(console :warn "Subscription cache should be empty after clearing it.")))
(defn clear-all-handlers!
"Unregisters all existing subscription handlers"
[]
(clear-handlers kind)
(clear-subscription-cache!))
(defn cache-and-return
"cache the reaction r"
[query-v dynv r]
(let [cache-key [query-v dynv]]
;; when this reaction is no longer being used, remove it from the cache
(add-on-dispose! r #(do (swap! query->reaction dissoc cache-key)
(trace/with-trace {:operation (first-in-vector query-v)
:op-type :sub/dispose
:tags {:query-v query-v
:reaction (reagent-id r)}}
nil)))
;; cache this reaction, so it can be used to deduplicate other, later "=" subscriptions
(swap! query->reaction assoc cache-key r)
(trace/merge-trace! {:tags {:reaction (reagent-id r)}})
r)) ;; return the actual reaction
(defn cache-lookup
([query-v]
(cache-lookup query-v []))
([query-v dyn-v]
(get @query->reaction [query-v dyn-v])))
;; -- subscribe -----------------------------------------------------
(defn subscribe
"Returns a Reagent/reaction which contains a computation"
([query-v]
(trace/with-trace {:operation (first-in-vector query-v)
:op-type :sub/create
:tags {:query-v query-v}}
(if-let [cached (cache-lookup query-v)]
(do
(trace/merge-trace! {:tags {:cached? true
:reaction (reagent-id cached)}})
cached)
(let [query-id (first-in-vector query-v)
handler-fn (get-handler kind query-id)]
(trace/merge-trace! {:tags {:cached? false}})
(if (nil? handler-fn)
(do (trace/merge-trace! {:error true})
(console :error (str "re-frame: no subscription handler registered for: \"" query-id "\". Returning a nil subscription.")))
(cache-and-return query-v [] (handler-fn app-db query-v)))))))
([v dynv]
(trace/with-trace {:operation (first-in-vector v)
:op-type :sub/create
:tags {:query-v v
:dyn-v dynv}}
(if-let [cached (cache-lookup v dynv)]
(do
(trace/merge-trace! {:tags {:cached? true
:reaction (reagent-id cached)}})
cached)
(let [query-id (first-in-vector v)
handler-fn (get-handler kind query-id)]
(trace/merge-trace! {:tags {:cached? false}})
(when debug-enabled?
(when-let [not-reactive (not-empty (remove ratom? dynv))]
(console :warn "re-frame: your subscription's dynamic parameters that don't implement IReactiveAtom:" not-reactive)))
(if (nil? handler-fn)
(do (trace/merge-trace! {:error true})
(console :error (str "re-frame: no subscription handler registered for: \"" query-id "\". Returning a nil subscription.")))
(let [dyn-vals (make-reaction (fn [] (mapv deref dynv)))
sub (make-reaction (fn [] (handler-fn app-db v @dyn-vals)))]
;; handler-fn returns a reaction which is then wrapped in the sub reaction
;; need to double deref it to get to the actual value.
;(console :log "Subscription created: " v dynv)
(cache-and-return v dynv (make-reaction (fn [] @@sub))))))))))
;; -- reg-sub -----------------------------------------------------------------
(defn- map-vals
"Returns a new version of 'm' in which 'f' has been applied to each value.
(map-vals inc {:a 4, :b 2}) => {:a 5, :b 3}"
[f m]
(into (empty m)
(map (fn [[k v]] [k (f v)]))
m))
(defn- deref-input-signals
[signals query-id]
(let [signals (cond
(sequential? signals) (map deref signals)
(map? signals) (map-vals deref signals)
(deref? signals) @signals
:else (console :error "re-frame: in the reg-sub for " query-id ", the input-signals function returns: " signals))]
(trace/merge-trace! {:tags {:input-signals (map reagent-id signals)}})
signals))
(defn reg-sub
"Associate the given `query id` with a handler function and an optional signal function.
There's 3 ways this function can be called
1. (reg-sub
:test-sub
(fn [db [_]] db))
The value in app-db is passed to the computation function as the 1st argument.
2. (reg-sub
:a-b-sub
(fn [q-vec d-vec]
[(subs/subscribe [:a-sub])
(subs/subscribe [:b-sub])])
(fn [[a b] [_]] {:a a :b b}))
Two functions provided. The 2nd is computation function, as before. The 1st
is returns what `input signals` should be provided to the computation. The
`input signals` function is called with two arguments: the query vector
and the dynamic vector. The return value can be singleton reaction or
a sequence of reactions.
3. (reg-sub
:a-b-sub
:<- [:a-sub]
:<- [:b-sub]
(fn [[a b] [_]] {:a a :b b}))```
This 3rd variation is just syntactic sugar for the 2nd. Pairs are supplied instead
of an `input signals` functions. `:<-` is supplied followed by the subscription
vector.
"
[query-id & args]
(let [computation-fn (last args)
input-args (butlast args) ;; may be empty, or one fn, or pairs of :<- / vector
err-header (str "re-frame: reg-sub for " query-id ", ")
inputs-fn (case (count input-args)
;; no `inputs` function provided - give the default
0 (fn
([_] app-db)
([_ _] app-db))
;; a single `inputs` fn
1 (let [f (first input-args)]
(when-not (fn? f)
(console :error err-header "2nd argument expected to be an inputs function, got:" f))
f)
;; one sugar pair
2 (let [[marker vec] input-args]
(when-not (= :<- marker)
(console :error err-header "expected :<-, got:" marker))
(fn inp-fn
([_] (subscribe vec))
([_ _] (subscribe vec))))
;; multiple sugar pairs
(let [pairs (partition 2 input-args)
markers (map first pairs)
vecs (map last pairs)]
(when-not (and (every? #{:<-} markers) (every? vector? vecs))
(console :error err-header "expected pairs of :<- and vectors, got:" pairs))
(fn inp-fn
([_] (map subscribe vecs))
([_ _] (map subscribe vecs)))))]
(register-handler
kind
query-id
(fn subs-handler-fn
([db query-vec]
(let [subscriptions (inputs-fn query-vec)
reaction-id (atom nil)
reaction (make-reaction
(fn [] (trace/with-trace {:operation (first-in-vector query-vec)
:op-type :sub/run
:tags {:query-v query-vec
:reaction @reaction-id}}
(computation-fn (deref-input-signals subscriptions query-id) query-vec))))]
(reset! reaction-id (reagent-id reaction))
reaction))
([db query-vec dyn-vec]
(let [subscriptions (inputs-fn query-vec dyn-vec)
reaction-id (atom nil)
reaction (make-reaction
(fn []
(trace/with-trace {:operation (first-in-vector query-vec)
:op-type :sub/run
:tags {:query-v query-vec
:dyn-v dyn-vec
:reaction @reaction-id}}
(computation-fn (deref-input-signals subscriptions query-id) query-vec dyn-vec))))]
(reset! reaction-id (reagent-id reaction))
reaction))))))