|
70 | 70 | (defonce ^:private !results
|
71 | 71 | (atom {}))
|
72 | 72 |
|
73 |
| -(def !global-error-dispatch |
| 73 | +(def ^:private !global-error-dispatch |
74 | 74 | (atom nil))
|
75 | 75 |
|
76 | 76 | (def set-global-error-dispatch!
|
77 |
| - "Sets global error dispatch to prevent using advanced structure for `inject-acofx`." |
| 77 | + "Sets global error dispatch." |
78 | 78 | (partial reset! !global-error-dispatch))
|
79 | 79 |
|
80 | 80 | (defn- fx-handler-run?
|
|
177 | 177 | (normalize-acofx cofx)]]
|
178 | 178 | (assoc cofx :data-id id))))
|
179 | 179 |
|
| 180 | +(defn inject-acofxs |
| 181 | + "Give as much acofxs as you want as vector or map to compute async (pseudo parallel) values. Use map keys to rename keys within event coeffects map. For further reading see `inject-acofx`." |
| 182 | + ([acofxs] |
| 183 | + (inject-acofxs acofxs nil)) |
| 184 | + |
| 185 | + ([acofxs {:keys [error-dispatch] :as opts}] |
| 186 | + |
| 187 | + (let [inject-id |
| 188 | + (random-uuid) |
| 189 | + |
| 190 | + acofxs-n-options |
| 191 | + (->> acofxs |
| 192 | + (normalize-acofxs) |
| 193 | + (assoc opts :id inject-id, :acofxs))] |
| 194 | + |
| 195 | + (->interceptor |
| 196 | + :id |
| 197 | + :acoeffects |
| 198 | + |
| 199 | + :before |
| 200 | + (fn [context] |
| 201 | + (let [dispatch-id |
| 202 | + (or (some-> context (:coeffects) (:event) (meta) (::dispatch-id)) |
| 203 | + (random-uuid)) |
| 204 | + |
| 205 | + context |
| 206 | + (assoc-in context [:acoeffects :dispatch-id] dispatch-id)] |
| 207 | + |
| 208 | + (if-let [result (get-in @!results [dispatch-id inject-id])] |
| 209 | + (update context :coeffects merge result) |
| 210 | + (run-acofxs-n-abort-event! context acofxs-n-options)))) |
| 211 | + |
| 212 | + :after |
| 213 | + (fn [context] |
| 214 | + (when (fx-handler-run? context) |
| 215 | + (->> context |
| 216 | + (:acoeffects) |
| 217 | + (:dispatch-id) |
| 218 | + (swap! !results dissoc))) |
| 219 | + context))))) |
| 220 | + |
180 | 221 | (defn inject-acofx
|
181 |
| - "Given async-coeffects (acofxs) returns an interceptor whose `:before` adds to the `:coeffects` (map) by calling a pre-registered 'async coeffect handler' identified by `id`. |
| 222 | + "Given an async-coeffect (acofx) returns an interceptor whose `:before` adds to the `:coeffects` (map) by calling a pre-registered 'async coeffect handler' identified by `id`. |
182 | 223 |
|
183 |
| - Give as much acofxs as you want to compute async (pseudo parallel) values via `id` of the acofx or an vector of `id` and `args`. `args` can be mixed of any val and fns. fns will be applied with coeffects and event. Computed args prepended with coeffects will be applied to acofx handler. |
| 224 | + As first argument give the `id` of the acofx or an vector of `id` and `args`. `args` can be mixed of any val and fns. fns will be applied with coeffects and event. Computed args prepended with coeffects will be applied to acofx handler. |
184 | 225 |
|
185 |
| - Give a map instead of multiple acofxs like `{:acofxs ...}` to carry a `:error-dispatch` vector, see also `set-global-error-dispatch`. `error-dispatch` will be called on error of any acofxs, event will be aborted. `:acofxs` can be given as vector or map. Use map keys to rename keys within event coeffects map. |
| 226 | + As second optional argument give a map of options to carry a `:error-dispatch` vector, see also `set-global-error-dispatch`. `error-dispatch` will be called on error, event will be aborted. |
186 | 227 |
|
187 |
| - The previous association of a `async coeffect handler` with an `id` will have happened via a call to `reg-acofx` - generally on program startup. |
| 228 | + The previous association of a `async coeffect handler` with an `id` will have happened via a call to `reg-acofx` - generally on program startup. See also `reg-acofx-by-fx` to reuse effects as coeffects. |
188 | 229 |
|
189 | 230 | Within the created interceptor, this 'looked up' `async coeffect handler` will be called (within the `:before`) with arguments:
|
190 | 231 |
|
191 | 232 | - the current value of `:coeffects`
|
192 |
| - - optionally, the given or computed args by `args` or `args-fn` |
| 233 | + - optionally, the given or computed args by `args` |
193 | 234 |
|
194 | 235 | This `coeffect handler` is expected to modify and return its first, `coeffects` argument.
|
195 | 236 |
|
196 | 237 | **Example of `inject-acofx` and `reg-acofx` working together**
|
197 | 238 |
|
198 | 239 |
|
199 |
| - First - Early in app startup, you register a `async coeffect handler` for `:async-datetime`: |
| 240 | + First - Early in app startup, you register a `async coeffect handler` for `:async-now`: |
200 | 241 |
|
201 | 242 | #!clj
|
202 | 243 | (reg-acofx
|
203 |
| - :async-datetime ;; usage (inject-acofx :async-datetime) |
| 244 | + :async-now ;; usage (inject-acofx :async-now) |
204 | 245 | (fn async-coeffect-handler
|
205 | 246 | [coeffect]
|
206 | 247 | (go
|
|
212 | 253 | #!clj
|
213 | 254 | (re-frame.core/reg-event-fx ;; when registering an event handler
|
214 | 255 | :event-id
|
215 |
| - [ ... (inject-acofx :async-datetime) ... ] ;; <-- create an injecting interceptor |
| 256 | + [ ... (inject-acofx :async-now) ... ] ;; <-- create an injecting interceptor |
216 | 257 | (fn event-handler
|
217 | 258 | [coeffect event]
|
218 |
| - ;;... in here can access (:async-now coeffect) to obtain current async-datetime ... |
| 259 | + ;;... in here can access (:async-now coeffect) to obtain current async-now ... |
219 | 260 | )))
|
220 | 261 |
|
221 | 262 | **Background**
|
222 | 263 |
|
223 | 264 | `coeffects` are the input resources required by an event handler to perform its job. The two most obvious ones are `db` and `event`. But sometimes an event handler might need other resources maybe async resources.
|
224 | 265 |
|
225 |
| - Perhaps an event handler needs data from backend or some other async call. |
| 266 | + Perhaps an event handler needs data from backend or some other async api. |
226 | 267 |
|
227 | 268 | If an event handler directly accesses these resources, it stops being pure and, consequently, it becomes harder to test, etc. So we don't want that.
|
228 | 269 |
|
229 | 270 | Instead, the interceptor created by this function is a way to 'inject' 'necessary resources' into the `:coeffects` (map) subsequently given to the event handler at call time.
|
230 | 271 |
|
231 | 272 | See also `reg-acofx`
|
232 | 273 | "
|
233 |
| - ([& [map-or-acofx :as acofxs]] |
234 |
| - |
235 |
| - (let [inject-id |
236 |
| - (random-uuid) |
237 |
| - |
238 |
| - acofxs-n-options |
239 |
| - (if (map? map-or-acofx) |
240 |
| - (-> map-or-acofx |
241 |
| - (update :acofxs normalize-acofxs) |
242 |
| - (assoc :id inject-id)) |
243 |
| - {:id inject-id, :acofxs (normalize-acofxs acofxs)})] |
244 |
| - |
245 |
| - (->interceptor |
246 |
| - :id |
247 |
| - :acoeffects |
248 |
| - |
249 |
| - :before |
250 |
| - (fn [context] |
251 |
| - (let [dispatch-id |
252 |
| - (or (some-> context (:coeffects) (:event) (meta) (::dispatch-id)) |
253 |
| - (random-uuid)) |
| 274 | + ([acofx] |
| 275 | + (inject-acofx acofx nil)) |
254 | 276 |
|
255 |
| - context |
256 |
| - (assoc-in context [:acoeffects :dispatch-id] dispatch-id)] |
257 |
| - |
258 |
| - (if-let [result (get-in @!results [dispatch-id inject-id])] |
259 |
| - (update context :coeffects merge result) |
260 |
| - (run-acofxs-n-abort-event! context acofxs-n-options)))) |
261 |
| - |
262 |
| - :after |
263 |
| - (fn [context] |
264 |
| - (when (fx-handler-run? context) |
265 |
| - (->> context |
266 |
| - (:acoeffects) |
267 |
| - (:dispatch-id) |
268 |
| - (swap! !results dissoc))) |
269 |
| - context))))) |
| 277 | + ([acofx {:keys [error-dispatch] :as opts}] |
| 278 | + (inject-acofxs [acofx] opts))) |
0 commit comments