Skip to content

Commit

Permalink
Betters api and functionality
Browse files Browse the repository at this point in the history
- Adds support for :fx effect
- Adds support for async coeffects
- Adds more default completion keys
- Adds original event to task map
- Adds global default completion keys
- Renames on-completed to on-complete
- Moves some deps out of provided profile
- Fixes some unregister fx issues
- Fixes examples, readme and docs
  • Loading branch information
jtkDvlp committed Sep 20, 2022
1 parent 2a96e3e commit 8e00aae
Show file tree
Hide file tree
Showing 8 changed files with 402 additions and 101 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ pom.xml.asc
.hgignore
.hg/
.DS_Store
*.~undo-tree~
43 changes: 22 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ Interceptor and helpers to register and unregister (background-)tasks (FXs) in y
* register / unregister tasks / fxs via one line interceptor injection
* support multiple and any fx on-completion keys
* subscriptions for tasks list and running task boolean
* running task boolean can be quick filtered by task id
* running task boolean can be quick filtered by task name
* events to register / unregister tasks yourself
* helpers to register / unregister tasks into db yourself

Alsoe works for async coeffects injections, see https://github.com/jtkDvlp/re-frame-async-coeffects.

## Getting started

### Get it / add dependency
Expand All @@ -24,6 +26,8 @@ Add the following dependency to your `project.clj`:<br>

### Usage

See api docs [![cljdoc badge](https://cljdoc.org/badge/jtk-dvlp/re-frame-tasks)](https://cljdoc.org/d/jtk-dvlp/re-frame-tasks/CURRENT)

```clojure
(ns jtk-dvlp.your-project
(:require
Expand All @@ -32,40 +36,37 @@ Add the following dependency to your `project.clj`:<br>


(rf/reg-event-fx :some-event
;; give it the fx to identify the task emitted by this event
[(tasks/as-task :some-fx)
;; of course you can use this interceptor for more than one fx in a event call
;; futher more you can give it the handler keys to hang in finishing the task
(tasks/as-task :some-other-fx :on-done :on-done-with-errors)]
;; give it a name and fx keys to identify the task and effects emitted by this event.
[(tasks/as-task :some-task [:some-fx :some-other-fx])
;; futher more you can give it the handler keys to hang in finishing the tasks effects
;; (tasks/as-task :some-task
;; [[:some-fx :on-done :on-done-with-errors]
;; [:some-other-fx :on-yuhu :on-oh-no]])
;; last but not least supports the special effect :fx giving an path for the fx to monitor.
;; (tasks/as-task :some-task [[[:fx 1] :on-done ,,,] ; you need to give it the index of the effect within :fx vector to monitor.
;; ,,,])
]
(fn [_ _]
{:some-fx
{,,,
;; you can give the tasks an id (default: uuid), see subscription `:jtk-dvlp.re-frame.tasks/running?` for usage.
::tasks/id :some-important-stuff
:label "Do some fx"
{:label "Do some fx"
:on-success [:some-event-success]
:on-error [:some-event-error]
;; calling this by `:some-fx` will unregister the task via `tasks/as-task`
:on-completed [:some-event-completed]}
:on-done [:some-event-completed]}

:some-other-fx
{,,,
:label "Do some other fx"
;; calling this by some-fx will unregister the task via `tasks/as-task`
;; `:on-done-with-error` will also untergister the task when called by `:some-other-fx`
:on-done [:some-other-event-completed]}}))

(defn app-view
[]
(let [block-ui?
;; for sugar you can give it also a pred (called with the task id) e.g. a set of task ids to filter the running tasks.
(rf/subscribe [:jtk-dvlp.re-frame.tasks/running?])

any-running-task?
(rf/subscribe [:jtk-dvlp.re-frame.tasks/running?])

;; for sugar you can give it also a task name to filter the running tasks.
some-important-stuff-running?
(rf/subscribe [:jtk-dvlp.re-frame.tasks/running? #{:some-important-stuff}])
(rf/subscribe [:jtk-dvlp.re-frame.tasks/running? :some-important-stuff])

tasks
(rf/subscribe [:jtk-dvlp.re-frame.tasks/tasks])]
Expand All @@ -75,10 +76,10 @@ Add the following dependency to your `project.clj`:<br>
[:div "some app content"]

[:ul "task list"
;; each task is the original fx map plus an `::tasks/id` and `::tasks/effect`
(for [{:keys [label :jtk-dvlp.re-frame.tasks/id] :as _task} @tasks]
;; each task is a map with the original event vector plus name and id.
(for [{:keys [:jtk-dvlp.re-frame.tasks/id] :as task} @tasks]
^{:key id}
[:li label])]
[:li task])]

(when @block-ui?
[:div "this div blocks the UI if there are running tasks"])])))
Expand Down
2 changes: 1 addition & 1 deletion dev.cljs.edn
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
,,,}

{:main
jtk-dvlp.user}
jtk-dvlp.your-project}
1 change: 0 additions & 1 deletion dev/jtk_dvlp/user.cljs

This file was deleted.

172 changes: 152 additions & 20 deletions dev/jtk_dvlp/your_project.cljs
Original file line number Diff line number Diff line change
@@ -1,57 +1,189 @@
(ns jtk-dvlp.your-project
(ns ^:figwheel-hooks jtk-dvlp.your-project
(:require
[cljs.pprint]
[cljs.core.async :refer [timeout]]
[jtk-dvlp.async :refer [go <!] :as a]

[goog.dom :as gdom]
[reagent.dom :as rdom]

[re-frame.core :as rf]
[jtk-dvlp.re-frame.async-coeffects :as acoeffects]
[jtk-dvlp.re-frame.tasks :as tasks]))


(defn- some-async-stuff
[]
(go
(<! (timeout 5000))
:result))

(rf/reg-fx :some-fx
(fn [{:keys [on-complete] :as x}]
(println ":some-fx" x)
(go
(let [result (<! (some-async-stuff))]
(println ":some-fx finished")
(rf/dispatch (conj on-complete result))))))

(rf/reg-fx :some-other-fx
(fn [{:keys [bad-data on-done on-done-with-errors] :as x}]
(println ":some-other-fx" x)
(go
(try
(when bad-data
(throw (ex-info "bad data" {:code :bad-data})))
(let [result (<! (some-async-stuff))]
(rf/dispatch (conj on-done result)))
(catch :default e
(println ":some-other-fx error" e)
(rf/dispatch (conj on-done-with-errors e)))
(finally
(println ":some-other-fx finished"))))))

(acoeffects/reg-acofx :some-acofx
(fn [cofxs & [bad-data :as args]]
(println ":some-acofx")
(go
(when bad-data
(throw (ex-info "bad data" {:code :bad-data})))
(let [result
(->> args
(apply some-async-stuff)
(<!)
(assoc cofxs :some-acofx))]

(println ":some-acofx finished")
result))))

(rf/reg-event-fx :some-event
;; give it the fx to identify the task emitted by this event
[(tasks/as-task :some-fx)
;; of course you can use this interceptor for more than one fx in a event call
;; futher more you can give it the handler keys to hang in finishing the task
(tasks/as-task :some-other-fx :on-done :on-done-with-errors)]
[(tasks/as-task :some-task
[:some-fx
[:some-other-fx :on-done :on-done-with-errors]
[[:fx 1] :on-done]])
(acoeffects/inject-acofx :some-acofx)]
(fn [_ _]
(println "handler")
{:some-fx
{,,,
;; you can give the tasks an id (default: uuid), see subscription `:jtk-dvlp.re-frame.tasks/running?` for usage.
:on-success [:some-event-success]
:on-error [:some-event-error]
;; calling this by `:some-fx` will unregister the task via `tasks/as-task`
:on-complete [:some-event-completed]}

:some-other-fx
{,,,
;; calling this by some-fx will unregister the task via `tasks/as-task`
;; `:on-done-with-error` will also untergister the task when called by `:some-other-fx`
:on-done [:some-other-event-completed]}

:fx
[[:some-fx
{:on-success [:some-event-success]
:on-error [:some-event-error]
:on-complete [:some-event-completed]}]

;; same with :fx effect referenzed via path [:fx 1]
[:some-other-fx
{:on-done [:some-other-event-completed]}]]}))

(rf/reg-event-fx :some-bad-event
;; give it the fx to identify the task emitted by this event
[(tasks/as-task :some-task
[:some-fx
{:effect-key [:some-other-fx]
:completion-keys #{:on-done :on-done-with-errors}}])
(acoeffects/inject-acofx [:some-acofx :bad-data])]
(fn [_ _]
(println "handler")
{:some-fx
{,,,
;; you can give the tasks an id (default: uuid), see subscription `:jtk-dvlp.re-frame.tasks/running?` for usage.
::tasks/id :some-important-stuff
:label "Do some fx"
:on-success [:some-event-success]
:on-error [:some-event-error]
;; calling this by `:some-fx` will unregister the task via `tasks/as-task`
:on-completed [:some-event-completed]}
:on-complete [:some-event-completed]}

:some-other-fx
{,,,
:label "Do some other fx"
;; calling this by some-fx will unregister the task via `tasks/as-task`
;; `:on-done-with-error` will also untergister the task when called by `:some-other-fx`
:on-done [:some-other-event-completed]}}))

(rf/reg-event-fx :some-other-bad-event
;; give it the fx to identify the task emitted by this event
[(tasks/as-task :some-task
[:some-fx
{:effect-key [:some-other-fx]
:completion-keys #{:on-done :on-done-with-errors}}])
(acoeffects/inject-acofx :some-acofx)]
(fn [_ _]
(println "handler")
{:some-fx
{,,,
;; you can give the tasks an id (default: uuid), see subscription `:jtk-dvlp.re-frame.tasks/running?` for usage.
:on-success [:some-event-success]
:on-error [:some-event-error]
;; calling this by `:some-fx` will unregister the task via `tasks/as-task`
:on-complete [:some-event-completed]}

:some-other-fx
{,,,
:bad-data true
;; calling this by some-fx will unregister the task via `tasks/as-task`
;; `:on-done-with-error` will also untergister the task when called by `:some-other-fx`
:on-done [:some-other-event-completed]}}))

(rf/reg-event-db :some-event-completed
(fn [db _]
db))

(rf/reg-event-db :some-other-event-completed
(fn [db _]
db))

(defn app-view
[]
(let [block-ui?
;; for sugar you can give it also a pred (called with the task id) e.g. a set of task ids to filter the running tasks.
(rf/subscribe [:jtk-dvlp.re-frame.tasks/running?])

any-running-task?
(rf/subscribe [:jtk-dvlp.re-frame.tasks/running?])

some-important-stuff-running?
(rf/subscribe [:jtk-dvlp.re-frame.tasks/running? #{:some-important-stuff}])

tasks
(rf/subscribe [:jtk-dvlp.re-frame.tasks/tasks])]

(fn []
[:<>
[:div "some app content"]
[:button {:on-click #(rf/dispatch [:some-event])}
"exec some event"]
[:button {:on-click #(rf/dispatch [:some-bad-event])}
"exec some bad event"]
[:button {:on-click #(rf/dispatch [:some-other-bad-event])}
"exec some other bad event"]

[:ul "task list"
[:ul "task list " (count @tasks)
;; each task is the original fx map plus an `::tasks/id` and `::tasks/effect`
(for [{:keys [label :jtk-dvlp.re-frame.tasks/id] :as _task} @tasks]
(for [{:keys [::tasks/id] :as task} (vals @tasks)]
^{:key id}
[:li label])]
[:li [:pre (with-out-str (cljs.pprint/pprint task))]])]

(when @block-ui?
[:div "this div blocks the UI if there are running tasks"])])))


;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; re-frame setup

(defn- mount-app
[]
(rdom/render
[app-view]
(gdom/getElement "app")))

(defn ^:after-load on-reload
[]
(rf/clear-subscription-cache!)
(mount-app))

(defonce on-init
(mount-app))
6 changes: 5 additions & 1 deletion dev/user.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@

(defn fig-init
[]
(figwheel.main.api/start "dev"))
(figwheel/start {:mode :serve} "dev"))

(defn cljs-repl
[]
(figwheel/cljs-repl "dev"))
26 changes: 15 additions & 11 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject jtk-dvlp/re-frame-tasks "1.0.1"
(defproject jtk-dvlp/re-frame-tasks "2.0.0-SNAPSHOT"
:description
"A re-frame interceptor and helpers to register / unregister (background-)tasks"

Expand All @@ -22,17 +22,18 @@
^{:protect false}
[:target-path]

:profiles
{:provided
{:dependencies
[[org.clojure/clojure "1.10.0"]
[org.clojure/clojurescript "1.10.773"]

[re-frame "0.12.0"]]}
:dependencies
[[org.clojure/clojure "1.10.0"]
[org.clojure/clojurescript "1.10.773"]
[jtk-dvlp/core.async-helpers "3.2.0-SNAPSHOT"]
[re-frame "1.1.2"]]

:dev
:profiles
{:dev
{:dependencies
[[com.bhauman/figwheel-main "0.2.7"]]
[[com.bhauman/figwheel-main "0.2.7"]
[org.clojure/core.async "1.3.610"]
[net.clojars.jtkdvlp/re-frame-async-coeffects "2.0.0"]]

:source-paths
["dev"]}
Expand All @@ -46,6 +47,9 @@
[cider.piggieback/wrap-cljs-repl]

:init-ns
user}}}
user

:init
(fig-init)}}}

,,,)
Loading

0 comments on commit 8e00aae

Please sign in to comment.