diff --git a/just/observable-test.fnl b/just/observable-test.fnl index 011df31..47bbef8 100644 --- a/just/observable-test.fnl +++ b/just/observable-test.fnl @@ -45,11 +45,20 @@ (let [s (observable.new {:foo {:bar 43}})] (var win false) - ;; observers live on subtrees, not individual nodes - (s:observe [:foo] #(set win true)) + ;; observer on leaf + (s:observe [:foo :bar] #(set win true)) (s:update [:foo :bar] 42) (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}}})] (var win 0) ;; observers on ancestor trees are called after child trees diff --git a/just/observable.fnl b/just/observable.fnl index eb3014f..701cd27 100644 --- a/just/observable.fnl +++ b/just/observable.fnl @@ -3,32 +3,40 @@ (fn concat [dest src] (table.move dest 1 (# dest) (# src) src)) -(fn update [data self path value] +(fn update [data path value vivify?] (let [[first & rest] path] (if (next rest) - (update (. data first) self rest value) - (tset data first value)) - (if data._subscribers - (each [_ f (pairs data._subscribers)] (f))))) - -(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]))))) + (do + (if (and vivify? (not (. data first))) + (tset data first {})) + (update (. data first) rest value)) + (tset data first value)))) (fn new [data] - { - :observe (partial observe data) - :update (partial update data) - :get (partial get data) - }) + (let [observers {} + key [:obs]] + (fn get [data path] + (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 }