A module for defacto
that generically handles "asynchronous" resources.
;; deps.edn
{:deps {skuttleman/defacto-res {:git/url "https://github.com/skuttleman/defacto"
:git/sha "{SHA_OF_HEAD}"
:deps/root "res"}}}
(ns killer-app.core
(:require
[defacto.core :as defacto]
[defacto.resources.core :as res]))
;; define your resource spec by returning a map by including any of the following
(defmethod res/->resource-spec ::fetch-thing
[_ input]
{:pre-events [[::fetch-started]]
:params {:request-method :get
:url (str "http://www.example.com/things/" (:id input))}
:err-commands [[::toast!]]
:ok-events ...})
;; define a request-fn
(defn my-request-fn [resource-type params]
;; returns a vector tuple or a core.async channel
(async/go
;; does whatever, http prolly
...
;; succeeds with a vector tuple
[::res/ok {:some :data}] ;; if it isn't `::res/ok`, it's `::res/err`
;; or fails with a vector tuple
[::res/err {:some :error}]))
;; resource key
(def resource-key [::fetch-thing ::numero-uno])
;; create your store your request handler
(def store (defacto/create (res/with-ctx {:some :ctx-map} my-request-fn) {}))
(def sub (defacto/subscribe store [::res/?:resource resource-key]))
;; submit the resource
(defacto/dispatch! store [::res/submit! resource-key {:id 123}])
(res/requesting? @sub) ;; => true
... after the request finishes
(res/success? @sub) ;; true (one would hope)
(res/payload @sub) ;; => {...}
A resource
is defined by extending [[defacto.resources.core/->resource-spec]] with your resource-type
which
you can use to create and reference resources in the system.
(defmethod defacto.resources.core/->request-spec ::resource-type
[resource-key input] ;; resource-key is a vector beginning with the `resource-type`
{:params {...}
...})
Your spec can return any of the following keys
:params
- a NON-nil
argument to request the resource. If this key isnil
, the resource will not be requested.:pre-events
,:pre-command
- optional sequences of events/commands to be emitted/dispatched before the request is submitted. These occur even if:params
isnil
:ok-events
,:ok-commands
- optional sequences of events/commands to emitted/dispatched after the request succeeds. These events/commands should be express "callback" style with the final argument to be the success resultconj
'ed on to the event/command vector.:err-events
,:err-commands
- optional sequences of events/commands to emitted/dispatched after the request fails. These events/commands should be express "callback" style with the final argument to be the error resultconj
'ed on to the event/command vector.
This module exposes the following commands
.
This submits a resource with the provided params.
(defacto/dispatch! store [::res/submit! [::resource-type] {:a 1}])
This submits a resource if it is currently in the :init
state.
(defacto/dispatch! store [::res/ensure! [::resource-type] {:a 1}])
Continuously submits a resource in intervals of milliseconds
.
;; sends a request now, and after every 2 seconds forever
(defacto/dispatch! store [::res/poll! 2000 [::resource-type] {:a 1}])
;; destroy the resource to stop the polling
(defacto/emit! store [::res/destroyed [::resource-type]])
Executes a command after milliseconds
have expired.
(defacto/dispatch! store [::res/delay! 123 [::any-command! {:a 1}]])
This module exposes the following queries
.
Returns a sequence of all resources.
@(defacto/subscribe store [::res/?:resources])
;; returns `nil` for undefined resources
Retrieves the current state of a resource.
@(defacto/subscribe store [::res/?:resource [::some-key 123]])
;; returns `nil` for undefined resources
This module exposes the following events
.
Transitions the resource from any state to :requesting
. Not intended to be used directly
Transitions the resource from a :requesting
state to a :success
state. Not intended to be used directly
Transitions the resource from a :requesting
state to an :error
state. Not intended to be used directly
Destroys a resource.
(defacto/emit! store [::res/destroyed [::resource-type]])