diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..0fa44ce --- /dev/null +++ b/docs/API.md @@ -0,0 +1,45 @@ +## re-frame API + +I have good news and bad news. + +First, the bad news: + 1. [there's this problem](https://github.com/Day8/re-frame/blob/master/src/re_frame/core.cljc#L19-L33) + 2. I lied about the extra nudity + +Now, the good news: + 1. the re-frame API is indeed [all in one namespace](https://github.com/Day8/re-frame/blob/master/src/re_frame/core.cljc) + 2. That namespace has occasional comments + 3. Each API function has passable documentation (see links below), just not auto-generated. + +## Links To API docs + +The core API is these functions: + - [dispatch](), [dispatch-sync]() + - [reg-event-db](), [reg-event-fx]() + - [reg-sub]() + - [subscribe]() + +Occasionally, you'll need to use: + - [reg-fx]() + - [reg-cofx]() and [inject-cofx]() + +And, finally, there are some standard Interceptors: + - [path]() + - [after]() + - [debug]() + + +Builtin effects: + XXX + +Builtin coeffects: + XXX + +Previous: [XXX](../README.md)       +Up: [Index](README.md)       +Next: [XXX](CodeWalkthrough.md) + + + + + diff --git a/src/re_frame/cofx.cljc b/src/re_frame/cofx.cljc index 819f5ed..854343d 100644 --- a/src/re_frame/cofx.cljc +++ b/src/re_frame/cofx.cljc @@ -26,7 +26,7 @@ Perhaps it needs access to the connection to a DataScript database. If the handler directly access these resources, it stops being as - pure. It immedaitely becomes harder to test, etc. + pure. It immediately becomes harder to test, etc. So the necessary resources are \"injected\" into the `coeffect` (map) given the handler. diff --git a/src/re_frame/core.cljc b/src/re_frame/core.cljc index 4802138..ba43f84 100644 --- a/src/re_frame/core.cljc +++ b/src/re_frame/core.cljc @@ -18,16 +18,19 @@ ;; -- API --------------------------------------------------------------------- ;; -;; When originally writing this namespace, we used this technique: +;; This namespace represents the re-frame API +;; +;; When originally writing this API namespace, we used this technique: ;; (def api-name-for-fn deeper.namespace/where-the-defn-is) ;; ;; Turns out, not doing a `defn` in the API itself makes it hard: ;; - to auto-generate API docs ;; - for IDEs to provide code completion on functions in the API ;; -;; Which is annoying. But there are pros and cons and we haven't yet -;; revisited the decision. So, sorry, in advance. To compensate we've -;; added more nudity to the official docs. +;; Sigh. Which is annoying. But there are pros and cons and we haven't +;; yet revisited the decision. So, sorry, in advance for the lack of +;; auto-generated API docs and auto-completion in your IDE. +;; To compensate we've added more nudity to the official docs. ;; -- dispatch ---------------------------------------------------------------- diff --git a/src/re_frame/subs.cljc b/src/re_frame/subs.cljc index 37a8e0a..bb159c9 100644 --- a/src/re_frame/subs.cljc +++ b/src/re_frame/subs.cljc @@ -62,10 +62,26 @@ (get @query->reaction [query-v dyn-v]))) -;; -- subscribe ----------------------------------------------------- +;; -- subscribe --------------------------------------------------------------- (defn subscribe - "Returns a Reagent/reaction which contains a computation" + "Given a `query` (vector), returns a Reagent `reaction` which, over + time, reactively delivers a stream of freshly computed values. + + To obtain the obtain the current value, the returned `reaction` + must be `deref`ed. + + The `query` vector must have at least one value, the query-id, typically + a namespaced keyword. The rest of the elements are optional, additional + values which parameterise the query. + + There can be a further optional, argument `dynv` which is a vector of + further input signals (atoms, reactions, etc). This arg is not really + used/needed any more and is borderline deprecated. + + XXX mention de-duplication and caching. + " + ([query-v] (trace/with-trace {:operation (first-in-vector query-v) :op-type :sub/create @@ -133,40 +149,95 @@ (defn reg-sub - "Associate the given `query id` with a handler function and an optional signal function. + "For a given `query-id`, register a `computation` function and input `signals`. - There's 3 ways this function can be called + At an abstract level, a call to this function allows you to register 'the mechanism' + to later fulfil a call to `(subscribe [query-id ...])`. - 1. (reg-sub - :test-sub - (fn [db [_]] db)) - The value in app-db is passed to the computation function as the 1st argument. + To say that another way, reg-sub allows you to create a template for a node + in the signal graph. But note: reg-sub does not cause a node to be created. + It simply allows you to register the template from which such a + node could be created, if it were needed, sometime later, when the call + to `subscribe` is made. - 2. (reg-sub - :a-b-sub - (fn [q-vec d-vec] - [(subs/subscribe [:a-sub]) - (subs/subscribe [:b-sub])]) - (fn [[a b] [_]] {:a a :b b})) + reg-sub needs three things: + - a `query-id` + - the required inputs for this node + - a computation function for this node - Two functions provided. The 2nd is computation function, as before. The 1st - is returns what `input signals` should be provided to the computation. The - `input signals` function is called with two arguments: the query vector - and the dynamic vector. The return value can be singleton reaction or - a sequence of reactions. + The `query-id` is always the 1st argument to reg-sub and it is typically + a namespaced keyword. - 3. (reg-sub + A computation function is always the last argument and it has this general form: + `(input-signals, query-vector) -> a-value` + + What goes in between the 1st and last args can vary, but whatever is there will + define the input signals part of the template, and, as a result, it will control + what values the computation functions gets as a first argument. + + There's 3 ways this function can be called - 3 ways to supply input signals: + + 1. No input signals given: + + (reg-sub + :query-id + a-computation-fn) ;; (fn [db v] ... a-value) + + The node's input signal defaults to `app-db`, and the value within `app-db` is + is given as the 1st argument to the computation function. + + 2. A signal function is supplied: + + (reg-sub + :query-id + signal-fn ;; <-- here + computation-fn) + + When a node is created from the template, the `signal-fn` will be called and it + is expected to return the input signal(s) as either a singleton, if there is only + one, or a sequence if there are many, or a map with the signals as the values. + + The values from the nominated signals will be supplied as the 1st argument to the + computation function - either a singleton, sequence or map of them, paralleling + the structure returned by the signal function. + + Here, is an example signal-fn, which returns a vector of input signals. + + (fn [query-vec dynamic-vec] + [(subscribe [:a-sub]) + (subscribe [:b-sub])]) + + For that signal function, the computation function must be written + to expect a vector of values for its first argument. + (fn [[a b] _] ....) + + If the signal function was simpler and returned a singleton, like this: + (fn [query-vec dynamic-vec] + (subscribe [:a-sub])) + + then the computation function must be written to expect a single value + as the 1st argument: + + (fn [a _] ...) + + 3. Syntax Sugar + + (reg-sub :a-b-sub :<- [:a-sub] :<- [:b-sub] - (fn [[a b] [_]] {:a a :b b}))``` - This 3rd variation is just syntactic sugar for the 2nd. Pairs are supplied instead - of an `input signals` functions. `:<-` is supplied followed by the subscription - vector. + (fn [[a b] [_]] {:a a :b b})) + + This 3rd variation is syntactic sugar for the 2nd. Pairs are supplied instead + of an `input signals` functions. Each pair starts with a `:<-` and a subscription + vector follows. + + For further understanding, read `/docs`, and look at the detailed comments in + /examples/todomvc/src/subs.cljs " [query-id & args] (let [computation-fn (last args) - input-args (butlast args) ;; may be empty, or one fn, or pairs of :<- / vector + input-args (butlast args) ;; may be empty, or one signal fn, or pairs of :<- / vector err-header (str "re-frame: reg-sub for " query-id ", ") inputs-fn (case (count input-args) ;; no `inputs` function provided - give the default