Skip to content

Commit

Permalink
rename :default-handler to :citrus/handler
Browse files Browse the repository at this point in the history
  • Loading branch information
martinklepsch committed May 4, 2020
1 parent 94e63ff commit c9cc240
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 26 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ An effect is key/value pair where the key is the name of the effect handler and

It's important to understand that `state` value that is passed in won't affect the whole state, but only the part corresponding to its associated key in the `:controllers` map of the reconciler.

> :rocket: Citrus event handling is very customizable through an (alpha level) [`:default-handler` option](doc/default-handler.md).
> :rocket: Citrus' event handling is very customizable through an (alpha level) [`:citrus/handler` option](doc/default-handler.md).
### Side effects

Expand Down
42 changes: 24 additions & 18 deletions doc/custom-handler.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
# Default Handlers
# Citrus Handler

Default handlers are a way to adapt Citrus' event handling to users needs that
haven't been anticipated by the framework. Initially they have been motivated
by [the need to access all controllers state in event
handlers](https://github.com/clj-commons/citrus/issues/50) but theoretically
they open up Citrus' event handling to many more customizations.
Citrus event handling is implemented via a handler function that processes
a batch of events. The behavior of this handler function can be customized
by passing an alternative handler function as `:citrus/handler` when creating
a reconciler instance.

:warning: Default handlers are experimental and subject to change. Please
report your experiences using them [in this GitHub issue](https://github.com/clj-commons/citrus/issues/50).
By providing a custom `:citrus/handler` you can adapt your event handling
in ways that haven't been anticipated by the Citrus framework.

### Usage
Initially this customization option has been motivated by [the need to access
all controllers state in event handlers](https://github.com/clj-commons/citrus/issues/50).

When constructing a reconciler the `:default-handler` option can be used to
pass a default handler.
:bulb: This feature is experimental and subject to change. Please report your
experiences using them [in this GitHub issue](https://github.com/clj-commons/citrus/issues/50).

- `:default-handler` should be a function like
### Usage

When constructing a reconciler the `:citrus/handler` option can be used to
pass a custom handler that processes a batch of events.

- `:citrus/handler` should be a function like
```clj
(fn handler [reconciler events])
;; where `events` is a list of event tuples like this one
Expand All @@ -32,9 +37,9 @@ pass a default handler.

#### Open extension

With the ability to override the `default-handler` the `controller` and
With the ability to override the `citrus/handler` the `controller` and
`effect-handlers` options almost become superfluous as all behavior influenced
by these options can now also be controlled via `default-handler`. Some ideas
by these options can now also be controlled via `:citrus/handler`. Some ideas
for things that are now possible to build on top of Citrus that previously
weren't:

Expand All @@ -44,19 +49,20 @@ weren't:
- Completely replace Citrus multimethod dispatching with a custom handler registry

> **Note** that breaking out of controllers as Citrus provides them impacts how
> Citrus' `broadcast` functions work.
> Citrus' `broadcast` functions work. `broadcast!` and `broadcast-sync!` rely
> on what is being passed to the reconciler as `:controllers`.

### Recipes

:wave: Have you used `default-handler` to do something interesting? Open a PR and share your approach here!
:wave: Have you used `:citrus/handler` to do something interesting? Open a PR and share your approach here!

#### Passing the entire state as fourth argument

Event handlers currently take four arguments `[controller-kw event-kw
event-args co-effects]`. As described above one motivation for default handlers
event-args co-effects]`. As described above one motivation for custom handlers
has been to give event handlers access to the entire state of the application.

By implementing a new default handler based on [`citrus.reconciler/citrus-default-handler`](https://github.com/clj-commons/citrus/blob/220d6608c62e5deb91f0efb3ea37a6e435807148/src/citrus/reconciler.cljs#L17-L55) we can change how our controller multimethods are called, replacing the `co-effects` argument with the full state of the reconciler.
By implementing a new handler based on [`citrus.reconciler/citrus-default-handler`](https://github.com/clj-commons/citrus/blob/220d6608c62e5deb91f0efb3ea37a6e435807148/src/citrus/reconciler.cljs#L17-L55) we can change how our controller multimethods are called, replacing the `co-effects` argument with the full state of the reconciler.

> Co-effects are largely undocumented right now and might be removed in a
> future release. Please [add a note to this
Expand Down
7 changes: 4 additions & 3 deletions src/citrus/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,22 @@
config - a map of
state - app state atom
controllers - a hash of state controllers
default-handler - a function to handle incoming events (see doc/default-handler.md)
citrus/handler - a function to handle incoming events (see doc/custom-handler.md)
effect-handlers - a hash of effects handlers
batched-updates - a hash of two functions used to batch reconciler updates, defaults to
`{:schedule-fn js/requestAnimationFrame :release-fn js/cancelAnimationFrame}`
chunked-updates - a function used to divide reconciler update into chunks, doesn't used by default
Returned value supports deref, watches and metadata.
The only supported option is `:meta`"
[{:keys [state controllers default-handler effect-handlers co-effects batched-updates chunked-updates]}
[{:keys [state controllers effect-handlers co-effects batched-updates chunked-updates]
:citrus/keys [handler]}
& {:as options}]
(binding []
(let [watch-fns (volatile! {})
rec (r/->Reconciler
controllers
(or default-handler r/citrus-default-handler)
(or handler r/citrus-default-handler)
effect-handlers
co-effects
state
Expand Down
6 changes: 3 additions & 3 deletions src/citrus/reconciler.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
(broadcast! [this event args])
(broadcast-sync! [this event args]))

(deftype Reconciler [controllers default-handler effect-handlers co-effects state queue scheduled? batched-updates chunked-updates meta watch-fns]
(deftype Reconciler [controllers citrus-handler effect-handlers co-effects state queue scheduled? batched-updates chunked-updates meta watch-fns]

Object
(equiv [this other]
Expand Down Expand Up @@ -107,11 +107,11 @@
(fn batch-runner []
(let [events @queue]
(clear-queue! queue)
(default-handler this events)))))
(citrus-handler this events)))))

(dispatch-sync! [this cname event args]
(assert (some? event) (str "dispatch! was called without event name:" (pr-str [cname event args])))
(default-handler this [[cname event args]]))
(citrus-handler this [[cname event args]]))

(broadcast! [this event args]
(m/doseq [controller (keys controllers)]
Expand Down
2 changes: 1 addition & 1 deletion test/citrus/custom_handler_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
(update-in state [:counters counter-key] (fnil dec 0)))})

(def r (citrus/reconciler {:state (atom {})
:default-handler (mk-citrus-map-dispatch-handler handlers)}))
:citrus/handler (mk-citrus-map-dispatch-handler handlers)}))

(def current-user (citrus/subscription r [:current-user]))
(def counters (citrus/subscription r [:counters]))
Expand Down

0 comments on commit c9cc240

Please sign in to comment.