re-frame/src/re_frame/handlers.cljs

95 lines
3.3 KiB
Plaintext
Raw Normal View History

2014-12-15 11:56:32 +00:00
(ns re-frame.handlers
(:require [re-frame.db :refer [app-db]]
2015-05-02 00:49:27 +00:00
[re-frame.utils :refer [first-in-vector warn error]]))
2014-12-08 03:48:59 +00:00
;; -- composing middleware -----------------------------------------------------------------------
2015-05-28 13:15:47 +00:00
(defn report-middleware-factories
"See https://github.com/Day8/re-frame/issues/65"
[v]
(letfn [(name-of-factory
[f]
(-> f meta :re-frame-factory-name))
(factory-names-in
[v]
(remove nil? (map name-of-factory v)))]
(doseq [name (factory-names-in v)]
(error "re-frame: \"" name "\" used incorrectly. Must be used like this \"(" name " ...)\", whereas you just used \"" name "\"."))))
(defn comp-middleware
"Given a vector of middleware, filter out any nils, and use \"comp\" to compose the elements.
2015-04-18 11:15:53 +00:00
v can have nested vectors, and will be flattened before \"comp\" is applied.
For convienience, if v is a function (assumed to be middleware already), just return it.
2015-04-18 11:15:53 +00:00
Filtering out nils allows us to create Middleware conditionally like this:
2015-03-02 14:03:09 +00:00
(comp-middleware [pure (when debug? debug)]) ;; that 'when' might leave a nil
"
[v]
2015-05-28 13:15:47 +00:00
(cond
2015-03-02 14:06:45 +00:00
(fn? v) v ;; assumed to be existing middleware
(coll? v) (let [v (remove nil? (flatten v))]
(report-middleware-factories v)
(apply comp v))
2015-03-04 23:40:02 +00:00
:else (warn "re-frame: comp-middleware expects a vector, got: " v)))
2015-03-02 14:03:09 +00:00
;; -- the register of event handlers --------------------------------------------------------------
(def ^:private id->fn (atom {}))
2014-12-15 11:56:32 +00:00
2015-03-02 14:03:09 +00:00
(defn lookup-handler
[event-id]
(get @id->fn event-id))
(defn clear-handlers!
"Unregister all event handlers"
[]
(reset! id->fn {}))
2015-03-04 23:40:02 +00:00
(defn register-base
"register a handler for an event.
This is low level and it is expected that \"re-frame.core/register-handler\" would
generally be used."
([event-id handler-fn]
(when (contains? @id->fn event-id)
(warn "re-frame: overwriting an event-handler for: " event-id)) ;; allow it, but warn.
(swap! id->fn assoc event-id handler-fn))
2014-12-15 11:56:32 +00:00
([event-id middleware handler-fn]
(let [mid-ware (comp-middleware middleware) ;; compose the middleware
midware+hfn (mid-ware handler-fn)] ;; wrap the handler in the middleware
(register-base event-id midware+hfn))))
2015-02-22 11:28:40 +00:00
;; -- lookup and call -----------------------------------------------------------------------------
2015-04-21 14:16:55 +00:00
(def ^:dynamic *handling* nil) ;; remember what event we are currently handling
2015-03-02 12:03:49 +00:00
(defn handle
2015-03-04 23:40:02 +00:00
"Given an event vector, look up the handler, then call it.
2015-03-01 20:57:11 +00:00
By default, handlers are not assumed to be pure. They are called with
two paramters:
- the `app-db` atom
2015-03-04 23:40:02 +00:00
- the event vector
The handler is assumed to side-effect on `app-db` - the return value is ignored.
To write a pure handler, use the \"pure\" middleware when registering the handler."
[event-v]
(let [event-id (first-in-vector event-v)
2015-03-02 12:03:49 +00:00
handler-fn (lookup-handler event-id)]
(if (nil? handler-fn)
2015-05-02 00:49:27 +00:00
(error "re-frame: no event handler registered for: \"" event-id "\". Ignoring.")
2015-04-21 14:16:55 +00:00
(if *handling*
2015-05-02 00:49:27 +00:00
(error "re-frame: while handling \"" *handling* "\" dispatch-sync was called for \"" event-v "\". You can't call dispatch-sync in an event handler.")
2015-04-21 14:16:55 +00:00
(binding [*handling* event-v]
(handler-fn app-db event-v))))))