From 022ce6da0376a6bedd159f6006331907c385b16c Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sun, 20 Feb 2022 12:02:36 +0000 Subject: [PATCH] savepoint --- just/observable-test.fnl | 51 ++++++++++++++++++++++++++++++++++++++++ just/observable.fnl | 35 +++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 just/observable-test.fnl create mode 100644 just/observable.fnl diff --git a/just/observable-test.fnl b/just/observable-test.fnl new file mode 100644 index 0000000..e6e922b --- /dev/null +++ b/just/observable-test.fnl @@ -0,0 +1,51 @@ +(local fennel (require :fennel)) +(local observable (require :observable)) + +(macro expect [form] + `(do + (assert ,form ,(view [:failed form])) + (print ,(view [:passed form])))) + +(macro expect-error [form] + `(let [(ok?# msg#) (pcall (fn [] ,form))] + (assert (not ok?#) msg#) + (print ,(view [:passed (list :error form)])))) + +(local app-state + (observable.new + { + :foo 27 + :bar 54 + :nest { + :name "beatles" + :artists [:john :paul: :ringo :george ] + } + })) + + +(expect (= (app-state:get [:foo]) 27)) +(expect (= (app-state:get [:nest :name]) "beatles")) + +(let [s (observable.new {:foo 43})] + ;; s:get on a non-leaf returns the subtree. use table.concat + ;; for comparison of values + (expect (= (table.concat (s:get [])) (table.concat {:foo 43})))) + +(let [s (observable.new {:foo 43})] + ;; update existing entry + (s:update [:foo] 84) + (expect (= (s:get [:foo]) 84)) + + ;; create new entry + (s:update [:baz] 48) + (expect (= (s:get [:baz]) 48)) + + ;; doesn't create new nested keys + (expect-error (s:update [:nonexistent :path] 22))) + +(let [s (observable.new {:foo {:bar 43}})] + (var win false) + ;; observers live on subtrees, not individual nodes + (s:observe [:foo] #(set win true)) + (s:update [:foo :bar] 42) + (expect (and win))) diff --git a/just/observable.fnl b/just/observable.fnl new file mode 100644 index 0000000..e8edcc3 --- /dev/null +++ b/just/observable.fnl @@ -0,0 +1,35 @@ +(local {: view} (require :fennel)) + +(fn concat [dest src] + (table.move dest 1 (# dest) (# src) src)) + +(fn update [data self path value] + (let [[first & rest] path] + (if (next rest) + (update (. data first) self rest value) + (do + (if data._subscribers + (each [_ f (pairs data._subscribers)] (f))) + (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 + (el._subscribers:insert fun) + (tset el :_subscribers [fun]))))) + +(fn new [data] + { + :observe (partial observe data) + :update (partial update data) + :get (partial get data) + }) + +{: new }