diff --git a/src/cljx/grub/state.cljx b/src/cljx/grub/state.cljx index b4ded9c..d92e66b 100644 --- a/src/cljx/grub/state.cljx +++ b/src/cljx/grub/state.cljx @@ -10,18 +10,17 @@ (defmulti handle-event (fn [event] (:type event))) (defmethod handle-event :diff [{:keys [hash diff states shadow client?] :as msg}] - (let [shadow (sync/get-history-state states hash)] - (if shadow + (let [history-shadow (sync/get-history-state states hash)] + (if history-shadow (let [new-states (sync/apply-diff states diff) - new-shadow (diff/patch-state shadow diff) + new-shadow (diff/patch-state history-shadow diff) {new-diff :diff new-hash :hash} (sync/diff-states (sync/get-current-state new-states) new-shadow)] - (if client? - {:new-states (sync/new-state (sync/get-current-state new-states)) - :new-shadow new-shadow} - {:out-event (when-not (sync/empty-diff? diff) - (message/diff-msg new-diff new-hash)) - :new-states new-states - :new-shadow (sync/get-current-state new-states)})) + {:out-event (when-not (sync/empty-diff? diff) + (message/diff-msg new-diff new-hash)) + :new-states (if client? + (sync/new-state (sync/get-current-state new-states)) + new-states) + :new-shadow new-shadow}) (if client? {:out-event message/full-sync-request :new-shadow shadow} @@ -38,9 +37,9 @@ {:new-states (sync/new-state state) :new-shadow state}) -(defmethod handle-event :new-state [{:keys [state states shadow]}] +(defmethod handle-event :new-state [{:keys [client? state states shadow] :as event}] (let [{:keys [diff hash]} (sync/diff-states state shadow)] - {:new-shadow shadow + {:new-states (sync/add-history-state states state) :out-event (when-not (sync/empty-diff? diff) (message/diff-msg diff hash))})) (defn make-agent @@ -53,15 +52,15 @@ {:keys [new-states new-shadow out-event]} (handle-event event)] (when (and new-states (not= states new-states)) (reset! states* new-states)) (when out-event (a/put! >remote out-event)) - (recur shadow))))))) + (recur (if new-shadow new-shadow shadow)))))))) (defn make-server-agent - ([in out states] (make-agent false in out states)) - ([in out states initial-shadow] (make-agent false in out states initial-shadow))) + ([remote states] (make-agent false remote states)) + ([remote states initial-shadow] (make-agent false remote states initial-shadow))) (defn make-client-agent - ([in out states] (make-agent true in out states)) - ([in out states initial-shadow] (make-agent true in out states initial-shadow))) + ([remote states] (make-agent true remote states)) + ([remote states initial-shadow] (make-agent true remote states initial-shadow))) (def states (atom [])) (def empty-state sync/empty-state) diff --git a/src/cljx/grub/sync.cljx b/src/cljx/grub/sync.cljx index 3da74e1..2193529 100644 --- a/src/cljx/grub/sync.cljx +++ b/src/cljx/grub/sync.cljx @@ -32,11 +32,9 @@ (into [] (rest new-states)) new-states))))) -(defn diff-states [states shadow] - (let [state states;(get-current-state states) - ] - {:hash (hasch/uuid shadow) - :diff (diff/diff-states shadow state)})) +(defn diff-states [state shadow] + {:hash (hasch/uuid shadow) + :diff (diff/diff-states shadow state)}) (defn apply-diff [states diff] (let [new-state (diff/patch-state (get-current-state states) diff)] diff --git a/src/test/grub/test/unit/state.clj b/src/test/grub/test/unit/state.clj index 8d9bbdd..9d882db 100644 --- a/src/test/grub/test/unit/state.clj +++ b/src/test/grub/test/unit/state.clj @@ -10,7 +10,7 @@ :state s})) (into []))) -(fact "Applies diff and returns empty diff when no server changes" +(fact "Server applies diff and returns empty diff when no server changes" (let [states (hashed-states {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}}) event {:type :diff @@ -23,12 +23,31 @@ new-states => (hashed-states {:grubs {"1" {:completed false, :text "2 apples"}}, :recipes {}} {:grubs {"1" {:completed true, :text "2 apples"}}, :recipes {}}) + new-shadow {:grubs {"1" {:completed true, :text "2 apples"}}, :recipes {}} out-event => {:type :diff :diff {:grubs {:deleted #{}, :updated nil} :recipes {:deleted #{}, :updated nil}} :hash (:hash (last new-states))})) -(fact "Applies diff and returns changes when server has changed" +(fact "Client applies diff, clears history, updates shadow, returns empty diff when no client changes" + (let [states (hashed-states + {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}}) + event {:type :diff + :diff {:grubs {:updated {"1" {:completed true}} :deleted #{}}} + :hash (:hash (first states)) + :states states + :shadow (:state (last states)) + :client? true} + {:keys [new-states new-shadow out-event]} (state/handle-event event)] + new-states => (hashed-states + {:grubs {"1" {:completed true, :text "2 apples"}}, :recipes {}}) + new-shadow => {:grubs {"1" {:completed true, :text "2 apples"}}, :recipes {}} + out-event => {:type :diff + :diff {:grubs {:deleted #{}, :updated nil} + :recipes {:deleted #{}, :updated nil}} + :hash (:hash (last new-states))})) + +(fact "Server applies diff and returns changes when server has changed" (let [states (hashed-states {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}} {:grubs {"1" {:text "2 apples" :completed false} @@ -56,7 +75,7 @@ :hash (hasch/uuid {:grubs {"1" {:text "2 apples" :completed true}} :recipes {}})})) -(fact "Force full sync if client is out of sync" +(fact "Server forces full sync if client is out of sync" (let [states (hashed-states {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}} {:grubs {"1" {:text "2 apples" :completed false} @@ -76,7 +95,7 @@ "2" {:text "3 onions" :completed false}} :recipes {}}})) -(fact "Full sync if client requests it" +(fact "Server sends full sync if client requests it" (let [states (hashed-states {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}} {:grubs {"1" {:text "2 apples" :completed false}