aide
aide copied to clipboard
ClojureScript/Clojure event-driven application framework.
= Aide
ClojureScript/Clojure event-driven application framework. Successor to https://github.com/metametadata/carry/[Carry].
[link=https://clojars.org/aide] image::https://img.shields.io/clojars/v/aide.svg[Clojars Project]
== Why not Carry?
-
Carry framework is very strict about separating signals (side effects) and actions (model changing functions). In a long run it turned out to produce a lot of boilerplate (e.g. many signal handlers are anemic and only dispatch an action). Such separation was mainly needed for implementing Redux-ish time traveling debugger, but in the end I don't find such tool too useful, it's pretty easy to debug the app using traditional approaches.
-
By default Carry recommends using keywords for events and actions. It makes it cumbersome to maintain the codebase, because IDEs don't understand this abstraction.
== Why not re-frame?
re-frame has an implicit global state and hard dependency on Reagent.
Also it's based on patterns which are too heavyweight:
- Subscriptions (vs., for instance, Reagent reactions or Rum derived atoms).
- Effects & co-effects (vs. usual function and/or method calls and using mocks in tests).
- Interceptors (vs. middleware pattern).
- Async event handling queue.
== Features
- Events can be defined as vars (so that IDE can jump to definition and compiler can detect undefined events).
- Event definitions look similarly to
defn
(so that IDE can highlight arguments, etc.). - Events can be async.
- Events can be serialized/deserialized. E.g. it's possible to record and replay user's actions.
- No enforced separation between "side-effectful" and "pure" events.
- Testable. It's possible to test event handlers similarly to usual functions and also mock the events.
- Supports injecting additional functionality via third-party packages (such as logging, routing, etc.). See https://github.com/metametadata/aide/tree/master/contrib.
- Proposes a pattern for SPAs (single app atom, view/view-model separation).
- Framework core is agnostic to UI and model layers.
- Proposes app lifecycle pattern (via standard start/stop events). This is needed for "stateful" third-party packages.
- (TODO) Time traveling debugger.
== Status
Alpha (API is a subject to change, needs more examples, docs, etc.). Is used in production.
== Code Examples
Define an app which has some model:
[source, clojure] .... (ns app.core (:require [aide.core :as aide]))
(let [*model (atom {:val 0}) app (aide/object {:*model *model})] ,,,) ....
Define an event which modifies the app model:
[source, clojure] .... (aide/defevent on-increment [app _data] (swap! (:*model app) update :val inc)) ....
Define an event which emits another event asynchronously:
[source, clojure] .... (aide/defevent on-delayed-increment [app delay-ms] (.setTimeout js/window #(aide/emit app on-increment) delay-ms)) ....
Emit events into the app:
[source, clojure] .... (aide/emit app aide-lifecycle/on-start) (aide/emit app on-increment) (aide/emit app on-delayed-increment 1000) (aide/emit app aide-lifecycle/on-stop) ....
Middleware example:
[source, clojure]
(defn add-logging "Will log all events." [object] (update app :aide.core/emit (fn wrap-emit [original-emit] (fn emit [object event data] (println (str event) (pr-str data)) (original-emit object event data)))))
(def app-with-logging (-> app add-logging))
== Integration with Reagent
See https://github.com/metametadata/aide/tree/master/examples.
== Developer Guide
=== Tests (Clojure)
Run tests once: lein test
or lein test-refresh :run-once
Autorun tests: lein test-refresh
=== Tests (ClojureScript)
To run tests once:
lein doo phantom test once
or automatically re-run on code changes:
lein doo phantom test auto
== License
Copyright © 2018 Yuri Govorushchenko.
Released under an MIT license.