Allowing specification of dependencies of a state

This commit is contained in:
Tejas Dinkar 2016-12-18 11:01:24 +05:30
parent 9a62688c2f
commit a51a34dad7
3 changed files with 60 additions and 3 deletions

View file

@ -26,6 +26,7 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt
- [Using State](#using-state)
- [Dependencies](#dependencies)
- [Talking States](#talking-states)
- [Automatically Starting Dependencies](#automatically-starting-dependencies)
- [Value of Values](#value-of-values)
- [The Importance of Being Reloadable](#the-importance-of-being-reloadable)
- [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)
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
Lifecycle functions start/stop can take both functions and values. This is "valuable" and also works:

View file

@ -135,14 +135,15 @@
#?(:clj
(defmacro defstate [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)
order (make-state-seq state-name)]
(validate lifecycle)
(let [s-meta (cond-> {:order order
:start `(fn [] ~start)
:status #{:stopped}}
stop (assoc :stop `(fn [] ~stop)))]
stop (assoc :stop `(fn [] ~stop))
deps (assoc :deps deps))]
`(do
;; (log (str "|| mounting... " ~state-name))
(~'defonce ~state (DerefableState. ~state-name))
@ -241,13 +242,22 @@
(defn- all-without-subs []
(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]
(let [fs (-> states first)]
(if (coll? fs)
(if-not (empty? fs) ;; (mount/start) vs. (mount/start #{}) vs. (mount/start #{1 2 3})
(apply start fs)
{:started #{}})
(let [states (or (seq states)
(let [states (or (deps-chain states)
(all-without-subs))]
{:started (bring states up <)}))))

View 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))))