From 829be2efa1ea1cbe268c44560bbd0d05efb530cf Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 31 Aug 2015 08:28:21 +0200 Subject: [PATCH 01/58] Start switch to three-state dirty status --- src/reagent/ratom.cljs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index b796be2..55fa5b8 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -197,6 +197,10 @@ (-handle-change [k sender oldval newval]) (-peek-at [this])) +(def clean 0) +(def maybe-dirty 1) +(def is-dirty 2) + (deftype Reaction [f ^:mutable state ^:mutable dirty? ^:mutable active? ^:mutable watching ^:mutable watches auto-run on-set on-dispose] @@ -224,7 +228,7 @@ (let [oldval state] (set! state newval) (when on-set - (set! dirty? true) + (set! dirty? is-dirty) (on-set oldval newval)) (-notify-watches a oldval newval) newval)) @@ -242,7 +246,7 @@ IComputedImpl (-handle-change [this sender oldval newval] (when (and active? (not (identical? oldval newval))) - (set! dirty? true) + (set! dirty? is-dirty) ((or auto-run run) this))) (-update-watching [this derefed] @@ -255,7 +259,7 @@ (set! watching derefed)) (-peek-at [this] - (if-not dirty? + (if (== dirty? clean) state (binding [*ratom-context* nil] (-deref this)))) @@ -270,7 +274,7 @@ (when-not active? (when debug (swap! -running inc)) (set! active? true)) - (set! dirty? false) + (set! dirty? clean) (set! state res) (-notify-watches this oldstate state) res)) @@ -280,11 +284,11 @@ (if (or auto-run (some? *ratom-context*)) (do (notify-deref-watcher! this) - (if dirty? + (if-not (== dirty? clean) (run this) state)) (do - (when dirty? + (when-not (== dirty? clean) (let [oldstate state] (set! state (f)) (when-not (identical? oldstate state) @@ -297,7 +301,7 @@ (remove-watch w this)) (set! watching nil) (set! state nil) - (set! dirty? true) + (set! dirty? is-dirty) (when active? (when debug (swap! -running dec)) (set! active? false)) @@ -319,7 +323,7 @@ (defn make-reaction [f & {:keys [auto-run on-set on-dispose derefed]}] (let [runner (if (= auto-run true) run auto-run) active (not (nil? derefed)) - dirty (not active) + dirty (if (not active) is-dirty clean) reaction (Reaction. f nil dirty active nil nil runner on-set on-dispose)] From 1782ff1832c8299c4c51ce483c748c3efd40fece Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 31 Aug 2015 10:05:53 +0200 Subject: [PATCH 02/58] wip - not working --- src/reagent/ratom.cljs | 15 ++++++++++++--- test/reagenttest/testcursor.cljs | 6 ++++-- test/reagenttest/testratom.cljs | 6 ++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 55fa5b8..a9d7440 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -245,9 +245,18 @@ IComputedImpl (-handle-change [this sender oldval newval] - (when (and active? (not (identical? oldval newval))) - (set! dirty? is-dirty) - ((or auto-run run) this))) + (when active? + (set! dirty? (max dirty? + (if (identical? oldval newval) + maybe-dirty is-dirty))) + (if auto-run + (when (> dirty? clean) + ((or auto-run run) this)) + (-notify-watches this state state))) + ;; (when (and active? (not (identical? oldval newval))) + ;; (set! dirty? is-dirty) + ;; ((or auto-run run) this)) + ) (-update-watching [this derefed] (doseq [w derefed] diff --git a/test/reagenttest/testcursor.cljs b/test/reagenttest/testcursor.cljs index 77fa32f..a3d14de 100644 --- a/test/reagenttest/testcursor.cljs +++ b/test/reagenttest/testcursor.cljs @@ -11,6 +11,8 @@ (rv/running)) (defn dispose [v] (rv/dispose! v)) +(def testite 1) + (deftest basic-cursor (let [runs (running) start-base (rv/atom {:a {:b {:c 0}}}) @@ -85,7 +87,7 @@ (deftest test-unsubscribe - (dotimes [x 10] + (dotimes [x testite] (let [runs (running) a-base (rv/atom {:test {:unsubscribe 0 :value 42}}) a (r/cursor a-base [:test :unsubscribe]) @@ -171,7 +173,7 @@ (is (= runs (running))))) (deftest test-dispose - (dotimes [x 10] + (dotimes [x testite] (let [runs (running) a-base (rv/atom {:a 0 :b 0}) a (r/cursor a-base [:a]) diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index f96a8fc..2258e0d 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -8,6 +8,8 @@ (set! rv/debug true) (rv/running)) +(def testite 1) + (defn dispose [v] (rv/dispose! v)) @@ -89,7 +91,7 @@ (deftest test-unsubscribe - (dotimes [x 10] + (dotimes [x testite] (let [runs (running) a (rv/atom 0) a1 (reaction (inc @a)) @@ -166,7 +168,7 @@ (is (= runs (running))))) (deftest test-dispose - (dotimes [x 10] + (dotimes [x testite] (let [runs (running) a (rv/atom 0) disposed (rv/atom nil) From 043c8b0dee191bb3ebd03e1b9140920f6d73a6a9 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 31 Aug 2015 10:18:21 +0200 Subject: [PATCH 03/58] wip: sort of working --- src/reagent/ratom.cljs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index a9d7440..1621492 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -203,7 +203,7 @@ (deftype Reaction [f ^:mutable state ^:mutable dirty? ^:mutable active? ^:mutable watching ^:mutable watches - auto-run on-set on-dispose] + ^:mutable auto-run on-set on-dispose] IAtom IReactiveAtom @@ -290,6 +290,14 @@ IDeref (-deref [this] + (when (== dirty? maybe-dirty) + (let [oldauto auto-run] + (set! auto-run nil) + (doseq [w watching] + (-deref w)) + (set! auto-run oldauto)) + (when (== dirty? maybe-dirty) + (set! dirty? clean))) (if (or auto-run (some? *ratom-context*)) (do (notify-deref-watcher! this) From 6c345db29e45a64b144ab0ddda007c694e9533b7 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 31 Aug 2015 10:39:17 +0200 Subject: [PATCH 04/58] Now almost working --- src/reagent/ratom.cljs | 30 +++++++++++++++++++----------- test/reagenttest/testcursor.cljs | 2 +- test/reagenttest/testratom.cljs | 2 +- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 1621492..ad6d3b1 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -195,7 +195,8 @@ (defprotocol IComputedImpl (-update-watching [this derefed]) (-handle-change [k sender oldval newval]) - (-peek-at [this])) + (-peek-at [this]) + (-check-clean [this])) (def clean 0) (def maybe-dirty 1) @@ -250,8 +251,12 @@ (if (identical? oldval newval) maybe-dirty is-dirty))) (if auto-run - (when (> dirty? clean) - ((or auto-run run) this)) + (do + ;; FIXME: is this correct? + (binding [*ratom-context* this] + (-check-clean this)) + (when (> dirty? clean) + ((or auto-run run) this))) (-notify-watches this state state))) ;; (when (and active? (not (identical? oldval newval))) ;; (set! dirty? is-dirty) @@ -273,6 +278,16 @@ (binding [*ratom-context* nil] (-deref this)))) + (-check-clean [this] + (when (== dirty? maybe-dirty) + (let [oldauto auto-run] + (set! auto-run nil) + (doseq [w watching] + (-deref w)) + (set! auto-run oldauto)) + (when (== dirty? maybe-dirty) + (set! dirty? clean)))) + IRunnable (run [this] (let [oldstate state @@ -290,14 +305,7 @@ IDeref (-deref [this] - (when (== dirty? maybe-dirty) - (let [oldauto auto-run] - (set! auto-run nil) - (doseq [w watching] - (-deref w)) - (set! auto-run oldauto)) - (when (== dirty? maybe-dirty) - (set! dirty? clean))) + (-check-clean this) (if (or auto-run (some? *ratom-context*)) (do (notify-deref-watcher! this) diff --git a/test/reagenttest/testcursor.cljs b/test/reagenttest/testcursor.cljs index a3d14de..5c08736 100644 --- a/test/reagenttest/testcursor.cljs +++ b/test/reagenttest/testcursor.cljs @@ -11,7 +11,7 @@ (rv/running)) (defn dispose [v] (rv/dispose! v)) -(def testite 1) +(def testite 10) (deftest basic-cursor (let [runs (running) diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index 2258e0d..0f91a40 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -8,7 +8,7 @@ (set! rv/debug true) (rv/running)) -(def testite 1) +(def testite 10) (defn dispose [v] (rv/dispose! v)) From be7bb6954807314e7c63fe18ba9504799732ce98 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 31 Aug 2015 10:59:59 +0200 Subject: [PATCH 05/58] wip --- src/reagent/ratom.cljs | 24 +++++++++++++----------- test/reagenttest/testreagent.cljs | 1 + 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index ad6d3b1..f658645 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -247,17 +247,19 @@ IComputedImpl (-handle-change [this sender oldval newval] (when active? - (set! dirty? (max dirty? - (if (identical? oldval newval) - maybe-dirty is-dirty))) - (if auto-run - (do - ;; FIXME: is this correct? - (binding [*ratom-context* this] - (-check-clean this)) - (when (> dirty? clean) - ((or auto-run run) this))) - (-notify-watches this state state))) + (let [old-dirty dirty?] + (set! dirty? (max dirty? + (if (identical? oldval newval) + maybe-dirty is-dirty))) + (if auto-run + (do + ;; FIXME: is this correct? + (when (== dirty? maybe-dirty) + (binding [*ratom-context* (js-obj)] + (-check-clean this))) + (when-not (== dirty? clean) + ((or auto-run run) this))) + (-notify-watches this state state)))) ;; (when (and active? (not (identical? oldval newval))) ;; (set! dirty? is-dirty) ;; ((or auto-run run) this)) diff --git a/test/reagenttest/testreagent.cljs b/test/reagenttest/testreagent.cljs index bad298a..45d3d66 100644 --- a/test/reagenttest/testreagent.cljs +++ b/test/reagenttest/testreagent.cljs @@ -119,6 +119,7 @@ (reset! val 1) (reset! val 2) (reset! val 1) + (is (= 1 @ran)) (r/flush) (is (found-in #"val 1" div)) (is (= 2 @ran)) From c4cd8c29210fad0c79f67929c43c4ec10637d0a6 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 31 Aug 2015 11:36:42 +0200 Subject: [PATCH 06/58] wip: tests pass --- src/reagent/ratom.cljs | 20 +++++++++++--------- test/reagenttest/testcursor.cljs | 2 +- test/reagenttest/testratom.cljs | 2 +- test/reagenttest/testreagent.cljs | 4 ++-- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index f658645..24a74d8 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -204,7 +204,7 @@ (deftype Reaction [f ^:mutable state ^:mutable dirty? ^:mutable active? ^:mutable watching ^:mutable watches - ^:mutable auto-run on-set on-dispose] + auto-run on-set on-dispose ^:mutable norun?] IAtom IReactiveAtom @@ -251,7 +251,7 @@ (set! dirty? (max dirty? (if (identical? oldval newval) maybe-dirty is-dirty))) - (if auto-run + (if (and auto-run (not norun?)) (do ;; FIXME: is this correct? (when (== dirty? maybe-dirty) @@ -259,7 +259,8 @@ (-check-clean this))) (when-not (== dirty? clean) ((or auto-run run) this))) - (-notify-watches this state state)))) + (when (== old-dirty clean) + (-notify-watches this state state))))) ;; (when (and active? (not (identical? oldval newval))) ;; (set! dirty? is-dirty) ;; ((or auto-run run) this)) @@ -282,16 +283,16 @@ (-check-clean [this] (when (== dirty? maybe-dirty) - (let [oldauto auto-run] - (set! auto-run nil) - (doseq [w watching] - (-deref w)) - (set! auto-run oldauto)) + (set! norun? true) + (doseq [w watching] + (-deref w)) + (set! norun? false) (when (== dirty? maybe-dirty) (set! dirty? clean)))) IRunnable (run [this] + (set! norun? true) (let [oldstate state res (capture-derefed f this) derefed (captured this)] @@ -300,6 +301,7 @@ (when-not active? (when debug (swap! -running inc)) (set! active? true)) + (set! norun? false) (set! dirty? clean) (set! state res) (-notify-watches this oldstate state) @@ -353,7 +355,7 @@ dirty (if (not active) is-dirty clean) reaction (Reaction. f nil dirty active nil nil - runner on-set on-dispose)] + runner on-set on-dispose false)] (when-not (nil? derefed) (when debug (swap! -running inc)) (-update-watching reaction derefed)) diff --git a/test/reagenttest/testcursor.cljs b/test/reagenttest/testcursor.cljs index 5c08736..ac73d31 100644 --- a/test/reagenttest/testcursor.cljs +++ b/test/reagenttest/testcursor.cljs @@ -31,7 +31,7 @@ (is (= @out 2)) (reset! start 1) (is (= @out 3)) - (is (= @count 4)) + (is (= @count 2)) (dispose const) (is (= @start-base {:a {:b {:c 1}}})) (is (= (running) runs)))) diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index 0f91a40..8df5eb2 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -42,7 +42,7 @@ (is (= @out 2)) (reset! start 1) (is (= @out 3)) - (is (= @count 4)) + (is (= @count 2)) (dispose const) (is (= (running) runs)))) diff --git a/test/reagenttest/testreagent.cljs b/test/reagenttest/testreagent.cljs index 45d3d66..d7d94d9 100644 --- a/test/reagenttest/testreagent.cljs +++ b/test/reagenttest/testreagent.cljs @@ -122,13 +122,13 @@ (is (= 1 @ran)) (r/flush) (is (found-in #"val 1" div)) - (is (= 2 @ran)) + (is (= 2 @ran) "ran once more") ;; should not be rendered (reset! val 1) (r/flush) (is (found-in #"val 1" div)) - (is (= 2 @ran)))) + (is (= 2 @ran) "did not run"))) (is (= runs (running))) (is (= 2 @ran))))) From 02be9425e571b4af4802825dded6931452514a59 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 10 Sep 2015 14:55:07 +0200 Subject: [PATCH 07/58] Move parts of Reaction to plain functions --- src/reagent/ratom.cljs | 110 +++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 24a74d8..1ac4dff 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -193,15 +193,58 @@ (run [this])) (defprotocol IComputedImpl - (-update-watching [this derefed]) - (-handle-change [k sender oldval newval]) - (-peek-at [this]) - (-check-clean [this])) + (-peek-at [this])) (def clean 0) (def maybe-dirty 1) (def is-dirty 2) +(declare reaction?) + +(defn- ra-notify-watches [ra oldval newval] + (reduce-kv (fn [_ key f] + (f key ra oldval newval) + nil) + nil ra.watches)) + +(defn- ra-check-clean [ra] + (when (== ra.dirty? maybe-dirty) + (set! ra.norun? true) + (doseq [w ra.watching] + (when (reaction? w) + (-deref w))) + (set! ra.norun? false) + (when (== ra.dirty? maybe-dirty) + (set! ra.dirty? clean)))) + +(defn- ra-handle-change [ra sender oldval newval] + (when ra.active? + (let [old-dirty ra.dirty?] + (set! ra.dirty? (max ra.dirty? + (if (identical? oldval newval) + (if (reaction? sender) + maybe-dirty clean) + is-dirty))) + (if (and ra.auto-run (not ra.norun?)) + (do + ;; FIXME: is this correct? + (when (== ra.dirty? maybe-dirty) + (binding [*ratom-context* (js-obj)] + (ra-check-clean ra))) + (when-not (== ra.dirty? clean) + ((or ra.auto-run run) ra))) + (when (== old-dirty clean) + (ra-notify-watches ra ra.state ra.state)))))) + +(defn- ra-update-watching [ra derefed] + (doseq [w derefed] + (when-not (contains? ra.watching w) + (add-watch w ra ra-handle-change))) + (doseq [w ra.watching] + (when-not (contains? derefed w) + (remove-watch w ra))) + (set! ra.watching derefed)) + (deftype Reaction [f ^:mutable state ^:mutable dirty? ^:mutable active? ^:mutable watching ^:mutable watches auto-run on-set on-dispose ^:mutable norun?] @@ -210,10 +253,7 @@ IWatchable (-notify-watches [this oldval newval] - (reduce-kv (fn [_ key f] - (f key this oldval newval) - nil) - nil watches)) + (ra-notify-watches this oldval newval)) (-add-watch [this k wf] (set! watches (assoc watches k wf))) @@ -231,7 +271,7 @@ (when on-set (set! dirty? is-dirty) (on-set oldval newval)) - (-notify-watches a oldval newval) + (ra-notify-watches a oldval newval) newval)) ISwap @@ -245,51 +285,12 @@ (-reset! a (apply f (-peek-at a) x y more))) IComputedImpl - (-handle-change [this sender oldval newval] - (when active? - (let [old-dirty dirty?] - (set! dirty? (max dirty? - (if (identical? oldval newval) - maybe-dirty is-dirty))) - (if (and auto-run (not norun?)) - (do - ;; FIXME: is this correct? - (when (== dirty? maybe-dirty) - (binding [*ratom-context* (js-obj)] - (-check-clean this))) - (when-not (== dirty? clean) - ((or auto-run run) this))) - (when (== old-dirty clean) - (-notify-watches this state state))))) - ;; (when (and active? (not (identical? oldval newval))) - ;; (set! dirty? is-dirty) - ;; ((or auto-run run) this)) - ) - - (-update-watching [this derefed] - (doseq [w derefed] - (when-not (contains? watching w) - (add-watch w this -handle-change))) - (doseq [w watching] - (when-not (contains? derefed w) - (remove-watch w this))) - (set! watching derefed)) - (-peek-at [this] (if (== dirty? clean) state (binding [*ratom-context* nil] (-deref this)))) - (-check-clean [this] - (when (== dirty? maybe-dirty) - (set! norun? true) - (doseq [w watching] - (-deref w)) - (set! norun? false) - (when (== dirty? maybe-dirty) - (set! dirty? clean)))) - IRunnable (run [this] (set! norun? true) @@ -297,19 +298,19 @@ res (capture-derefed f this) derefed (captured this)] (when (not= derefed watching) - (-update-watching this derefed)) + (ra-update-watching this derefed)) (when-not active? (when debug (swap! -running inc)) (set! active? true)) (set! norun? false) (set! dirty? clean) (set! state res) - (-notify-watches this oldstate state) + (ra-notify-watches this oldstate state) res)) IDeref (-deref [this] - (-check-clean this) + (ra-check-clean this) (if (or auto-run (some? *ratom-context*)) (do (notify-deref-watcher! this) @@ -321,7 +322,7 @@ (let [oldstate state] (set! state (f)) (when-not (identical? oldstate state) - (-notify-watches this oldstate state)))) + (ra-notify-watches this oldstate state)))) state))) IDisposable @@ -349,6 +350,9 @@ IHash (-hash [this] (goog/getUid this))) +(defn- reaction? [ra] + (instance? Reaction ra)) + (defn make-reaction [f & {:keys [auto-run on-set on-dispose derefed]}] (let [runner (if (= auto-run true) run auto-run) active (not (nil? derefed)) @@ -358,7 +362,7 @@ runner on-set on-dispose false)] (when-not (nil? derefed) (when debug (swap! -running inc)) - (-update-watching reaction derefed)) + (ra-update-watching reaction derefed)) reaction)) From 06348061044aae02ac252b44ff6e3153a6836dce Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 10 Sep 2015 15:14:20 +0200 Subject: [PATCH 08/58] wip --- project.clj | 1 + src/reagent/ratom.cljs | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/project.clj b/project.clj index 10f06a3..2c78928 100644 --- a/project.clj +++ b/project.clj @@ -47,6 +47,7 @@ {:compiler {:optimizations :advanced :elide-asserts true :pretty-print false + ;; :pseudo-names true :output-dir "target/client"}}}}}] :prod-test [:test :prod] diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 1ac4dff..4fa47f5 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -211,12 +211,17 @@ (when (== ra.dirty? maybe-dirty) (set! ra.norun? true) (doseq [w ra.watching] - (when (reaction? w) + (when (and (reaction? w) + (not (identical? (.-dirty? w) clean))) (-deref w))) (set! ra.norun? false) (when (== ra.dirty? maybe-dirty) (set! ra.dirty? clean)))) +(defn- ra-run-check [ra] + (binding [*ratom-context* (js-obj)] + (ra-check-clean ra))) + (defn- ra-handle-change [ra sender oldval newval] (when ra.active? (let [old-dirty ra.dirty?] @@ -229,8 +234,7 @@ (do ;; FIXME: is this correct? (when (== ra.dirty? maybe-dirty) - (binding [*ratom-context* (js-obj)] - (ra-check-clean ra))) + (ra-run-check ra)) (when-not (== ra.dirty? clean) ((or ra.auto-run run) ra))) (when (== old-dirty clean) From 31b06375ef0bf325655b13f28dc47f226941f120 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 10 Sep 2015 16:06:53 +0200 Subject: [PATCH 09/58] Use run instead of deref --- src/reagent/ratom.cljs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 4fa47f5..09de49d 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -212,16 +212,14 @@ (set! ra.norun? true) (doseq [w ra.watching] (when (and (reaction? w) - (not (identical? (.-dirty? w) clean))) - (-deref w))) + (not (== (.-dirty? w) clean))) + (ra-check-clean w) + (when-not (== (.-dirty? w) clean) + (run w)))) (set! ra.norun? false) (when (== ra.dirty? maybe-dirty) (set! ra.dirty? clean)))) -(defn- ra-run-check [ra] - (binding [*ratom-context* (js-obj)] - (ra-check-clean ra))) - (defn- ra-handle-change [ra sender oldval newval] (when ra.active? (let [old-dirty ra.dirty?] @@ -232,9 +230,7 @@ is-dirty))) (if (and ra.auto-run (not ra.norun?)) (do - ;; FIXME: is this correct? - (when (== ra.dirty? maybe-dirty) - (ra-run-check ra)) + (ra-check-clean ra) (when-not (== ra.dirty? clean) ((or ra.auto-run run) ra))) (when (== old-dirty clean) From e09534c8cd1d2cbce6446a4c24b5c1f68f5bf526 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 10 Sep 2015 17:40:55 +0200 Subject: [PATCH 10/58] Experiment with alternative to set --- src/reagent/ratom.cljs | 98 +++++++++++++++++++++++++++------ test/reagenttest/testratom.cljs | 1 + 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 09de49d..a75f038 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -12,6 +12,56 @@ (defn running [] @-running) +(defn- make-oset [] + (let [o (js-obj)] + (set! (.-size o) 0) + o)) + +(defn- oset-size [o] + (.-size o)) + +(defn- oset-add [o x] + (let [h (goog/getUid x)] + (when-not (identical? (js-in h o) true) + (aset o h x) + (set! (.-size o) (inc (.-size o))))) + o) + +(defn- ^boolean oset-contains? [o x] + (if (nil? o) + false + (identical? true (js-in (goog/getUid x) o)))) + +(defn- oset-for-each [o f] + (when (some? o) + (goog.object.forEach o (fn [v k _] + (when-not (identical? k "size") + (f v)))))) + +(defn- ^boolean oset-equal? [o1 o2] + ;; (or (and (nil? o1) (nil? o2)) + (if (and (nil? o1) (nil? o2)) + true + (and (not (nil? o1)) (not (nil? o2)) + (== (.-size o1) (.-size o2)) + (goog.object.every o1 (fn [v k _] + (js-in k o2)))))) + +(do + (def someobj #js{:foo-bar 1}) + (def os (make-oset)) + (oset-add os someobj) + (oset-add os someobj) + (js/console.log os (oset-size os)) + (js/console.log (oset-contains? os someobj)) + (oset-for-each os #(js/console.log "fe" %1)) + + (def os2 (make-oset)) + (js/console.log (oset-equal? os os2)) + (oset-add os2 someobj) + (js/console.log (oset-equal? os os2))) + + (defn capture-derefed [f obj] (set! (.-cljsCaptured obj) nil) (binding [*ratom-context* obj] @@ -27,7 +77,9 @@ (when-not (nil? obj) (let [captured (.-cljsCaptured obj)] (set! (.-cljsCaptured obj) - (conj (if (nil? captured) #{} captured) + (oset-add (if (nil? captured) (make-oset) captured) + derefable) + #_(conj (if (nil? captured) #{} captured) derefable)))))) @@ -210,12 +262,13 @@ (defn- ra-check-clean [ra] (when (== ra.dirty? maybe-dirty) (set! ra.norun? true) - (doseq [w ra.watching] - (when (and (reaction? w) - (not (== (.-dirty? w) clean))) - (ra-check-clean w) - (when-not (== (.-dirty? w) clean) - (run w)))) + (oset-for-each ra.watching + (fn [w] + (when (and (reaction? w) + (not (== (.-dirty? w) clean))) + (ra-check-clean w) + (when-not (== (.-dirty? w) clean) + (run w))))) (set! ra.norun? false) (when (== ra.dirty? maybe-dirty) (set! ra.dirty? clean)))) @@ -237,12 +290,20 @@ (ra-notify-watches ra ra.state ra.state)))))) (defn- ra-update-watching [ra derefed] - (doseq [w derefed] - (when-not (contains? ra.watching w) - (add-watch w ra ra-handle-change))) - (doseq [w ra.watching] - (when-not (contains? derefed w) - (remove-watch w ra))) + (oset-for-each derefed + (fn [w] + (when-not (oset-contains? ra.watching w) + (add-watch w ra ra-handle-change)))) + (oset-for-each ra.watching + (fn [w] + (when-not (oset-contains? derefed w) + (remove-watch w ra)))) + ;; (doseq [w derefed] + ;; (when-not (contains? ra.watching w) + ;; (add-watch w ra ra-handle-change))) + ;; (doseq [w ra.watching] + ;; (when-not (contains? derefed w) + ;; (remove-watch w ra))) (set! ra.watching derefed)) (deftype Reaction [f ^:mutable state ^:mutable dirty? ^:mutable active? @@ -297,8 +358,10 @@ (let [oldstate state res (capture-derefed f this) derefed (captured this)] - (when (not= derefed watching) + (when-not (oset-equal? derefed watching) (ra-update-watching this derefed)) + ;; (when (not= derefed watching) + ;; (ra-update-watching this derefed)) (when-not active? (when debug (swap! -running inc)) (set! active? true)) @@ -327,8 +390,11 @@ IDisposable (dispose! [this] - (doseq [w watching] - (remove-watch w this)) + (oset-for-each watching + (fn [w] + (remove-watch w this))) + ;; (doseq [w watching] + ;; (remove-watch w this)) (set! watching nil) (set! state nil) (set! dirty? is-dirty) diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index 8df5eb2..b64414f 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -23,6 +23,7 @@ (swap! a inc))) (dispose res))) +(enable-console-print!) ;; (ratom-perf) (deftest basic-ratom From c6ca75ff0ef01db615fa1bcb5f8de0042a0efbfd Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 10 Sep 2015 17:43:07 +0200 Subject: [PATCH 11/58] Revert last commit --- src/reagent/ratom.cljs | 98 +++++++----------------------------------- 1 file changed, 16 insertions(+), 82 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index a75f038..09de49d 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -12,56 +12,6 @@ (defn running [] @-running) -(defn- make-oset [] - (let [o (js-obj)] - (set! (.-size o) 0) - o)) - -(defn- oset-size [o] - (.-size o)) - -(defn- oset-add [o x] - (let [h (goog/getUid x)] - (when-not (identical? (js-in h o) true) - (aset o h x) - (set! (.-size o) (inc (.-size o))))) - o) - -(defn- ^boolean oset-contains? [o x] - (if (nil? o) - false - (identical? true (js-in (goog/getUid x) o)))) - -(defn- oset-for-each [o f] - (when (some? o) - (goog.object.forEach o (fn [v k _] - (when-not (identical? k "size") - (f v)))))) - -(defn- ^boolean oset-equal? [o1 o2] - ;; (or (and (nil? o1) (nil? o2)) - (if (and (nil? o1) (nil? o2)) - true - (and (not (nil? o1)) (not (nil? o2)) - (== (.-size o1) (.-size o2)) - (goog.object.every o1 (fn [v k _] - (js-in k o2)))))) - -(do - (def someobj #js{:foo-bar 1}) - (def os (make-oset)) - (oset-add os someobj) - (oset-add os someobj) - (js/console.log os (oset-size os)) - (js/console.log (oset-contains? os someobj)) - (oset-for-each os #(js/console.log "fe" %1)) - - (def os2 (make-oset)) - (js/console.log (oset-equal? os os2)) - (oset-add os2 someobj) - (js/console.log (oset-equal? os os2))) - - (defn capture-derefed [f obj] (set! (.-cljsCaptured obj) nil) (binding [*ratom-context* obj] @@ -77,9 +27,7 @@ (when-not (nil? obj) (let [captured (.-cljsCaptured obj)] (set! (.-cljsCaptured obj) - (oset-add (if (nil? captured) (make-oset) captured) - derefable) - #_(conj (if (nil? captured) #{} captured) + (conj (if (nil? captured) #{} captured) derefable)))))) @@ -262,13 +210,12 @@ (defn- ra-check-clean [ra] (when (== ra.dirty? maybe-dirty) (set! ra.norun? true) - (oset-for-each ra.watching - (fn [w] - (when (and (reaction? w) - (not (== (.-dirty? w) clean))) - (ra-check-clean w) - (when-not (== (.-dirty? w) clean) - (run w))))) + (doseq [w ra.watching] + (when (and (reaction? w) + (not (== (.-dirty? w) clean))) + (ra-check-clean w) + (when-not (== (.-dirty? w) clean) + (run w)))) (set! ra.norun? false) (when (== ra.dirty? maybe-dirty) (set! ra.dirty? clean)))) @@ -290,20 +237,12 @@ (ra-notify-watches ra ra.state ra.state)))))) (defn- ra-update-watching [ra derefed] - (oset-for-each derefed - (fn [w] - (when-not (oset-contains? ra.watching w) - (add-watch w ra ra-handle-change)))) - (oset-for-each ra.watching - (fn [w] - (when-not (oset-contains? derefed w) - (remove-watch w ra)))) - ;; (doseq [w derefed] - ;; (when-not (contains? ra.watching w) - ;; (add-watch w ra ra-handle-change))) - ;; (doseq [w ra.watching] - ;; (when-not (contains? derefed w) - ;; (remove-watch w ra))) + (doseq [w derefed] + (when-not (contains? ra.watching w) + (add-watch w ra ra-handle-change))) + (doseq [w ra.watching] + (when-not (contains? derefed w) + (remove-watch w ra))) (set! ra.watching derefed)) (deftype Reaction [f ^:mutable state ^:mutable dirty? ^:mutable active? @@ -358,10 +297,8 @@ (let [oldstate state res (capture-derefed f this) derefed (captured this)] - (when-not (oset-equal? derefed watching) + (when (not= derefed watching) (ra-update-watching this derefed)) - ;; (when (not= derefed watching) - ;; (ra-update-watching this derefed)) (when-not active? (when debug (swap! -running inc)) (set! active? true)) @@ -390,11 +327,8 @@ IDisposable (dispose! [this] - (oset-for-each watching - (fn [w] - (remove-watch w this))) - ;; (doseq [w watching] - ;; (remove-watch w this)) + (doseq [w watching] + (remove-watch w this)) (set! watching nil) (set! state nil) (set! dirty? is-dirty) From 80a3c27fac84637ff813d4e75587b5709e00e528 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 10 Sep 2015 18:30:00 +0200 Subject: [PATCH 12/58] Some performance tweaking --- src/reagent/ratom.cljs | 57 ++++++++++++++++----------------- test/reagenttest/testratom.cljs | 11 +++++-- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 09de49d..b60581a 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -199,51 +199,53 @@ (def maybe-dirty 1) (def is-dirty 2) -(declare reaction?) +(declare Reaction) +(defn- ^boolean reaction? [ra] + (instance? Reaction ra)) (defn- ra-notify-watches [ra oldval newval] (reduce-kv (fn [_ key f] (f key ra oldval newval) nil) - nil ra.watches)) + nil (.-watches ra))) (defn- ra-check-clean [ra] - (when (== ra.dirty? maybe-dirty) - (set! ra.norun? true) - (doseq [w ra.watching] + (when (== (.-dirty? ra) maybe-dirty) + (set! (.-norun? ra) true) + (doseq [w (.-watching ra)] (when (and (reaction? w) (not (== (.-dirty? w) clean))) (ra-check-clean w) (when-not (== (.-dirty? w) clean) (run w)))) - (set! ra.norun? false) - (when (== ra.dirty? maybe-dirty) - (set! ra.dirty? clean)))) + (set! (.-norun? ra) false) + (when (== (.-dirty? ra) maybe-dirty) + (set! (.-dirty? ra) clean)))) (defn- ra-handle-change [ra sender oldval newval] - (when ra.active? - (let [old-dirty ra.dirty?] - (set! ra.dirty? (max ra.dirty? + (when ^boolean (.-active? ra) + (let [old-dirty (.-dirty? ra)] + (set! (.-dirty? ra) (max (.-dirty? ra) (if (identical? oldval newval) (if (reaction? sender) maybe-dirty clean) is-dirty))) - (if (and ra.auto-run (not ra.norun?)) + (if (and (some? (.-auto-run ra)) (not ^boolean (.-norun? ra))) (do (ra-check-clean ra) - (when-not (== ra.dirty? clean) - ((or ra.auto-run run) ra))) + (when-not (== (.-dirty? ra) clean) + ((or (.-auto-run ra) run) ra))) (when (== old-dirty clean) - (ra-notify-watches ra ra.state ra.state)))))) + (ra-notify-watches ra (.-state ra) (.-state ra))))))) (defn- ra-update-watching [ra derefed] (doseq [w derefed] - (when-not (contains? ra.watching w) - (add-watch w ra ra-handle-change))) - (doseq [w ra.watching] + (when-not (contains? (.-watching ra) w) + (-add-watch w ra ra-handle-change))) + (doseq [w (.-watching ra)] (when-not (contains? derefed w) - (remove-watch w ra))) - (set! ra.watching derefed)) + (-remove-watch w ra))) + (set! (.-watching ra) derefed)) (deftype Reaction [f ^:mutable state ^:mutable dirty? ^:mutable active? ^:mutable watching ^:mutable watches @@ -299,8 +301,8 @@ derefed (captured this)] (when (not= derefed watching) (ra-update-watching this derefed)) - (when-not active? - (when debug (swap! -running inc)) + (when-not ^boolean active? + (when ^boolean debug (swap! -running inc)) (set! active? true)) (set! norun? false) (set! dirty? clean) @@ -311,7 +313,7 @@ IDeref (-deref [this] (ra-check-clean this) - (if (or auto-run (some? *ratom-context*)) + (if (or (some? auto-run) (some? *ratom-context*)) (do (notify-deref-watcher! this) (if-not (== dirty? clean) @@ -332,8 +334,8 @@ (set! watching nil) (set! state nil) (set! dirty? is-dirty) - (when active? - (when debug (swap! -running dec)) + (when ^boolean active? + (when ^boolean debug (swap! -running dec)) (set! active? false)) (when on-dispose (on-dispose))) @@ -350,9 +352,6 @@ IHash (-hash [this] (goog/getUid this))) -(defn- reaction? [ra] - (instance? Reaction ra)) - (defn make-reaction [f & {:keys [auto-run on-set on-dispose derefed]}] (let [runner (if (= auto-run true) run auto-run) active (not (nil? derefed)) @@ -361,7 +360,7 @@ nil nil runner on-set on-dispose false)] (when-not (nil? derefed) - (when debug (swap! -running inc)) + (when ^boolean debug (swap! -running inc)) (ra-update-watching reaction derefed)) reaction)) diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index b64414f..0901a8a 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -13,15 +13,20 @@ (defn dispose [v] (rv/dispose! v)) +(def perf-check 0) (defn ratom-perf [] (dbg "ratom-perf") - (let [a (rv/atom 0) + (set! perf-check 0) + (let [nite 100000 + a (rv/atom 0) mid (reaction (inc @a)) res (run! + (set! perf-check (inc perf-check)) (inc @mid))] - (time (dotimes [x 100000] + (time (dotimes [x nite] (swap! a inc))) - (dispose res))) + (dispose res) + (assert (= perf-check nite)))) (enable-console-print!) ;; (ratom-perf) From fe091ad5c19ebd6d81f51c0126672feffc9430f0 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 10 Sep 2015 19:53:24 +0200 Subject: [PATCH 13/58] Back to defining everything inside deftype --- src/reagent/ratom.cljs | 145 ++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index b60581a..82148ed 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -6,7 +6,7 @@ (declare ^:dynamic *ratom-context*) -(defonce debug false) +(defonce ^boolean debug false) (defonce -running (clojure.core/atom 0)) @@ -193,69 +193,28 @@ (run [this])) (defprotocol IComputedImpl - (-peek-at [this])) + (-peek-at [this]) + (-check-clean [this]) + (-handle-change [this sender oldval newval]) + (-update-watching [this derefed])) (def clean 0) (def maybe-dirty 1) -(def is-dirty 2) +(def dirty 2) -(declare Reaction) -(defn- ^boolean reaction? [ra] - (instance? Reaction ra)) - -(defn- ra-notify-watches [ra oldval newval] - (reduce-kv (fn [_ key f] - (f key ra oldval newval) - nil) - nil (.-watches ra))) - -(defn- ra-check-clean [ra] - (when (== (.-dirty? ra) maybe-dirty) - (set! (.-norun? ra) true) - (doseq [w (.-watching ra)] - (when (and (reaction? w) - (not (== (.-dirty? w) clean))) - (ra-check-clean w) - (when-not (== (.-dirty? w) clean) - (run w)))) - (set! (.-norun? ra) false) - (when (== (.-dirty? ra) maybe-dirty) - (set! (.-dirty? ra) clean)))) - -(defn- ra-handle-change [ra sender oldval newval] - (when ^boolean (.-active? ra) - (let [old-dirty (.-dirty? ra)] - (set! (.-dirty? ra) (max (.-dirty? ra) - (if (identical? oldval newval) - (if (reaction? sender) - maybe-dirty clean) - is-dirty))) - (if (and (some? (.-auto-run ra)) (not ^boolean (.-norun? ra))) - (do - (ra-check-clean ra) - (when-not (== (.-dirty? ra) clean) - ((or (.-auto-run ra) run) ra))) - (when (== old-dirty clean) - (ra-notify-watches ra (.-state ra) (.-state ra))))))) - -(defn- ra-update-watching [ra derefed] - (doseq [w derefed] - (when-not (contains? (.-watching ra) w) - (-add-watch w ra ra-handle-change))) - (doseq [w (.-watching ra)] - (when-not (contains? derefed w) - (-remove-watch w ra))) - (set! (.-watching ra) derefed)) - -(deftype Reaction [f ^:mutable state ^:mutable dirty? ^:mutable active? +(deftype Reaction [f ^:mutable state ^:mutable dirtyness + ^:mutable ^boolean active? ^:mutable watching ^:mutable watches - auto-run on-set on-dispose ^:mutable norun?] + auto-run on-set on-dispose ^:mutable ^boolean norun?] IAtom IReactiveAtom IWatchable (-notify-watches [this oldval newval] - (ra-notify-watches this oldval newval)) + (reduce-kv (fn [_ key f] + (f key this oldval newval) + nil) + nil watches)) (-add-watch [this k wf] (set! watches (assoc watches k wf))) @@ -263,7 +222,7 @@ (-remove-watch [this k] (set! watches (dissoc watches k)) (when (and (empty? watches) - (not auto-run)) + (nil? auto-run)) (dispose! this))) IReset @@ -271,9 +230,9 @@ (let [oldval state] (set! state newval) (when on-set - (set! dirty? is-dirty) + (set! dirtyness dirty) (on-set oldval newval)) - (ra-notify-watches a oldval newval) + (-notify-watches a oldval newval) newval)) ISwap @@ -288,11 +247,51 @@ IComputedImpl (-peek-at [this] - (if (== dirty? clean) + (if (== dirtyness clean) state (binding [*ratom-context* nil] (-deref this)))) + (-check-clean [this] + (when (== dirtyness maybe-dirty) + (set! norun? true) + (doseq [w watching] + (when (and (instance? Reaction w) + (not (== (.-dirtyness w) clean))) + (-check-clean w) + (when-not (== (.-dirtyness w) clean) + (run w)))) + (set! norun? false) + (when (== dirtyness maybe-dirty) + (set! dirtyness clean)))) + + (-handle-change [this sender oldval newval] + (when active? + (let [old-dirty dirtyness] + (set! dirtyness (max dirtyness + (if (identical? oldval newval) + (if (instance? Reaction sender) + maybe-dirty clean) + dirty))) + (if (and (some? auto-run) (not norun?)) + (do + (-check-clean this) + (when-not (== dirtyness clean) + ((or auto-run run) this))) + (when (and (not (== dirtyness clean)) + (== old-dirty clean)) + (-notify-watches this state state)))))) + + (-update-watching [this derefed] + (doseq [w derefed] + (when-not (contains? watching w) + (-add-watch w this -handle-change))) + (doseq [w watching] + (when-not (contains? derefed w) + (-remove-watch w this))) + (set! watching derefed) + nil) + IRunnable (run [this] (set! norun? true) @@ -300,31 +299,31 @@ res (capture-derefed f this) derefed (captured this)] (when (not= derefed watching) - (ra-update-watching this derefed)) - (when-not ^boolean active? - (when ^boolean debug (swap! -running inc)) + (-update-watching this derefed)) + (when-not active? + (when debug (swap! -running inc)) (set! active? true)) (set! norun? false) - (set! dirty? clean) + (set! dirtyness clean) (set! state res) - (ra-notify-watches this oldstate state) + (-notify-watches this oldstate state) res)) IDeref (-deref [this] - (ra-check-clean this) + (-check-clean this) (if (or (some? auto-run) (some? *ratom-context*)) (do (notify-deref-watcher! this) - (if-not (== dirty? clean) + (if-not (== dirtyness clean) (run this) state)) (do - (when-not (== dirty? clean) + (when-not (== dirtyness clean) (let [oldstate state] (set! state (f)) (when-not (identical? oldstate state) - (ra-notify-watches this oldstate state)))) + (-notify-watches this oldstate state)))) state))) IDisposable @@ -333,9 +332,9 @@ (remove-watch w this)) (set! watching nil) (set! state nil) - (set! dirty? is-dirty) - (when ^boolean active? - (when ^boolean debug (swap! -running dec)) + (set! dirtyness dirty) + (when active? + (when debug (swap! -running dec)) (set! active? false)) (when on-dispose (on-dispose))) @@ -355,13 +354,13 @@ (defn make-reaction [f & {:keys [auto-run on-set on-dispose derefed]}] (let [runner (if (= auto-run true) run auto-run) active (not (nil? derefed)) - dirty (if (not active) is-dirty clean) + dirty (if (not active) dirty clean) reaction (Reaction. f nil dirty active nil nil runner on-set on-dispose false)] (when-not (nil? derefed) - (when ^boolean debug (swap! -running inc)) - (ra-update-watching reaction derefed)) + (when debug (swap! -running inc)) + (-update-watching reaction derefed)) reaction)) From 67a724b540fa7870929c69017de5048a4a2489eb Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 10 Sep 2015 20:27:52 +0200 Subject: [PATCH 14/58] Cleaner update logic --- src/reagent/ratom.cljs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 82148ed..370500f 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -202,7 +202,7 @@ (def maybe-dirty 1) (def dirty 2) -(deftype Reaction [f ^:mutable state ^:mutable dirtyness +(deftype Reaction [f ^:mutable state ^:mutable ^number dirtyness ^:mutable ^boolean active? ^:mutable watching ^:mutable watches auto-run on-set on-dispose ^:mutable ^boolean norun?] @@ -267,19 +267,18 @@ (-handle-change [this sender oldval newval] (when active? - (let [old-dirty dirtyness] - (set! dirtyness (max dirtyness - (if (identical? oldval newval) - (if (instance? Reaction sender) - maybe-dirty clean) - dirty))) - (if (and (some? auto-run) (not norun?)) - (do - (-check-clean this) - (when-not (== dirtyness clean) - ((or auto-run run) this))) - (when (and (not (== dirtyness clean)) - (== old-dirty clean)) + (let [old-dirty dirtyness + new-dirty (if (identical? oldval newval) + (if (instance? Reaction sender) + maybe-dirty clean) + dirty)] + (when (> new-dirty old-dirty) + (set! dirtyness new-dirty) + (if (some? auto-run) + (when-not norun? + (-check-clean this) + (when-not (== dirtyness clean) + (auto-run this))) (-notify-watches this state state)))))) (-update-watching [this derefed] From 0b77d9af31f9b34d3c31e21a0955559bc76235b6 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 10 Sep 2015 20:40:37 +0200 Subject: [PATCH 15/58] Get rid of active? --- src/reagent/ratom.cljs | 57 ++++++++++++++------------------ test/reagenttest/testcursor.cljs | 4 +-- test/reagenttest/testratom.cljs | 6 ++-- 3 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 370500f..8e022d5 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -30,6 +30,10 @@ (conj (if (nil? captured) #{} captured) derefable)))))) +(defn- check-watches [old new] + (when debug + (swap! -running + (- (count new) (count old)))) + new) ;;; Atom @@ -83,9 +87,9 @@ nil) nil watches)) (-add-watch [this key f] - (set! watches (assoc watches key f))) + (set! watches (check-watches watches (assoc watches key f)))) (-remove-watch [this key] - (set! watches (dissoc watches key))) + (set! watches (check-watches watches (dissoc watches key)))) IHash (-hash [this] (goog/getUid this))) @@ -203,7 +207,6 @@ (def dirty 2) (deftype Reaction [f ^:mutable state ^:mutable ^number dirtyness - ^:mutable ^boolean active? ^:mutable watching ^:mutable watches auto-run on-set on-dispose ^:mutable ^boolean norun?] IAtom @@ -216,11 +219,11 @@ nil) nil watches)) - (-add-watch [this k wf] - (set! watches (assoc watches k wf))) + (-add-watch [this key f] + (set! watches (check-watches watches (assoc watches key f)))) - (-remove-watch [this k] - (set! watches (dissoc watches k)) + (-remove-watch [this key] + (set! watches (check-watches watches (dissoc watches key))) (when (and (empty? watches) (nil? auto-run)) (dispose! this))) @@ -266,20 +269,19 @@ (set! dirtyness clean)))) (-handle-change [this sender oldval newval] - (when active? - (let [old-dirty dirtyness - new-dirty (if (identical? oldval newval) - (if (instance? Reaction sender) - maybe-dirty clean) - dirty)] - (when (> new-dirty old-dirty) - (set! dirtyness new-dirty) - (if (some? auto-run) - (when-not norun? - (-check-clean this) - (when-not (== dirtyness clean) - (auto-run this))) - (-notify-watches this state state)))))) + (let [old-dirty dirtyness + new-dirty (if (identical? oldval newval) + (if (instance? Reaction sender) + maybe-dirty clean) + dirty)] + (when (> new-dirty old-dirty) + (set! dirtyness new-dirty) + (if (some? auto-run) + (when-not norun? + (-check-clean this) + (when-not (== dirtyness clean) + (auto-run this))) + (-notify-watches this state state))))) (-update-watching [this derefed] (doseq [w derefed] @@ -299,9 +301,6 @@ derefed (captured this)] (when (not= derefed watching) (-update-watching this derefed)) - (when-not active? - (when debug (swap! -running inc)) - (set! active? true)) (set! norun? false) (set! dirtyness clean) (set! state res) @@ -332,9 +331,6 @@ (set! watching nil) (set! state nil) (set! dirtyness dirty) - (when active? - (when debug (swap! -running dec)) - (set! active? false)) (when on-dispose (on-dispose))) @@ -352,13 +348,10 @@ (defn make-reaction [f & {:keys [auto-run on-set on-dispose derefed]}] (let [runner (if (= auto-run true) run auto-run) - active (not (nil? derefed)) - dirty (if (not active) dirty clean) - reaction (Reaction. f nil dirty active - nil nil + dirty (if (nil? derefed) dirty clean) + reaction (Reaction. f nil dirty nil nil runner on-set on-dispose false)] (when-not (nil? derefed) - (when debug (swap! -running inc)) (-update-watching reaction derefed)) reaction)) diff --git a/test/reagenttest/testcursor.cljs b/test/reagenttest/testcursor.cljs index ac73d31..dbe6006 100644 --- a/test/reagenttest/testcursor.cljs +++ b/test/reagenttest/testcursor.cljs @@ -193,14 +193,14 @@ :on-dispose #(reset! disposed-cns true))] @cns (is (= @res 2)) - (is (= (+ 4 runs) (running))) + (is (= (+ 5 runs) (running))) (is (= @count-b 1)) (is (= {:a 0 :b 0} @a-base)) (reset! a -1) (is (= @res 1)) (is (= @disposed nil)) (is (= @count-b 2)) - (is (= (+ 4 runs) (running)) "still running") + (is (= (+ 5 runs) (running)) "still running") (is (= {:a -1 :b 0} @a-base)) (reset! a 2) (is (= @res 1)) diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index 0901a8a..792c1aa 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -26,7 +26,7 @@ (time (dotimes [x nite] (swap! a inc))) (dispose res) - (assert (= perf-check nite)))) + (assert (= perf-check (inc nite))))) (enable-console-print!) ;; (ratom-perf) @@ -193,13 +193,13 @@ :on-dispose #(reset! disposed-cns true))] @cns (is (= @res 2)) - (is (= (+ 3 runs) (running))) + (is (= (+ 4 runs) (running))) (is (= @count-b 1)) (reset! a -1) (is (= @res 1)) (is (= @disposed nil)) (is (= @count-b 2)) - (is (= (+ 3 runs) (running)) "still running") + (is (= (+ 4 runs) (running)) "still running") (reset! a 2) (is (= @res 1)) (is (= @disposed true)) From f785362b46dfcabf312667aa069d3e04c5589869 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 10 Sep 2015 21:16:59 +0200 Subject: [PATCH 16/58] Some cleanup --- project.clj | 2 +- src/reagent/ratom.cljs | 23 +++++++++-------------- test/reagenttest/testratom.cljs | 1 + 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/project.clj b/project.clj index 2c78928..b73eca9 100644 --- a/project.clj +++ b/project.clj @@ -47,7 +47,7 @@ {:compiler {:optimizations :advanced :elide-asserts true :pretty-print false - ;; :pseudo-names true + :pseudo-names true :output-dir "target/client"}}}}}] :prod-test [:test :prod] diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 8e022d5..463fac1 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -295,34 +295,29 @@ IRunnable (run [this] - (set! norun? true) (let [oldstate state res (capture-derefed f this) derefed (captured this)] (when (not= derefed watching) (-update-watching this derefed)) - (set! norun? false) (set! dirtyness clean) - (set! state res) - (-notify-watches this oldstate state) + (-notify-watches this oldstate (set! state res)) res)) IDeref (-deref [this] (-check-clean this) - (if (or (some? auto-run) (some? *ratom-context*)) + (if (and (nil? auto-run) (nil? *ratom-context*)) + (when-not (== dirtyness clean) + (let [oldstate state + newstate (f)] + (when-not (identical? oldstate newstate) + (-notify-watches this oldstate (set! state newstate))))) (do (notify-deref-watcher! this) - (if-not (== dirtyness clean) - (run this) - state)) - (do (when-not (== dirtyness clean) - (let [oldstate state] - (set! state (f)) - (when-not (identical? oldstate state) - (-notify-watches this oldstate state)))) - state))) + (run this)))) + state) IDisposable (dispose! [this] diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index 792c1aa..1f983b2 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -16,6 +16,7 @@ (def perf-check 0) (defn ratom-perf [] (dbg "ratom-perf") + (set! rv/debug false) (set! perf-check 0) (let [nite 100000 a (rv/atom 0) From 679136f5d542d7141f6598108fa5769245bff822 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Fri, 11 Sep 2015 12:35:06 +0200 Subject: [PATCH 17/58] Make constants const --- project.clj | 2 +- src/reagent/ratom.cljs | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/project.clj b/project.clj index b73eca9..2c78928 100644 --- a/project.clj +++ b/project.clj @@ -47,7 +47,7 @@ {:compiler {:optimizations :advanced :elide-asserts true :pretty-print false - :pseudo-names true + ;; :pseudo-names true :output-dir "target/client"}}}}}] :prod-test [:test :prod] diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 463fac1..b986a70 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -202,9 +202,9 @@ (-handle-change [this sender oldval newval]) (-update-watching [this derefed])) -(def clean 0) -(def maybe-dirty 1) -(def dirty 2) +(def ^:const clean 0) +(def ^:const maybe-dirty 1) +(def ^:const dirty 2) (deftype Reaction [f ^:mutable state ^:mutable ^number dirtyness ^:mutable watching ^:mutable watches @@ -326,7 +326,7 @@ (set! watching nil) (set! state nil) (set! dirtyness dirty) - (when on-dispose + (when (some? on-dispose) (on-dispose))) IEquiv @@ -419,3 +419,24 @@ (util/partial-ifn. callback-fn args nil) false nil)) +(comment + (def perf-check 0) + (defn ratom-perf [] + (dbg "ratom-perf") + (set! debug false) + (dotimes [_ 10] + (set! perf-check 0) + (let [nite 100000 + a (atom 0) + mid (make-reaction (fn [] (inc @a))) + res (make-reaction (fn [] + (set! perf-check (inc perf-check)) + (inc @mid)) + :auto-run true)] + @res + (time (dotimes [x nite] + (swap! a inc))) + (dispose! res) + (assert (= perf-check (inc nite)))))) + (enable-console-print!) + (ratom-perf)) From b1acdea09022e41475725b913c34bef77ba3e8fe Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Fri, 11 Sep 2015 15:06:42 +0200 Subject: [PATCH 18/58] Use lazy reactions in components --- src/reagent/impl/batching.cljs | 8 ++++-- src/reagent/ratom.cljs | 27 +++++++++++-------- test/reagenttest/testreagent.cljs | 7 ++++- test/reagenttest/testwrap.cljs | 43 +++++++++++++++++++++++++++++-- 4 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/reagent/impl/batching.cljs b/src/reagent/impl/batching.cljs index bd9fb57..094427d 100644 --- a/src/reagent/impl/batching.cljs +++ b/src/reagent/impl/batching.cljs @@ -37,7 +37,10 @@ (dotimes [i (alength a)] (let [c (aget a i)] (when (.' c :cljsIsDirty) - (.' c forceUpdate))))) + (let [a (.' c :cljsRatom)] + (if (ratom/-check-clean a) + (.! c :cljsIsDirty false) + (.' c forceUpdate))))))) (defn run-funs [a] (dotimes [i (alength a)] @@ -98,7 +101,8 @@ (.! c :cljsRatom (ratom/make-reaction run :auto-run #(queue-render c) - :derefed derefed))) + :derefed derefed + :no-cache true))) res) (ratom/run rat)))) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index b986a70..99a1474 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -208,7 +208,8 @@ (deftype Reaction [f ^:mutable state ^:mutable ^number dirtyness ^:mutable watching ^:mutable watches - auto-run on-set on-dispose ^:mutable ^boolean norun?] + auto-run on-set on-dispose ^:mutable ^boolean norun? + ^boolean nocache?] IAtom IReactiveAtom @@ -261,12 +262,12 @@ (doseq [w watching] (when (and (instance? Reaction w) (not (== (.-dirtyness w) clean))) - (-check-clean w) - (when-not (== (.-dirtyness w) clean) - (run w)))) + (when-not (-check-clean w) + ((or (.-auto-run w) run) w)))) (set! norun? false) (when (== dirtyness maybe-dirty) - (set! dirtyness clean)))) + (set! dirtyness clean))) + (== dirtyness clean)) (-handle-change [this sender oldval newval] (let [old-dirty dirtyness @@ -278,9 +279,10 @@ (set! dirtyness new-dirty) (if (some? auto-run) (when-not norun? - (-check-clean this) - (when-not (== dirtyness clean) - (auto-run this))) + (if-not (identical? auto-run run) + (auto-run this) + (when-not (-check-clean this) + (auto-run this)))) (-notify-watches this state state))))) (-update-watching [this derefed] @@ -301,7 +303,9 @@ (when (not= derefed watching) (-update-watching this derefed)) (set! dirtyness clean) - (-notify-watches this oldstate (set! state res)) + (when-not nocache? + (set! state res)) + (-notify-watches this oldstate state) res)) IDeref @@ -341,11 +345,12 @@ IHash (-hash [this] (goog/getUid this))) -(defn make-reaction [f & {:keys [auto-run on-set on-dispose derefed]}] +(defn make-reaction [f & {:keys [auto-run on-set on-dispose derefed no-cache]}] (let [runner (if (= auto-run true) run auto-run) dirty (if (nil? derefed) dirty clean) + nocache (if (nil? no-cache) false no-cache) reaction (Reaction. f nil dirty nil nil - runner on-set on-dispose false)] + runner on-set on-dispose false nocache)] (when-not (nil? derefed) (-update-watching reaction derefed)) reaction)) diff --git a/test/reagenttest/testreagent.cljs b/test/reagenttest/testreagent.cljs index d7d94d9..718b052 100644 --- a/test/reagenttest/testreagent.cljs +++ b/test/reagenttest/testreagent.cljs @@ -103,7 +103,8 @@ runs (running) val (r/atom 0) secval (r/atom 0) - v1 (reaction @val) + v1-ran (atom 0) + v1 (reaction (swap! v1-ran inc) @val) comp (fn [] (swap! ran inc) [:div (str "val " @v1 @val @secval)])] @@ -120,13 +121,17 @@ (reset! val 2) (reset! val 1) (is (= 1 @ran)) + (is (= 1 @v1-ran)) (r/flush) (is (found-in #"val 1" div)) (is (= 2 @ran) "ran once more") + (is (= 2 @v1-ran)) ;; should not be rendered (reset! val 1) + (is (= 2 @v1-ran)) (r/flush) + (is (= 2 @v1-ran)) (is (found-in #"val 1" div)) (is (= 2 @ran) "did not run"))) (is (= runs (running))) diff --git a/test/reagenttest/testwrap.cljs b/test/reagenttest/testwrap.cljs index f4377c8..4ad6865 100644 --- a/test/reagenttest/testwrap.cljs +++ b/test/reagenttest/testwrap.cljs @@ -175,8 +175,8 @@ (is (= @ran 7))))))) (deftest test-cursor - (let [state (r/atom {:a 0 - :b 0}) + (let [state (r/atom {:a 1 + :b 2}) a-count (r/atom 0) b-count (r/atom 0) derefer (fn derefer [cur count] @@ -192,6 +192,45 @@ (is (= @b-count 1)) (swap! state update-in [:a] inc) + (is (= @a-count 1)) + + (r/flush) + (is (= @a-count 2)) + (is (= @b-count 1)) + + (reset! state @state) (r/flush) (is (= @a-count 2)) (is (= @b-count 1)))))) + +(deftest test-fn-cursor + (let [state (r/atom {:a 1 + :b 2}) + a-count (r/atom 0) + b-count (r/atom 0) + derefer (fn derefer [cur count] + [:div @cur]) + f (fn [[x y]] (swap! y inc) (get-in @state x)) + ac (r/cursor f [[:a] a-count]) + bc (r/cursor f [[:b] b-count]) + comp (fn test-cursor [] + [:div + [derefer ac] + [derefer bc]])] + (with-mounted-component [comp] + (fn [c div] + (is (= @a-count 1)) + (is (= @b-count 1)) + + (swap! state update-in [:a] inc) + (is (= @a-count 1)) + (is (= @b-count 1)) + + (r/flush) + (is (= @a-count 2)) + (is (= @b-count 2)) + + (reset! state @state) + (r/flush) + (is (= @a-count 2)) + (is (= @b-count 2)))))) From 2895c9bed7e399676640987ddb068e945685958c Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Fri, 11 Sep 2015 18:23:18 +0200 Subject: [PATCH 19/58] A little cleanup --- src/reagent/ratom.cljs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 99a1474..1be00fc 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -198,7 +198,7 @@ (defprotocol IComputedImpl (-peek-at [this]) - (-check-clean [this]) + (^boolean -check-clean [this]) (-handle-change [this sender oldval newval]) (-update-watching [this derefed])) @@ -261,9 +261,10 @@ (set! norun? true) (doseq [w watching] (when (and (instance? Reaction w) - (not (== (.-dirtyness w) clean))) - (when-not (-check-clean w) - ((or (.-auto-run w) run) w)))) + (not (-check-clean w))) + (if-some [ar (.-auto-run w)] + (ar w) + (run w)))) (set! norun? false) (when (== dirtyness maybe-dirty) (set! dirtyness clean))) @@ -277,13 +278,13 @@ dirty)] (when (> new-dirty old-dirty) (set! dirtyness new-dirty) - (if (some? auto-run) + (if-some [arun auto-run] (when-not norun? - (if-not (identical? auto-run run) - (auto-run this) - (when-not (-check-clean this) - (auto-run this)))) - (-notify-watches this state state))))) + (when-not (and (identical? arun run) + (-check-clean this)) + (arun this))) + (-notify-watches this state state)))) + nil) (-update-watching [this derefed] (doseq [w derefed] From b0395b5461b13a10a331c0308df952cee401c748 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Fri, 11 Sep 2015 19:36:55 +0200 Subject: [PATCH 20/58] Make reaction notify only when it actually changes --- src/reagent/ratom.cljs | 7 +++++-- test/reagenttest/testwrap.cljs | 31 +++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 1be00fc..73a84f4 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -305,8 +305,11 @@ (-update-watching this derefed)) (set! dirtyness clean) (when-not nocache? - (set! state res)) - (-notify-watches this oldstate state) + (set! state res) + ;; Use = to determine equality from reactions, since + ;; they are likely to produce new data structures. + (when (not= oldstate res) + (-notify-watches this oldstate res))) res)) IDeref diff --git a/test/reagenttest/testwrap.cljs b/test/reagenttest/testwrap.cljs index 4ad6865..1913a6d 100644 --- a/test/reagenttest/testwrap.cljs +++ b/test/reagenttest/testwrap.cljs @@ -175,8 +175,8 @@ (is (= @ran 7))))))) (deftest test-cursor - (let [state (r/atom {:a 1 - :b 2}) + (let [state (r/atom {:a {:v 1} + :b {:v 2}}) a-count (r/atom 0) b-count (r/atom 0) derefer (fn derefer [cur count] @@ -191,26 +191,32 @@ (is (= @a-count 1)) (is (= @b-count 1)) - (swap! state update-in [:a] inc) + (swap! state update-in [:a :v] inc) (is (= @a-count 1)) (r/flush) (is (= @a-count 2)) (is (= @b-count 1)) - (reset! state @state) + (reset! state {:a {:v 2} :b {:v 2}}) (r/flush) (is (= @a-count 2)) + (is (= @b-count 1)) + + (reset! state {:a {:v 3} :b {:v 2}}) + (r/flush) + (is (= @a-count 3)) (is (= @b-count 1)))))) (deftest test-fn-cursor - (let [state (r/atom {:a 1 - :b 2}) + (let [state (r/atom {:a {:v 1} + :b {:v 2}}) + statec (r/cursor state []) a-count (r/atom 0) b-count (r/atom 0) derefer (fn derefer [cur count] [:div @cur]) - f (fn [[x y]] (swap! y inc) (get-in @state x)) + f (fn [[x y]] (swap! y inc) (get-in @statec x)) ac (r/cursor f [[:a] a-count]) bc (r/cursor f [[:b] b-count]) comp (fn test-cursor [] @@ -222,7 +228,7 @@ (is (= @a-count 1)) (is (= @b-count 1)) - (swap! state update-in [:a] inc) + (swap! state update-in [:a :v] inc) (is (= @a-count 1)) (is (= @b-count 1)) @@ -230,7 +236,12 @@ (is (= @a-count 2)) (is (= @b-count 2)) - (reset! state @state) + (reset! state {:a {:v 2} :b {:v 2}}) (r/flush) (is (= @a-count 2)) - (is (= @b-count 2)))))) + (is (= @b-count 2)) + + (reset! state {:a {:v 3} :b {:v 2}}) + (r/flush) + (is (= @a-count 3)) + (is (= @b-count 3)))))) From bb1fa312d6226b559a548200a6b8676f5dd49e2f Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Fri, 11 Sep 2015 22:13:36 +0200 Subject: [PATCH 21/58] Use = in deref as well, for consistency --- src/reagent/ratom.cljs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 73a84f4..50ab1f6 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -308,7 +308,8 @@ (set! state res) ;; Use = to determine equality from reactions, since ;; they are likely to produce new data structures. - (when (not= oldstate res) + (when (and (some? watches) + (not= oldstate res)) (-notify-watches this oldstate res))) res)) @@ -319,8 +320,10 @@ (when-not (== dirtyness clean) (let [oldstate state newstate (f)] - (when-not (identical? oldstate newstate) - (-notify-watches this oldstate (set! state newstate))))) + (set! state newstate) + (when (and (some? watches) + (not= oldstate newstate)) + (-notify-watches this oldstate newstate)))) (do (notify-deref-watcher! this) (when-not (== dirtyness clean) From f18d4a7656442547d3598d15c4c9e1136361a95d Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sat, 12 Sep 2015 09:37:15 +0200 Subject: [PATCH 22/58] Get rid of norun --- src/reagent/ratom.cljs | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 50ab1f6..fe1decc 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -208,8 +208,7 @@ (deftype Reaction [f ^:mutable state ^:mutable ^number dirtyness ^:mutable watching ^:mutable watches - auto-run on-set on-dispose ^:mutable ^boolean norun? - ^boolean nocache?] + ^:mutable auto-run on-set on-dispose ^boolean nocache?] IAtom IReactiveAtom @@ -218,7 +217,8 @@ (reduce-kv (fn [_ key f] (f key this oldval newval) nil) - nil watches)) + nil watches) + nil) (-add-watch [this key f] (set! watches (check-watches watches (assoc watches key f)))) @@ -258,14 +258,16 @@ (-check-clean [this] (when (== dirtyness maybe-dirty) - (set! norun? true) - (doseq [w watching] - (when (and (instance? Reaction w) - (not (-check-clean w))) - (if-some [ar (.-auto-run w)] - (ar w) - (run w)))) - (set! norun? false) + (let [ar auto-run] + ;; TODO: try/catch + (set! auto-run nil) + (doseq [w watching] + (when (and (instance? Reaction w) + (not (-check-clean w))) + (if-some [ar (.-auto-run w)] + (ar w) + (run w)))) + (set! auto-run ar)) (when (== dirtyness maybe-dirty) (set! dirtyness clean))) (== dirtyness clean)) @@ -278,12 +280,12 @@ dirty)] (when (> new-dirty old-dirty) (set! dirtyness new-dirty) - (if-some [arun auto-run] - (when-not norun? - (when-not (and (identical? arun run) + (when (== old-dirty clean) + (if-some [ar auto-run] + (when-not (and (identical? ar run) (-check-clean this)) - (arun this))) - (-notify-watches this state state)))) + (ar this)) + (-notify-watches this state state))))) nil) (-update-watching [this derefed] @@ -338,7 +340,8 @@ (set! state nil) (set! dirtyness dirty) (when (some? on-dispose) - (on-dispose))) + (on-dispose)) + nil) IEquiv (-equiv [o other] (identical? o other)) @@ -357,7 +360,7 @@ dirty (if (nil? derefed) dirty clean) nocache (if (nil? no-cache) false no-cache) reaction (Reaction. f nil dirty nil nil - runner on-set on-dispose false nocache)] + runner on-set on-dispose nocache)] (when-not (nil? derefed) (-update-watching reaction derefed)) reaction)) From bc711c4228fda0f4ab1f07af8a57dbb38ddd62b8 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sat, 12 Sep 2015 18:31:20 +0200 Subject: [PATCH 23/58] Add async reaction --- src/reagent/impl/batching.cljs | 7 +++++-- src/reagent/ratom.cljs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/reagent/impl/batching.cljs b/src/reagent/impl/batching.cljs index 094427d..4488e2c 100644 --- a/src/reagent/impl/batching.cljs +++ b/src/reagent/impl/batching.cljs @@ -59,14 +59,17 @@ (set! scheduled? true) (next-tick #(.run-queue this)))) (run-queue [_] - (let [q queue aq after-render] + (ratom/flush!) + (let [q queue + aq after-render] (set! queue (array)) (set! after-render (array)) (set! scheduled? false) (run-queue q) (run-funs aq)))) -(def render-queue (RenderQueue. (array) false (array))) +(defonce render-queue (RenderQueue. (array) false (array))) +(set! ratom/render-queue render-queue) (defn flush [] (.run-queue render-queue)) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index fe1decc..6c11cd2 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -35,6 +35,7 @@ (swap! -running + (- (count new) (count old)))) new) + ;;; Atom (defprotocol IReactiveAtom) @@ -338,6 +339,7 @@ (remove-watch w this)) (set! watching nil) (set! state nil) + (set! auto-run nil) (set! dirtyness dirty) (when (some? on-dispose) (on-dispose)) @@ -355,14 +357,44 @@ IHash (-hash [this] (goog/getUid this))) + + +;;; Queueing + +(defonce render-queue nil) ;; Gets set up from batching + +(def dirty-queue nil) + +(defn enqueue [r] + (when (nil? dirty-queue) + (set! dirty-queue (array)) + (.schedule render-queue)) + (.push dirty-queue r)) + +(defn flush! [] + (let [q dirty-queue] + (when (some? q) + (set! dirty-queue nil) + (dotimes [i (alength q)] + (let [r (aget q i)] + (when-not (or (nil? (.-auto-run r)) + (-check-clean r)) + (run r))))))) + + (defn make-reaction [f & {:keys [auto-run on-set on-dispose derefed no-cache]}] - (let [runner (if (= auto-run true) run auto-run) + (let [runner (case auto-run + true run + :async enqueue + auto-run) dirty (if (nil? derefed) dirty clean) nocache (if (nil? no-cache) false no-cache) reaction (Reaction. f nil dirty nil nil runner on-set on-dispose nocache)] (when-not (nil? derefed) (-update-watching reaction derefed)) + (when (keyword-identical? auto-run :async) + (enqueue reaction)) reaction)) From 544a3339d0dfd370f6baeb13492c64c293d5cb2c Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sat, 12 Sep 2015 18:34:10 +0200 Subject: [PATCH 24/58] Start testing async reaction --- test/reagenttest/runtests.cljs | 1 + test/reagenttest/testratomasync.cljs | 261 +++++++++++++++++++++++++++ 2 files changed, 262 insertions(+) create mode 100644 test/reagenttest/testratomasync.cljs diff --git a/test/reagenttest/runtests.cljs b/test/reagenttest/runtests.cljs index 8a689b7..5ae560c 100644 --- a/test/reagenttest/runtests.cljs +++ b/test/reagenttest/runtests.cljs @@ -3,6 +3,7 @@ [reagenttest.testcursor] [reagenttest.testinterop] [reagenttest.testratom] + [reagenttest.testratomasync] [reagenttest.testwrap] [cljs.test :as test :include-macros true] [reagent.core :as r] diff --git a/test/reagenttest/testratomasync.cljs b/test/reagenttest/testratomasync.cljs new file mode 100644 index 0000000..58a53f1 --- /dev/null +++ b/test/reagenttest/testratomasync.cljs @@ -0,0 +1,261 @@ +(ns reagenttest.testratomasync + (:require [cljs.test :as t :refer-macros [is deftest testing]] + [reagent.ratom :as rv :refer-macros [run! reaction]] + [reagent.debug :refer-macros [dbg]] + [reagent.core :as r])) + +(defn running [] + (set! rv/debug true) + (rv/running)) + +(def testite 10) + +(defn dispose [v] + (rv/dispose! v)) + +(defn sync [] (r/flush)) + +(defn ar [f] (rv/make-reaction f :auto-run :async)) + +(deftest basic-ratom + (sync) + (let [runs (running) + start (rv/atom 0) + sv (reaction @start) + comp (reaction @sv (+ 2 @sv)) + c2 (reaction (inc @comp)) + count (rv/atom 0) + out (rv/atom 0) + res (reaction + (swap! count inc) + @sv @c2 @comp) + const (ar (fn [] + (reset! out @res)))] + (is (= @count 0)) + (sync) + (is (= @count 1) "constrain ran") + (is (= @out 2)) + (reset! start 1) + (is (= @count 1)) + (sync) + (is (= @out 3)) + (is (= @count 2)) + (reset! start 2) + (dispose const) + (is (= (running) runs) "did dispose") + (sync) + (is (= (running) runs) "should not awaken"))) + +(deftest double-dependency + (sync) + (let [runs (running) + start (rv/atom 0) + c3-count (rv/atom 0) + c1 (reaction @start 1) + c2 (reaction @start) + c3 (rv/make-reaction + (fn [] + (swap! c3-count inc) + (+ @c1 @c2)) + :auto-run :async)] + (is (= @c3-count 0) "t0") + (sync) + (is (= @c3 1)) + (is (= @c3-count 1) "t1") + (swap! start inc) + (is (= @c3-count 1)) + (sync) + (is (= @c3-count 2) "t2") + (is (= @c3 2)) + (is (= @c3-count 2) "t3") + (dispose c3) + (is (= (running) runs)) + (sync) + (is (= @c3 2)) + (is (= (running) runs)))) + +(deftest test-from-reflex + (let [runs (running)] + (let [!counter (rv/atom 0) + !signal (rv/atom "All I do is change") + co (run! + ;;when I change... + @!signal + ;;update the counter + (swap! !counter inc))] + (is (= 1 @!counter) "Constraint run on init") + (reset! !signal "foo") + (is (= 2 @!counter) + "Counter auto updated") + (dispose co)) + (let [!x (rv/atom 0) + !co (rv/make-reaction #(inc @!x) :auto-run true)] + (is (= 1 @!co) "CO has correct value on first deref") + (swap! !x inc) + (is (= 2 @!co) "CO auto-updates") + (dispose !co)) + (is (= runs (running))))) + + +(deftest test-unsubscribe + (dotimes [x testite] + (let [runs (running) + a (rv/atom 0) + a1 (reaction (inc @a)) + a2 (reaction @a) + b-changed (rv/atom 0) + c-changed (rv/atom 0) + b (reaction + (swap! b-changed inc) + (inc @a1)) + c (reaction + (swap! c-changed inc) + (+ 10 @a2)) + res (run! + (if (< @a2 1) @b @c))] + (is (= @res (+ 2 @a))) + (is (= @b-changed 1)) + (is (= @c-changed 0)) + + (reset! a -1) + (is (= @res (+ 2 @a))) + (is (= @b-changed 2)) + (is (= @c-changed 0)) + + (reset! a 2) + (is (= @res (+ 10 @a))) + (is (<= 2 @b-changed 3)) + (is (= @c-changed 1)) + + (reset! a 3) + (is (= @res (+ 10 @a))) + (is (<= 2 @b-changed 3)) + (is (= @c-changed 2)) + + (reset! a 3) + (is (= @res (+ 10 @a))) + (is (<= 2 @b-changed 3)) + (is (= @c-changed 2)) + + (reset! a -1) + (is (= @res (+ 2 @a))) + (dispose res) + (is (= runs (running)))))) + +(deftest maybe-broken + (let [runs (running)] + (let [runs (running) + a (rv/atom 0) + b (reaction (inc @a)) + c (reaction (dec @a)) + d (reaction (str @b)) + res (rv/atom 0) + cs (run! + (reset! res @d))] + (is (= @res "1")) + (dispose cs)) + ;; should be broken according to https://github.com/lynaghk/reflex/issues/1 + ;; but isnt + (let [a (rv/atom 0) + b (reaction (inc @a)) + c (reaction (dec @a)) + d (run! [@b @c])] + (is (= @d [1 -1])) + (dispose d)) + (let [a (rv/atom 0) + b (reaction (inc @a)) + c (reaction (dec @a)) + d (run! [@b @c]) + res (rv/atom 0)] + (is (= @d [1 -1])) + (let [e (run! (reset! res @d))] + (is (= @res [1 -1])) + (dispose e)) + (dispose d)) + (is (= runs (running))))) + +(deftest test-dispose + (dotimes [x testite] + (let [runs (running) + a (rv/atom 0) + disposed (rv/atom nil) + disposed-c (rv/atom nil) + disposed-cns (rv/atom nil) + count-b (rv/atom 0) + b (rv/make-reaction (fn [] + (swap! count-b inc) + (inc @a)) + :on-dispose #(reset! disposed true)) + c (rv/make-reaction #(if (< @a 1) (inc @b) (dec @a)) + :on-dispose #(reset! disposed-c true)) + res (rv/atom nil) + cns (rv/make-reaction #(reset! res @c) + :auto-run true + :on-dispose #(reset! disposed-cns true))] + @cns + (is (= @res 2)) + (is (= (+ 4 runs) (running))) + (is (= @count-b 1)) + (reset! a -1) + (is (= @res 1)) + (is (= @disposed nil)) + (is (= @count-b 2)) + (is (= (+ 4 runs) (running)) "still running") + (reset! a 2) + (is (= @res 1)) + (is (= @disposed true)) + (is (= (+ 2 runs) (running)) "less running count") + + (reset! disposed nil) + (reset! a -1) + ;; This fails sometimes on node. I have no idea why. + (is (= 1 @res) "should be one again") + (is (= @disposed nil)) + (reset! a 2) + (is (= @res 1)) + (is (= @disposed true)) + (dispose cns) + (is (= @disposed-c true)) + (is (= @disposed-cns true)) + (is (= runs (running)))))) + +(deftest test-on-set + (let [runs (running) + a (rv/atom 0) + b (rv/make-reaction #(+ 5 @a) + :auto-run true + :on-set (fn [oldv newv] + (reset! a (+ 10 newv))))] + @b + (is (= 5 @b)) + (reset! a 1) + (is (= 6 @b)) + (reset! b 1) + (is (= 11 @a)) + (is (= 16 @b)) + (dispose b) + (is (= runs (running))))) + +(deftest non-reactive-deref + (let [runs (running) + a (rv/atom 0) + b (rv/make-reaction #(+ 5 @a))] + (is (= @b 5)) + (is (= runs (running))) + + (reset! a 1) + (is (= @b 6)) + (is (= runs (running))))) + +;; (deftest catching +;; (let [runs (running) +;; a (rv/atom false) +;; catch-count (atom 0) +;; b (reaction (if @a (throw {}))) +;; c (run! (try @b (catch js/Object e +;; (swap! catch-count inc))))] +;; (is (= @catch-count 0)) +;; (reset! a false) +;; (is (= @catch-count 0)) +;; (reset! a true) +;; (is (= @catch-count 1)))) From b036cf938ebe4b8b3ca1a48c2ae3687a710184e5 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sat, 12 Sep 2015 19:13:31 +0200 Subject: [PATCH 25/58] Complete async testing --- test/reagenttest/testratomasync.cljs | 74 ++++++++++++++++++---------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/test/reagenttest/testratomasync.cljs b/test/reagenttest/testratomasync.cljs index 58a53f1..7678301 100644 --- a/test/reagenttest/testratomasync.cljs +++ b/test/reagenttest/testratomasync.cljs @@ -75,29 +75,36 @@ (is (= (running) runs)))) (deftest test-from-reflex + (sync) (let [runs (running)] (let [!counter (rv/atom 0) !signal (rv/atom "All I do is change") - co (run! - ;;when I change... - @!signal - ;;update the counter - (swap! !counter inc))] + co (ar (fn [] + ;;when I change... + @!signal + ;;update the counter + (swap! !counter inc)))] + (is (= 0 @!counter)) + (sync) (is (= 1 @!counter) "Constraint run on init") (reset! !signal "foo") + (sync) (is (= 2 @!counter) "Counter auto updated") (dispose co)) (let [!x (rv/atom 0) - !co (rv/make-reaction #(inc @!x) :auto-run true)] + !co (rv/make-reaction #(inc @!x) :auto-run :async)] + (sync) (is (= 1 @!co) "CO has correct value on first deref") - (swap! !x inc) + (swap! !x inc) + (sync) (is (= 2 @!co) "CO auto-updates") (dispose !co)) (is (= runs (running))))) (deftest test-unsubscribe + (sync) (dotimes [x testite] (let [runs (running) a (rv/atom 0) @@ -111,31 +118,37 @@ c (reaction (swap! c-changed inc) (+ 10 @a2)) - res (run! - (if (< @a2 1) @b @c))] + res (ar (fn [] + (if (< @a2 1) @b @c)))] (is (= @res (+ 2 @a))) (is (= @b-changed 1)) (is (= @c-changed 0)) (reset! a -1) - (is (= @res (+ 2 @a))) + (is (= @b-changed 1)) + (sync) + (is (= @b-changed 2)) (is (= @c-changed 0)) + (is (= @res (+ 2 @a))) (reset! a 2) - (is (= @res (+ 10 @a))) - (is (<= 2 @b-changed 3)) + (sync) + (is (= @b-changed 3)) (is (= @c-changed 1)) + (is (= @res (+ 10 @a))) (reset! a 3) - (is (= @res (+ 10 @a))) - (is (<= 2 @b-changed 3)) + (sync) + (is (= @b-changed 3)) (is (= @c-changed 2)) + (is (= @res (+ 10 @a))) (reset! a 3) - (is (= @res (+ 10 @a))) - (is (<= 2 @b-changed 3)) + (sync) + (is (= @b-changed 3)) (is (= @c-changed 2)) + (is (= @res (+ 10 @a))) (reset! a -1) (is (= @res (+ 2 @a))) @@ -143,6 +156,7 @@ (is (= runs (running)))))) (deftest maybe-broken + (sync) (let [runs (running)] (let [runs (running) a (rv/atom 0) @@ -150,8 +164,9 @@ c (reaction (dec @a)) d (reaction (str @b)) res (rv/atom 0) - cs (run! - (reset! res @d))] + cs (ar + #(reset! res @d))] + (sync) (is (= @res "1")) (dispose cs)) ;; should be broken according to https://github.com/lynaghk/reflex/issues/1 @@ -159,16 +174,17 @@ (let [a (rv/atom 0) b (reaction (inc @a)) c (reaction (dec @a)) - d (run! [@b @c])] + d (ar (fn [] [@b @c]))] (is (= @d [1 -1])) (dispose d)) (let [a (rv/atom 0) b (reaction (inc @a)) c (reaction (dec @a)) - d (run! [@b @c]) + d (ar (fn [] [@b @c])) res (rv/atom 0)] (is (= @d [1 -1])) - (let [e (run! (reset! res @d))] + (let [e (ar #(reset! res @d))] + (sync) (is (= @res [1 -1])) (dispose e)) (dispose d)) @@ -190,28 +206,33 @@ :on-dispose #(reset! disposed-c true)) res (rv/atom nil) cns (rv/make-reaction #(reset! res @c) - :auto-run true + :auto-run :async :on-dispose #(reset! disposed-cns true))] - @cns + (sync) (is (= @res 2)) (is (= (+ 4 runs) (running))) (is (= @count-b 1)) (reset! a -1) + (is (= @res 2)) + (sync) (is (= @res 1)) (is (= @disposed nil)) (is (= @count-b 2)) (is (= (+ 4 runs) (running)) "still running") (reset! a 2) + (sync) (is (= @res 1)) (is (= @disposed true)) (is (= (+ 2 runs) (running)) "less running count") (reset! disposed nil) (reset! a -1) + (sync) ;; This fails sometimes on node. I have no idea why. (is (= 1 @res) "should be one again") (is (= @disposed nil)) (reset! a 2) + (sync) (is (= @res 1)) (is (= @disposed true)) (dispose cns) @@ -220,17 +241,20 @@ (is (= runs (running)))))) (deftest test-on-set + (sync) (let [runs (running) a (rv/atom 0) b (rv/make-reaction #(+ 5 @a) - :auto-run true + :auto-run :async :on-set (fn [oldv newv] (reset! a (+ 10 newv))))] - @b + (sync) (is (= 5 @b)) (reset! a 1) + (sync) (is (= 6 @b)) (reset! b 1) + (sync) (is (= 11 @a)) (is (= 16 @b)) (dispose b) From 04daf6ac57a5c2106afd3dfd9ec589fc83bd715a Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sun, 13 Sep 2015 11:00:07 +0200 Subject: [PATCH 26/58] Exceptions from reactions are now thrown on deref only --- src/reagent/ratom.cljs | 30 +++++++++++++++++++++------- test/reagenttest/testratom.cljs | 26 +++++++++++++----------- test/reagenttest/testratomasync.cljs | 27 ++++++++++++++----------- 3 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 6c11cd2..d54aaa8 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -206,6 +206,7 @@ (def ^:const clean 0) (def ^:const maybe-dirty 1) (def ^:const dirty 2) +(def ^:const failed 3) (deftype Reaction [f ^:mutable state ^:mutable ^number dirtyness ^:mutable watching ^:mutable watches @@ -221,7 +222,7 @@ nil watches) nil) - (-add-watch [this key f] + (-add-watch [_ key f] (set! watches (check-watches watches (assoc watches key f)))) (-remove-watch [this key] @@ -234,7 +235,7 @@ (-reset! [a newval] (let [oldval state] (set! state newval) - (when on-set + (when (some? on-set) (set! dirtyness dirty) (on-set oldval newval)) (-notify-watches a oldval newval) @@ -260,14 +261,11 @@ (-check-clean [this] (when (== dirtyness maybe-dirty) (let [ar auto-run] - ;; TODO: try/catch (set! auto-run nil) (doseq [w watching] (when (and (instance? Reaction w) (not (-check-clean w))) - (if-some [ar (.-auto-run w)] - (ar w) - (run w)))) + (._try-run this w))) (set! auto-run ar)) (when (== dirtyness maybe-dirty) (set! dirtyness clean))) @@ -299,6 +297,17 @@ (set! watching derefed) nil) + Object + (_try-run [_ parent] + (try + (if-some [ar (.-auto-run parent)] + (ar parent) + (run parent)) + (catch :default e + (set! (.-dirtyness parent) failed) + (set! (.-state parent) e) + (set! dirtyness dirty)))) + IRunnable (run [this] (let [oldstate state @@ -319,6 +328,11 @@ IDeref (-deref [this] (-check-clean this) + (when (== dirtyness failed) + (let [e state] + (set! dirtyness dirty) + (set! state nil) + (throw e))) (if (and (nil? auto-run) (nil? *ratom-context*)) (when-not (== dirtyness clean) (let [oldstate state @@ -361,7 +375,9 @@ ;;; Queueing -(defonce render-queue nil) ;; Gets set up from batching +;; Gets set up from batching +;; TODO: Refactor so that isn't needed +(defonce render-queue nil) (def dirty-queue nil) diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index 1f983b2..ada284a 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -247,15 +247,17 @@ (is (= @b 6)) (is (= runs (running))))) -;; (deftest catching -;; (let [runs (running) -;; a (rv/atom false) -;; catch-count (atom 0) -;; b (reaction (if @a (throw {}))) -;; c (run! (try @b (catch js/Object e -;; (swap! catch-count inc))))] -;; (is (= @catch-count 0)) -;; (reset! a false) -;; (is (= @catch-count 0)) -;; (reset! a true) -;; (is (= @catch-count 1)))) +(deftest catching + (let [runs (running) + a (rv/atom false) + catch-count (atom 0) + b (reaction (if @a (throw (js/Error. "fail")))) + c (run! (try @b (catch :default e + (swap! catch-count inc))))] + (is (= @catch-count 0)) + (reset! a false) + (is (= @catch-count 0)) + (reset! a true) + (is (= @catch-count 1)) + (reset! a false) + (is (= @catch-count 1)))) diff --git a/test/reagenttest/testratomasync.cljs b/test/reagenttest/testratomasync.cljs index 7678301..b45fefa 100644 --- a/test/reagenttest/testratomasync.cljs +++ b/test/reagenttest/testratomasync.cljs @@ -271,15 +271,18 @@ (is (= @b 6)) (is (= runs (running))))) -;; (deftest catching -;; (let [runs (running) -;; a (rv/atom false) -;; catch-count (atom 0) -;; b (reaction (if @a (throw {}))) -;; c (run! (try @b (catch js/Object e -;; (swap! catch-count inc))))] -;; (is (= @catch-count 0)) -;; (reset! a false) -;; (is (= @catch-count 0)) -;; (reset! a true) -;; (is (= @catch-count 1)))) +(deftest catching + (let [runs (running) + a (rv/atom false) + catch-count (atom 0) + b (reaction (if @a (throw (js/Error. "reaction fail")))) + c (ar (fn [] (try @b (catch js/Object e + (swap! catch-count inc)))))] + (is (= @catch-count 0)) + (reset! a false) + (sync) + (is (= @catch-count 0)) + (reset! a true) + (is (= @catch-count 0)) + (sync) + (is (= @catch-count 1)))) From e12716da5dc546296cfa75be3e43e3f862592054 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sun, 13 Sep 2015 14:25:17 +0200 Subject: [PATCH 27/58] Log caught errors instead of rethrowing --- src/reagent/ratom.cljs | 15 ++++++--------- test/reagenttest/testratom.cljs | 6 +++++- test/reagenttest/testratomasync.cljs | 6 +++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index d54aaa8..7539c18 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -7,6 +7,7 @@ (declare ^:dynamic *ratom-context*) (defonce ^boolean debug false) +(defonce ^boolean silent false) (defonce -running (clojure.core/atom 0)) @@ -206,7 +207,6 @@ (def ^:const clean 0) (def ^:const maybe-dirty 1) (def ^:const dirty 2) -(def ^:const failed 3) (deftype Reaction [f ^:mutable state ^:mutable ^number dirtyness ^:mutable watching ^:mutable watches @@ -304,8 +304,10 @@ (ar parent) (run parent)) (catch :default e - (set! (.-dirtyness parent) failed) - (set! (.-state parent) e) + ;; Just log error: it will most likely pop up again at deref time. + (when-not silent + (js/console.error "Error in reaction:" e)) + (set! (.-dirtyness parent) dirty) (set! dirtyness dirty)))) IRunnable @@ -328,11 +330,6 @@ IDeref (-deref [this] (-check-clean this) - (when (== dirtyness failed) - (let [e state] - (set! dirtyness dirty) - (set! state nil) - (throw e))) (if (and (nil? auto-run) (nil? *ratom-context*)) (when-not (== dirtyness clean) (let [oldstate state @@ -482,7 +479,7 @@ (util/partial-ifn. callback-fn args nil) false nil)) -(comment +(do (def perf-check 0) (defn ratom-perf [] (dbg "ratom-perf") diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index ada284a..e4e56e8 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -254,10 +254,14 @@ b (reaction (if @a (throw (js/Error. "fail")))) c (run! (try @b (catch :default e (swap! catch-count inc))))] + (set! rv/silent true) (is (= @catch-count 0)) (reset! a false) (is (= @catch-count 0)) (reset! a true) (is (= @catch-count 1)) (reset! a false) - (is (= @catch-count 1)))) + (is (= @catch-count 1)) + (set! rv/silent false) + (dispose c) + (is (= runs (running))))) diff --git a/test/reagenttest/testratomasync.cljs b/test/reagenttest/testratomasync.cljs index b45fefa..03fd473 100644 --- a/test/reagenttest/testratomasync.cljs +++ b/test/reagenttest/testratomasync.cljs @@ -278,6 +278,7 @@ b (reaction (if @a (throw (js/Error. "reaction fail")))) c (ar (fn [] (try @b (catch js/Object e (swap! catch-count inc)))))] + (set! rv/silent true) (is (= @catch-count 0)) (reset! a false) (sync) @@ -285,4 +286,7 @@ (reset! a true) (is (= @catch-count 0)) (sync) - (is (= @catch-count 1)))) + (is (= @catch-count 1)) + (set! rv/silent false) + (dispose c) + (is (= runs (running))))) From ee72c60d3a4a430afd8dbc1d37b3ac997a5fc99a Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sun, 13 Sep 2015 18:17:38 +0200 Subject: [PATCH 28/58] Start implementing monitor --- src/reagent/ratom.cljs | 34 +++- test/reagenttest/runtests.cljs | 1 + test/reagenttest/testmonitor.cljs | 255 ++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 test/reagenttest/testmonitor.cljs diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 7539c18..863efc4 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -103,10 +103,40 @@ -;;; cursor +;;; monitor (declare make-reaction) +(defonce cached-reactions {}) + +(defn- cached-reaction [obj key f] + (if-some [r (get cached-reactions [key])] + r + (if (some? *ratom-context*) + (let [r (make-reaction + f :on-dispose #(set! cached-reactions + (dissoc cached-reactions key))) + v (-deref r)] + (set! cached-reactions (assoc cached-reactions key f)) + (set! (.-reaction obj) r) + v) + (f)))) + +(deftype Monitor [f args ^:mutable reaction] + IReactiveAtom + + IDeref + (-deref [this] + (if-some [r reaction] + (-deref r) + (cached-reaction this [f args] + #(apply f args))))) + +(defn monitor [f & args] + (Monitor. f args nil)) + +;;; cursor + (deftype RCursor [ratom path ^:mutable reaction] IAtom IReactiveAtom @@ -479,7 +509,7 @@ (util/partial-ifn. callback-fn args nil) false nil)) -(do +(comment (def perf-check 0) (defn ratom-perf [] (dbg "ratom-perf") diff --git a/test/reagenttest/runtests.cljs b/test/reagenttest/runtests.cljs index 5ae560c..40f0bf3 100644 --- a/test/reagenttest/runtests.cljs +++ b/test/reagenttest/runtests.cljs @@ -4,6 +4,7 @@ [reagenttest.testinterop] [reagenttest.testratom] [reagenttest.testratomasync] + [reagenttest.testmonitor] [reagenttest.testwrap] [cljs.test :as test :include-macros true] [reagent.core :as r] diff --git a/test/reagenttest/testmonitor.cljs b/test/reagenttest/testmonitor.cljs new file mode 100644 index 0000000..ee4686e --- /dev/null +++ b/test/reagenttest/testmonitor.cljs @@ -0,0 +1,255 @@ +(ns reagenttest.testmonitor + (:require [cljs.test :as t :refer-macros [is deftest testing]] + [reagent.ratom :as rv :refer [monitor] + :refer-macros [run! reaction]] + [reagent.debug :refer-macros [dbg]] + [reagent.core :as r])) + +(defn running [] + (set! rv/debug true) + (rv/running)) + +(def testite 10) + +(defn dispose [v] + (rv/dispose! v)) + +(enable-console-print!) + + +(deftest basic-ratom + (let [runs (running) + start (rv/atom 0) + svf (fn [] @start) + sv (monitor svf) + compf (fn [x] @sv (+ x @sv)) + comp (monitor compf 2) + c2f (fn [] (inc @comp)) + count (rv/atom 0) + out (rv/atom 0) + resf (fn [] + (swap! count inc) + (+ @sv @(monitor c2f) @comp)) + res (monitor resf) + const (run! + (reset! out @res))] + (is (= @count 1) "constrain ran") + (is (= @out 5)) + (reset! start 1) + (is (= @out 8)) + (is (= @count 2)) + (dispose const) + (is (= (running) runs)))) + +(deftest double-dependency + (let [runs (running) + start (rv/atom 0) + c3-count (rv/atom 0) + c1 (reaction @start 1) + c2 (reaction @start) + c3 (rv/make-reaction + (fn [] + (swap! c3-count inc) + (+ @c1 @c2)) + :auto-run true)] + (is (= @c3-count 0)) + (is (= @c3 1)) + (is (= @c3-count 1) "t1") + (swap! start inc) + (is (= @c3-count 2) "t2") + (is (= @c3 2)) + (is (= @c3-count 2) "t3") + (dispose c3) + (is (= (running) runs)))) + +(deftest test-from-reflex + (let [runs (running)] + (let [!counter (rv/atom 0) + !signal (rv/atom "All I do is change") + co (run! + ;;when I change... + @!signal + ;;update the counter + (swap! !counter inc))] + (is (= 1 @!counter) "Constraint run on init") + (reset! !signal "foo") + (is (= 2 @!counter) + "Counter auto updated") + (dispose co)) + (let [!x (rv/atom 0) + !co (rv/make-reaction #(inc @!x) :auto-run true)] + (is (= 1 @!co) "CO has correct value on first deref") + (swap! !x inc) + (is (= 2 @!co) "CO auto-updates") + (dispose !co)) + (is (= runs (running))))) + + +(deftest test-unsubscribe + (dotimes [x testite] + (let [runs (running) + a (rv/atom 0) + a1 (reaction (inc @a)) + a2 (reaction @a) + b-changed (rv/atom 0) + c-changed (rv/atom 0) + b (reaction + (swap! b-changed inc) + (inc @a1)) + c (reaction + (swap! c-changed inc) + (+ 10 @a2)) + res (run! + (if (< @a2 1) @b @c))] + (is (= @res (+ 2 @a))) + (is (= @b-changed 1)) + (is (= @c-changed 0)) + + (reset! a -1) + (is (= @res (+ 2 @a))) + (is (= @b-changed 2)) + (is (= @c-changed 0)) + + (reset! a 2) + (is (= @res (+ 10 @a))) + (is (<= 2 @b-changed 3)) + (is (= @c-changed 1)) + + (reset! a 3) + (is (= @res (+ 10 @a))) + (is (<= 2 @b-changed 3)) + (is (= @c-changed 2)) + + (reset! a 3) + (is (= @res (+ 10 @a))) + (is (<= 2 @b-changed 3)) + (is (= @c-changed 2)) + + (reset! a -1) + (is (= @res (+ 2 @a))) + (dispose res) + (is (= runs (running)))))) + +(deftest maybe-broken + (let [runs (running)] + (let [runs (running) + a (rv/atom 0) + b (reaction (inc @a)) + c (reaction (dec @a)) + d (reaction (str @b)) + res (rv/atom 0) + cs (run! + (reset! res @d))] + (is (= @res "1")) + (dispose cs)) + ;; should be broken according to https://github.com/lynaghk/reflex/issues/1 + ;; but isnt + (let [a (rv/atom 0) + b (reaction (inc @a)) + c (reaction (dec @a)) + d (run! [@b @c])] + (is (= @d [1 -1])) + (dispose d)) + (let [a (rv/atom 0) + b (reaction (inc @a)) + c (reaction (dec @a)) + d (run! [@b @c]) + res (rv/atom 0)] + (is (= @d [1 -1])) + (let [e (run! (reset! res @d))] + (is (= @res [1 -1])) + (dispose e)) + (dispose d)) + (is (= runs (running))))) + +(deftest test-dispose + (dotimes [x testite] + (let [runs (running) + a (rv/atom 0) + disposed (rv/atom nil) + disposed-c (rv/atom nil) + disposed-cns (rv/atom nil) + count-b (rv/atom 0) + b (rv/make-reaction (fn [] + (swap! count-b inc) + (inc @a)) + :on-dispose #(reset! disposed true)) + c (rv/make-reaction #(if (< @a 1) (inc @b) (dec @a)) + :on-dispose #(reset! disposed-c true)) + res (rv/atom nil) + cns (rv/make-reaction #(reset! res @c) + :auto-run true + :on-dispose #(reset! disposed-cns true))] + @cns + (is (= @res 2)) + (is (= (+ 4 runs) (running))) + (is (= @count-b 1)) + (reset! a -1) + (is (= @res 1)) + (is (= @disposed nil)) + (is (= @count-b 2)) + (is (= (+ 4 runs) (running)) "still running") + (reset! a 2) + (is (= @res 1)) + (is (= @disposed true)) + (is (= (+ 2 runs) (running)) "less running count") + + (reset! disposed nil) + (reset! a -1) + ;; This fails sometimes on node. I have no idea why. + (is (= 1 @res) "should be one again") + (is (= @disposed nil)) + (reset! a 2) + (is (= @res 1)) + (is (= @disposed true)) + (dispose cns) + (is (= @disposed-c true)) + (is (= @disposed-cns true)) + (is (= runs (running)))))) + +(deftest test-on-set + (let [runs (running) + a (rv/atom 0) + b (rv/make-reaction #(+ 5 @a) + :auto-run true + :on-set (fn [oldv newv] + (reset! a (+ 10 newv))))] + @b + (is (= 5 @b)) + (reset! a 1) + (is (= 6 @b)) + (reset! b 1) + (is (= 11 @a)) + (is (= 16 @b)) + (dispose b) + (is (= runs (running))))) + +(deftest non-reactive-deref + (let [runs (running) + a (rv/atom 0) + b (rv/make-reaction #(+ 5 @a))] + (is (= @b 5)) + (is (= runs (running))) + + (reset! a 1) + (is (= @b 6)) + (is (= runs (running))))) + +(deftest catching + (let [runs (running) + a (rv/atom false) + catch-count (atom 0) + b (reaction (if @a (throw (js/Error. "fail")))) + c (run! (try @b (catch :default e + (swap! catch-count inc))))] + (set! rv/silent true) + (is (= @catch-count 0)) + (reset! a false) + (is (= @catch-count 0)) + (reset! a true) + (is (= @catch-count 1)) + (reset! a false) + (is (= @catch-count 1)) + (set! rv/silent false) + (dispose c) + (is (= runs (running))))) From aaf1d56edb5c54fdc9291d9c368760fb9ebccab0 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sun, 13 Sep 2015 19:01:58 +0200 Subject: [PATCH 29/58] More work on monitor --- src/reagent/ratom.cljs | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 863efc4..95273f2 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -109,31 +109,47 @@ (defonce cached-reactions {}) -(defn- cached-reaction [obj key f] - (if-some [r (get cached-reactions [key])] - r +(defn- cached-reaction [f key obj] + (if-some [r (get cached-reactions key)] + (-deref r) (if (some? *ratom-context*) (let [r (make-reaction - f :on-dispose #(set! cached-reactions - (dissoc cached-reactions key))) + f :on-dispose (fn [] + (set! cached-reactions + (dissoc cached-reactions key)) + (set! (.-reaction obj) nil))) v (-deref r)] - (set! cached-reactions (assoc cached-reactions key f)) + (set! cached-reactions (assoc cached-reactions key r)) (set! (.-reaction obj) r) v) (f)))) -(deftype Monitor [f args ^:mutable reaction] +(deftype Monitor [f key ^:mutable reaction] IReactiveAtom IDeref (-deref [this] (if-some [r reaction] (-deref r) - (cached-reaction this [f args] - #(apply f args))))) + (cached-reaction f key this))) + + IEquiv + (-equiv [o other] + (and (instance? Monitor other) + (= key (.-key other)))) + + IHash + (-hash [this] (hash key)) + + IPrintWithWriter + (-pr-writer [a writer opts] + (-write writer (str "#"))) (defn monitor [f & args] - (Monitor. f args nil)) + (Monitor. #(apply f args) [f args] nil)) ;;; cursor From b0d86598ce88cfe0827c5effe08c43353cc5ed6a Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sun, 13 Sep 2015 20:31:31 +0200 Subject: [PATCH 30/58] Start to add tests for monitor --- test/reagenttest/testmonitor.cljs | 53 ++++++++++++------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/test/reagenttest/testmonitor.cljs b/test/reagenttest/testmonitor.cljs index ee4686e..ebf798a 100644 --- a/test/reagenttest/testmonitor.cljs +++ b/test/reagenttest/testmonitor.cljs @@ -45,12 +45,12 @@ (let [runs (running) start (rv/atom 0) c3-count (rv/atom 0) - c1 (reaction @start 1) - c2 (reaction @start) + c1f (fn [] @start 1) + c2f (fn [] @start) c3 (rv/make-reaction (fn [] (swap! c3-count inc) - (+ @c1 @c2)) + (+ @(monitor c1f) @(monitor c2f))) :auto-run true)] (is (= @c3-count 0)) (is (= @c3 1)) @@ -64,22 +64,11 @@ (deftest test-from-reflex (let [runs (running)] - (let [!counter (rv/atom 0) - !signal (rv/atom "All I do is change") - co (run! - ;;when I change... - @!signal - ;;update the counter - (swap! !counter inc))] - (is (= 1 @!counter) "Constraint run on init") - (reset! !signal "foo") - (is (= 2 @!counter) - "Counter auto updated") - (dispose co)) (let [!x (rv/atom 0) - !co (rv/make-reaction #(inc @!x) :auto-run true)] - (is (= 1 @!co) "CO has correct value on first deref") - (swap! !x inc) + f #(inc @!x) + !co (run! @(monitor f))] + (is (= 1 @!co) "CO has correct value on first deref") + (swap! !x inc) (is (= 2 @!co) "CO auto-updates") (dispose !co)) (is (= runs (running))))) @@ -89,42 +78,42 @@ (dotimes [x testite] (let [runs (running) a (rv/atom 0) - a1 (reaction (inc @a)) - a2 (reaction @a) + af (fn [x] (+ @a x)) + a1 (monitor af 1) + a2 (monitor af 0) b-changed (rv/atom 0) c-changed (rv/atom 0) - b (reaction - (swap! b-changed inc) - (inc @a1)) - c (reaction - (swap! c-changed inc) - (+ 10 @a2)) + mf (fn [v x spy] + (swap! spy inc) + (+ @v x)) res (run! - (if (< @a2 1) @b @c))] + (if (< @a2 1) + @(monitor mf a1 1 b-changed) + @(monitor mf a2 10 c-changed)))] (is (= @res (+ 2 @a))) (is (= @b-changed 1)) (is (= @c-changed 0)) - + (reset! a -1) (is (= @res (+ 2 @a))) (is (= @b-changed 2)) (is (= @c-changed 0)) - + (reset! a 2) (is (= @res (+ 10 @a))) (is (<= 2 @b-changed 3)) (is (= @c-changed 1)) - + (reset! a 3) (is (= @res (+ 10 @a))) (is (<= 2 @b-changed 3)) (is (= @c-changed 2)) - + (reset! a 3) (is (= @res (+ 10 @a))) (is (<= 2 @b-changed 3)) (is (= @c-changed 2)) - + (reset! a -1) (is (= @res (+ 2 @a))) (dispose res) From e05c74e6a566e691d93b9fe1a7ef99391b71bde5 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 09:09:04 +0200 Subject: [PATCH 31/58] More tests for monitor --- src/reagent/ratom.cljs | 1 + test/reagenttest/runtests.cljs | 6 +-- test/reagenttest/testmonitor.cljs | 85 +++++-------------------------- 3 files changed, 18 insertions(+), 74 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 95273f2..f402e2d 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -149,6 +149,7 @@ (-write writer ">"))) (defn monitor [f & args] + {:pre [(ifn? f)]} (Monitor. #(apply f args) [f args] nil)) ;;; cursor diff --git a/test/reagenttest/runtests.cljs b/test/reagenttest/runtests.cljs index 40f0bf3..0183057 100644 --- a/test/reagenttest/runtests.cljs +++ b/test/reagenttest/runtests.cljs @@ -1,4 +1,5 @@ -(ns reagenttest.runtests +(ns ^:figwheel-always + reagenttest.runtests (:require [reagenttest.testreagent] [reagenttest.testcursor] [reagenttest.testinterop] @@ -53,7 +54,6 @@ (run-tests))) (defn reload [] - (demo/init!) - (init!)) + (demo/init!)) (init!) diff --git a/test/reagenttest/testmonitor.cljs b/test/reagenttest/testmonitor.cljs index ebf798a..958e763 100644 --- a/test/reagenttest/testmonitor.cljs +++ b/test/reagenttest/testmonitor.cljs @@ -123,9 +123,10 @@ (let [runs (running)] (let [runs (running) a (rv/atom 0) - b (reaction (inc @a)) - c (reaction (dec @a)) - d (reaction (str @b)) + f (fn [x] (+ x @a)) + b (monitor f 1) + c (monitor f -1) + d (monitor #(str @b)) res (rv/atom 0) cs (run! (reset! res @d))] @@ -134,14 +135,16 @@ ;; should be broken according to https://github.com/lynaghk/reflex/issues/1 ;; but isnt (let [a (rv/atom 0) - b (reaction (inc @a)) - c (reaction (dec @a)) + f (fn [x] (+ x @a)) + b (monitor f 1) + c (monitor f -1) d (run! [@b @c])] (is (= @d [1 -1])) (dispose d)) (let [a (rv/atom 0) - b (reaction (inc @a)) - c (reaction (dec @a)) + f (fn [x] (+ x @a)) + b (monitor f 1) + c (monitor f -1) d (run! [@b @c]) res (rv/atom 0)] (is (= @d [1 -1])) @@ -151,72 +154,10 @@ (dispose d)) (is (= runs (running))))) -(deftest test-dispose - (dotimes [x testite] - (let [runs (running) - a (rv/atom 0) - disposed (rv/atom nil) - disposed-c (rv/atom nil) - disposed-cns (rv/atom nil) - count-b (rv/atom 0) - b (rv/make-reaction (fn [] - (swap! count-b inc) - (inc @a)) - :on-dispose #(reset! disposed true)) - c (rv/make-reaction #(if (< @a 1) (inc @b) (dec @a)) - :on-dispose #(reset! disposed-c true)) - res (rv/atom nil) - cns (rv/make-reaction #(reset! res @c) - :auto-run true - :on-dispose #(reset! disposed-cns true))] - @cns - (is (= @res 2)) - (is (= (+ 4 runs) (running))) - (is (= @count-b 1)) - (reset! a -1) - (is (= @res 1)) - (is (= @disposed nil)) - (is (= @count-b 2)) - (is (= (+ 4 runs) (running)) "still running") - (reset! a 2) - (is (= @res 1)) - (is (= @disposed true)) - (is (= (+ 2 runs) (running)) "less running count") - - (reset! disposed nil) - (reset! a -1) - ;; This fails sometimes on node. I have no idea why. - (is (= 1 @res) "should be one again") - (is (= @disposed nil)) - (reset! a 2) - (is (= @res 1)) - (is (= @disposed true)) - (dispose cns) - (is (= @disposed-c true)) - (is (= @disposed-cns true)) - (is (= runs (running)))))) - -(deftest test-on-set - (let [runs (running) - a (rv/atom 0) - b (rv/make-reaction #(+ 5 @a) - :auto-run true - :on-set (fn [oldv newv] - (reset! a (+ 10 newv))))] - @b - (is (= 5 @b)) - (reset! a 1) - (is (= 6 @b)) - (reset! b 1) - (is (= 11 @a)) - (is (= 16 @b)) - (dispose b) - (is (= runs (running))))) - (deftest non-reactive-deref (let [runs (running) a (rv/atom 0) - b (rv/make-reaction #(+ 5 @a))] + b (monitor #(+ 5 @a))] (is (= @b 5)) (is (= runs (running))) @@ -228,10 +169,11 @@ (let [runs (running) a (rv/atom false) catch-count (atom 0) - b (reaction (if @a (throw (js/Error. "fail")))) + b (monitor #(if @a (throw (js/Error. "fail")))) c (run! (try @b (catch :default e (swap! catch-count inc))))] (set! rv/silent true) + (is (= @catch-count 0)) (reset! a false) (is (= @catch-count 0)) @@ -239,6 +181,7 @@ (is (= @catch-count 1)) (reset! a false) (is (= @catch-count 1)) + (set! rv/silent false) (dispose c) (is (= runs (running))))) From 995e110946071a7e039b59b8d9d670fcb1fa93a9 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 13:34:17 +0200 Subject: [PATCH 32/58] Setting dirty on on-set is not necessary any more --- src/reagent/ratom.cljs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index f402e2d..b4b6fd3 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -283,7 +283,6 @@ (let [oldval state] (set! state newval) (when (some? on-set) - (set! dirtyness dirty) (on-set oldval newval)) (-notify-watches a oldval newval) newval)) From d362e0a26c3c255d3b8a5c02d51b8587c8f8f819 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 13:34:54 +0200 Subject: [PATCH 33/58] Mix calling style a bit in tests --- test/reagenttest/testmonitor.cljs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/reagenttest/testmonitor.cljs b/test/reagenttest/testmonitor.cljs index 958e763..c5465a9 100644 --- a/test/reagenttest/testmonitor.cljs +++ b/test/reagenttest/testmonitor.cljs @@ -137,15 +137,13 @@ (let [a (rv/atom 0) f (fn [x] (+ x @a)) b (monitor f 1) - c (monitor f -1) - d (run! [@b @c])] + d (run! [@b @(monitor f -1)])] (is (= @d [1 -1])) (dispose d)) (let [a (rv/atom 0) f (fn [x] (+ x @a)) - b (monitor f 1) c (monitor f -1) - d (run! [@b @c]) + d (run! [@(monitor f 1) @c]) res (rv/atom 0)] (is (= @d [1 -1])) (let [e (run! (reset! res @d))] From 3acfa689d9ef8e94c29eac8b1c2f5cbdcee8af60 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 14:03:32 +0200 Subject: [PATCH 34/58] Start to rework cursor --- src/reagent/ratom.cljs | 45 +++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index b4b6fd3..934511e 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -154,7 +154,7 @@ ;;; cursor -(deftype RCursor [ratom path ^:mutable reaction] +(deftype RCursor [ratom path setter ^:mutable reaction] IAtom IReactiveAtom @@ -169,12 +169,8 @@ (if (nil? reaction) (set! reaction (if (satisfies? IDeref ratom) - (make-reaction #(get-in @ratom path) - :on-set (if (= path []) - #(reset! ratom %2) - #(swap! ratom assoc-in path %2))) - (make-reaction #(ratom path) - :on-set #(ratom path %2)))) + (make-reaction #(get-in @ratom path) :on-set setter) + (make-reaction #(ratom path) :on-set setter))) reaction)) (_peek [this] @@ -191,13 +187,13 @@ ISwap (-swap! [a f] - (-swap! (._reaction a) f)) + (-reset! a (f (._peek a)))) (-swap! [a f x] - (-swap! (._reaction a) f x)) + (-reset! a (f (._peek a) x))) (-swap! [a f x y] - (-swap! (._reaction a) f x y)) + (-reset! a (f (._peek a) x y))) (-swap! [a f x y more] - (-swap! (._reaction a) f x y more)) + (-reset! a (apply f (._peek a) x y more))) IPrintWithWriter (-pr-writer [a writer opts] @@ -218,22 +214,17 @@ (defn cursor [src path] - (if (satisfies? IDeref path) - (do - (warn "Calling cursor with an atom as the second arg is " - "deprecated, in (cursor " - src " " (pr-str path) ")") - (assert (satisfies? IReactiveAtom path) - (str "src must be a reactive atom, not " - (pr-str path))) - (RCursor. path src nil)) - (do - (assert (or (satisfies? IReactiveAtom src) - (and (ifn? src) - (not (vector? src)))) - (str "src must be a reactive atom or a function, not " - (pr-str src))) - (RCursor. src path nil)))) + (assert (or (satisfies? IReactiveAtom src) + (and (ifn? src) + (not (vector? src)))) + (str "src must be a reactive atom or a function, not " + (pr-str src))) + (let [s (if (satisfies? IDeref src) + (if (= path []) + #(reset! src %2) + #(swap! src assoc-in path %2)) + #(src path %2))] + (RCursor. src path s nil))) From 653e4c15f9fd5b4a34793ec1105d7237660ed6e0 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 14:47:38 +0200 Subject: [PATCH 35/58] Prepare cursor for cached reaction --- src/reagent/ratom.cljs | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 934511e..c848c73 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -154,7 +154,8 @@ ;;; cursor -(deftype RCursor [ratom path setter ^:mutable reaction] +(deftype RCursor [ratom path setter ^:mutable reaction + ^:mutable state ^:mutable watches] IAtom IReactiveAtom @@ -169,21 +170,33 @@ (if (nil? reaction) (set! reaction (if (satisfies? IDeref ratom) - (make-reaction #(get-in @ratom path) :on-set setter) - (make-reaction #(ratom path) :on-set setter))) + (make-reaction #(get-in @ratom path)) + (make-reaction #(ratom path)))) reaction)) (_peek [this] (binding [*ratom-context* nil] - (-deref (._reaction this)))) + (-deref this))) + + (_set-state [this oldstate newstate] + (when-not (identical? oldstate newstate) + (set! state newstate) + (when (some? watches) + (-notify-watches this oldstate newstate)))) IDeref (-deref [this] - (-deref (._reaction this))) + (let [oldstate state + newstate (-deref (._reaction this))] + (._set-state this oldstate newstate) + newstate)) IReset (-reset! [this new-value] - (-reset! (._reaction this) new-value)) + (let [oldstate state] + (._set-state this oldstate new-value) + (setter path new-value) + new-value)) ISwap (-swap! [a f] @@ -203,11 +216,12 @@ IWatchable (-notify-watches [this oldval newval] - (-notify-watches (._reaction this) oldval newval)) + (doseq [[key f] watches] + (f key this oldval newval))) (-add-watch [this key f] - (-add-watch (._reaction this) key f)) + (set! watches (assoc watches key f))) (-remove-watch [this key] - (-remove-watch (._reaction this) key)) + (set! watches (dissoc watches key))) IHash (-hash [this] (hash [ratom path]))) @@ -224,7 +238,7 @@ #(reset! src %2) #(swap! src assoc-in path %2)) #(src path %2))] - (RCursor. src path s nil))) + (RCursor. src path s nil nil nil))) From fdef5e6c9f9423f19eac1460435432a57c25c3cd Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 15:03:39 +0200 Subject: [PATCH 36/58] Make cursor used cached reactions --- demo/reagentdemo/news/news050.cljs | 13 +++++++++++++ src/reagent/ratom.cljs | 15 ++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/demo/reagentdemo/news/news050.cljs b/demo/reagentdemo/news/news050.cljs index 0fbf016..895a0d8 100644 --- a/demo/reagentdemo/news/news050.cljs +++ b/demo/reagentdemo/news/news050.cljs @@ -29,10 +29,23 @@ [:input {:value @val :on-change #(reset! val (.-target.value %))}]]) +(defn name-part [key] + (get-in @person [:name key])) + +(def monitor reagent.ratom/monitor) + +(defn foo []) + (defn name-edit [n] (let [{:keys [first-name last-name]} @n] [:div [:p "I'm editing " first-name " " last-name "."] + [:p "I'm editing " @(monitor name-part :first-name) " " + @(monitor name-part :last-name) "."] + [:p "I'm editing " @(monitor name-part :first-name) " " + @(monitor name-part :last-name) "."] + [:p "I'm editing " @(monitor name-part :first-name) " " + @(monitor name-part :last-name) "."] [input "First name: " (r/wrap first-name swap! n assoc :first-name)] diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index c848c73..4d8552e 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -166,14 +166,6 @@ (= ratom (.-ratom other)))) Object - (_reaction [this] - (if (nil? reaction) - (set! reaction - (if (satisfies? IDeref ratom) - (make-reaction #(get-in @ratom path)) - (make-reaction #(ratom path)))) - reaction)) - (_peek [this] (binding [*ratom-context* nil] (-deref this))) @@ -187,7 +179,12 @@ IDeref (-deref [this] (let [oldstate state - newstate (-deref (._reaction this))] + newstate (if-some [r reaction] + (-deref r) + (let [f (if (satisfies? IDeref ratom) + #(get-in @ratom path) + #(ratom path))] + (cached-reaction f [ratom path ::cursor] this)))] (._set-state this oldstate newstate) newstate)) From 9e9cf93016b6fb319c4033fd276b2b905c42318a Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 18:02:13 +0200 Subject: [PATCH 37/58] Simmplify new cursor a bit --- src/reagent/ratom.cljs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 4d8552e..7a9f864 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -152,9 +152,10 @@ {:pre [(ifn? f)]} (Monitor. #(apply f args) [f args] nil)) + ;;; cursor -(deftype RCursor [ratom path setter ^:mutable reaction +(deftype RCursor [ratom path ^:mutable reaction ^:mutable state ^:mutable watches] IAtom IReactiveAtom @@ -184,7 +185,7 @@ (let [f (if (satisfies? IDeref ratom) #(get-in @ratom path) #(ratom path))] - (cached-reaction f [ratom path ::cursor] this)))] + (cached-reaction f [::cursor ratom path] this)))] (._set-state this oldstate newstate) newstate)) @@ -192,7 +193,11 @@ (-reset! [this new-value] (let [oldstate state] (._set-state this oldstate new-value) - (setter path new-value) + (if (satisfies? IDeref ratom) + (if (= path []) + (reset! ratom new-value) + (swap! ratom assoc-in path new-value)) + (ratom path new-value)) new-value)) ISwap @@ -230,12 +235,7 @@ (not (vector? src)))) (str "src must be a reactive atom or a function, not " (pr-str src))) - (let [s (if (satisfies? IDeref src) - (if (= path []) - #(reset! src %2) - #(swap! src assoc-in path %2)) - #(src path %2))] - (RCursor. src path s nil nil nil))) + (RCursor. src path nil nil nil)) From 8e7624ea45714bae9445f9a79e85defc329b06a0 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 18:14:13 +0200 Subject: [PATCH 38/58] Add a test for monitor! --- src/reagent/ratom.cljs | 3 +++ test/reagenttest/testmonitor.cljs | 35 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 7a9f864..7a2a5bc 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -152,6 +152,9 @@ {:pre [(ifn? f)]} (Monitor. #(apply f args) [f args] nil)) +(defn monitor! [f & args] + (make-reaction #(deref (apply monitor f args)) + :auto-run :async)) ;;; cursor diff --git a/test/reagenttest/testmonitor.cljs b/test/reagenttest/testmonitor.cljs index c5465a9..42be9e1 100644 --- a/test/reagenttest/testmonitor.cljs +++ b/test/reagenttest/testmonitor.cljs @@ -14,6 +14,8 @@ (defn dispose [v] (rv/dispose! v)) +(defn sync [] (r/flush)) + (enable-console-print!) @@ -41,6 +43,39 @@ (dispose const) (is (= (running) runs)))) +(deftest test-monitor! + (let [runs (running) + start (rv/atom 0) + svf (fn [] @start) + sv (monitor svf) + compf (fn [x] @sv (+ x @sv)) + comp (monitor compf 2) + c2f (fn [] (inc @comp)) + count (rv/atom 0) + out (rv/atom 0) + resf (fn [] + (swap! count inc) + (+ @sv @(monitor c2f) @comp)) + res (monitor resf) + const (rv/monitor! + #(reset! out @res))] + (is (= @count 0)) + (sync) + (is (= @count 1) "constrain ran") + (is (= @out 5)) + (reset! start 1) + (is (= @count 1)) + (sync) + (is (= @out 8)) + (is (= @count 2)) + (dispose const) + (swap! start inc) + (sync) + (is (= @count 2)) + (is (= @const 11)) + (is (= @count 3)) + (is (= (running) runs)))) + (deftest double-dependency (let [runs (running) start (rv/atom 0) From e11c881aa76b1db93ca5185290572e3ddf862f0d Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 19:35:48 +0200 Subject: [PATCH 39/58] Add rswap! Works just like swap!, except that it allows recursive swaps on the same atom, and it always returns nil. --- src/reagent/core.cljs | 21 +++++++++++++++++++++ test/reagenttest/testratom.cljs | 24 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/reagent/core.cljs b/src/reagent/core.cljs index 3e40c56..49d8118 100644 --- a/src/reagent/core.cljs +++ b/src/reagent/core.cljs @@ -277,6 +277,27 @@ another cursor) these cursors are equivalent: ;; Utilities +(defn rswap! + "Swaps the value of a to be (apply f current-value-of-atom args). + + rswap! works like swap!, except that recursive calls to rswap! on + the same atom are allowed – and it always returns nil." + [a f & args] + {:pre [(satisfies? IAtom a) + (ifn? f)]} + (if a.rswapping + (-> (or a.rswapfs (set! a.rswapfs (array))) + (.push #(apply f % args))) + (do (set! a.rswapping true) + (try (swap! a (fn [state] + (loop [s (apply f state args)] + (if-some [sf (some-> a.rswapfs .shift)] + (recur (sf s)) + s)))) + (finally + (set! a.rswapping false))))) + nil) + (defn next-tick "Run f using requestAnimationFrame or equivalent." [f] diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index e4e56e8..6051965 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -265,3 +265,27 @@ (set! rv/silent false) (dispose c) (is (= runs (running))))) + +(deftest test-rswap + (let [a (atom {:foo 1})] + (r/rswap! a update-in [:foo] inc) + (is (= (:foo @a) 2)) + (r/rswap! a #(assoc %1 :foo %2) 3) + (is (= (:foo @a) 3)) + (r/rswap! a #(assoc %1 :foo %3) 0 4) + (is (= (:foo @a) 4)) + (r/rswap! a #(assoc %1 :foo %4) 0 0 5) + (is (= (:foo @a) 5)) + (r/rswap! a #(assoc %1 :foo %5) 0 0 0 6) + (is (= (:foo @a) 6)) + (let [disp (atom nil) + f (fn [o v] + (assert (= v :add)) + (if (< (:foo o) 10) + (do + (@disp v) + (update-in o [:foo] inc)) + o)) + _ (reset! disp #(r/rswap! a f %))] + (@disp :add) + (is (= (:foo @a) 10))))) From 87758ae396c11364c154bc56fd4f8faeb6c90ca2 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 19:39:44 +0200 Subject: [PATCH 40/58] Extend test for rswap! --- test/reagenttest/testratom.cljs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/test/reagenttest/testratom.cljs b/test/reagenttest/testratom.cljs index 6051965..5b318d0 100644 --- a/test/reagenttest/testratom.cljs +++ b/test/reagenttest/testratom.cljs @@ -268,24 +268,31 @@ (deftest test-rswap (let [a (atom {:foo 1})] - (r/rswap! a update-in [:foo] inc) + (is (nil? (r/rswap! a update-in [:foo] inc))) (is (= (:foo @a) 2)) - (r/rswap! a #(assoc %1 :foo %2) 3) + (is (nil? (r/rswap! a identity))) + (is (= (:foo @a) 2)) + (is (nil? (r/rswap! a #(assoc %1 :foo %2) 3))) (is (= (:foo @a) 3)) - (r/rswap! a #(assoc %1 :foo %3) 0 4) + (is (nil? (r/rswap! a #(assoc %1 :foo %3) 0 4))) (is (= (:foo @a) 4)) - (r/rswap! a #(assoc %1 :foo %4) 0 0 5) + (is (nil? (r/rswap! a #(assoc %1 :foo %4) 0 0 5))) (is (= (:foo @a) 5)) - (r/rswap! a #(assoc %1 :foo %5) 0 0 0 6) + (is (nil? (r/rswap! a #(assoc %1 :foo %5) 0 0 0 6))) (is (= (:foo @a) 6)) (let [disp (atom nil) f (fn [o v] (assert (= v :add)) (if (< (:foo o) 10) (do - (@disp v) + (is (nil? (@disp v))) (update-in o [:foo] inc)) o)) _ (reset! disp #(r/rswap! a f %))] (@disp :add) (is (= (:foo @a) 10))))) + + + + + From ce1486a7cd4b52b9763885f33de60daeeabbdb94 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 14 Sep 2015 21:11:33 +0200 Subject: [PATCH 41/58] Make sure on-set is valid before allowing change of Reaction --- src/reagent/ratom.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 7a2a5bc..49c7206 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -285,10 +285,10 @@ IReset (-reset! [a newval] + (assert (ifn? on-set) "Reaction is read only.") (let [oldval state] (set! state newval) - (when (some? on-set) - (on-set oldval newval)) + (on-set oldval newval) (-notify-watches a oldval newval) newval)) From 54ca0f927f8ffb9e3279214e754629bbdccafc3a Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Tue, 15 Sep 2015 18:11:36 +0200 Subject: [PATCH 42/58] Make monitor! run immediately and async reaction not schedule anything from start --- src/reagent/ratom.cljs | 8 ++++---- test/reagenttest/testmonitor.cljs | 2 -- test/reagenttest/testratomasync.cljs | 18 +++++++++--------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 49c7206..9d7a759 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -153,8 +153,10 @@ (Monitor. #(apply f args) [f args] nil)) (defn monitor! [f & args] - (make-reaction #(deref (apply monitor f args)) - :auto-run :async)) + (let [r (make-reaction #(deref (apply monitor f args)) + :auto-run :async)] + @r + r)) ;;; cursor @@ -457,8 +459,6 @@ runner on-set on-dispose nocache)] (when-not (nil? derefed) (-update-watching reaction derefed)) - (when (keyword-identical? auto-run :async) - (enqueue reaction)) reaction)) diff --git a/test/reagenttest/testmonitor.cljs b/test/reagenttest/testmonitor.cljs index 42be9e1..49704f2 100644 --- a/test/reagenttest/testmonitor.cljs +++ b/test/reagenttest/testmonitor.cljs @@ -59,8 +59,6 @@ res (monitor resf) const (rv/monitor! #(reset! out @res))] - (is (= @count 0)) - (sync) (is (= @count 1) "constrain ran") (is (= @out 5)) (reset! start 1) diff --git a/test/reagenttest/testratomasync.cljs b/test/reagenttest/testratomasync.cljs index 03fd473..cdf2377 100644 --- a/test/reagenttest/testratomasync.cljs +++ b/test/reagenttest/testratomasync.cljs @@ -8,7 +8,7 @@ (set! rv/debug true) (rv/running)) -(def testite 10) +(def testite 1) (defn dispose [v] (rv/dispose! v)) @@ -32,7 +32,7 @@ const (ar (fn [] (reset! out @res)))] (is (= @count 0)) - (sync) + @const (is (= @count 1) "constrain ran") (is (= @out 2)) (reset! start 1) @@ -85,7 +85,7 @@ ;;update the counter (swap! !counter inc)))] (is (= 0 @!counter)) - (sync) + @co (is (= 1 @!counter) "Constraint run on init") (reset! !signal "foo") (sync) @@ -94,7 +94,7 @@ (dispose co)) (let [!x (rv/atom 0) !co (rv/make-reaction #(inc @!x) :auto-run :async)] - (sync) + @!co (is (= 1 @!co) "CO has correct value on first deref") (swap! !x inc) (sync) @@ -126,7 +126,7 @@ (reset! a -1) (is (= @b-changed 1)) - (sync) + @res (is (= @b-changed 2)) (is (= @c-changed 0)) @@ -166,7 +166,7 @@ res (rv/atom 0) cs (ar #(reset! res @d))] - (sync) + @cs (is (= @res "1")) (dispose cs)) ;; should be broken according to https://github.com/lynaghk/reflex/issues/1 @@ -184,7 +184,7 @@ res (rv/atom 0)] (is (= @d [1 -1])) (let [e (ar #(reset! res @d))] - (sync) + @e (is (= @res [1 -1])) (dispose e)) (dispose d)) @@ -208,7 +208,7 @@ cns (rv/make-reaction #(reset! res @c) :auto-run :async :on-dispose #(reset! disposed-cns true))] - (sync) + @cns (is (= @res 2)) (is (= (+ 4 runs) (running))) (is (= @count-b 1)) @@ -281,7 +281,7 @@ (set! rv/silent true) (is (= @catch-count 0)) (reset! a false) - (sync) + @c (is (= @catch-count 0)) (reset! a true) (is (= @catch-count 0)) From 897b766624c21d0f7fe1f4a957934ca46147bdf8 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Fri, 18 Sep 2015 14:23:50 +0200 Subject: [PATCH 43/58] Experiment with with-resource --- src/reagent/ratom.clj | 28 ++++++++++++++++++++++++++++ src/reagent/ratom.cljs | 31 ++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/reagent/ratom.clj b/src/reagent/ratom.clj index b14ced4..47dbeca 100644 --- a/src/reagent/ratom.clj +++ b/src/reagent/ratom.clj @@ -12,3 +12,31 @@ :auto-run true)] (deref co#) co#)) + +(defmacro with-resource [bindings & body] + (assert (vector? bindings)) + (let [v (gensym "res-v") + init (gensym "res-init-v") + bs (into [] (map-indexed (fn [i x] + (if (even? i) + x + (let [pos (-> (/ i 2) int)] + `(if ~init + (aget ~v ~pos) + (aset ~v ~pos ~x))))) + bindings)) + [forms destroy] (let [fin (last body)] + (if (and (list? fin) + (= 'finally (first fin))) + [(butlast body) `(fn [] ~@(rest fin))] + [body nil]))] + `(let [o# (cljs.core/js-obj) + ~v (reagent.ratom/get-cached-values (quote ~v) o#) + ~init (if (true? (.-init ~v)) + true + (do + (set! (.-init ~v) true) + false))] + (let ~bs + (set! (.-destroy o#) ~destroy) + ~@forms)))) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 9d7a759..db7f7f9 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -6,6 +6,8 @@ (declare ^:dynamic *ratom-context*) +(defn reactive? [] (some? *ratom-context*)) + (defonce ^boolean debug false) (defonce ^boolean silent false) @@ -109,7 +111,7 @@ (defonce cached-reactions {}) -(defn- cached-reaction [f key obj] +(defn- cached-reaction [f key obj destroy] (if-some [r (get cached-reactions key)] (-deref r) (if (some? *ratom-context*) @@ -117,12 +119,19 @@ f :on-dispose (fn [] (set! cached-reactions (dissoc cached-reactions key)) - (set! (.-reaction obj) nil))) + (when (some? obj) + (set! (.-reaction obj) nil)) + (when (some-> destroy .-destroy) + (.destroy destroy)))) v (-deref r)] (set! cached-reactions (assoc cached-reactions key r)) - (set! (.-reaction obj) r) + (when (some? obj) + (set! (.-reaction obj) r)) v) - (f)))) + (let [res (f)] + (when (some-> destroy .-destroy) + (.destroy destroy)) + res)))) (deftype Monitor [f key ^:mutable reaction] IReactiveAtom @@ -131,7 +140,7 @@ (-deref [this] (if-some [r reaction] (-deref r) - (cached-reaction f key this))) + (cached-reaction f key this nil))) IEquiv (-equiv [o other] @@ -190,7 +199,7 @@ (let [f (if (satisfies? IDeref ratom) #(get-in @ratom path) #(ratom path))] - (cached-reaction f [::cursor ratom path] this)))] + (cached-reaction f [::cursor ratom path] this nil)))] (._set-state this oldstate newstate) newstate)) @@ -244,6 +253,15 @@ +;;; with-resource support + +(defn get-cached-values [key destroy] + (cached-reaction #(let [o #js{}] + (set! (.-values o) #js[]) + o) + key nil destroy)) + + ;;;; reaction (defprotocol IDisposable @@ -422,7 +440,6 @@ (-hash [this] (goog/getUid this))) - ;;; Queueing ;; Gets set up from batching From 6f448f296fe9b2de7b73d6a6ac3cc0c22735575d Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Fri, 18 Sep 2015 17:59:40 +0200 Subject: [PATCH 44/58] on-dispose should always be called in Reaction --- src/reagent/ratom.cljs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index db7f7f9..dc6c85e 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -121,7 +121,7 @@ (dissoc cached-reactions key)) (when (some? obj) (set! (.-reaction obj) nil)) - (when (some-> destroy .-destroy) + (when (some-> destroy .-destroy some?) (.destroy destroy)))) v (-deref r)] (set! cached-reactions (assoc cached-reactions key r)) @@ -129,7 +129,7 @@ (set! (.-reaction obj) r)) v) (let [res (f)] - (when (some-> destroy .-destroy) + (when (some-> destroy .-destroy some?) (.destroy destroy)) res)))) @@ -401,14 +401,19 @@ IDeref (-deref [this] (-check-clean this) - (if (and (nil? auto-run) (nil? *ratom-context*)) - (when-not (== dirtyness clean) - (let [oldstate state - newstate (f)] - (set! state newstate) - (when (and (some? watches) - (not= oldstate newstate)) - (-notify-watches this oldstate newstate)))) + (if (and (nil? *ratom-context*) + (nil? auto-run)) + (do + (when-not (== dirtyness clean) + (let [oldstate state + newstate (f)] + (set! state newstate) + (when (and (some? watches) + (not= oldstate newstate)) + (-notify-watches this oldstate newstate)))) + (when (and (some? on-dispose) + (nil? watches)) + (on-dispose))) (do (notify-deref-watcher! this) (when-not (== dirtyness clean) From 37703c283409ca53949f07c5ccf0c3050a42e51f Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sat, 19 Sep 2015 08:46:26 +0200 Subject: [PATCH 45/58] Make with-resource use a proper key --- src/reagent/impl/batching.cljs | 2 +- src/reagent/ratom.cljs | 42 ++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/reagent/impl/batching.cljs b/src/reagent/impl/batching.cljs index 4488e2c..6227ca6 100644 --- a/src/reagent/impl/batching.cljs +++ b/src/reagent/impl/batching.cljs @@ -104,7 +104,7 @@ (.! c :cljsRatom (ratom/make-reaction run :auto-run #(queue-render c) - :derefed derefed + :capture derefed :no-cache true))) res) (ratom/run rat)))) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index dc6c85e..d9fd405 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -21,6 +21,10 @@ (f))) (defn captured [obj] + (when (some? (.-cljsCaptured obj)) + obj)) + +(defn- -captured [obj] (let [c (.-cljsCaptured obj)] (set! (.-cljsCaptured obj) nil) c)) @@ -112,7 +116,7 @@ (defonce cached-reactions {}) (defn- cached-reaction [f key obj destroy] - (if-some [r (get cached-reactions key)] + (if-some [r (some->> key (get cached-reactions))] (-deref r) (if (some? *ratom-context*) (let [r (make-reaction @@ -255,11 +259,23 @@ ;;; with-resource support +(def reaction-counter 0) + +(defn reaction-key [] + (when-some [c *ratom-context*] + (if-some [k (.-reaction-id c)] + k + (->> reaction-counter inc + (set! reaction-counter) + (set! (.-reaction-id c)))))) + (defn get-cached-values [key destroy] - (cached-reaction #(let [o #js{}] - (set! (.-values o) #js[]) - o) - key nil destroy)) + (let [key (when-some [k (reaction-key)] + [k key])] + (cached-reaction #(let [o #js{}] + (set! (.-values o) #js[]) + o) + key nil destroy))) ;;;; reaction @@ -385,7 +401,7 @@ (run [this] (let [oldstate state res (capture-derefed f this) - derefed (captured this)] + derefed (-captured this)] (when (not= derefed watching) (-update-watching this derefed)) (set! dirtyness clean) @@ -470,17 +486,25 @@ (run r))))))) -(defn make-reaction [f & {:keys [auto-run on-set on-dispose derefed no-cache]}] +(defn make-reaction [f & {:keys [auto-run on-set on-dispose derefed no-cache + capture]}] (let [runner (case auto-run true run :async enqueue auto-run) - dirty (if (nil? derefed) dirty clean) + derefs (if-some [c capture] + (-captured c) + derefed) + dirty (if (nil? derefs) dirty clean) nocache (if (nil? no-cache) false no-cache) reaction (Reaction. f nil dirty nil nil runner on-set on-dispose nocache)] + (when-some [rid (some-> capture .-reaction-id)] + (set! (.-reaction-id reaction) rid)) (when-not (nil? derefed) - (-update-watching reaction derefed)) + (warn "using derefed is deprecated")) + (when-not (nil? derefs) + (-update-watching reaction derefs)) reaction)) From 0b553ef6bd9f65ea3f414a973deebad6d7f53761 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sat, 19 Sep 2015 09:14:14 +0200 Subject: [PATCH 46/58] Reorg with-resource --- src/reagent/ratom.clj | 8 ++++++-- src/reagent/ratom.cljs | 20 ++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/reagent/ratom.clj b/src/reagent/ratom.clj index 47dbeca..0ae8123 100644 --- a/src/reagent/ratom.clj +++ b/src/reagent/ratom.clj @@ -38,5 +38,9 @@ (set! (.-init ~v) true) false))] (let ~bs - (set! (.-destroy o#) ~destroy) - ~@forms)))) + (let [dest# ~destroy + res# (do ~@forms)] + (if (reagent.ratom/reactive?) + (set! (.-destroy o#) ~destroy) + (dest#)) + res#))))) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index d9fd405..b0e9888 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -6,7 +6,8 @@ (declare ^:dynamic *ratom-context*) -(defn reactive? [] (some? *ratom-context*)) +(defn reactive? [] + (some? *ratom-context*)) (defonce ^boolean debug false) (defonce ^boolean silent false) @@ -116,7 +117,7 @@ (defonce cached-reactions {}) (defn- cached-reaction [f key obj destroy] - (if-some [r (some->> key (get cached-reactions))] + (if-some [r (get cached-reactions key)] (-deref r) (if (some? *ratom-context*) (let [r (make-reaction @@ -132,10 +133,7 @@ (when (some? obj) (set! (.-reaction obj) r)) v) - (let [res (f)] - (when (some-> destroy .-destroy some?) - (.destroy destroy)) - res)))) + (f)))) (deftype Monitor [f key ^:mutable reaction] IReactiveAtom @@ -270,12 +268,10 @@ (set! (.-reaction-id c)))))) (defn get-cached-values [key destroy] - (let [key (when-some [k (reaction-key)] - [k key])] - (cached-reaction #(let [o #js{}] - (set! (.-values o) #js[]) - o) - key nil destroy))) + (if-some [k (reaction-key)] + (cached-reaction #(array) + [k key] nil destroy) + (array))) ;;;; reaction From ea7cd26cf59d7009b034f7db98de615e14ae6fbb Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sat, 19 Sep 2015 11:27:50 +0200 Subject: [PATCH 47/58] Rename with-resource to with-kept and clean up --- src/reagent/ratom.clj | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/reagent/ratom.clj b/src/reagent/ratom.clj index 0ae8123..654073b 100644 --- a/src/reagent/ratom.clj +++ b/src/reagent/ratom.clj @@ -13,34 +13,29 @@ (deref co#) co#)) -(defmacro with-resource [bindings & body] +(defmacro with-kept [bindings & body] (assert (vector? bindings)) - (let [v (gensym "res-v") - init (gensym "res-init-v") - bs (into [] (map-indexed (fn [i x] - (if (even? i) - x - (let [pos (-> (/ i 2) int)] - `(if ~init - (aget ~v ~pos) - (aset ~v ~pos ~x))))) - bindings)) + (let [v (gensym "bind-v") + bs (->> bindings + (map-indexed (fn [i x] + (if (even? i) + x + (let [pos (quot i 2)] + `(if (> (alength ~v) ~pos) + (aget ~v ~pos) + (aset ~v ~pos ~x)))))) + vec) [forms destroy] (let [fin (last body)] (if (and (list? fin) (= 'finally (first fin))) [(butlast body) `(fn [] ~@(rest fin))] [body nil]))] - `(let [o# (cljs.core/js-obj) - ~v (reagent.ratom/get-cached-values (quote ~v) o#) - ~init (if (true? (.-init ~v)) - true - (do - (set! (.-init ~v) true) - false))] + `(let [destroy-obj# (cljs.core/js-obj) + ~v (reagent.ratom/get-cached-values (quote ~v) destroy-obj#)] (let ~bs - (let [dest# ~destroy + (let [destroy# ~destroy res# (do ~@forms)] (if (reagent.ratom/reactive?) - (set! (.-destroy o#) ~destroy) - (dest#)) + (set! (.-destroy destroy-obj#) destroy#) + (destroy#)) res#))))) From ca5ce00c349001fbe0add88387e6c084e08eb00c Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sat, 19 Sep 2015 16:49:13 +0200 Subject: [PATCH 48/58] Rename monitor -> track, and monitor! -> track! --- demo/reagentdemo/news/news050.cljs | 14 ++++---- src/reagent/ratom.cljs | 16 ++++----- test/reagenttest/testmonitor.cljs | 53 +++++++++++++++--------------- 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/demo/reagentdemo/news/news050.cljs b/demo/reagentdemo/news/news050.cljs index 895a0d8..1b5e31b 100644 --- a/demo/reagentdemo/news/news050.cljs +++ b/demo/reagentdemo/news/news050.cljs @@ -32,7 +32,7 @@ (defn name-part [key] (get-in @person [:name key])) -(def monitor reagent.ratom/monitor) +(def track reagent.ratom/track) (defn foo []) @@ -40,12 +40,12 @@ (let [{:keys [first-name last-name]} @n] [:div [:p "I'm editing " first-name " " last-name "."] - [:p "I'm editing " @(monitor name-part :first-name) " " - @(monitor name-part :last-name) "."] - [:p "I'm editing " @(monitor name-part :first-name) " " - @(monitor name-part :last-name) "."] - [:p "I'm editing " @(monitor name-part :first-name) " " - @(monitor name-part :last-name) "."] + [:p "I'm editing " @(track name-part :first-name) " " + @(track name-part :last-name) "."] + [:p "I'm editing " @(track name-part :first-name) " " + @(track name-part :last-name) "."] + [:p "I'm editing " @(track name-part :first-name) " " + @(track name-part :last-name) "."] [input "First name: " (r/wrap first-name swap! n assoc :first-name)] diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index b0e9888..707201e 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -110,7 +110,7 @@ -;;; monitor +;;; track (declare make-reaction) @@ -135,7 +135,7 @@ v) (f)))) -(deftype Monitor [f key ^:mutable reaction] +(deftype Track [f key ^:mutable reaction] IReactiveAtom IDeref @@ -146,7 +146,7 @@ IEquiv (-equiv [o other] - (and (instance? Monitor other) + (and (instance? Track other) (= key (.-key other)))) IHash @@ -154,17 +154,17 @@ IPrintWithWriter (-pr-writer [a writer opts] - (-write writer (str "#"))) -(defn monitor [f & args] +(defn track [f & args] {:pre [(ifn? f)]} - (Monitor. #(apply f args) [f args] nil)) + (Track. #(apply f args) [f args] nil)) -(defn monitor! [f & args] - (let [r (make-reaction #(deref (apply monitor f args)) +(defn track! [f & args] + (let [r (make-reaction #(deref (apply track f args)) :auto-run :async)] @r r)) diff --git a/test/reagenttest/testmonitor.cljs b/test/reagenttest/testmonitor.cljs index 49704f2..c8b1724 100644 --- a/test/reagenttest/testmonitor.cljs +++ b/test/reagenttest/testmonitor.cljs @@ -1,7 +1,6 @@ (ns reagenttest.testmonitor (:require [cljs.test :as t :refer-macros [is deftest testing]] - [reagent.ratom :as rv :refer [monitor] - :refer-macros [run! reaction]] + [reagent.ratom :as rv :refer [track] :refer-macros [run! reaction]] [reagent.debug :refer-macros [dbg]] [reagent.core :as r])) @@ -23,16 +22,16 @@ (let [runs (running) start (rv/atom 0) svf (fn [] @start) - sv (monitor svf) + sv (track svf) compf (fn [x] @sv (+ x @sv)) - comp (monitor compf 2) + comp (track compf 2) c2f (fn [] (inc @comp)) count (rv/atom 0) out (rv/atom 0) resf (fn [] (swap! count inc) - (+ @sv @(monitor c2f) @comp)) - res (monitor resf) + (+ @sv @(track c2f) @comp)) + res (track resf) const (run! (reset! out @res))] (is (= @count 1) "constrain ran") @@ -43,21 +42,21 @@ (dispose const) (is (= (running) runs)))) -(deftest test-monitor! +(deftest test-track! (let [runs (running) start (rv/atom 0) svf (fn [] @start) - sv (monitor svf) + sv (track svf) compf (fn [x] @sv (+ x @sv)) - comp (monitor compf 2) + comp (track compf 2) c2f (fn [] (inc @comp)) count (rv/atom 0) out (rv/atom 0) resf (fn [] (swap! count inc) - (+ @sv @(monitor c2f) @comp)) - res (monitor resf) - const (rv/monitor! + (+ @sv @(track c2f) @comp)) + res (track resf) + const (rv/track! #(reset! out @res))] (is (= @count 1) "constrain ran") (is (= @out 5)) @@ -83,7 +82,7 @@ c3 (rv/make-reaction (fn [] (swap! c3-count inc) - (+ @(monitor c1f) @(monitor c2f))) + (+ @(track c1f) @(track c2f))) :auto-run true)] (is (= @c3-count 0)) (is (= @c3 1)) @@ -99,7 +98,7 @@ (let [runs (running)] (let [!x (rv/atom 0) f #(inc @!x) - !co (run! @(monitor f))] + !co (run! @(track f))] (is (= 1 @!co) "CO has correct value on first deref") (swap! !x inc) (is (= 2 @!co) "CO auto-updates") @@ -112,8 +111,8 @@ (let [runs (running) a (rv/atom 0) af (fn [x] (+ @a x)) - a1 (monitor af 1) - a2 (monitor af 0) + a1 (track af 1) + a2 (track af 0) b-changed (rv/atom 0) c-changed (rv/atom 0) mf (fn [v x spy] @@ -121,8 +120,8 @@ (+ @v x)) res (run! (if (< @a2 1) - @(monitor mf a1 1 b-changed) - @(monitor mf a2 10 c-changed)))] + @(track mf a1 1 b-changed) + @(track mf a2 10 c-changed)))] (is (= @res (+ 2 @a))) (is (= @b-changed 1)) (is (= @c-changed 0)) @@ -157,9 +156,9 @@ (let [runs (running) a (rv/atom 0) f (fn [x] (+ x @a)) - b (monitor f 1) - c (monitor f -1) - d (monitor #(str @b)) + b (track f 1) + c (track f -1) + d (track #(str @b)) res (rv/atom 0) cs (run! (reset! res @d))] @@ -169,14 +168,14 @@ ;; but isnt (let [a (rv/atom 0) f (fn [x] (+ x @a)) - b (monitor f 1) - d (run! [@b @(monitor f -1)])] + b (track f 1) + d (run! [@b @(track f -1)])] (is (= @d [1 -1])) (dispose d)) (let [a (rv/atom 0) f (fn [x] (+ x @a)) - c (monitor f -1) - d (run! [@(monitor f 1) @c]) + c (track f -1) + d (run! [@(track f 1) @c]) res (rv/atom 0)] (is (= @d [1 -1])) (let [e (run! (reset! res @d))] @@ -188,7 +187,7 @@ (deftest non-reactive-deref (let [runs (running) a (rv/atom 0) - b (monitor #(+ 5 @a))] + b (track #(+ 5 @a))] (is (= @b 5)) (is (= runs (running))) @@ -200,7 +199,7 @@ (let [runs (running) a (rv/atom false) catch-count (atom 0) - b (monitor #(if @a (throw (js/Error. "fail")))) + b (track #(if @a (throw (js/Error. "fail")))) c (run! (try @b (catch :default e (swap! catch-count inc))))] (set! rv/silent true) From 6c9f71e51186734dcdecff4cf31e6c5c7d0ca3df Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Sat, 19 Sep 2015 19:03:29 +0200 Subject: [PATCH 49/58] Rename testmonitor --- test/reagenttest/runtests.cljs | 2 +- test/reagenttest/{testmonitor.cljs => testtrack.cljs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename test/reagenttest/{testmonitor.cljs => testtrack.cljs} (99%) diff --git a/test/reagenttest/runtests.cljs b/test/reagenttest/runtests.cljs index 0183057..5a37525 100644 --- a/test/reagenttest/runtests.cljs +++ b/test/reagenttest/runtests.cljs @@ -5,7 +5,7 @@ [reagenttest.testinterop] [reagenttest.testratom] [reagenttest.testratomasync] - [reagenttest.testmonitor] + [reagenttest.testtrack] [reagenttest.testwrap] [cljs.test :as test :include-macros true] [reagent.core :as r] diff --git a/test/reagenttest/testmonitor.cljs b/test/reagenttest/testtrack.cljs similarity index 99% rename from test/reagenttest/testmonitor.cljs rename to test/reagenttest/testtrack.cljs index c8b1724..e36ef49 100644 --- a/test/reagenttest/testmonitor.cljs +++ b/test/reagenttest/testtrack.cljs @@ -1,4 +1,4 @@ -(ns reagenttest.testmonitor +(ns reagenttest.testtrack (:require [cljs.test :as t :refer-macros [is deftest testing]] [reagent.ratom :as rv :refer [track] :refer-macros [run! reaction]] [reagent.debug :refer-macros [dbg]] From a73761e72e32075c4a149252939a483dd0610fb0 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Mon, 21 Sep 2015 10:49:43 +0200 Subject: [PATCH 50/58] Warn if the same with-kept is used more than once --- src/reagent/ratom.clj | 8 ++++++++ src/reagent/ratom.cljs | 7 +++++++ test/reagenttest/testtrack.cljs | 1 + 3 files changed, 16 insertions(+) diff --git a/src/reagent/ratom.clj b/src/reagent/ratom.clj index 654073b..2acaaa5 100644 --- a/src/reagent/ratom.clj +++ b/src/reagent/ratom.clj @@ -32,6 +32,14 @@ [body nil]))] `(let [destroy-obj# (cljs.core/js-obj) ~v (reagent.ratom/get-cached-values (quote ~v) destroy-obj#)] + (when *assert* + (when-some [c# reagent.ratom/*ratom-context*] + (when (== (.-ratomGeneration c#) + (.-generation ~v)) + (js/console.error + "The same with-kept is being used more than once in the + same reactive context.")) + (set! (.-generation ~v) (.-ratomGeneration c#)))) (let ~bs (let [destroy# ~destroy res# (do ~@forms)] diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 707201e..840ecd7 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -11,6 +11,7 @@ (defonce ^boolean debug false) (defonce ^boolean silent false) +(defonce generation 0) (defonce -running (clojure.core/atom 0)) @@ -18,6 +19,9 @@ (defn capture-derefed [f obj] (set! (.-cljsCaptured obj) nil) + (when (dev?) + (set! (.-ratomGeneration obj) + (set! generation (inc generation)))) (binding [*ratom-context* obj] (f))) @@ -500,6 +504,9 @@ (when-not (nil? derefed) (warn "using derefed is deprecated")) (when-not (nil? derefs) + (when (dev?) + (set! (.-ratomGeneration reaction) + (.-ratomGeneration derefs))) (-update-watching reaction derefs)) reaction)) diff --git a/test/reagenttest/testtrack.cljs b/test/reagenttest/testtrack.cljs index e36ef49..a2dd851 100644 --- a/test/reagenttest/testtrack.cljs +++ b/test/reagenttest/testtrack.cljs @@ -43,6 +43,7 @@ (is (= (running) runs)))) (deftest test-track! + (sync) (let [runs (running) start (rv/atom 0) svf (fn [] @start) From 51163c1d07a3f380b84838d0b4ae607fe52946cc Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Wed, 23 Sep 2015 10:39:49 +0200 Subject: [PATCH 51/58] Rename to with-let, add basic test --- src/reagent/ratom.clj | 4 ++-- test/reagenttest/testreagent.cljs | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/reagent/ratom.clj b/src/reagent/ratom.clj index 2acaaa5..2758871 100644 --- a/src/reagent/ratom.clj +++ b/src/reagent/ratom.clj @@ -13,7 +13,7 @@ (deref co#) co#)) -(defmacro with-kept [bindings & body] +(defmacro with-let [bindings & body] (assert (vector? bindings)) (let [v (gensym "bind-v") bs (->> bindings @@ -37,7 +37,7 @@ (when (== (.-ratomGeneration c#) (.-generation ~v)) (js/console.error - "The same with-kept is being used more than once in the + "The same with-let is being used more than once in the same reactive context.")) (set! (.-generation ~v) (.-ratomGeneration c#)))) (let ~bs diff --git a/test/reagenttest/testreagent.cljs b/test/reagenttest/testreagent.cljs index 718b052..b588e28 100644 --- a/test/reagenttest/testreagent.cljs +++ b/test/reagenttest/testreagent.cljs @@ -1,6 +1,6 @@ (ns reagenttest.testreagent (:require [cljs.test :as t :refer-macros [is deftest testing]] - [reagent.ratom :as rv :refer-macros [reaction]] + [reagent.ratom :as rv :refer-macros [reaction with-let]] [reagent.debug :refer-macros [dbg println log]] [reagent.interop :refer-macros [.' .!]] [reagent.core :as r])) @@ -565,3 +565,23 @@ c2 (fn [] [c1 (sorted-map 1 "foo" 2 "bar")])] (is (= (rstr [c2]) "
foo
")))) + +(deftest basic-with-let + (let [n1 (atom 0) + n2 (atom 0) + n3 (atom 0) + val (r/atom 0) + c (fn [] + (with-let [v (swap! n1 inc)] + (swap! n2 inc) + [:div @val] + (finally + (swap! n3 inc))))] + (with-mounted-component [c] + (fn [_ div] + (is (= [1 1 0] [@n1 @n2 @n3])) + (swap! val inc) + (is (= [1 1 0] [@n1 @n2 @n3])) + (r/flush) + (is (= [1 2 0] [@n1 @n2 @n3])))) + (is (= [1 2 1] [@n1 @n2 @n3])))) From 119fbfe1737587e475a88c80b07cdd88f5071fcd Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Wed, 23 Sep 2015 15:55:57 +0200 Subject: [PATCH 52/58] Add a couple of more tests for with-let --- test/reagenttest/testreagent.cljs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/reagenttest/testreagent.cljs b/test/reagenttest/testreagent.cljs index b588e28..6026717 100644 --- a/test/reagenttest/testreagent.cljs +++ b/test/reagenttest/testreagent.cljs @@ -585,3 +585,30 @@ (r/flush) (is (= [1 2 0] [@n1 @n2 @n3])))) (is (= [1 2 1] [@n1 @n2 @n3])))) + +(deftest with-let-destroy-only + (let [n1 (atom 0) + n2 (atom 0) + c (fn [] + (with-let [] + (swap! n1 inc) + [:div] + (finally + (swap! n2 inc))))] + (with-mounted-component [c] + (fn [_ div] + (is (= [1 0] [@n1 @n2])))) + (is (= [1 1] [@n1 @n2])))) + +(deftest with-let-non-reactive + (let [n1 (atom 0) + n2 (atom 0) + n3 (atom 0) + c (fn [] + (with-let [a (swap! n1 inc)] + (swap! n2 inc) + [:div a] + (finally + (swap! n3 inc))))] + (is (= (rstr [c]) (rstr [:div 1]))) + (is (= [1 1 1] [@n1 @n2 @n3])))) From 9163695832014a2cebc2ee84b99a949cafdf406d Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Wed, 23 Sep 2015 17:02:48 +0200 Subject: [PATCH 53/58] Start adding more tests for with-let --- test/reagenttest/runtests.cljs | 1 + test/reagenttest/testwithlet.cljs | 43 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 test/reagenttest/testwithlet.cljs diff --git a/test/reagenttest/runtests.cljs b/test/reagenttest/runtests.cljs index 5a37525..2a2c948 100644 --- a/test/reagenttest/runtests.cljs +++ b/test/reagenttest/runtests.cljs @@ -6,6 +6,7 @@ [reagenttest.testratom] [reagenttest.testratomasync] [reagenttest.testtrack] + [reagenttest.testwithlet] [reagenttest.testwrap] [cljs.test :as test :include-macros true] [reagent.core :as r] diff --git a/test/reagenttest/testwithlet.cljs b/test/reagenttest/testwithlet.cljs new file mode 100644 index 0000000..985e7ec --- /dev/null +++ b/test/reagenttest/testwithlet.cljs @@ -0,0 +1,43 @@ +(ns reagenttest.testwithlet + (:require [cljs.test :as t :refer-macros [is deftest testing]] + [reagent.ratom :as rv :refer [track track! dispose!] + :refer-macros [with-let]] + [reagent.debug :refer-macros [dbg]] + [reagent.core :as r :refer [flush]])) + +(defn running [] + (r/flush) + (set! rv/debug true) + (rv/running)) + +(deftest basic-with-let + (let [runs (running) + n1 (atom 0) + n2 (atom 0) + n3 (atom 0) + a (r/atom 10) + f1 (fn [] + (with-let [v (swap! n1 inc)] + (swap! n2 inc) + [@a v] + (finally + (swap! n3 inc)))) + r (atom nil) + t (track! (fn [] (reset! r @(track f1))))] + (is (= [[10 1] 1 1 0] [@r @n1 @n2 @n3])) + (swap! a inc) + (is (= [[10 1] 1 1 0] [@r @n1 @n2 @n3])) + (flush) + (is (= [[11 1] 1 2 0] [@r @n1 @n2 @n3])) + (is (= [11 1] @t)) + + (dispose! t) + (is (= [[11 1] 1 2 1] [@r @n1 @n2 @n3])) + (is (= runs (running))) + + (swap! a inc) + (flush) + (is (= [[11 1] 1 2 1] [@r @n1 @n2 @n3])) + (is (= [12 2] @t)) + (is (= [[12 2] 2 3 2] [@r @n1 @n2 @n3])) + (is (= runs (running))))) From e1585f6730e3693770b25b5d7142159dfdebc17c Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Wed, 23 Sep 2015 19:40:19 +0200 Subject: [PATCH 54/58] Add more with-let tests, and fix dispose bug --- src/reagent/ratom.clj | 11 +++- src/reagent/ratom.cljs | 2 +- test/reagenttest/testwithlet.cljs | 92 ++++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 4 deletions(-) diff --git a/src/reagent/ratom.clj b/src/reagent/ratom.clj index 2758871..3769a0e 100644 --- a/src/reagent/ratom.clj +++ b/src/reagent/ratom.clj @@ -45,5 +45,14 @@ res# (do ~@forms)] (if (reagent.ratom/reactive?) (set! (.-destroy destroy-obj#) destroy#) - (destroy#)) + (when (some? destroy#) + (destroy#))) res#))))) + + + + + + + + diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 840ecd7..528d5e9 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -259,7 +259,7 @@ -;;; with-resource support +;;; with-let support (def reaction-counter 0) diff --git a/test/reagenttest/testwithlet.cljs b/test/reagenttest/testwithlet.cljs index 985e7ec..3ba08d9 100644 --- a/test/reagenttest/testwithlet.cljs +++ b/test/reagenttest/testwithlet.cljs @@ -3,7 +3,8 @@ [reagent.ratom :as rv :refer [track track! dispose!] :refer-macros [with-let]] [reagent.debug :refer-macros [dbg]] - [reagent.core :as r :refer [flush]])) + [reagent.core :as r :refer [flush]] + [clojure.walk :as w])) (defn running [] (r/flush) @@ -23,7 +24,8 @@ (finally (swap! n3 inc)))) r (atom nil) - t (track! (fn [] (reset! r @(track f1))))] + t (track! (fn [] + (reset! r @(track f1))))] (is (= [[10 1] 1 1 0] [@r @n1 @n2 @n3])) (swap! a inc) (is (= [[10 1] 1 1 0] [@r @n1 @n2 @n3])) @@ -41,3 +43,89 @@ (is (= [12 2] @t)) (is (= [[12 2] 2 3 2] [@r @n1 @n2 @n3])) (is (= runs (running))))) + + +(deftest test-with-let-args + (let [runs (running) + n1 (atom 0) + n2 (atom 0) + a (r/atom 0) + ran (fn [] + (swap! n2 inc) + @a) + f1 #(with-let [] + (ran) + []) + f2 #(with-let [x1 (swap! n1 inc)] + (ran) + [x1]) + f3 #(with-let [x1 (swap! n1 inc) + x2 (swap! n1 inc)] + (ran) + [x1 x2]) + f4 #(with-let [x1 (swap! n1 inc) + x2 (swap! n1 inc) + x3 (swap! n1 inc)] + (ran) + [x1 x2 x3]) + f5 #(with-let [x1 (swap! n1 inc) + x2 (swap! n1 inc) + x3 (swap! n1 inc) + x4 (swap! n1 inc)] + (ran) + [x1 x2 x3 x4]) + f6 #(with-let [x1 (swap! n1 inc) + x2 (swap! n1 inc) + x3 (swap! n1 inc) + x4 (swap! n1 inc) + x5 (swap! n1 inc)] + (ran) + [x1 x2 x3 x4 x5]) + f7 #(with-let [x1 (swap! n1 inc) + x2 (swap! n1 inc) + x3 (swap! n1 inc) + x4 (swap! n1 inc) + x5 (swap! n1 inc) + x6 (swap! n1 inc)] + (ran) + [x1 x2 x3 x4 x5 x6]) + r (atom nil) + all (fn [] {:f1 @(track f1) + :f2 @(track f2) + :f3 @(track f3) + :f4 @(track f4) + :f5 @(track f5) + :f6 @(track f6) + :f7 @(track f7)}) + t (track! (fn [] (reset! r (all)))) + expected {:f1 [] + :f2 [1] + :f3 [2 3] + :f4 [4 5 6] + :f5 [7 8 9 10] + :f6 [11 12 13 14 15] + :f7 [16 17 18 19 20 21]}] + (is (< runs (running))) + (is (= @n2 7)) + (is (= @r expected)) + (is (= (all) expected)) + (is (= @t expected)) + (swap! a inc) + (is (= @n2 7)) + (flush) + (is (= @n2 14)) + (is (= @r expected)) + (is (= (all) expected)) + (is (= @t expected)) + (is (= @n2 14)) + (dispose! t) + (is (= runs (running))) + (is (= @r expected)) + (is (= @n2 14)) + (is (= (all) (w/postwalk #(if (number? %) (+ 21 %) %) + expected))) + (is (= @n2 21)) + (is (= @t (w/postwalk #(if (number? %) (+ 42 %) %) + expected))) + (is (= @n2 28)) + (is (= runs (running))))) From d476222abbf58d805b454f757aa9bf060ce1d3c1 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Wed, 23 Sep 2015 20:14:29 +0200 Subject: [PATCH 55/58] Optimize track! and with-let a bit --- src/reagent/ratom.clj | 23 +++++++++-------------- src/reagent/ratom.cljs | 4 ++-- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/reagent/ratom.clj b/src/reagent/ratom.clj index 3769a0e..b4b988b 100644 --- a/src/reagent/ratom.clj +++ b/src/reagent/ratom.clj @@ -29,10 +29,13 @@ (if (and (list? fin) (= 'finally (first fin))) [(butlast body) `(fn [] ~@(rest fin))] - [body nil]))] - `(let [destroy-obj# (cljs.core/js-obj) + [body nil])) + destroy-obj (when destroy + `(cljs.core/js-obj)) + asserting (if *assert* true false)] + `(let [destroy-obj# ~destroy-obj ~v (reagent.ratom/get-cached-values (quote ~v) destroy-obj#)] - (when *assert* + (when ~asserting (when-some [c# reagent.ratom/*ratom-context*] (when (== (.-ratomGeneration c#) (.-generation ~v)) @@ -43,16 +46,8 @@ (let ~bs (let [destroy# ~destroy res# (do ~@forms)] - (if (reagent.ratom/reactive?) - (set! (.-destroy destroy-obj#) destroy#) - (when (some? destroy#) + (when-not (nil? destroy#) + (if (reagent.ratom/reactive?) + (set! (.-destroy destroy-obj#) destroy#) (destroy#))) res#))))) - - - - - - - - diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 528d5e9..2d72f23 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -6,7 +6,7 @@ (declare ^:dynamic *ratom-context*) -(defn reactive? [] +(defn ^boolean reactive? [] (some? *ratom-context*)) (defonce ^boolean debug false) @@ -168,7 +168,7 @@ (Track. #(apply f args) [f args] nil)) (defn track! [f & args] - (let [r (make-reaction #(deref (apply track f args)) + (let [r (make-reaction #(-deref (apply track f args)) :auto-run :async)] @r r)) From 28d454ece9e400de6e2479d9c4c81cb400286fed Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 24 Sep 2015 07:48:03 +0200 Subject: [PATCH 56/58] Include size of cached reactions in tests --- src/reagent/ratom.cljs | 7 ++++--- test/reagenttest/testcursor.cljs | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index 2d72f23..a0b0a47 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -5,6 +5,7 @@ [reagent.debug :refer-macros [dbg log warn dev?]])) (declare ^:dynamic *ratom-context*) +(defonce cached-reactions {}) (defn ^boolean reactive? [] (some? *ratom-context*)) @@ -15,7 +16,9 @@ (defonce -running (clojure.core/atom 0)) -(defn running [] @-running) +(defn running [] + (+ @-running + (count cached-reactions))) (defn capture-derefed [f obj] (set! (.-cljsCaptured obj) nil) @@ -118,8 +121,6 @@ (declare make-reaction) -(defonce cached-reactions {}) - (defn- cached-reaction [f key obj destroy] (if-some [r (get cached-reactions key)] (-deref r) diff --git a/test/reagenttest/testcursor.cljs b/test/reagenttest/testcursor.cljs index dbe6006..0453266 100644 --- a/test/reagenttest/testcursor.cljs +++ b/test/reagenttest/testcursor.cljs @@ -193,19 +193,19 @@ :on-dispose #(reset! disposed-cns true))] @cns (is (= @res 2)) - (is (= (+ 5 runs) (running))) + (is (= (+ 6 runs) (running))) (is (= @count-b 1)) (is (= {:a 0 :b 0} @a-base)) (reset! a -1) (is (= @res 1)) (is (= @disposed nil)) (is (= @count-b 2)) - (is (= (+ 5 runs) (running)) "still running") + (is (= (+ 6 runs) (running)) "still running") (is (= {:a -1 :b 0} @a-base)) (reset! a 2) (is (= @res 1)) (is (= @disposed true)) - (is (= (+ 3 runs) (running)) "less running count") + (is (= (+ 4 runs) (running)) "less running count") (is (= {:a 2 :b 0} @a-base)) (reset! disposed nil) From 034ffa6b7f84e1a9d044873deeefc625d211f4fb Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 24 Sep 2015 09:18:04 +0200 Subject: [PATCH 57/58] More tests for with-let --- test/reagenttest/testwithlet.cljs | 87 +++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/test/reagenttest/testwithlet.cljs b/test/reagenttest/testwithlet.cljs index 3ba08d9..f6ba359 100644 --- a/test/reagenttest/testwithlet.cljs +++ b/test/reagenttest/testwithlet.cljs @@ -40,8 +40,13 @@ (swap! a inc) (flush) (is (= [[11 1] 1 2 1] [@r @n1 @n2 @n3])) + (is (= [12 2] @t)) (is (= [[12 2] 2 3 2] [@r @n1 @n2 @n3])) + + (is (= [12 3] (f1))) + (is (= [[12 2] 3 4 3] [@r @n1 @n2 @n3])) + (is (= runs (running))))) @@ -129,3 +134,85 @@ expected))) (is (= @n2 28)) (is (= runs (running))))) + +(deftest non-reactive-with-let + (let [n1 (atom 0) + n2 (atom 0) + n3 (atom 0) + n4 (atom 0) + f1 (fn [] + (with-let [] + (swap! n2 inc))) + f2 (fn [] + (with-let [v (swap! n1 inc)] + v)) + f3 (fn [] + (with-let [v (swap! n1 inc)] + (swap! n2 inc) + (finally (swap! n3 inc)))) + f4 (fn [] + (with-let [] + (finally (swap! n3 inc) + (swap! n4 inc)))) + f5 (fn [] + [(f1) (f2) (f4)]) + tst (fn [f] + [(f) @n1 @n2 @n3])] + (is (= [1 0 1 0] (tst f1))) + (is (= [1 1 1 0] (tst f2))) + (is (= [2 2 2 1] (tst f3))) + (is (= 0 @n4)) + (is (= [nil 2 2 2] (tst f4))) + (is (= 1 @n4)) + (is (= [[3 3 nil] 3 3 3] (tst f5))))) + +(deftest with-let-args + (let [runs (running) + active (atom 0) + n1 (atom 0) + f1 (fn [x y] + (with-let [_ (swap! active inc) + v (r/atom @x)] + (swap! n1 inc) + (+ y @v) + (finally + (reset! v nil) + (swap! active dec)))) + f2 (fn [x y] + (with-let [t1 (track f1 x y) + t2 (track f1 x y)] + (let [v @(track f1 x y)] + (is (= v @t1 @t2)) + v))) + f2t (partial track f2) + res (atom nil) + val (r/atom 1) + valtrack (track deref val) + t (track! #(reset! res (let [v valtrack] + (if (> @v 2) + [@(f2t v 10)] + [@(f2t val 0) + @(f2t val 0) + @(f2t v 10) + (f1 v 10)]))))] + (is (= [1 1 11 11] @res)) + (is (= [3 3] [@n1 @active])) + (reset! val 1) + (flush) + (is (= [1 1 11 11] @res)) + (is (= [3 3] [@n1 @active])) + + (swap! val inc) + (is (= [3 3] [@n1 @active])) + (flush) + (is (= [6 3] [@n1 @active])) + (is (= [1 1 11 11] @res)) + + (swap! val inc) + (flush) + (is (= [6 1] [@n1 @active])) + (is (= [11] @res)) + + (dispose! t) + (is (= [6 0] [@n1 @active])) + (is (= runs (running))))) From cfcf3567439c01ba673bb2d0b79ddc55a7843161 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Thu, 24 Sep 2015 10:19:30 +0200 Subject: [PATCH 58/58] Add track, track!, dispose! and with-let to core --- src/reagent/core.clj | 5 +++++ src/reagent/core.cljs | 15 ++++++++++++++- src/reagent/ratom.cljs | 15 +++++++++++---- test/reagenttest/testreagent.cljs | 8 ++++---- test/reagenttest/testwithlet.cljs | 6 +++--- 5 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 src/reagent/core.clj diff --git a/src/reagent/core.clj b/src/reagent/core.clj new file mode 100644 index 0000000..47ba3a7 --- /dev/null +++ b/src/reagent/core.clj @@ -0,0 +1,5 @@ +(ns reagent.core + (:require [reagent.ratom :as ra])) + +(defmacro with-let [bindings & body] + `(ra/with-let ~bindings ~@body)) diff --git a/src/reagent/core.cljs b/src/reagent/core.cljs index 49d8118..7b5927f 100644 --- a/src/reagent/core.cljs +++ b/src/reagent/core.cljs @@ -1,5 +1,5 @@ - (ns reagent.core + (:require-macros [reagent.core]) (:refer-clojure :exclude [partial atom flush]) (:require [cljsjs.react] [reagent.impl.template :as tmpl] @@ -221,6 +221,19 @@ re-rendered." ([x] (ratom/atom x)) ([x & rest] (apply ratom/atom x rest))) +(defn track + [f & args] + {:pre [(ifn? f)]} + (ratom/make-track f args)) + +(defn track! + [f & args] + {:pre [(ifn? f)]} + (ratom/make-track! f args)) + +(defn dispose! + [x] + (ratom/dispose! x)) (defn wrap "Provide a combination of value and callback, that looks like an atom. diff --git a/src/reagent/ratom.cljs b/src/reagent/ratom.cljs index a0b0a47..7d0e99a 100644 --- a/src/reagent/ratom.cljs +++ b/src/reagent/ratom.cljs @@ -164,16 +164,23 @@ (pr-writer (-deref a) writer opts)) (-write writer ">"))) -(defn track [f & args] - {:pre [(ifn? f)]} +(defn make-track [f args] (Track. #(apply f args) [f args] nil)) -(defn track! [f & args] - (let [r (make-reaction #(-deref (apply track f args)) +(defn make-track! [f args] + (let [r (make-reaction #(-deref (make-track f args)) :auto-run :async)] @r r)) +(defn track [f & args] + {:pre [(ifn? f)]} + (make-track f args)) + +(defn track! [f & args] + {:pre [(ifn? f)]} + (make-track! f args)) + ;;; cursor (deftype RCursor [ratom path ^:mutable reaction diff --git a/test/reagenttest/testreagent.cljs b/test/reagenttest/testreagent.cljs index 6026717..7231de8 100644 --- a/test/reagenttest/testreagent.cljs +++ b/test/reagenttest/testreagent.cljs @@ -1,6 +1,6 @@ (ns reagenttest.testreagent (:require [cljs.test :as t :refer-macros [is deftest testing]] - [reagent.ratom :as rv :refer-macros [reaction with-let]] + [reagent.ratom :as rv :refer-macros [reaction]] [reagent.debug :refer-macros [dbg println log]] [reagent.interop :refer-macros [.' .!]] [reagent.core :as r])) @@ -572,7 +572,7 @@ n3 (atom 0) val (r/atom 0) c (fn [] - (with-let [v (swap! n1 inc)] + (r/with-let [v (swap! n1 inc)] (swap! n2 inc) [:div @val] (finally @@ -590,7 +590,7 @@ (let [n1 (atom 0) n2 (atom 0) c (fn [] - (with-let [] + (r/with-let [] (swap! n1 inc) [:div] (finally @@ -605,7 +605,7 @@ n2 (atom 0) n3 (atom 0) c (fn [] - (with-let [a (swap! n1 inc)] + (r/with-let [a (swap! n1 inc)] (swap! n2 inc) [:div a] (finally diff --git a/test/reagenttest/testwithlet.cljs b/test/reagenttest/testwithlet.cljs index f6ba359..93f182f 100644 --- a/test/reagenttest/testwithlet.cljs +++ b/test/reagenttest/testwithlet.cljs @@ -1,9 +1,9 @@ (ns reagenttest.testwithlet (:require [cljs.test :as t :refer-macros [is deftest testing]] - [reagent.ratom :as rv :refer [track track! dispose!] - :refer-macros [with-let]] + [reagent.ratom :as rv] [reagent.debug :refer-macros [dbg]] - [reagent.core :as r :refer [flush]] + [reagent.core :as r + :refer [flush track track! dispose!] :refer-macros [with-let]] [clojure.walk :as w])) (defn running []