-
Notifications
You must be signed in to change notification settings - Fork 21
/
reconciler.cljs
133 lines (112 loc) · 4.35 KB
/
reconciler.cljs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
(ns citrus.reconciler
(:require-macros [citrus.macros :as m])
(:require [cljs.spec.alpha :as s]))
(defn- queue-effects! [queue f]
(vswap! queue conj f))
(defn- clear-queue! [queue]
(vreset! queue []))
(defn- schedule-update! [{:keys [schedule-fn release-fn]} scheduled? f]
(when-let [id @scheduled?]
(vreset! scheduled? nil)
(release-fn id))
(vreset! scheduled? (schedule-fn f)))
(defprotocol IReconciler
(dispatch! [this controller event args])
(dispatch-sync! [this controller event args])
(broadcast! [this event args])
(broadcast-sync! [this event args]))
(deftype Reconciler [controllers effect-handlers co-effects state queue scheduled? batched-updates chunked-updates meta watch-fns]
Object
(equiv [this other]
(-equiv this other))
IAtom
IMeta
(-meta [_] meta)
IEquiv
(-equiv [this other]
(identical? this other))
IDeref
(-deref [_]
(-deref state))
IWatchable
(-add-watch [this key callback]
(vswap! watch-fns assoc key callback)
this)
(-remove-watch [this key]
(vswap! watch-fns dissoc key)
this)
IHash
(-hash [this] (goog/getUid this))
IPrintWithWriter
(-pr-writer [this writer opts]
(-write writer "#object [citrus.reconciler.Reconciler ")
(pr-writer {:val (-deref this)} writer opts)
(-write writer "]"))
IReconciler
(dispatch! [this cname event args]
(assert (contains? controllers cname) (str "Controller " cname " is not found"))
(assert (some? event) (str "Controller " cname " was called without event name"))
(assert (-> (get controllers cname) methods (contains? event))
(str "Controller " cname " doesn't declare " event " method"))
(queue-effects!
queue
[cname event #((get controllers cname) event args (get %1 cname) %2)])
(schedule-update!
batched-updates
scheduled?
(fn []
(let [events @queue
_ (clear-queue! queue)
next-state
(loop [st @state
[event & events] events]
(if (seq event)
(let [[cname ename ctrl] event
cofx (get-in (.-meta ctrl) [:citrus ename :cofx])
cofx (reduce
(fn [cofx [key & args]]
(assoc cofx key (apply (co-effects key) args)))
{}
cofx)
effects (ctrl st cofx)]
(m/doseq [effect (dissoc effects :state)]
(let [[id effect] effect]
(when (s/check-asserts?)
(when-let [spec (s/get-spec id)]
(s/assert spec effect)))
(when-let [handler (get effect-handlers id)]
(handler this cname effect))))
(if (contains? effects :state)
(recur (assoc st cname (:state effects)) events)
(recur st events)))
st))]
(reset! state next-state)))))
(dispatch-sync! [this cname event args]
(assert (contains? controllers cname) (str "Controller " cname " is not found"))
(assert (some? event) (str "Controller " cname " was called without event name"))
(assert (-> (get controllers cname) methods (contains? event))
(str "Controller " cname " doesn't declare " event " method"))
(let [ctrl (get controllers cname)
cofx (get-in (.-meta ctrl) [:citrus event :cofx])
cofx (reduce
(fn [cofx [key & args]]
(assoc cofx key (apply (co-effects key) args)))
{}
cofx)
effects (ctrl event args (get @state cname) cofx)]
(m/doseq [effect effects]
(let [[id effect] effect
handler (get effect-handlers id)]
(when (s/check-asserts?)
(when-let [spec (s/get-spec id)]
(s/assert spec effect)))
(cond
(= id :state) (swap! state assoc cname effect)
handler (handler this cname effect)
:else nil)))))
(broadcast! [this event args]
(m/doseq [controller (keys controllers)]
(dispatch! this controller event args)))
(broadcast-sync! [this event args]
(m/doseq [controller (keys controllers)]
(dispatch-sync! this controller event args))))