Skip to content

Commit

Permalink
Pass a single arg to :output and :live?
Browse files Browse the repository at this point in the history
We couldn't think of a use-case for passing in the old output value as
a separate arg. Same for passing the db to the live function.

If no inputs are declared, then we pass the entire app-db instead.

That means we can do really concise flows, like:

{:id :bedroom-area
 :output (fn [{{:keys [width height]} :bedroom :as db}]
           (* width height))}
  • Loading branch information
kimo-k committed Nov 16, 2023
1 parent 1e86b9f commit fe0f293
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 20 deletions.
22 changes: 11 additions & 11 deletions docs/Flows.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@ Here's a basic `flow`. It describes how to derive the area of a room from its di
{:id :room-area
:inputs {:w [:room :width]
:h [:room :length]}
:output (fn calc-area [previous-area {:keys [w h] :as inputs}]
:output (fn calc-area [{:keys [w h] :as inputs}]
(* w h))
:path [:room :area]}
</div>

- **`:id`** - uniquely identifies this flow.
- **`:inputs`** - a map of `app-db` paths to observe for changes.
- **`:output`** - calculates the new derived value. Accepts the previous result, and a map of input values.
- **`:output`** - calculates the new derived value.
- Takes a map of resolved inputs.
- Simply takes `app-db` if there are no inputs.
- **`:path`** - denotes *where* the derived value should be stored.

Every event, when the values of `:inputs` change, `:output` is run, and the result is stored in `app-db` at `:path`.
Expand Down Expand Up @@ -90,8 +92,7 @@ Now the interesting part, we use `reg-flow`:
{:id :garage-area
:inputs {:w [:garage :width]
:h [:garage :length]}
:output (fn area [previous-area {:keys [w h] :as inputs}]
(* w h))
:output (fn [{:keys [w h]}] (* w h))
:path [:garage :area]})

(rf/reg-flow garage-area-flow)
Expand Down Expand Up @@ -252,7 +253,7 @@ Here's a flow using two other flows as inputs: `::kitchen-area` and `::living-ro
{:id :main-room-ratio
:inputs {:kitchen (rf/flow-input ::kitchen-area)
:living-room (rf/flow-input ::living-room-area)}
:output (fn [_ {:keys [kitchen living-room]}]
:output (fn [{:keys [kitchen living-room]}]
(/ kitchen living-room))
:path [:ratios :main-rooms]}
</div>
Expand Down Expand Up @@ -286,11 +287,10 @@ Here's another room area flow:
{:id :kitchen-area
:inputs {:w [:kitchen :width]
:h [:kitchen :length]}
:output (fn area [previous-area {:keys [w h] :as inputs}]
(* w h))
:output (fn [{:keys [w h]}] (* w h))
:path [:kitchen :area]
:live-inputs {:tab [:tab]}
:live? (fn [db {:keys [tab]}]
:live? (fn [{:keys [tab]}]
(= tab :kitchen))})
</div>

Expand Down Expand Up @@ -478,7 +478,7 @@ It builds a flow that validates our item list against the requirements:
:path [::error-state]
:inputs {:items [::items]
:tab (rf/flow-input :current-tab)}
:output (fn [_ {:keys [items]}]
:output (fn [{:keys [items]}]
(let [ct (count items)]
(cond
(> ct max-items) :too-many
Expand Down Expand Up @@ -738,14 +738,14 @@ Even though `::num-balloons-to-fill-kitchen` depends on other flows, we can acce
{:id ::kitchen-volume
:inputs {:area [:kitchen :area]
:height [:kitchen :height]}
:output (fn [_ {:keys [area height]}]
:output (fn [{:keys [area height]}]
(* area height))
:path [:kitchen :volume]})

(rf/reg-flow
{:id ::num-balloons-to-fill-kitchen
:inputs {:volume ::kitchen-volume}
:output (fn [_ {:keys [volume]}]
:output (fn [{:keys [volume]}]
(let [std-balloon-volume 2.5]
(/ volume std-balloon-volume)))})

Expand Down
21 changes: 12 additions & 9 deletions src/re_frame/flow/alpha.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -146,26 +146,29 @@
" which has no associated handler. Ignoring.")))
(update-in ctx [:effects :fx] (partial remove (set flow-fx)))))))}))

(defn resolve-inputs [db inputs]
(if (empty? inputs) db (u/map-vals (partial get-output db) inputs)))

(defn update-flow [ctx {:as flow
:keys [path init cleanup live? inputs live-inputs output id]
::keys [cleared?]}]
(let [{::keys [new?]} (meta flow)
old-db (get-coeffect ctx :db)
new-db (or (get-effect ctx :db) old-db)
id->old-live-input (u/map-vals (partial get-output old-db) live-inputs)
id->live-input (u/map-vals (partial get-output new-db) live-inputs)
id->old-input (u/map-vals (partial get-output old-db) inputs)
id->input (u/map-vals (partial get-output new-db) inputs)
id->old-live-input (resolve-inputs old-db live-inputs)
id->live-input (resolve-inputs new-db live-inputs)
id->old-input (resolve-inputs old-db inputs)
id->input (resolve-inputs new-db inputs)
dirty? (not= id->input id->old-input)
bardo [(cond new? :new (live? old-db id->old-live-input) :live :else :dead)
(cond cleared? :cleared (live? new-db id->live-input) :live :else :dead)]
bardo [(cond new? :new (live? id->old-live-input) :live :else :dead)
(cond cleared? :cleared (live? id->live-input) :live :else :dead)]
update-db (case bardo
[:new :live] #(do (swap! flows update id vary-meta dissoc ::new?)
(update-in (init % path) path output id->input))
(assoc-in (init % path) path (output id->input)))
[:live :cleared] #(cleanup % path)
[:live :live] #(cond-> % dirty? (update-in path output id->input))
[:live :live] #(cond-> % dirty? (assoc-in path (output id->input)))
[:live :dead] #(cleanup % path)
[:dead :live] #(update-in (init % path) path output id->input)
[:dead :live] #(assoc-in (init % path) path (output id->input))
identity)]
(update-effect ctx :db (fnil update-db (get-coeffect ctx :db)))))

Expand Down

0 comments on commit fe0f293

Please sign in to comment.