Skip to content

Commit 311367d

Browse files
committedAug 3, 2016
Change to Interceptors (away from middleware). This is a big code drop.
Tests pass and todomvc works. But there is some ugliness to now resolve, and a couple of unresolved issues.
1 parent 0327d2c commit 311367d

17 files changed

+866
-512
lines changed
 

‎CHANGES.md

+13-12
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
## 0.8.0 (2016.07.XX)
33

44
Staying on the leading edge of new buzzwords is obviously critical for any framework.
5-
Angular's terrifying faceplant is a sobering reminder to us all.
5+
Angular's terrifying faceplant is a sobering reminder to us all.
66
With this release, re-frame's already impressive buzzword muscles
7-
bulge further with new walnuts like "effects", "coeffects", and "de-duplicated signal graph". I know, right?
7+
bulge further with new walnuts like "effects", "coeffects", "interceptors"
8+
and "de-duplicated signal graph". I know, right?
89

910
Some may even find these new features useful.
1011

@@ -51,26 +52,26 @@ Joking aside, this is a substantial release which will change how you use re-fra
5152
2. https://github.com/Day8/re-frame-forward-events-fx
5253
3. https://github.com/Day8/re-frame-async-flow-fx
5354

54-
- You can now run and debug re-frame tests on the JVM. This will be a really big deal for some.
55+
- You can now run and debug re-frame tests on the JVM. This will be a really big deal for some.
5556

5657
Just to be clear: this does not mean you can run re-frame apps on the JVM (there's no React or
5758
Reagent available). But you can debug your handler tests using full JVM tooling goodness.
5859

5960
@samroberton and and @escherize have provided the thought leadership and drive here. They converted
6061
re-frame to `.cljc`, supplying plugable interop for both the `js` and `jvm` platforms.
6162

62-
They then worked with @danielcompton to create a library of testing utilities which are actually
63-
a nice step forward for both platforms: <br>
63+
Further, they have worked with @danielcompton to create a library of testing utilities which are actually
64+
a really nice step forward for both platforms: <br>
6465
https://github.com/Day8/re-frame-undo
6566

66-
- the undo/redo feature buried in re-frame has been factored out into its own library.
67+
- the undo/redo features buried in re-frame has been factored out into [their own library](https://github.com/Day8/re-frame-undo).
6768

68-
undo and redo have been a part of re-frame from the beginning, but this feature has been hidden
69-
(not a part of the official API) and not documented. So it nice to see it made available, and fully
70-
documented here: https://github.com/Day8/re-frame-undo
69+
undo and redo have been a part of re-frame from the beginning, but they have
70+
not been a part of the official API, and have not been documented. So it nice to see it available, and fully
71+
documented.
7172

72-
This library also has [a couple of enhancements](https://github.com/Day8/re-frame-undo#harvesting-and-re-instating)
73-
over that which previously existed previously, including a feature which works in with the new effects handler.
73+
This new library also has [a couple of enhancements](https://github.com/Day8/re-frame-undo#harvesting-and-re-instating)
74+
over that which previously existed, including a feature which works in with the new effects handler.
7475

7576
- we now have a logo designed by Sketch Maester @martinklepsch. Thank you Martin! But remember, no
7677
good deed ever goes unpunished - we'll be pestering you every time from now on :-)
@@ -114,7 +115,7 @@ Joking aside, this is a substantial release which will change how you use re-fra
114115
115116
- Bug fix: `post-event-callbacks` were not called when `dispatch-sync` was called.
116117
- added new API `re-frame.core/clear-post-event-callback` which de-registers a callback
117-
added by `re-frame.core/add-post-event-callback`
118+
previously added by `re-frame.core/add-post-event-callback`
118119
- when an event-handler makes no change to `app-db`, the `debug` middleware now logs a
119120
single line saying so, rather than a "group". Makes it slightly easier to grok
120121
the absence of change.

‎examples/simple/src/simpleexample/core.cljs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(ns simpleexample.core
22
(:require-macros [reagent.ratom :refer [reaction]])
33
(:require [reagent.core :as reagent]
4-
[re-frame.core :refer [reg-event
4+
[re-frame.core :refer [reg-event-db
55
path
66
reg-sub
77
dispatch
@@ -20,22 +20,22 @@
2020
;; -- Event Handlers ----------------------------------------------------------
2121

2222

23-
(reg-event ;; setup initial state
23+
(reg-event-db ;; setup initial state
2424
:initialize ;; usage: (dispatch [:initialize])
2525
(fn
2626
[db _]
2727
(merge db initial-state))) ;; what it returns becomes the new state
2828

2929

30-
(reg-event
30+
(reg-event-db
3131
:time-color ;; usage: (dispatch [:time-color 34562])
3232
(path [:time-color]) ;; this is middleware
3333
(fn
3434
[time-color [_ value]] ;; path middleware adjusts the first parameter
3535
value))
3636

3737

38-
(reg-event
38+
(reg-event-db
3939
:timer
4040
(fn
4141
;; the first item in the second argument is :timer the second is the

‎examples/todomvc/src/todomvc/db.cljs

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@
5454

5555
(def lsk "todos-reframe") ;; localstore key
5656

57-
(defn ls->todos
57+
(defn localstore->todos
5858
"Read in todos from LS, and process into a map we can merge into app-db."
5959
[]
6060
(some->> (.getItem js/localStorage lsk)
6161
(cljs.reader/read-string) ;; stored as an EDN map.
6262
(into (sorted-map)) ;; map -> sorted-map
6363
(hash-map :todos))) ;; access via the :todos key
6464

65-
(defn todos->ls!
65+
(defn todos->local-store
6666
"Puts todos into localStorage"
6767
[todos]
6868
(.setItem js/localStorage lsk (str todos))) ;; sorted-map writen as an EDN map

‎examples/todomvc/src/todomvc/events.cljs

+41-39
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
11
(ns todomvc.events
22
(:require
3-
[todomvc.db :refer [default-value ls->todos todos->ls!]]
4-
[re-frame.core :refer [reg-event path trim-v after debug]]
3+
[todomvc.db :refer [default-value localstore->todos todos->local-store]]
4+
[re-frame.core :refer [reg-event-db path trim-v after debug]]
55
[cljs.spec :as s]))
66

77

8-
;; -- Middleware --------------------------------------------------------------
9-
;;
10-
;; See https://github.com/Day8/re-frame/wiki/Using-Handler-Middleware
8+
;; -- Interceptors --------------------------------------------------------------
119
;;
10+
;; XXX Add URL for docs here
11+
;; XXX Ask Stu to figure out first time spec error
1212

1313
(defn check-and-throw
1414
"throw an exception if db doesn't match the spec."
1515
[a-spec db]
16-
(if-not (s/valid? a-spec db)
16+
(when-not (s/valid? a-spec db)
1717
(throw (ex-info "spec check failed: " {:problems
1818
(s/explain-str a-spec db)}))))
1919

2020
;; Event handlers change state, that's their job. But what happens if there's
21-
;; a bug and it corrupts this state in some subtle way? This middleware is run after
21+
;; a bug which corrupts app state in some subtle way? This interceptor is run after
2222
;; each event handler has finished, and it checks app-db against a spec. This
2323
;; helps us detect event handler bugs early.
24-
(def check-spec-mw (after (partial check-and-throw :todomvc.db/db)))
24+
(def check-spec-interceptor (after (partial check-and-throw :todomvc.db/db)))
2525

2626

27-
(def ->ls (after todos->ls!)) ;; middleware to store todos into local storage
27+
(def ->local-store (after todos->local-store)) ;; interceptor to store todos into local storage
2828

2929

30-
;; middleware for any handler that manipulates todos
31-
(def todo-middleware [check-spec-mw ;; ensure the spec is still valid
32-
(path :todos) ;; 1st param to handler will be the value from this path
33-
->ls ;; write to localstore each time
34-
(when ^boolean js/goog.DEBUG debug) ;; look in your browser console
35-
trim-v]) ;; remove the first (event id) element from the event vec
30+
;; interceptors for any handler that manipulates todos
31+
(def todo-interceptors [check-spec-interceptor ;; ensure the spec is still valid
32+
(path :todos) ;; 1st param to handler will be the value from this path
33+
->local-store ;; write to localstore each time
34+
(when ^boolean js/goog.DEBUG debug) ;; look in your browser console
35+
trim-v]) ;; removes first (event id) element from the event vec
3636

3737

3838
;; -- Helpers -----------------------------------------------------------------
@@ -47,71 +47,73 @@
4747

4848
;; -- Event Handlers ----------------------------------------------------------
4949

50+
;; XXX make localstore a coeffect interceptor
51+
5052
;; usage: (dispatch [:initialise-db])
51-
(reg-event ;; On app startup, create initial state
53+
(reg-event-db ;; on app startup, create initial state
5254
:initialise-db ;; event id being handled
53-
check-spec-mw ;; afterwards: check that app-db matches the spec
55+
check-spec-interceptor ;; after the event handler runs, check that app-db matches the spec
5456
(fn [_ _] ;; the handler being registered
55-
(merge default-value (ls->todos)))) ;; all hail the new state
57+
(merge default-value (localstore->todos)))) ;; all hail the new state
5658

5759

5860
;; usage: (dispatch [:set-showing :active])
59-
(reg-event ;; this handler changes the todo filter
61+
(reg-event-db ;; this handler changes the todo filter
6062
:set-showing ;; event-id
61-
[check-spec-mw (path :showing) trim-v] ;; middleware (wraps the handler)
63+
[check-spec-interceptor (path :showing) trim-v] ;; this colelction of interceptors wrap wrap the handler
6264

63-
;; Because of the path middleware above, the 1st parameter to
65+
;; Because of the path interceptor above, the 1st parameter to
6466
;; the handler below won't be the entire 'db', and instead will
6567
;; be the value at a certain path within db, namely :showing.
66-
;; Also, the use of the 'trim-v' middleware means we can omit
68+
;; Also, the use of the 'trim-v' interceptor means we can omit
6769
;; the leading underscore from the 2nd parameter (event vector).
68-
(fn [old-kw [new-filter-kw]] ;; handler
69-
new-filter-kw)) ;; return new state for the path
70+
(fn [old-keyword [new-filter-kw]] ;; handler
71+
new-filter-kw)) ;; return new state for the path
7072

7173

7274
;; usage: (dispatch [:add-todo "Finsih comments"])
73-
(reg-event ;; given the text, create a new todo
75+
(reg-event-db ;; given the text, create a new todo
7476
:add-todo
75-
todo-middleware
76-
(fn [todos [text]] ;; "path" middlware in "todo-middleware" means 1st parameter is :todos
77+
todo-interceptors
78+
(fn [todos [text]] ;; the "path" interceptor in `todo-interceptors` means 1st parameter is :todos
7779
(let [id (allocate-next-id todos)]
7880
(assoc todos id {:id id :title text :done false}))))
7981

8082

81-
(reg-event
83+
(reg-event-db
8284
:toggle-done
83-
todo-middleware
85+
todo-interceptors
8486
(fn [todos [id]]
8587
(update-in todos [id :done] not)))
8688

8789

88-
(reg-event
90+
(reg-event-db
8991
:save
90-
todo-middleware
92+
todo-interceptors
9193
(fn [todos [id title]]
9294
(assoc-in todos [id :title] title)))
9395

9496

95-
(reg-event
97+
(reg-event-db
9698
:delete-todo
97-
todo-middleware
99+
todo-interceptors
98100
(fn [todos [id]]
99101
(dissoc todos id)))
100102

101103

102-
(reg-event
104+
(reg-event-db
103105
:clear-completed
104-
todo-middleware
106+
todo-interceptors
105107
(fn [todos _]
106-
(->> (vals todos) ;; remove all todos where :done is true
108+
(->> (vals todos) ;; find the ids of all todos where :done is true
107109
(filter :done)
108110
(map :id)
109-
(reduce dissoc todos)))) ;; returns the new version of todos
111+
(reduce dissoc todos)))) ;; now do the delete of these done ids
110112

111113

112-
(reg-event
114+
(reg-event-db
113115
:complete-all-toggle
114-
todo-middleware
116+
todo-interceptors
115117
(fn [todos _]
116118
(let [new-done (not-every? :done (vals todos))] ;; toggle true or false?
117119
(reduce #(assoc-in %1 [%2 :done] new-done)

‎src/re_frame/core.cljc

+54-33
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,85 @@
55
[re-frame.fx :as fx]
66
[re-frame.router :as router]
77
[re-frame.loggers :as loggers]
8-
[re-frame.middleware :as middleware]))
8+
[re-frame.registrar :as registrar]
9+
[re-frame.interceptor :as interceptor :refer [base ->interceptor db-handler->interceptor fx-handler->interceptor]]))
910

1011

1112
;; -- dispatch
1213
(def dispatch router/dispatch)
1314
(def dispatch-sync router/dispatch-sync)
1415

1516

17+
;; XXX move API functions up here
18+
1619
;; -- subscribe
1720
(def reg-sub-raw subs/register)
1821
(def reg-sub subs/register-pure)
19-
(def clear-all-subs! subs/clear-all-handlers!)
2022
(def subscribe subs/subscribe)
2123

2224
;; -- effects
23-
(def reg-fx fx/register)
24-
(def clear-fx! fx/clear-handler!)
25-
(def clear-all-fx! fx/clear-all-handlers!)
25+
(def reg-fx fx/register)
26+
(def clear-fx (partial registrar/clear-handlers fx/kind))
2627

28+
;; XXX add a clear all handlers:
29+
;; XXX add a push handlers for testing purposes
2730

2831
;; -- middleware
29-
(def pure middleware/pure)
30-
(def fx fx/fx)
31-
(def debug middleware/debug)
32-
(def path middleware/path)
33-
(def enrich middleware/enrich)
34-
(def trim-v middleware/trim-v)
35-
(def after middleware/after)
36-
(def on-changes middleware/on-changes)
32+
33+
; (def fx fx/fx)
34+
(def debug interceptor/debug)
35+
(def path interceptor/path)
36+
(def enrich interceptor/enrich)
37+
(def trim-v interceptor/trim-v)
38+
(def after interceptor/after)
39+
(def on-changes interceptor/on-changes)
3740

3841
;; -- Events
39-
(def clear-all-events! events/clear-all-handlers!)
40-
(def clear-event! events/clear-handler!)
4142

42-
;; Registers a pure event handler. Places pure middleware in the correct, LHS position.
43+
;; usage (clear-event! :some-id)
44+
(def clear-event! (partial registrar/clear-handlers events/kind)) ;; XXX name with !
4345

44-
(defn reg-event
45-
([id handler]
46-
(events/register-base id pure handler))
47-
([id middleware handler]
48-
(events/register-base id [pure middleware] handler)))
4946

5047

51-
;; Registers an effectful event handler. Places fx middleware in the correct, LHS position.
48+
;; XXX note name change in changes.md
49+
50+
(defn reg-event-db
51+
"Register the given `id`, typically a kewyword, with the combination of
52+
`db-handler` and an interceptor chain.
53+
`db-handler` is a function: (db event) -> db
54+
`interceptors` is a collection of interceptors, possibly nested (needs flattenting).
55+
`db-handler` is wrapped in an interceptor and added to the end of the chain, so in the end
56+
there is only a chain."
57+
([id db-handler]
58+
(reg-event-db id nil db-handler))
59+
60+
([id interceptors db-handler]
61+
(events/register id [base interceptors (db-handler->interceptor db-handler)]))) ;; XXX add a base-interceptor
62+
63+
5264
(defn reg-event-fx
65+
([id fx-handler]
66+
(reg-event-fx id nil fx-handler))
67+
68+
([id interceptors fx-handler]
69+
(events/register id [base interceptors (fx-handler->interceptor fx-handler)]))) ;; XXX add a base-interceptor
70+
71+
72+
(defn reg-event-context
5373
([id handler]
54-
(events/register-base id fx handler))
55-
([id middleware handler]
56-
(events/register-base id [fx middleware] handler)))
74+
(reg-event-context id nil handler))
75+
76+
([id interceptors handler]
77+
(events/register id [base interceptors handler]))) ;; XXX can't just put in handler, must wrap it
5778

5879

5980
;; -- Logging -----
6081
;; Internally, re-frame uses the logging functions: warn, log, error, group and groupEnd
6182
;; By default, these functions map directly to the js/console implementations,
6283
;; but you can override with your own fns (set or subset).
6384
;; Example Usage:
64-
;; (defn my-fn [& args] (post-it-somewhere (apply str args)))
65-
;; (re-frame.core/set-loggers! {:warn my-fn :log my-fn}) ;; I should override the rest of them too.
85+
;; (defn my-fn [& args] (post-it-somewhere (apply str args))) ;; here is my alternative
86+
;; (re-frame.core/set-loggers! {:warn my-fn :log my-fn}) ;; override the defaults with mine
6687
(def set-loggers! loggers/set-loggers!)
6788

6889
;; If you are writing an extension to re-frame, like perhaps
@@ -76,7 +97,7 @@
7697
;; -- Event Procssing Callbacks
7798

7899
(defn add-post-event-callback
79-
"Registers a callback function `f` to be called after each event is procecessed
100+
"Registers a function `f` to be called after each event is procecessed
80101
`f` will be called with two arguments:
81102
- `event`: a vector. The event just processed.
82103
- `queue`: a PersistentQueue, possibly empty, of events yet to be processed.
@@ -87,11 +108,11 @@
87108
handlers for the one event, etc. Hook in here.
88109
- libraries providing 'isomorphic javascript' rendering on Nodejs or Nashorn.
89110
90-
'id' is typically a keyword. It is an identifier, supplied initially
91-
at \"add time\", which can be subsequently be used to remove a callback.
111+
'id' is typically a keyword. Supplied at \"add time\" so it can subsequently
112+
be used at \"remove time\" to get rid of the right callback.
92113
"
93114
([f]
94-
(router/add-post-event-callback re-frame.router/event-queue f f))
115+
(add-post-event-callback f f)) ;; use f as its own identifier
95116
([id f]
96117
(router/add-post-event-callback re-frame.router/event-queue id f)))
97118

@@ -105,8 +126,8 @@
105126
;; Assisting the v0.0.7 -> v0.0.8 tranistion.
106127
(defn register-handler
107128
[& args]
108-
(console :warn "re-frame: \"register-handler\" has been renamed \"reg-event\"")
109-
(apply reg-event args))
129+
(console :warn "re-frame: \"register-handler\" has been renamed \"reg-event-db\"")
130+
(apply reg-event-db args))
110131

111132
(defn register-sub
112133
[& args]

‎src/re_frame/events.cljc

+40-90
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,59 @@
11
(ns re-frame.events
2-
(:require [re-frame.db :refer [app-db]]
3-
[re-frame.utils :refer [first-in-vector]]
4-
[re-frame.loggers :refer [console]]))
2+
(:require [re-frame.db :refer [app-db]]
3+
[re-frame.utils :refer [first-in-vector]]
4+
[re-frame.interop :refer [empty-queue debug-enabled?]]
5+
[re-frame.registrar :refer [get-handler register-handler]]
6+
[re-frame.loggers :refer [console]]
7+
[re-frame.interceptor :as interceptor]))
58

69

7-
;; -- composing middleware -----------------------------------------------------------------------
10+
(def kind :event)
11+
(assert (re-frame.registrar/kinds kind))
812

13+
(defn- flatten-and-remove-nils
14+
"`interceptors` might have nested collections, and contain nil elements.
15+
return a flat collection, with all nils removed."
16+
[id interceptors]
17+
(let [make-chain #(->> % flatten (remove nil?))]
18+
(if-not debug-enabled?
19+
(make-chain interceptors)
20+
(do ;; do a whole lot of development time checks
21+
(when-not (coll? interceptors)
22+
(console :error "re-frame: when registering " id ", expected a collection of interceptors, got: " interceptors))
23+
(let [chain (make-chain interceptors)]
24+
(when (empty? chain)
25+
(console :error "re-frame: when registering " id ", given an empty interceptor chain"))
26+
(when-let [not-i (some (comp not interceptor/interceptor?) chain)]
27+
(if (fn? not-i)
28+
(console :error "re-frame: when registering " id ", got a function instead of an interceptor. Did you provide an old style middleware by mistake? Got: " not-i)
29+
(console :error "re-frame: when registering " id ", expected interceptors, but got: " not-i)))
30+
chain)))))
931

10-
(defn report-middleware-factories
11-
"See https://github.com/Day8/re-frame/issues/65"
12-
[v]
13-
(letfn [(name-of-factory
14-
[f]
15-
(-> f meta :re-frame-factory-name))
16-
(factory-names-in
17-
[v]
18-
(remove nil? (map name-of-factory v)))]
19-
(doseq [name (factory-names-in v)]
20-
(console :error "re-frame: \"" name "\" is a factory. It must be called like this \"(" name " ...)\", whereas you just used \"" name "\"."))))
2132

33+
(defn register
34+
"Associate given `event id` with the given collection of interceptors.
2235
23-
(defn comp-middleware
24-
"Use \"comp\" to compose a vector 'v' of middleware.
25-
For convienience, if 'v' is a function (assumed to be middleware already), return it unchanged.
26-
Prior to the \"comp\", flatten is used to remove all nested vectors, and nils are removed.
27-
Filtering out nils allows us to create Middleware conditionally like this:
28-
(comp-middleware [pure (when debug? debug)]) ;; that 'when' might leave a nil"
29-
[v]
30-
(cond
31-
(fn? v) v ;; assumed to be existing middleware
32-
(coll? v) (let [v (remove nil? (flatten v))]
33-
(report-middleware-factories v)
34-
(apply comp v))
35-
:else (console :warn "re-frame: comp-middleware expects a vector, got: " v)))
36+
`interceptors` may have nested collections and may contain nils, so process them
37+
into a nice linear collection before registration.
3638
39+
An `event handler` will likely be at the end of the chain (wrapped in an interceptor)."
40+
[id interceptors]
41+
(register-handler kind id (flatten-and-remove-nils id interceptors)))
3742

38-
;; -- the register of event handlers --------------------------------------------------------------
3943

40-
(def ^:private id->fn (atom {}))
4144

42-
43-
(defn lookup-handler
44-
[event-id]
45-
(get @id->fn event-id))
46-
47-
48-
(defn clear-all-handlers!
49-
[]
50-
(reset! id->fn {}))
51-
52-
53-
(defn clear-handler!
54-
[id]
55-
(if (lookup-handler id)
56-
(swap! id->fn dissoc id)
57-
(console :warn "re-frame: unable to clear event handler for " id ". Not defined.")))
58-
59-
60-
(defn register-base
61-
"register a handler for an event.
62-
This is low level and it is expected that a higher level function like
63-
\"re-frame.core/reg-event\" or \"re-frame.core/reg-event-fx\" would generally be used."
64-
([event-id handler-fn]
65-
(when (contains? @id->fn event-id)
66-
(console :warn "re-frame: overwriting an event-handler for: " event-id)) ;; allow it, but warn.
67-
(swap! id->fn assoc event-id handler-fn)
68-
nil)
69-
70-
([event-id middleware handler-fn]
71-
(let [mid-ware (comp-middleware middleware) ;; compose the middleware
72-
midware+hfn (mid-ware handler-fn)] ;; wrap the handler in the middleware
73-
(register-base event-id midware+hfn))))
74-
75-
76-
77-
78-
;; -- lookup and call -----------------------------------------------------------------------------
45+
;; -- handle event --------------------------------------------------------------------------------
7946

8047
(def ^:dynamic *handling* nil) ;; remember what event we are currently handling
8148

82-
8349
(defn handle
84-
"Given an event vector, look up the handler, then call it.
85-
By default, handlers are not assumed to be pure. They are called with
86-
two paramters:
87-
- the `app-db` atom
88-
- the event vector
89-
The handler is assumed to side-effect on `app-db` - the return value is ignored.
90-
To write a pure handler, use the \"pure\" middleware when registering the handler."
50+
"Given an event vector, look up the associated intercepter chain, and execute it."
9151
[event-v]
92-
(let [event-id (first-in-vector event-v)
93-
handler-fn (lookup-handler event-id)]
94-
(if (nil? handler-fn)
95-
(console :error "re-frame: no event handler registered for: \"" event-id "\". Ignoring.")
52+
(let [event-id (first-in-vector event-v)]
53+
(if-let [interceptors (get-handler kind event-id true)]
9654
(if *handling*
97-
(console :error "re-frame: while handling \"" *handling*
98-
"\" dispatch-sync was called for \"" event-v
99-
"\". You can't call dispatch-sync within an event handler.")
55+
(console :error "re-frame: while handling \"" *handling* "\" dispatch-sync was called for \"" event-v "\". You can't call dispatch-sync within an event handler.")
10056
(binding [*handling* event-v]
101-
(let [stack (some-> event-v
102-
meta
103-
:stack)]
104-
(try
105-
(handler-fn app-db event-v)
106-
(catch #?(:cljs :default :clj Exception) e
107-
(console :warn stack) ;; output a msg to help to track down dispatching point
108-
(throw e)))))))))
57+
(interceptor/execute event-v interceptors))))))
58+
10959

‎src/re_frame/fx.cljc

+43-59
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[re-frame.router :as router]
44
[re-frame.db :refer [app-db]]
55
[re-frame.events :as events]
6+
[re-frame.registrar :refer [get-handler clear-handlers register-handler]]
67
[re-frame.interop :refer [ratom? set-timeout!]]
78
[re-frame.loggers :refer [console]]))
89

@@ -15,105 +16,88 @@
1516

1617
;; -- Registration ------------------------------------------------------------
1718

18-
(def ^:private id->handler-fn (atom {}))
19-
20-
(defn lookup-handler
21-
[effect-id]
22-
(get @id->handler-fn effect-id))
23-
24-
25-
(defn clear-all-handlers!
26-
[]
27-
(reset! id->handler-fn {}))
28-
29-
30-
(defn clear-handler!
31-
[effect-id]
32-
(if (lookup-handler effect-id)
33-
(swap! id->handler-fn dissoc effect-id)
34-
(console :warn "re-frame: unable to clear effect handler for " effect-id ". Not defined.")))
3519

20+
(def kind :fx)
21+
(assert (re-frame.registrar/kinds kind))
3622

3723
(defn register
38-
"register a handler fn for an effect."
39-
[effect-id handler-fn]
40-
(when (lookup-handler effect-id)
41-
(console :warn "re-frame: overwriting an effects handler for: " effect-id)) ;; allow it, but warn.
42-
(swap! id->handler-fn assoc effect-id handler-fn))
24+
[id handler-fn]
25+
(register-handler kind id handler-fn))
4326

4427

4528
;; -- Standard Builtin Effects Handlers --------------------------------------
4629

47-
;; Dispatch event(s) after some time.
48-
;; Example:
49-
;; {:dispatch-later [{:ms 200 :dispatch [:event-id "param"]} ;; in 200ms do this: (dispatch [:event-id "param"])
50-
;; {:ms 100 :dispatch [:also :this :in :100ms]}]}
30+
31+
;; :dispatch-later
32+
;;
33+
;; `dispatch` one or more events in given times on the future. Expects a collection
34+
;; of maps with two keys: :`ms` and `:dispatch`
35+
;;
36+
;; usage:
37+
;;
38+
;; {:dispatch-later [{:ms 200 :dispatch [:event-id "param"]} ;; in 200ms do this: (dispatch [:event-id "param"])
39+
;; {:ms 100 :dispatch [:also :this :in :100ms]}]}
5140
;;
5241
(register
5342
:dispatch-later
5443
(fn [effects-v]
55-
;TODO: use Spec to verify vector and elements when clj 1.9.0 is rel.
5644
(doseq [{:keys [ms dispatch] :as effect} effects-v]
5745
(if (or (empty? dispatch) (-> ms number? not))
58-
(console :warn "re-frame: bad values given to :dispatch-later. Got: " effect ". Ignored.")
46+
(console :error "re-frame: ignoring bad :dispatch-later value: " effect)
5947
(set-timeout! #(router/dispatch dispatch) ms)))))
6048

6149

62-
;; Supply a vector. For example:
50+
;; :dispatch
6351
;;
52+
;; `dispatch` one event. Excepts a single vector.
53+
;;
54+
;; usage:
6455
;; {:dispatch [:event-id "param"] }
6556

6657
(register
6758
:dispatch
6859
(fn [val]
69-
(when-not (vector? val)
70-
(console :warn "re-frame: the value for :dispatch is not a vector. Got: " val))
71-
(router/dispatch val)))
60+
(if-not (vector? val)
61+
(console :error "re-frame: ignoring bad :dispatch value. Expected a vector, but got: " val)
62+
(router/dispatch val))))
7263

7364

74-
;; Supply sequencial coll. For example:
65+
;; :dispatch-n
66+
;;
67+
;; `dispatch` more than one event. Expects a list or vector of events. Something for which
68+
;; sequential? returns true.
7569
;;
70+
;; usage:
7671
;; {:dispatch-n (list [:do :all] [:three :of] [:these])}
7772
;;
78-
;; Coll can be anything that returns true to sequential? but should not be a map
79-
;; NOTE: this does not include Set
8073
(register
8174
:dispatch-n
8275
(fn [val]
83-
(when-not (sequential? val)
84-
(console :warn "re-frame: the value for :dispatch-n is not sequential. Got: " val))
76+
(if-not (sequential? val)
77+
(console :error "re-frame: ignoring bad :dispatch-n value. Expected a collection, got got: " val))
8578
(doseq [event val] (router/dispatch event))))
8679

8780

81+
;; :deregister-event-handler
82+
;;
83+
;; removes an event handler. Expects either a single id (typically a keyword), or a seq of ids.
84+
;;
85+
;; usage:
86+
;; {:deregister-event-handler: :my-id)}
87+
;; or:
88+
;; {:deregister-event-handler: [:one-id :another-id]}
89+
;;
8890
(register
8991
:deregister-event-handler
9092
(fn [val]
91-
(if (sequential? val)
92-
(doall (map events/clear-handler! val))
93-
(events/clear-handler! val))))
93+
(let [clear-event (partial clear-handlers events/kind)]
94+
(if (sequential? val)
95+
(doall (map clear-event val))
96+
(clear-event val)))))
9497

9598

9699
(register
97100
:db
98101
(fn [val]
99102
(reset! app-db val)))
100103

101-
;; -- Middleware --------------------------------------------------------------
102-
103-
104-
(defn fx
105-
[handler]
106-
(fn fx-handler
107-
[app-db event-vec]
108-
(if-not (ratom? app-db)
109-
(if (map? app-db)
110-
(console :warn "re-frame: Did you use \"fx\" middleware with \"reg-event\"? Use \"reg-event-fx\" instead (and don't directly use \"fx\")")
111-
(console :warn "re-frame: \"fx\" middleware not given a Ratom. Got: " app-db)))
112-
(let [run-effect (fn [[key val]]
113-
(if-let [effect-fn (lookup-handler key)]
114-
(effect-fn val)
115-
(console :error "re-frame: no effects handler registered for: " key ". Ignoring")))
116-
world {:db @app-db}]
117-
(->> (handler world event-vec) ;; is expected to return a map of effects
118-
(map run-effect) ;; process the returned effects
119-
doall))))

‎src/re_frame/interceptor.cljc

+459
Large diffs are not rendered by default.

‎src/re_frame/middleware.cljc

-191
This file was deleted.

‎src/re_frame/registrar.cljc

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
(ns re-frame.registrar
2+
(:require [re-frame.interop :refer [debug-enabled?]]
3+
[re-frame.loggers :refer [console]]))
4+
5+
6+
;; kinds of handlers
7+
(def kinds #{:event :fx :sub})
8+
9+
;; This atom contains a register of all handlers.
10+
;; Is a map keyed first by kind (of handler), and then id.
11+
;; leaf nodes are handlers.
12+
(def ^:private kind->id->handler (atom {}))
13+
14+
15+
(defn get-handler
16+
17+
([kind id]
18+
(-> (get @kind->id->handler kind)
19+
(get id)))
20+
21+
([kind id required?]
22+
(let [handler (get-handler kind id)]
23+
(when debug-enabled?
24+
(when (and required? (nil? handler))
25+
(console :error "re-frame: no " (str kind) " handler registered for: " id)))
26+
handler)))
27+
28+
29+
(defn register-handler
30+
[kind id handler-fn]
31+
(when debug-enabled?
32+
(when (get-handler kind id false)
33+
(console :warn "re-frame: overwriting " (str kind) " handler for: " id))) ;; allow it, but warn. Happens on figwheel reloads.
34+
(swap! kind->id->handler assoc-in [kind id] handler-fn)
35+
handler-fn) ;; note: returns the just registered handler
36+
37+
38+
(defn clear-handlers
39+
([] ;; clear all kinds
40+
(reset! kind->id->handler {}))
41+
42+
([kind] ;; clear all handlers for this kind
43+
(assert (kinds kind))
44+
(swap! kind->id->handler dissoc kind))
45+
46+
([kind id] ;; clear a single handler for a kind
47+
(assert (kinds kind))
48+
(if (get-handler kind id)
49+
(swap! kind->id->handler update-in [kind] dissoc id)
50+
(console :warn "re-frame: can't clear " (str kind) " handler for " id ". Not found."))))

‎src/re_frame/router.cljc

+21-15
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,19 @@
1616
;;
1717
;; But browsers only have a single thread of control and we must be
1818
;; careful to not hog the CPU. When processing events one after another, we
19-
;; must hand back control to the browser regularly, so it can redraw, process
19+
;; must regularly hand back control to the browser, so it can redraw, process
2020
;; websockets, etc. But not too regularly! If we are in a de-focused browser
2121
;; tab, our app will be CPU throttled. Each time we get back control, we have
2222
;; to process all queued events, or else something like a bursty websocket
2323
;; (producing events) might overwhelm the queue. So there's a balance.
2424
;;
2525
;; The processing/handling of an event happens "asynchronously" sometime after
2626
;; that event was enqueued via "dispatch". The original implementation of this router loop
27-
;; used core.async. It was fairly simple, and it mostly worked, but it did not
28-
;; give enough control. So now we hand-roll our own, finite-state-machine and all.
27+
;; used `core.async`. As a result, it was fairly simple, and it mostly worked,
28+
;; but it did not give enough control. So now we hand-roll our own,
29+
;; finite-state-machine and all.
2930
;;
30-
;; The strategy is this:
31+
;; In what follows, the strategy is this:
3132
;; - maintain a FIFO queue of `dispatched` events.
3233
;; - when a new event arrives, "schedule" processing of this queue using
3334
;; goog.async.nextTick, which means it will happen "very soon".
@@ -38,7 +39,7 @@
3839
;; and do these new events in the next processing cycle. That way we drain
3940
;; the queue up to a point, but we never hog the CPU forever. In
4041
;; particular, we handle the case where handling one event will beget
41-
;; another event. The freshly begat event will be handled next cycle,
42+
;; another event. The freshly begatted event will be handled next cycle,
4243
;; with yielding in-between.
4344
;; - In some cases, an event should not be handled until after the GUI has been
4445
;; updated, i.e., after the next Reagent animation frame. In such a case,
@@ -48,7 +49,7 @@
4849
;; events are processed sequentially: we handle one event completely
4950
;; before we handle the ones behind it.
5051
;;
51-
;; Implementation Notes:
52+
;; Implementation notes:
5253
;; - queue processing can be in a number of states: scheduled, running, paused
5354
;; etc. So it is modeled as a Finite State Machine.
5455
;; See "-fsm-trigger" (below) for the states and transitions.
@@ -65,7 +66,7 @@
6566
:yield next-tick}) ;; almost immediately
6667

6768

68-
;; Abstract representation of the Event Queue
69+
;; Event Queue Abstraction
6970
(defprotocol IEventQueue
7071

7172
;; -- API
@@ -221,9 +222,13 @@
221222
;;
222223

223224
(defn dispatch
224-
"Queue an event to be processed by the registered handler function.
225+
"Queue the given event for processing by the registered event handler.
225226
226-
Usage example:
227+
Just to be clear: the event handler is not run immediately - it is not run
228+
synchronously. It will likely be run 'very soon', although it may be
229+
added to the end of a FIFO queue which already contain events.
230+
231+
Usage:
227232
(dispatch [:delete-item 42])"
228233
[event-v]
229234
(let [;; At the point of dispatch, we capture the current stack.
@@ -239,19 +244,20 @@
239244
:clj "n/a")]
240245
(if (nil? event-v)
241246
(throw (ex-info "re-frame: you called \"dispatch\" without an event vector." {}))
242-
(push event-queue (with-meta event-v {:stack stack}))))
247+
#_(push event-queue (with-meta event-v {:stack stack}))
248+
(push event-queue event-v)))
243249
nil) ;; Ensure nil return. See https://github.com/Day8/re-frame/wiki/Beware-Returning-False
244250

245251

246252
(defn dispatch-sync
247-
"Immediately process an event using the registered handler.
253+
"Sychronously (immediaetly!) process the given event using the registered handler.
248254
249-
Generally, you shouldn't use this. Use \"dispatch\" instead. It
250-
is an error to even try and use it within an event handler.
255+
Generally, you shouldn't use this - you should use `dispatch` instead. It
256+
is an error to use `dispatch-sync` within an event handler.
251257
252-
Usage example:
258+
Usage:
253259
(dispatch-sync [:delete-item 42])"
254260
[event-v]
255261
(handle event-v)
256-
(-call-post-event-callbacks event-queue event-v) ;; ugly hack. Just so post-event-callbacks get called
262+
(-call-post-event-callbacks event-queue event-v) ;; slightly ugly hack. Run the registered post event callbacks.
257263
nil) ;; Ensure nil return. See https://github.com/Day8/re-frame/wiki/Beware-Returning-False

‎src/re_frame/subs.cljc

+24-22
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,31 @@
33
[re-frame.db :refer [app-db]]
44
[re-frame.interop :refer [add-on-dispose! debug-enabled? make-reaction ratom?]]
55
[re-frame.loggers :refer [console]]
6+
[re-frame.registrar :refer [get-handler register-handler]]
67
[re-frame.utils :refer [first-in-vector]]))
78

89

910
;; -- Subscription Handler Lookup and Registration --------------------------------------------------
10-
11-
;; Maps from a query-id to a handler function.
12-
(def ^:private qid->fn (atom {}))
11+
(def kind :sub)
12+
(assert (re-frame.registrar/kinds kind))
1313

1414
(defn register
15-
"Registers a subscription handler function for an query id"
15+
"Register a subscription handler fucntion for an query id"
1616
[query-id handler-fn]
17-
(if (contains? @qid->fn query-id)
18-
(console :warn "re-frame: overwriting subscription handler for: " query-id)) ;; allow it, but warn. Happens on figwheel reloads.
19-
(swap! qid->fn assoc query-id handler-fn))
17+
(register-handler kind query-id handler-fn))
2018

2119

2220
;; -- Subscription cache -----------------------------------------------------
2321
;;
24-
;; De-duplicate subscriptions. If two or more identical subscriptions
22+
;; De-duplicate subscriptions. If two or more equal subscriptions
2523
;; are concurrently active, we want only one handler running.
26-
;; Two subscriptions are "identical" if their query vectors
27-
;; test "=".
24+
;; Two subscriptions are "equal" if their query vectors test "=".
2825
(def ^:private query->reaction (atom {}))
2926

3027
(defn clear-all-handlers!
3128
"Unregisters all existing subscription handlers"
3229
[]
33-
(reset! qid->fn {})
30+
; (reset! qid->fn {}) XXX
3431
(reset! query->reaction {}))
3532

3633
(defn cache-and-return
@@ -61,7 +58,7 @@
6158
(do (console :log "Using cached subscription: " query-v)
6259
cached)
6360
(let [query-id (first-in-vector query-v)
64-
handler-fn (get @qid->fn query-id)]
61+
handler-fn (get-handler kind query-id)]
6562
(console :log "Subscription created: " query-v)
6663
(if-not handler-fn
6764
(console :error "re-frame: no subscription handler registered for: \"" query-id "\". Returning a nil subscription."))
@@ -72,7 +69,7 @@
7269
(do (console :log "Using cached subscription: " v " and " dynv)
7370
cached)
7471
(let [query-id (first-in-vector v)
75-
handler-fn (get @qid->fn query-id)]
72+
handler-fn (get-handler kind query-id)]
7673
(when debug-enabled?
7774
(when-let [not-reactive (not-empty (remove ratom? dynv))]
7875
(console :warn "re-frame: your subscription's dynamic parameters that don't implement IReactiveAtom: " not-reactive)))
@@ -87,6 +84,9 @@
8784

8885
;; -- Helper code for register-pure -------------------
8986

87+
;; add `metric` which (metric :using-cached-subscription {:text
88+
;; XXX no need for maps
89+
9090
(defn- map-vals
9191
"Returns a new version of 'm' in which 'f' has been applied to each value.
9292
(map-vals inc {:a 4, :b 2}) => {:a 5, :b 3}"
@@ -98,7 +98,7 @@
9898
[data]
9999
(cond
100100
(map? data) (map-vals deref data)
101-
(coll? data) (map deref data)
101+
(sequential? data) (map deref data)
102102
:else @data))
103103

104104
(defn register-pure
@@ -113,7 +113,7 @@
113113
In this example the entire app-db is derefed and passed to the subscription
114114
function as a singleton
115115
116-
```(subs/register-pure
116+
```(register-pure
117117
:a-b-sub
118118
(fn [q-vec d-vec]
119119
[(subs/subscribe [:a-sub])
@@ -126,7 +126,7 @@
126126
actually). Again the subscriptions are derefed and passed to the subscription
127127
function
128128
129-
```(subs/register-pure
129+
```(register-pure
130130
:a-b-sub
131131
:<- [:a-sub]
132132
:<- [:b-sub]
@@ -135,9 +135,9 @@
135135
of cases where only a simple subscription is needed without any parameters
136136
137137
"
138-
[sub-name & args]
139-
(let [f (last args) ;; grab the last arg
140-
middle-args (butlast args) ;; grab the middle args
138+
[sub-id & args]
139+
(let [f (last args) ;; computation function
140+
middle-args (butlast args) ;; middle args may be empty, or one or more :<-, or a single signal fn
141141
maybe-func (first middle-args)
142142
sub-fn (when (fn? maybe-func) maybe-func)
143143
arrow-args (if (fn? maybe-func)
@@ -149,7 +149,7 @@
149149
(cond
150150
sub-fn ;; first case the user provides a custom sub-fn
151151
(register
152-
sub-name
152+
sub-id
153153
(fn subs-handler-fn ;; multi-arity to match the arities `subscribe` might invoke.
154154
([db q-vec]
155155
(let [subscriptions (sub-fn q-vec)]
@@ -159,9 +159,10 @@
159159
(let [subscriptions (sub-fn q-vec d-vec)]
160160
(make-reaction
161161
(fn [] (f (multi-deref subscriptions) q-vec d-vec)))))))
162+
162163
(seq arrow-args) ;; the user uses the :<- sugar
163164
(register
164-
sub-name
165+
sub-id
165166
(letfn [(get-subscriptions []
166167
(let [subscriptions (map subscribe arrow-subs)]
167168
(if (< 1 (count subscriptions))
@@ -176,9 +177,10 @@
176177
(let [subscriptions (get-subscriptions)]
177178
(make-reaction
178179
(fn [] (f (multi-deref subscriptions) q-vec d-vec))))))))
180+
179181
:else
180182
(register ;; the simple case with no subs
181-
sub-name
183+
sub-id
182184
(fn subs-handler-fn
183185
([db q-vec]
184186
(make-reaction (fn [] (f @db q-vec))))

‎test/re-frame/event_test.cljs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"tests that an error thrown generates an informational warning"
1111
(re-frame/clear-all-events!)
1212

13-
(re-frame/reg-event
13+
(re-frame/reg-event-db
1414
:test-event
1515
(fn [db [event-kw stack]]
1616
(throw (js/Error. "thrown in handler"))

‎test/re-frame/fx_test.cljs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
(re-frame/reg-event-fx
2323
::later-test
2424
(fn [_world _event-v]
25-
(re-frame/reg-event
25+
(re-frame/reg-event-db
2626
::watcher
2727
(fn [db [_ token]]
2828
(is (#{:event1 :event2 :event3} token) "unexpected: token passed through")

‎test/re-frame/interceptor_test.cljs

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
(ns re-frame.interceptor-test
2+
(:require [cljs.test :refer-macros [is deftest]]
3+
[reagent.ratom :refer [atom]]
4+
[re-frame.interceptor :refer [context get-coeffect assoc-effect assoc-coeffect get-effect
5+
trim-v path on-changes
6+
db-handler->interceptor fx-handler->interceptor]]))
7+
8+
(enable-console-print!)
9+
10+
(deftest test-trim-v
11+
(let [c (-> (context [:a :b :c] [])
12+
((:before trim-v)))]
13+
(is (= (get-coeffect c :event)
14+
[:b :c]))))
15+
16+
17+
(deftest test-one-level-path
18+
(let [db {:showing true :another 1}
19+
p1 (path [:showing])] ;; a simple one level path
20+
21+
(let [b4 (-> (context [] [] db)
22+
((:before p1))) ;; before
23+
a (-> b4
24+
(assoc-effect :db false)
25+
((:after p1)))] ;; after
26+
27+
(is (= (get-coeffect b4 :db) ;; test before
28+
true))
29+
(is (= (get-effect a :db) ;; test after
30+
{:showing false :another 1})))))
31+
32+
33+
(deftest test-two-level-path
34+
(let [db {:1 {:2 :target}}
35+
p (path [:1 :2])] ;; a two level path
36+
37+
(let [b4 (-> (context [] [] db)
38+
((:before p))) ;; before
39+
a (-> b4
40+
(assoc-effect :db :4)
41+
((:after p)))] ;; after
42+
43+
(is (= (get-coeffect b4 :db)) ;; test before
44+
:target)
45+
(is (= (get-effect a :db) ;; test after
46+
{:1 {:2 :4}})))))
47+
48+
49+
(deftest test-db-handler-interceptor
50+
(let [event [:a :b]
51+
52+
handler (fn [db v]
53+
;; make sure it was given the right arguements
54+
(is (= db :original-db-val))
55+
(is (= v event))
56+
;; return a specific value for later checking
57+
:new-db-val)
58+
59+
i1 (db-handler->interceptor handler)
60+
db (-> (context event [] :original-db-val)
61+
((:before i1)) ;; calls handler - causing :db in :effects to change
62+
(get-effect :db))]
63+
(is (= db :new-db-val))))
64+
65+
66+
67+
(deftest test-fx-handler-interceptor
68+
(let [event [:a :b]
69+
coeffect {:db 4 :event event}
70+
effect {:db 5 :dispatch [:a]}
71+
72+
handler (fn [world v]
73+
;; make sure it was given the right arguements
74+
(is (= world coeffect))
75+
(is (= v event))
76+
77+
;; return a specific value for later checking
78+
effect)
79+
80+
i1 (fx-handler->interceptor handler)
81+
e (-> (context event [] (:db coeffect))
82+
((:before i1)) ;; call the handler
83+
(get-effect))]
84+
(is (= e {:db 5 :dispatch [:a]}))))
85+
86+
87+
88+
(deftest test-on-changes
89+
(let [change-handler-i (-> (fn [db v] (assoc db :a 10))
90+
db-handler->interceptor)
91+
92+
no-change-handler-i (-> (fn [db v] db)
93+
db-handler->interceptor)
94+
95+
change-i (on-changes + [:c] [:a] [:b])
96+
orig-db {:a 0 :b 2}]
97+
98+
(is (= {:a 0 :b 2}
99+
(-> (context [] [] orig-db)
100+
((:before no-change-handler-i)) ;; no change to :a and :b
101+
((:after change-i))
102+
(get-effect :db))))
103+
(is (= {:a 10 :b 2 :c 12}
104+
(-> (context [] [] orig-db)
105+
((:before change-handler-i)) ;; cause change to :a
106+
((:after change-i))
107+
(get-effect :db))))))
108+
109+
110+

‎test/re-frame/middleware_test.cljs

-40
This file was deleted.

‎test/re-frame/test_runner.cljs

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[jx.reporter.karma :as karma :include-macros true]
66
[devtools.core :as devtools]
77
;; Test Namespaces -------------------------------
8-
[re-frame.middleware-test]
8+
[re-frame.interceptor-test]
99
[re-frame.subs-test]
1010
[re-frame.fx-test]))
1111

@@ -19,7 +19,7 @@
1919

2020
(defn ^:export run-html-tests []
2121
(cljs-test/run-tests
22-
're-frame.middleware-test
22+
're-frame.interceptor-test
2323
're-frame.subs-test
2424
're-frame.fx-test))
2525

@@ -28,6 +28,6 @@
2828
(defn ^:export run-karma [karma]
2929
(karma/run-tests
3030
karma
31-
're-frame.middleware-test
31+
're-frame.interceptor-test
3232
're-frame.subs-test
3333
're-frame.fx-test))

0 commit comments

Comments
 (0)
Please sign in to comment.