Allowing specification of dependencies of a state
This commit is contained in:
parent
9a62688c2f
commit
a51a34dad7
3 changed files with 60 additions and 3 deletions
15
README.md
15
README.md
|
|
@ -26,6 +26,7 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt
|
||||||
- [Using State](#using-state)
|
- [Using State](#using-state)
|
||||||
- [Dependencies](#dependencies)
|
- [Dependencies](#dependencies)
|
||||||
- [Talking States](#talking-states)
|
- [Talking States](#talking-states)
|
||||||
|
- [Automatically Starting Dependencies](#automatically-starting-dependencies)
|
||||||
- [Value of Values](#value-of-values)
|
- [Value of Values](#value-of-values)
|
||||||
- [The Importance of Being Reloadable](#the-importance-of-being-reloadable)
|
- [The Importance of Being Reloadable](#the-importance-of-being-reloadable)
|
||||||
- [Start and Stop Order](#start-and-stop-order)
|
- [Start and Stop Order](#start-and-stop-order)
|
||||||
|
|
@ -163,6 +164,20 @@ this `config`, being top level, can be used in other namespaces, including the o
|
||||||
[here](dev/clj/app/www.clj#L32)
|
[here](dev/clj/app/www.clj#L32)
|
||||||
is an example of a web server that "depends" on a similar `config`.
|
is an example of a web server that "depends" on a similar `config`.
|
||||||
|
|
||||||
|
### Automatically Starting Dependencies
|
||||||
|
|
||||||
|
It is also possible to start the dependencies of a state, when a particular state is started. We can modify the example above as follows
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
(ns app.database
|
||||||
|
(:require [mount.core :refer [defstate]]
|
||||||
|
[app.config :refer [config]]))
|
||||||
|
|
||||||
|
(defstate conn :deps [#'config] :start (create-connection config))
|
||||||
|
|
||||||
|
(mount/start #'conn) ;; => {:started ["#'app.config/config" "#'app.database/conn"]}
|
||||||
|
```
|
||||||
|
|
||||||
## Value of values
|
## Value of values
|
||||||
|
|
||||||
Lifecycle functions start/stop can take both functions and values. This is "valuable" and also works:
|
Lifecycle functions start/stop can take both functions and values. This is "valuable" and also works:
|
||||||
|
|
|
||||||
|
|
@ -135,14 +135,15 @@
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defmacro defstate [state & body]
|
(defmacro defstate [state & body]
|
||||||
(let [[state params] (macro/name-with-attributes state body)
|
(let [[state params] (macro/name-with-attributes state body)
|
||||||
{:keys [start stop] :as lifecycle} (apply hash-map params)
|
{:keys [start stop deps] :as lifecycle} (apply hash-map params)
|
||||||
state-name (with-ns *ns* state)
|
state-name (with-ns *ns* state)
|
||||||
order (make-state-seq state-name)]
|
order (make-state-seq state-name)]
|
||||||
(validate lifecycle)
|
(validate lifecycle)
|
||||||
(let [s-meta (cond-> {:order order
|
(let [s-meta (cond-> {:order order
|
||||||
:start `(fn [] ~start)
|
:start `(fn [] ~start)
|
||||||
:status #{:stopped}}
|
:status #{:stopped}}
|
||||||
stop (assoc :stop `(fn [] ~stop)))]
|
stop (assoc :stop `(fn [] ~stop))
|
||||||
|
deps (assoc :deps deps))]
|
||||||
`(do
|
`(do
|
||||||
;; (log (str "|| mounting... " ~state-name))
|
;; (log (str "|| mounting... " ~state-name))
|
||||||
(~'defonce ~state (DerefableState. ~state-name))
|
(~'defonce ~state (DerefableState. ~state-name))
|
||||||
|
|
@ -241,13 +242,22 @@
|
||||||
(defn- all-without-subs []
|
(defn- all-without-subs []
|
||||||
(remove (comp :sub? @meta-state) (find-all-states)))
|
(remove (comp :sub? @meta-state) (find-all-states)))
|
||||||
|
|
||||||
|
(defn- deps-chain [states]
|
||||||
|
(when (seq states)
|
||||||
|
(into (set states)
|
||||||
|
(->> states
|
||||||
|
(map var-to-str)
|
||||||
|
(map @meta-state)
|
||||||
|
(mapcat :deps)
|
||||||
|
deps-chain))))
|
||||||
|
|
||||||
(defn start [& states]
|
(defn start [& states]
|
||||||
(let [fs (-> states first)]
|
(let [fs (-> states first)]
|
||||||
(if (coll? fs)
|
(if (coll? fs)
|
||||||
(if-not (empty? fs) ;; (mount/start) vs. (mount/start #{}) vs. (mount/start #{1 2 3})
|
(if-not (empty? fs) ;; (mount/start) vs. (mount/start #{}) vs. (mount/start #{1 2 3})
|
||||||
(apply start fs)
|
(apply start fs)
|
||||||
{:started #{}})
|
{:started #{}})
|
||||||
(let [states (or (seq states)
|
(let [states (or (deps-chain states)
|
||||||
(all-without-subs))]
|
(all-without-subs))]
|
||||||
{:started (bring states up <)}))))
|
{:started (bring states up <)}))))
|
||||||
|
|
||||||
|
|
|
||||||
32
test/core/mount/test/start_dependencies.cljc
Normal file
32
test/core/mount/test/start_dependencies.cljc
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
(ns core.mount.test.start-dependencies
|
||||||
|
(:require
|
||||||
|
#?@(:cljs [[cljs.test :as t :refer-macros [is deftest use-fixtures]]
|
||||||
|
[mount.core :as mount :refer-macros [defstate]]]
|
||||||
|
:clj [[clojure.test :as t :refer [is deftest use-fixtures]]
|
||||||
|
[mount.core :as mount :refer [defstate]]])
|
||||||
|
[mount.test.helper :refer [dval]]))
|
||||||
|
|
||||||
|
#?(:clj (alter-meta! *ns* assoc ::load false))
|
||||||
|
|
||||||
|
(defstate dependency-1 :start 1)
|
||||||
|
(defstate dependency-2 :start 2)
|
||||||
|
(defstate thing-to-start
|
||||||
|
:deps [#'dependency-1 #'dependency-2]
|
||||||
|
:start 3)
|
||||||
|
(defstate should-not-start :start 4)
|
||||||
|
|
||||||
|
(defn- start-states []
|
||||||
|
(mount/start #'dependency-1))
|
||||||
|
|
||||||
|
(use-fixtures :once
|
||||||
|
#?(:cljs {:before start-states
|
||||||
|
:after mount/stop}
|
||||||
|
:clj #((start-states) (%) (mount/stop))))
|
||||||
|
|
||||||
|
(deftest dependencies-test
|
||||||
|
(is (= {:started ["#'core.mount.test.start-dependencies/dependency-2" "#'core.mount.test.start-dependencies/thing-to-start"]}
|
||||||
|
(mount/start #'thing-to-start)))
|
||||||
|
(is (= 3 (dval thing-to-start)))
|
||||||
|
(is (= 2 (dval dependency-2)))
|
||||||
|
(is (= 1 (dval dependency-1)))
|
||||||
|
(is (instance? mount.core.NotStartedState (dval should-not-start))))
|
||||||
Loading…
Reference in a new issue