Show previous value for subscription state
This commit is contained in:
parent
e0dea398f4
commit
d652ef17e3
|
@ -10,6 +10,7 @@ This version requires re-frame 0.10.4 to make use of the newly added Event panel
|
|||
* New event panel. This panel shows the coeffects given to your event handler, the effects your event handler produced, and all of the interceptors in the chain.
|
||||
* Debugging instructions if re-frame-trace fails to start.
|
||||
* Setting to drop low level traces. This reduces the memory overhead of re-frame-trace as we can drop more traces that you are unlikely to want most of the time.
|
||||
* Diff the previous value of a subscription with its current value.
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
@ -513,40 +513,16 @@
|
|||
new-matches (remove (fn [match]
|
||||
(let [event (get-in (metam/matched-event match) [:tags :event])]
|
||||
(contains? events-to-ignore (first event)))) new-matches)
|
||||
sub-state {}
|
||||
subscription-match-state (rest
|
||||
(reductions (fn [state match]
|
||||
(let [epoch-traces (into []
|
||||
(comp
|
||||
(utils/id-between-xf (:id (first match)) (:id (last match)))
|
||||
(filter metam/subscription?))
|
||||
filtered-traces)
|
||||
reset-state (into {}
|
||||
(map (fn [[k v]]
|
||||
[k (dissoc v :order :created? :run? :disposed? :previous-value)]))
|
||||
state)]
|
||||
(->> epoch-traces
|
||||
(reduce (fn [state trace]
|
||||
(let [tags (get trace :tags)
|
||||
reaction-id (:reaction tags)]
|
||||
(case (:op-type trace)
|
||||
:sub/create (assoc state reaction-id {:created? true
|
||||
:subscription (:query-v tags)
|
||||
:order [:sub/create]})
|
||||
:sub/run (update state reaction-id (fn [sub-state]
|
||||
(-> (if (contains? sub-state :value)
|
||||
(assoc sub-state :previous-value (:value sub-state))
|
||||
sub-state)
|
||||
(assoc :run? true
|
||||
:value (:value tags))
|
||||
(update :order (fnil conj []) :sub/run))))
|
||||
:sub/dispose (-> (assoc-in state [reaction-id :disposed?] true)
|
||||
(update-in [reaction-id :order] (fnil conj []) :sub/dispose))
|
||||
(do (js/console.warn "Unhandled sub trace, this is a bug, report to re-frame-trace please" trace)
|
||||
state))))
|
||||
reset-state))))
|
||||
sub-state
|
||||
new-matches))
|
||||
;; subscription-info is calculated separately from subscription-match-state because they serve different purposes:
|
||||
;; - subscription-info collects all the data that we know about the subscription itself, like its layer, inputs and other
|
||||
;; things that are defined as part of the reg-sub.
|
||||
;; - subscription-match-state collects all the data that we know about the state of specific instances of subscriptions
|
||||
;; like its reagent id, when it was created, run, disposed, what values it returned, e.t.c.
|
||||
subscription-info (metam/subscription-info (get-in db [:epochs :subscription-info] {}) filtered-traces (get-in db [:app-db :reagent-id]))
|
||||
sub-state (get-in db [:epochs :sub-state] {})
|
||||
subscription-match-state (metam/subscription-match-state sub-state filtered-traces new-matches)
|
||||
subscription-matches (rest subscription-match-state)
|
||||
new-sub-state (last subscription-match-state)
|
||||
timing (mapv (fn [match]
|
||||
(let [epoch-traces (into []
|
||||
(comp
|
||||
|
@ -560,27 +536,10 @@
|
|||
|
||||
new-matches (map (fn [match sub-match t] {:match-info match
|
||||
:sub-state sub-match
|
||||
:timing t}) new-matches subscription-match-state timing)
|
||||
:timing t})
|
||||
new-matches subscription-matches timing)
|
||||
all-matches (reduce conj previous-matches new-matches)
|
||||
retained-matches (into [] (take-last number-of-epochs-to-retain all-matches))
|
||||
app-db-id (get-in db [:app-db :reagent-id])
|
||||
subscription-info (->> filtered-traces
|
||||
(filter metam/subscription-re-run?)
|
||||
(reduce (fn [state trace]
|
||||
;; Can we take any shortcuts by assuming that a sub with
|
||||
;; multiple input signals is a layer 3? I don't *think* so because
|
||||
;; one of those input signals could be a naughty subscription to app-db
|
||||
;; directly.
|
||||
;; If we knew when subscription handlers were loaded/reloaded then
|
||||
;; we could avoid doing most of this work, and only check the input
|
||||
;; signals if we hadn't seen it before, or it had been reloaded.
|
||||
(assoc-in state
|
||||
[(:operation trace) :layer]
|
||||
;; If any of the input signals are app-db, it is a layer 2 sub, else 3
|
||||
(if (some #(= app-db-id %) (get-in trace [:tags :input-signals]))
|
||||
2
|
||||
3)))
|
||||
(get-in db [:epochs :subscription-info] {})))
|
||||
first-id-to-retain (first-match-id (first retained-matches))
|
||||
retained-traces (into [] (comp (drop-while #(< (:id %) first-id-to-retain))
|
||||
(remove (fn [trace]
|
||||
|
@ -594,6 +553,7 @@
|
|||
:matches-by-id (into {} (map (juxt first-match-id identity)) retained-matches)
|
||||
:match-ids (mapv first-match-id retained-matches)
|
||||
:parse-state parse-state
|
||||
:sub-state new-sub-state
|
||||
:subscription-info subscription-info)))))
|
||||
;; Else
|
||||
db)))
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
(ns day8.re-frame.trace.metamorphic)
|
||||
|
||||
(defn id-between-xf
|
||||
;; Copied here because I got undeclared Var warnings from figwheel when requiring a CLJC utils ns.
|
||||
"Returns a transducer that filters for :id between beginning and ending."
|
||||
[beginning ending]
|
||||
(filter #(<= beginning (:id %) ending)))
|
||||
|
||||
;; What starts an epoch?
|
||||
|
||||
;;; idle -> dispatch -> running
|
||||
|
@ -245,3 +251,64 @@
|
|||
(->> match
|
||||
(filter event-run?)
|
||||
(first)))
|
||||
|
||||
(defn subscription-info
|
||||
"Collect information about the subscription that we'd like
|
||||
to know, like its layer."
|
||||
[initial-state filtered-traces app-db-id]
|
||||
(->> filtered-traces
|
||||
(filter subscription-re-run?)
|
||||
(reduce (fn [state trace]
|
||||
;; Can we take any shortcuts by assuming that a sub with
|
||||
;; multiple input signals is a layer 3? I don't *think* so because
|
||||
;; one of those input signals could be a naughty subscription to app-db
|
||||
;; directly.
|
||||
;; If we knew when subscription handlers were loaded/reloaded then
|
||||
;; we could avoid doing most of this work, and only check the input
|
||||
;; signals if we hadn't seen it before, or it had been reloaded.
|
||||
(assoc-in state
|
||||
[(:operation trace) :layer]
|
||||
;; If any of the input signals are app-db, it is a layer 2 sub, else 3
|
||||
(if (some #(= app-db-id %) (get-in trace [:tags :input-signals]))
|
||||
2
|
||||
3)))
|
||||
initial-state)))
|
||||
|
||||
(defn subscription-match-state
|
||||
"Build up the state of re-frame's running subscriptions over each matched epoch.
|
||||
Returns initial state as first item in list"
|
||||
[sub-state filtered-traces new-matches]
|
||||
(reductions (fn [state match]
|
||||
(let [epoch-traces (into []
|
||||
(comp
|
||||
(id-between-xf (:id (first match)) (:id (last match)))
|
||||
(filter subscription?))
|
||||
filtered-traces)
|
||||
reset-state (into {}
|
||||
(comp
|
||||
(filter (fn [me] (when-not (:disposed? (val me)) me)))
|
||||
(map (fn [[k v]]
|
||||
[k (dissoc v :order :created? :run? :disposed? :previous-value)])))
|
||||
state)]
|
||||
(->> epoch-traces
|
||||
(reduce (fn [state trace]
|
||||
(let [tags (get trace :tags)
|
||||
reaction-id (:reaction tags)]
|
||||
(case (:op-type trace)
|
||||
:sub/create (assoc state reaction-id {:created? true
|
||||
:subscription (:query-v tags)
|
||||
:order [:sub/create]})
|
||||
:sub/run (update state reaction-id (fn [sub-state]
|
||||
(-> (if (contains? sub-state :value)
|
||||
(assoc sub-state :previous-value (:value sub-state))
|
||||
sub-state)
|
||||
(assoc :run? true
|
||||
:value (:value tags))
|
||||
(update :order (fnil conj []) :sub/run))))
|
||||
:sub/dispose (-> (assoc-in state [reaction-id :disposed?] true)
|
||||
(update-in [reaction-id :order] (fnil conj []) :sub/dispose))
|
||||
(do #?(:cljs (js/console.warn "Unhandled sub trace, this is a bug, report to re-frame-trace please" trace))
|
||||
state))))
|
||||
reset-state))))
|
||||
sub-state
|
||||
new-matches))
|
||||
|
|
|
@ -373,6 +373,18 @@
|
|||
(fn [epoch]
|
||||
(:subscription-info epoch)))
|
||||
|
||||
(rf/reg-sub
|
||||
:subs/sub-state
|
||||
:<- [:epochs/epoch-root]
|
||||
(fn [epochs]
|
||||
(:sub-state epochs)))
|
||||
|
||||
(rf/reg-sub
|
||||
:subs/current-epoch-sub-state
|
||||
:<- [:epochs/current-match-state]
|
||||
(fn [match-state]
|
||||
(:sub-state match-state)))
|
||||
|
||||
(defn sub-sort-val
|
||||
[sub]
|
||||
(case (:type sub)
|
||||
|
@ -398,26 +410,27 @@
|
|||
:<- [:subs/all-sub-traces]
|
||||
:<- [:app-db/reagent-id]
|
||||
:<- [:subs/subscription-info]
|
||||
(fn [[traces app-db-id sub-info]]
|
||||
:<- [:subs/current-epoch-sub-state]
|
||||
(fn [[traces app-db-id sub-info sub-state]]
|
||||
(let [raw (map (fn [trace]
|
||||
(let [pod-type (sub-op-type->type trace)
|
||||
path-data (get-in trace [:tags :query-v])
|
||||
;; TODO: detect layer 2/3 for sub/create and sub/destroy
|
||||
;; This information needs to be accumulated.
|
||||
layer (if (some #(= app-db-id %) (get-in trace [:tags :input-signals]))
|
||||
2
|
||||
3)
|
||||
sub
|
||||
(-> {:id (str pod-type (get-in trace [:tags :reaction]))
|
||||
reagent-id (get-in trace [:tags :reaction])
|
||||
sub (-> {:id (str pod-type reagent-id)
|
||||
:reagent-id reagent-id
|
||||
:type pod-type
|
||||
:layer (get-in sub-info [(:operation trace) :layer])
|
||||
:path-data path-data
|
||||
:path (pr-str path-data)
|
||||
;; TODO: Get not run subscriptions
|
||||
})]
|
||||
(if (contains? (:tags trace) :value)
|
||||
})
|
||||
sub (if (contains? (:tags trace) :value)
|
||||
(assoc sub :value (get-in trace [:tags :value]))
|
||||
sub)))
|
||||
sub)
|
||||
sub (if (contains? (get sub-state reagent-id) :previous-value)
|
||||
(assoc sub :previous-value (get-in sub-state [reagent-id :previous-value]))
|
||||
sub)]
|
||||
sub))
|
||||
traces)
|
||||
re-run (->> raw
|
||||
(filter #(= :re-run (:type %)))
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
|
||||
(defn spy
|
||||
([x]
|
||||
#?(:cljs (js/console.log x))
|
||||
(js/console.log x)
|
||||
x)
|
||||
([label x]
|
||||
#?(:cljs (js/console.log label x))
|
||||
(js/console.log label x)
|
||||
x))
|
||||
|
|
|
@ -197,11 +197,7 @@
|
|||
|
||||
(defn pod [{:keys [id path open? diff?] :as pod-info}]
|
||||
(let [render-diff? (and open? diff?)
|
||||
app-db-after (rf/subscribe [:app-db/current-epoch-app-db-after])
|
||||
app-db-before (rf/subscribe [:app-db/current-epoch-app-db-before])
|
||||
[diff-before diff-after _] (when render-diff?
|
||||
(clojure.data/diff (get-in @app-db-before path)
|
||||
(get-in @app-db-after path)))]
|
||||
app-db-after (rf/subscribe [:app-db/current-epoch-app-db-after])]
|
||||
[rc/v-box
|
||||
:style {:margin-bottom pod-gap
|
||||
:margin-right "1px"}
|
||||
|
@ -242,6 +238,10 @@
|
|||
:leave-animation "accordionVertical"
|
||||
:duration animation-duration})
|
||||
(when render-diff?
|
||||
(let [app-db-before (rf/subscribe [:app-db/current-epoch-app-db-before])
|
||||
[diff-before diff-after _] (when render-diff?
|
||||
(clojure.data/diff (get-in @app-db-before path)
|
||||
(get-in @app-db-after path)))]
|
||||
[rc/v-box
|
||||
:children [[rc/v-box
|
||||
:class "app-db-path--link"
|
||||
|
@ -276,7 +276,7 @@
|
|||
:overflow-y "hidden"}
|
||||
:children [[components/simple-render
|
||||
diff-after
|
||||
["app-db-diff" path]]]]]])]
|
||||
["app-db-diff" path]]]]]]))]
|
||||
(when open?
|
||||
[rc/gap-f :size pod-padding])]]]]))
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns day8.re-frame.trace.view.debug
|
||||
(:require [day8.re-frame.trace.utils.re-com :as rc]
|
||||
[day8.re-frame.trace.view.components :as components]
|
||||
[mranderson047.re-frame.v0v10v2.re-frame.core :as rf]
|
||||
[day8.re-frame.trace.metamorphic :as metam]))
|
||||
|
||||
|
@ -14,6 +15,9 @@
|
|||
[rc/label :label (str "Ending " (prn-str @(rf/subscribe [:epochs/ending-trace-id])))]
|
||||
[rc/label :label (str "Current epoch ID " (prn-str @(rf/subscribe [:epochs/current-epoch-id])))]
|
||||
|
||||
[:h2 "Subscriptions"]
|
||||
[components/simple-render @(rf/subscribe [:subs/sub-state]) ["debug-subs"]]
|
||||
|
||||
[rc/label :label "Epochs"]
|
||||
(let [current-match @(rf/subscribe [:epochs/current-match])]
|
||||
(for [match (:matches @(rf/subscribe [:epochs/epoch-root]))
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
:children [[rc/checkbox
|
||||
:model ignore-unchanged?
|
||||
;; TODO: change from l2 subs to ignored l2 subs
|
||||
:label [:span "Ignore " [:b {:style {:font-weight "700"}} @ignore-unchanged-l2-count] #_ " unchanged" [:br]
|
||||
:label [:span "Ignore " [:b {:style {:font-weight "700"}} @ignore-unchanged-l2-count] #_" unchanged" [:br]
|
||||
[rc/link {:label "layer 2 subs"
|
||||
:href "https://github.com/Day8/re-frame-trace/blob/master/docs/HyperlinkedInformation/UnchangedLayer2.md"}]]
|
||||
:style {:margin-top "6px"}
|
||||
|
@ -128,15 +128,16 @@
|
|||
:width "100%"
|
||||
:model path
|
||||
:disabled? true]]]
|
||||
(when @(rf/subscribe [:settings/debug?])
|
||||
[rc/label :label (str id)])
|
||||
[rc/gap-f :size common/gs-12s]
|
||||
[rc/label :label (if (some? layer)
|
||||
(str "Layer " layer)
|
||||
[rc/link {:label "Layer ?"
|
||||
:href "https://github.com/Day8/re-frame-trace/blob/master/docs/HyperlinkedInformation/UnchangedLayer2.md#why-do-i-sometimes-see-layer--when-viewing-a-subscription"}])]
|
||||
|
||||
;; TODO: capture previous sub run value and allow diffing it.
|
||||
#_[rc/gap-f :size common/gs-12s]
|
||||
#_[rc/box
|
||||
[rc/gap-f :size common/gs-12s]
|
||||
[rc/box
|
||||
:class "bm-muted-button app-db-path--button noselect"
|
||||
:attr {:title "Show diff"
|
||||
:on-click #(when open? (rf/dispatch [:subs/diff-pod? id (not diff?)]))}
|
||||
|
@ -148,11 +149,8 @@
|
|||
|
||||
(defn pod [{:keys [id type layer path open? diff?] :as pod-info}]
|
||||
(let [render-diff? (and open? diff?)
|
||||
#_#_app-db-after (rf/subscribe [:app-db/current-epoch-app-db-after])
|
||||
#_#_app-db-before (rf/subscribe [:app-db/current-epoch-app-db-before])
|
||||
#_#_[diff-before diff-after _] (when render-diff?
|
||||
(clojure.data/diff (get-in @app-db-before path)
|
||||
(get-in @app-db-after path)))]
|
||||
value? (contains? pod-info :value)
|
||||
previous-value? (contains? pod-info :previous-value)]
|
||||
[rc/v-box
|
||||
:style {:margin-bottom pod-gap
|
||||
:margin-right "1px"}
|
||||
|
@ -164,22 +162,29 @@
|
|||
:leave-animation "accordionVertical"
|
||||
:duration animation-duration})
|
||||
(when open?
|
||||
(let [main-value (cond value? (:value pod-info)
|
||||
previous-value? (:previous-value pod-info)
|
||||
:else nil)]
|
||||
[rc/v-box
|
||||
:class (str "data-viewer" (when-not diff? " rounded-bottom"))
|
||||
:style {:margin (css-join pod-padding pod-padding "0px" pod-padding)
|
||||
:overflow-x "auto"
|
||||
:overflow-y "hidden"}
|
||||
:children [(if (contains? pod-info :value)
|
||||
:children [(if (or value? previous-value?)
|
||||
[components/simple-render
|
||||
(:value pod-info)
|
||||
main-value
|
||||
["sub-path" path]]
|
||||
[rc/label :style {:font-style "italic"} :label "Subscription not run, so no value produced."]
|
||||
)]])]
|
||||
)]]))]
|
||||
[animated/component
|
||||
(animated/v-box-options {:enter-animation "accordionVertical"
|
||||
:leave-animation "accordionVertical"
|
||||
:duration animation-duration})
|
||||
(when render-diff?
|
||||
(let [diffable? (and value? previous-value?)
|
||||
[diff-before diff-after _] (when render-diff?
|
||||
(clojure.data/diff (:previous-value pod-info)
|
||||
(:value pod-info)))]
|
||||
[rc/v-box
|
||||
:children [[rc/v-box
|
||||
:class "app-db-path--link"
|
||||
|
@ -195,8 +200,11 @@
|
|||
:class "data-viewer data-viewer--top-rule"
|
||||
:style {:overflow-x "auto"
|
||||
:overflow-y "hidden"}
|
||||
:height "50px"
|
||||
:children ["---before-diff---"]]
|
||||
:children [(if diffable?
|
||||
[components/simple-render
|
||||
diff-before
|
||||
["app-db-diff" path]]
|
||||
[:p {:style {:font-style "italic"}} "No previous value exists to diff"])]]
|
||||
[rc/v-box
|
||||
:class "app-db-path--link"
|
||||
:justify :end
|
||||
|
@ -211,8 +219,11 @@
|
|||
:class "data-viewer data-viewer--top-rule rounded-bottom"
|
||||
:style {:overflow-x "auto"
|
||||
:overflow-y "hidden"}
|
||||
:height "50px"
|
||||
:children ["---after-diff---"]]]])]
|
||||
:children [(if diffable?
|
||||
[components/simple-render
|
||||
diff-after
|
||||
["app-db-diff" path]]
|
||||
[:p {:style {:font-style "italic"}} "No previous value exists to diff"])]]]]))]
|
||||
(when open?
|
||||
[rc/gap-f :size pod-padding])]]]]))
|
||||
|
||||
|
@ -226,7 +237,10 @@
|
|||
|
||||
(defn pod-section []
|
||||
(let [all-subs @(rf/subscribe [:subs/visible-subs])
|
||||
sub-expansions @(rf/subscribe [:subs/sub-expansions])]
|
||||
sub-expansions @(rf/subscribe [:subs/sub-expansions])
|
||||
all-subs (if @(rf/subscribe [:settings/debug?])
|
||||
(cons {:id "debug" :value @(rf/subscribe [:subs/current-epoch-sub-state])} all-subs)
|
||||
all-subs)]
|
||||
[rc/v-box
|
||||
:size "1"
|
||||
;:gap pod-gap
|
||||
|
@ -246,11 +260,11 @@
|
|||
:style {:flex "1 1 0px"
|
||||
:overflow-x "hidden"
|
||||
:overflow-y "auto"}})
|
||||
|
||||
(for [p all-subs]
|
||||
^{:key (:id p)}
|
||||
[pod (merge p (get sub-expansions (:id p)))])]]
|
||||
|
||||
|
||||
]))
|
||||
|
||||
(defn render []
|
||||
|
|
Loading…
Reference in New Issue