-
Notifications
You must be signed in to change notification settings - Fork 0
/
store.cljs
114 lines (103 loc) · 3.34 KB
/
store.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
(ns readux.store
(:require [clojure.data :refer [diff]]
[reagent.core :as r]
[readux.utils :as rdu :include-macros true]
[readux.spec :as spec])
(:require-macros [reagent.ratom :refer [reaction]]))
(defn store->model*
"(R/W access) Get model atom from store.
NOTE: you should *not* access this outside of
core Readux code."
[store]
{:pre [(spec/store*? store)]}
(:model @store))
(defn store->model
"(R/O access) Get reaction encompassing the entire model."
[store]
{:pre [(spec/store*? store)]}
(:model-ro @store))
(defn store->reducer
"Get the store's top-level reducer function."
[store]
{:pre [(spec/store*? store)]}
(:reducer @store))
(defn store->queries
[store]
{:pre [(spec/store*? store)]}
(-> @store :queries))
(defn dispatch*
([store action]
{:pre
[(spec/action? action)]}
((@store :dispatch) store action))
([store type payload]
{:pre
[(rdu/spec-valid?
::spec/action.type type "Expect action type to be a keyword value")
(rdu/spec-valid? ::spec/action.payload payload)]}
(dispatch* store {:type type :payload payload})))
;; default end-of-chain dispatch fn
(defn dispatch-core
[store action]
{:pre [(spec/store*? store)
(spec/action? action)]}
(let [model* (store->model* store)
result ((store->reducer store) @model* action)]
(assert (some? result) "Root reducer returned 'nil' - result of reduce *must* be some new (non-nil) state!")
(reset! model* result)
nil))
(defn ->store*
"Create new store using the supplied reducer function.
reducer - function to act as the store's top-level reducer."
[reducer]
{:pre [(spec/fun? reducer)]}
(let [model (r/atom nil)]
(atom {:model model
:model-ro (reaction @model)
:reducer reducer
:queries (r/atom {})
:dispatch dispatch-core})))
(defn apply-mw
[& middleware]
(fn do-apply-mw
[store]
{:pre [(spec/store*? store)]}
(let [dispatch-fn (partial dispatch* store)
reducer (store->reducer store)]
(->> middleware
reverse
(mapv #(partial % dispatch-fn))
(reduce (fn [next mw] (partial mw next)) reducer)
(swap! store assoc :reducer))
store)))
(defn log-model-diff
"Store middleware to log model changes to console."
[dispatch next model action]
{:pre [(spec/fun? dispatch)
(spec/fun? next)
(spec/action? action)]}
(let [{:keys [type payload meta]} action
action-name (str (when-let [ns (namespace type)]
(str ns "/"))
(name type))]
(if (get-in meta [:log-model-diff :nolog])
;; no logging for this action
(do (-> (str "%c* Action['" action-name "'] -- logging skipped")
(rdu/log "color:blue; font-weight: bold"))
(next model action))
;; log the action
(rdu/log-group-collapsed
(str "* Action['" action-name "']")
(when payload
(rdu/log-group
"Data"
(rdu/log (rdu/ppstr payload))))
(let [new-model (next model action)
[removed added _] (diff model new-model)]
(rdu/log-group
"Added"
(rdu/log (rdu/ppstr added)))
(rdu/log-group
"Removed"
(rdu/log (rdu/ppstr removed)))
new-model)))))