allow observers on app-state leaf nodes

phoen
Daniel Barlow 2022-02-27 16:57:08 +00:00
parent 8b564f09a7
commit 712a657846
2 changed files with 42 additions and 25 deletions

View File

@ -45,11 +45,20 @@
(let [s (observable.new {:foo {:bar 43}})] (let [s (observable.new {:foo {:bar 43}})]
(var win false) (var win false)
;; observers live on subtrees, not individual nodes ;; observer on leaf
(s:observe [:foo] #(set win true)) (s:observe [:foo :bar] #(set win true))
(s:update [:foo :bar] 42) (s:update [:foo :bar] 42)
(expect (and win))) (expect (and win)))
(let [s (observable.new {:foo {:bar 43}})]
(var win false)
;; observer on subtree
(s:observe [:foo] #(set win true))
(s:update [:foo :bar] 42)
(expect (and true win)))
(let [s (observable.new {:foo {:bar {:baz 43}}})] (let [s (observable.new {:foo {:bar {:baz 43}}})]
(var win 0) (var win 0)
;; observers on ancestor trees are called after child trees ;; observers on ancestor trees are called after child trees

View File

@ -3,32 +3,40 @@
(fn concat [dest src] (fn concat [dest src]
(table.move dest 1 (# dest) (# src) src)) (table.move dest 1 (# dest) (# src) src))
(fn update [data self path value] (fn update [data path value vivify?]
(let [[first & rest] path] (let [[first & rest] path]
(if (next rest) (if (next rest)
(update (. data first) self rest value) (do
(tset data first value)) (if (and vivify? (not (. data first)))
(if data._subscribers (tset data first {}))
(each [_ f (pairs data._subscribers)] (f))))) (update (. data first) rest value))
(tset data first value))))
(fn get [data self path]
(let [[first & rest] path]
(if (not first) data
(next rest) (get (. data first) self rest)
(. data first))))
(fn observe [data self path fun]
(let [el (get data self path)]
(when el
(if el._subscribers
(table.insert el._subscribers fun)
(tset el :_subscribers [fun])))))
(fn new [data] (fn new [data]
{ (let [observers {}
:observe (partial observe data) key [:obs]]
:update (partial update data) (fn get [data path]
:get (partial get data) (let [[first & rest] path]
}) (if (not first) data
(next rest) (get (. data first) rest)
(. (or data {}) first))))
(fn publish [observers path]
(let [[first & rest] path
os (. (or observers {}) first)]
(if (and (next rest) (next os)) (publish os rest))
(match os
{key list} (each [_ f (pairs list)] (f)))))
(fn observe [observers path fun]
(let [el (get observers path)]
(if el
(table.insert (. el key) fun)
(update observers path {key [fun]} true))))
{
:observe #(observe observers $2 $3)
:update #(do (update data $2 $3) (publish observers $2))
:get #(get data $2)
}))
{: new } {: new }