Working towards API docs

This commit is contained in:
Mike Thompson 2017-07-21 01:01:51 +10:00
parent c0534e0839
commit 7556f67319
4 changed files with 149 additions and 30 deletions

45
docs/API.md Normal file
View File

@ -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)
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
<!-- END doctoc generated TOC please keep comment here to allow auto update -->

View File

@ -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.

View File

@ -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 ----------------------------------------------------------------

View File

@ -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