From 1c970e152a71fd92c6a0a06afe6bf0646ab69b67 Mon Sep 17 00:00:00 2001 From: anatoly Date: Fri, 27 Nov 2015 12:48:35 -0500 Subject: [PATCH 01/16] [#20] lifecycle fns take fns and values --- README.md | 26 +++++++++++++++++--------- doc/differences-from-component.md | 4 ++-- doc/intro.md | 3 --- doc/runtime-arguments.md | 12 ++++++------ doc/uberjar.md | 9 ++++----- src/mount/core.clj | 10 +++++----- test/app/app.clj | 4 ++-- test/app/config.clj | 2 +- test/app/nyse.clj | 4 ++-- test/check/parts_test.clj | 2 +- test/check/start_with_test.clj | 6 +++--- test/check/suspend_resume_test.clj | 16 ++++++++-------- 12 files changed, 51 insertions(+), 47 deletions(-) delete mode 100644 doc/intro.md diff --git a/README.md b/README.md index 425f3e4..be8e581 100644 --- a/README.md +++ b/README.md @@ -79,16 +79,16 @@ mount is an alternative to the [component](https://github.com/stuartsierra/compo Creating state is easy: ```clojure -(defstate conn :start (create-conn)) +(defstate conn :start create-conn) ``` -where `(create-conn)` is defined elsewhere, can be right above it. +where the `create-conn` function is defined elsewhere, can be right above it. In case this state needs to be cleaned / destryed between reloads, there is also `:stop` ```clojure -(defstate conn :start (create-conn) - :stop (disconnect conn)) +(defstate conn :start create-conn + :stop #(disconnect conn)) ``` That is pretty much it. But wait, there is more.. this state is _a top level being_, which means it can be simply @@ -101,6 +101,14 @@ dev=> conn #object[datomic.peer.LocalConnection 0x1661a4eb "datomic.peer.LocalConnection@1661a4eb"] ``` +#### Value of values + +Lifecycle functions start/stop/suspend/resume can take both functions and values. This is valuable and also works: + +```clojure +(mount/defstate answer-to-the-ultimate-question-of-life-the-universe-and-everything :start 42) +``` + ### Using State For example let's say an `app` needs a connection above. No problem: @@ -136,7 +144,7 @@ There are of course direct dependecies that `mount` respects: (:require [mount.core :refer [defstate]])) (defstate app-config - :start (load-config "test/resources/config.edn")) + :start #(load-config "test/resources/config.edn")) ``` this `app-config`, being top level, can be used in other namespaces, including the ones that create states: @@ -146,7 +154,7 @@ this `app-config`, being top level, can be used in other namespaces, including t (:require [mount.core :refer [defstate]] [app.config :refer [app-config]])) -(defstate conn :start (create-connection app-config)) +(defstate conn :start #(create-connection app-config)) ``` [here](https://github.com/tolitius/mount/blob/master/test/app/nyse.clj) @@ -310,9 +318,9 @@ and some other use cases. In additiong to `start` / `stop` functions, a state can also have `resume` and, if needed, `suspend` ones: ```clojure -(defstate web-server :start (start-server ...) - :resume (resume-server ...) - :stop (stop-server ...)) +(defstate web-server :start #(start-server ...) + :resume #(resume-server ...) + :stop #(stop-server ...)) ``` diff --git a/doc/differences-from-component.md b/doc/differences-from-component.md index 93f678e..28b0ba2 100644 --- a/doc/differences-from-component.md +++ b/doc/differences-from-component.md @@ -137,8 +137,8 @@ Depending on the number of application components the "extra" size may vary. Mount is pretty much: ```clojure -(defstate name :start (fn) - :stop (fn)) +(defstate name :start fn + :stop fn) ``` no "ceremony". diff --git a/doc/intro.md b/doc/intro.md deleted file mode 100644 index f1e987b..0000000 --- a/doc/intro.md +++ /dev/null @@ -1,3 +0,0 @@ -# Introduction to statuo - -TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) diff --git a/doc/runtime-arguments.md b/doc/runtime-arguments.md index d226cbb..b4d206b 100644 --- a/doc/runtime-arguments.md +++ b/doc/runtime-arguments.md @@ -63,8 +63,8 @@ For the example sake the app reads arguments in two places: * [inside](https://github.com/tolitius/mount/blob/with-args/test/app/nyse.clj#L17) a `defstate` ```clojure -(defstate conn :start (new-connection (mount/args)) - :stop (disconnect (mount/args) conn)) +(defstate conn :start #(new-connection (mount/args)) + :stop #(disconnect (mount/args) conn)) ``` * and from "any" [other place](https://github.com/tolitius/mount/blob/with-args/test/app/config.clj#L8) within a function: @@ -84,13 +84,13 @@ In order to demo all of the above, we'll build an uberjar: ```bash $ lein do clean, uberjar ... -Created .. mount/target/mount-0.2.0-SNAPSHOT-standalone.jar +Created .. mount/target/mount-0.1.5-SNAPSHOT-standalone.jar ``` Since we have a default for a Datomic URI, it'll work with no arguments: ```bash -$ java -jar target/mount-0.2.0-SNAPSHOT-standalone.jar +$ java -jar target/mount-0.1.5-SNAPSHOT-standalone.jar 22:12:03.290 [main] INFO mount - >> starting.. app-config 22:12:03.293 [main] INFO mount - >> starting.. conn @@ -101,7 +101,7 @@ $ java -jar target/mount-0.2.0-SNAPSHOT-standalone.jar Now let's ask it to help us: ```bash -$ java -jar target/mount-0.2.0-SNAPSHOT-standalone.jar --help +$ java -jar target/mount-0.1.5-SNAPSHOT-standalone.jar --help 22:13:48.798 [main] INFO mount - >> starting.. app-config 22:13:48.799 [main] INFO app.config - @@ -116,7 +116,7 @@ this is a sample mount app to demo how to pass and read runtime arguments And finally let's connect to the Single Malt Database. It's Friday.. ```bash -$ java -jar target/mount-0.2.0-SNAPSHOT-standalone.jar -d datomic:mem://single-malt-database +$ java -jar target/mount-0.1.5-SNAPSHOT-standalone.jar -d datomic:mem://single-malt-database 22:16:10.733 [main] INFO mount - >> starting.. app-config 22:16:10.737 [main] INFO mount - >> starting.. conn diff --git a/doc/uberjar.md b/doc/uberjar.md index d0d9444..40b2012 100644 --- a/doc/uberjar.md +++ b/doc/uberjar.md @@ -38,15 +38,14 @@ where `nyse-app` is _the_ app. It has the usual routes: and the reloadable state: ```clojure -(defn start-nyse [] - (create-nyse-schema) ;; creating schema (usually done long before the app is started..) +(defn start-nyse [{:keys [www]}] (-> (routes mount-example-routes) (handler/site) (run-jetty {:join? false - :port (get-in app-config [:www :port])}))) + :port (:port www)}))) -(defstate nyse-app :start (start-nyse) - :stop (.stop nyse-app)) ;; it's a "org.eclipse.jetty.server.Server" at this point +(defstate nyse-app :start #(start-nyse app-config) + :stop #(.stop nyse-app)) ;; it's a "org.eclipse.jetty.server.Server" at this point ``` In order not to block, and being reloadable, the Jetty server is started in the "`:join? false`" mode which starts the server, diff --git a/src/mount/core.clj b/src/mount/core.clj index 64e43e4..a26c0a0 100644 --- a/src/mount/core.clj +++ b/src/mount/core.clj @@ -35,16 +35,16 @@ (validate lifecycle) (let [s-meta (cond-> {:mount-state mount-state :order (make-state-seq state) - :start `(fn [] (~@start)) + :start `(fn [] ~start) :started? false} - stop (assoc :stop `(fn [] (~@stop))) - suspend (assoc :suspend `(fn [] (~@suspend))) - resume (assoc :resume `(fn [] (~@resume))))] + stop (assoc :stop `(fn [] ~stop)) + suspend (assoc :suspend `(fn [] ~suspend)) + resume (assoc :resume `(fn [] ~resume)))] `(defonce ~(with-meta state (merge (meta state) s-meta)) (NotStartedState. ~(str state)))))) (defn- record! [{:keys [ns name]} f done] - (let [state (f)] + (let [state (trampoline f)] (swap! done conj (ns-resolve ns name)) state)) diff --git a/test/app/app.clj b/test/app/app.clj index f529c95..037269f 100644 --- a/test/app/app.clj +++ b/test/app/app.clj @@ -11,8 +11,8 @@ (start-server :bind host :port port)) ;; nREPL is just another simple state -(defstate nrepl :start (start-nrepl (:nrepl app-config)) - :stop (stop-server nrepl)) +(defstate nrepl :start #(start-nrepl (:nrepl app-config)) + :stop #(stop-server nrepl)) ;; datomic schema (defn create-schema [conn] diff --git a/test/app/config.clj b/test/app/config.clj index 1e18a59..74303a5 100644 --- a/test/app/config.clj +++ b/test/app/config.clj @@ -10,4 +10,4 @@ edn/read-string)) (defstate app-config - :start (load-config "test/resources/config.edn")) + :start #(load-config "test/resources/config.edn")) diff --git a/test/app/nyse.clj b/test/app/nyse.clj index 649dfad..7de455f 100644 --- a/test/app/nyse.clj +++ b/test/app/nyse.clj @@ -17,5 +17,5 @@ (.release conn) ;; usually it's not released, here just to illustrate the access to connection on (stop) (d/delete-database uri))) -(defstate conn :start (new-connection app-config) - :stop (disconnect app-config conn)) +(defstate conn :start #(new-connection app-config) + :stop #(disconnect app-config conn)) diff --git a/test/check/parts_test.clj b/test/check/parts_test.clj index a8c506f..53f4b38 100644 --- a/test/check/parts_test.clj +++ b/test/check/parts_test.clj @@ -3,7 +3,7 @@ [app.nyse :refer [conn]] [clojure.test :refer :all])) -(defstate should-not-start :start (constantly 42)) +(defstate should-not-start :start #(constantly 42)) (defn with-parts [f] (m/start #'app.config/app-config #'app.nyse/conn) diff --git a/test/check/start_with_test.clj b/test/check/start_with_test.clj index b5fe507..5256f70 100644 --- a/test/check/start_with_test.clj +++ b/test/check/start_with_test.clj @@ -5,10 +5,10 @@ [app :refer [nrepl]] [clojure.test :refer :all])) -(defstate test-conn :start (long 42) - :stop (constantly 0)) +(defstate test-conn :start 42 + :stop #(constantly 0)) -(defstate test-nrepl :start (vector)) +(defstate test-nrepl :start vector) (deftest start-with diff --git a/test/check/suspend_resume_test.clj b/test/check/suspend_resume_test.clj index a4eb724..218d288 100644 --- a/test/check/suspend_resume_test.clj +++ b/test/check/suspend_resume_test.clj @@ -15,15 +15,15 @@ (defn suspend [s] (koncat s :suspended)) (defn resume [s] (koncat s :resumed)) -(defstate web-server :start (start :w) - :stop (stop :w) - :suspend (suspend :w) - :resume (resume :w)) +(defstate web-server :start #(start :w) + :stop #(stop :w) + :suspend #(suspend :w) + :resume #(resume :w)) -(defstate q-listener :start (start :q) - :stop (stop :q) - :suspend (suspend :q) - :resume (resume :q)) +(defstate q-listener :start #(start :q) + :stop #(stop :q) + :suspend #(suspend :q) + :resume #(resume :q)) (deftest suspendable From 865c4f278bd9d535a6ef3b830d2379b237a38047 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 27 Nov 2015 13:03:58 -0500 Subject: [PATCH 02/16] [docs]: adding 0.1.5 build status --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index be8e581..e1990a1 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt module | branch | status ----------|----------|---------- mount | `master` | [![Circle CI](https://circleci.com/gh/tolitius/mount/tree/master.png?style=svg)](https://circleci.com/gh/tolitius/mount/tree/master) + mount | `0.1.5` | [![Circle CI](https://circleci.com/gh/tolitius/mount/tree/master.png?style=svg)](https://circleci.com/gh/tolitius/mount/tree/0.1.5) [![Clojars Project](http://clojars.org/mount/latest-version.svg)](http://clojars.org/mount) @@ -18,6 +19,7 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt - [Differences from Component](#differences-from-component) - [How](#how) - [Creating State](#creating-state) + - [Value of Values](#value-of-values) - [Using State](#using-state) - [Dependencies](#dependencies) - [Talking States](#talking-states) @@ -103,7 +105,7 @@ dev=> conn #### Value of values -Lifecycle functions start/stop/suspend/resume can take both functions and values. This is valuable and also works: +Lifecycle functions start/stop/suspend/resume can take both functions and values. This is "valuable" and also works: ```clojure (mount/defstate answer-to-the-ultimate-question-of-life-the-universe-and-everything :start 42) @@ -318,9 +320,9 @@ and some other use cases. In additiong to `start` / `stop` functions, a state can also have `resume` and, if needed, `suspend` ones: ```clojure -(defstate web-server :start #(start-server ...) - :resume #(resume-server ...) - :stop #(stop-server ...)) +(defstate web-server :start start-server + :resume resume-server + :stop stop-server) ``` From 339d79e50803800b9a59f6ea120bfddf638d2a80 Mon Sep 17 00:00:00 2001 From: anatoly Date: Sun, 29 Nov 2015 13:27:26 -0500 Subject: [PATCH 03/16] [#19]: consolidating status (improvement from @aroemers) --- dev/dev.clj | 1 + src/mount/core.clj | 48 ++++++++++++++---------------- test/check/suspend_resume_test.clj | 30 +++++++++++++++---- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/dev/dev.clj b/dev/dev.clj index 7b23b0a..4da3e15 100644 --- a/dev/dev.clj +++ b/dev/dev.clj @@ -22,6 +22,7 @@ (mount/start-without #'check.start-with-test/test-conn #'check.start-with-test/test-nrepl #'check.parts-test/should-not-start + #'check.suspend-resume-test/randomizer #'check.suspend-resume-test/web-server #'check.suspend-resume-test/q-listener)) ;; example on how to start app without certain states diff --git a/src/mount/core.clj b/src/mount/core.clj index a26c0a0..44f4406 100644 --- a/src/mount/core.clj +++ b/src/mount/core.clj @@ -36,7 +36,7 @@ (let [s-meta (cond-> {:mount-state mount-state :order (make-state-seq state) :start `(fn [] ~start) - :started? false} + :status #{:stopped}} stop (assoc :stop `(fn [] ~stop)) suspend (assoc :suspend `(fn [] ~suspend)) resume (assoc :resume `(fn [] ~resume)))] @@ -48,44 +48,44 @@ (swap! done conj (ns-resolve ns name)) state)) -(defn- up [var {:keys [ns name start started? resume suspended?] :as state} done] - (when-not started? - (let [s (try (if suspended? +(defn- up [var {:keys [ns name start resume status] :as state} done] + (when-not (:started status) + (let [s (try (if (:suspended status) (record! state resume done) (record! state start done)) (catch Throwable t (throw (RuntimeException. (str "could not start [" name "] due to") t))))] (intern ns (symbol name) s) - (alter-meta! var assoc :started? true :suspended? false)))) + (alter-meta! var assoc :status #{:started})))) -(defn- down [var {:keys [ns name stop started? suspended?] :as state} done] - (when (or started? suspended?) +(defn- down [var {:keys [ns name stop status] :as state} done] + (when (some status #{:started :suspended}) (when stop (try (record! state stop done) (catch Throwable t (throw (RuntimeException. (str "could not stop [" name "] due to") t))))) (intern ns (symbol name) (NotStartedState. name)) ;; (!) if a state does not have :stop when _should_ this might leak - (alter-meta! var assoc :started? false :suspended? false))) + (alter-meta! var assoc :status #{:stopped}))) -(defn- sigstop [var {:keys [ns name started? suspend resume] :as state} done] - (when (and started? resume) ;; can't have suspend without resume, but the reverse is possible - (when suspend ;; don't suspend if there is only resume function (just mark it :suspended?) +(defn- sigstop [var {:keys [ns name suspend resume status] :as state} done] + (when (and (:started status) resume) ;; can't have suspend without resume, but the reverse is possible + (when suspend ;; don't suspend if there is only resume function (just mark it :suspended?) (let [s (try (record! state suspend done) (catch Throwable t (throw (RuntimeException. (str "could not suspend [" name "] due to") t))))] (intern ns (symbol name) s))) - (alter-meta! var assoc :started? false :suspended? true))) + (alter-meta! var assoc :status #{:suspended}))) -(defn- sigcont [var {:keys [ns name start started? resume suspended?] :as state} done] +(defn- sigcont [var {:keys [ns name start resume status] :as state} done] (when (instance? NotStartedState var) (throw (RuntimeException. (str "could not resume [" name "] since it is stoppped (i.e. not suspended)")))) - (when suspended? + (when (:suspended status) (let [s (try (record! state resume done) (catch Throwable t (throw (RuntimeException. (str "could not resume [" name "] due to") t))))] (intern ns (symbol name) s) - (alter-meta! var assoc :started? true :suspended? false)))) + (alter-meta! var assoc :status #{:started})))) ;;TODO args might need more thinking (defn args [] @-args) @@ -110,7 +110,7 @@ (defn states-with-deps [] (let [all (find-all-states)] (->> (map (comp #(add-deps % all) - #(select-keys % [:name :order :ns :started? :suspended?]) + #(select-keys % [:name :order :ns :status]) meta) all) (sort-by :order)))) @@ -129,9 +129,9 @@ however other keys of 'state' (such as :ns,:name,:order) should not be overriden" ([state sub] (merge-lifecycles state nil sub)) - ([state origin {:keys [start stop suspend resume suspended?]}] + ([state origin {:keys [start stop suspend resume status]}] (assoc state :origin origin - :suspended? suspended? + :status status :start start :stop stop :suspend suspend :resume resume))) (defn- rollback! [state] @@ -140,7 +140,7 @@ (alter-meta! state #(merge-lifecycles % origin))))) (defn- substitute! [state with] - (let [lifecycle-fns #(select-keys % [:start :stop :suspend :resume :suspended?]) + (let [lifecycle-fns #(select-keys % [:start :stop :suspend :resume :status]) origin (meta state) sub (meta with)] (alter-meta! with assoc :sub? true) @@ -148,8 +148,7 @@ (defn- unsub [state] (when (-> (meta state) :sub?) - (alter-meta! state assoc :sub? nil - :started false))) + (alter-meta! state dissoc :sub?))) (defn- all-without-subs [] (remove (comp :sub? meta) (find-all-states))) @@ -167,11 +166,8 @@ (defn stop-except [& states] (let [all (set (find-all-states)) - states (remove (set states) all) - _ (dorun (map unsub states)) ;; unmark substitutions marked by "start-with" - stopped (bring states down >)] - (dorun (map rollback! states)) ;; restore to origin from "start-with" - {:stopped stopped})) + states (remove (set states) all)] + (apply stop states))) (defn start-with-args [xs & states] (reset! -args xs) diff --git a/test/check/suspend_resume_test.clj b/test/check/suspend_resume_test.clj index 218d288..6e2b9a6 100644 --- a/test/check/suspend_resume_test.clj +++ b/test/check/suspend_resume_test.clj @@ -25,9 +25,9 @@ :suspend #(suspend :q) :resume #(resume :q)) -(deftest suspendable +(defstate randomizer :start #(rand-int 42)) - ;; lifecycle +(deftest suspendable-lifecycle (testing "should suspend _only suspendable_ states that are currently started" (let [_ (mount/start) @@ -66,9 +66,10 @@ (is (instance? mount.core.NotStartedState app-config)) (is (instance? mount.core.NotStartedState nrepl)) (is (instance? mount.core.NotStartedState conn)) - (is (instance? mount.core.NotStartedState web-server)))) + (is (instance? mount.core.NotStartedState web-server))))) - ;; start-with + +(deftest suspendable-start-with (testing "when replacing a non suspendable state with a suspendable one, the later should be able to suspend/resume, @@ -85,7 +86,24 @@ (mount/stop))) ;; this is a messy use case, but can still happen especially at REPL time - (testing "when replacing a suspended state with a non suspendable one, + ;; it also messy, because usually :stop function refers the _original_ state by name (i.e. #(disconnect conn)) + ;; (unchanged/not substituted in its lexical scope), and original state won't be started + (testing "when replacing a suspendable state with a non suspendable one, + the later should not be suspendable, + the original should still be suspendable and preserve its lifecycle fns after the rollback/stop" + (let [_ (mount/start-with {#'check.suspend-resume-test/web-server #'check.suspend-resume-test/randomizer}) + _ (mount/suspend)] + (is (integer? web-server)) + (is (instance? mount.core.NotStartedState randomizer)) + (mount/stop) + (mount/start) + (mount/suspend) + (is (integer? randomizer)) + (is (= web-server :w-suspended)) + (mount/stop))) + + ;; this is a messy use case, but can still happen especially at REPL time + (testing "when replacing a suspended state with a non suspendable started one, the later should not be suspendable, the original should still be suspended and preserve its lifecycle fns after the rollback/stop" (let [_ (mount/start) @@ -93,7 +111,7 @@ _ (mount/start-with {#'check.suspend-resume-test/web-server #'app.nyse/conn}) ;; TODO: good to WARN on started states during "start-with" _ (mount/suspend)] (is (instance? datomic.peer.LocalConnection conn)) - (is (instance? datomic.peer.LocalConnection web-server)) + (is (= web-server :w-suspended)) ;; since the "conn" does not have a resume method, so web-server was not started (mount/stop) (mount/start) (mount/suspend) From 6d2a8d3eec15e445de10524e6007eb489cefa1d6 Mon Sep 17 00:00:00 2001 From: anatoly Date: Sun, 29 Nov 2015 22:36:31 -0500 Subject: [PATCH 04/16] removing session id --- src/mount/core.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mount/core.clj b/src/mount/core.clj index 44f4406..b5feef8 100644 --- a/src/mount/core.clj +++ b/src/mount/core.clj @@ -1,7 +1,6 @@ (ns mount.core (:require [clojure.tools.macro :as macro])) -;; (defonce ^:private session-id (System/currentTimeMillis)) (defonce ^:private mount-state 42) (defonce ^:private -args (atom :no-args)) ;; mostly for command line args and external files (defonce ^:private state-seq (atom 0)) From 8e5926ac756a769ef3ecc563808da60fef1b1199 Mon Sep 17 00:00:00 2001 From: anatoly Date: Sun, 29 Nov 2015 23:36:26 -0500 Subject: [PATCH 05/16] adding ns to state order to avoid collisions --- src/mount/core.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mount/core.clj b/src/mount/core.clj index b5feef8..ba777f6 100644 --- a/src/mount/core.clj +++ b/src/mount/core.clj @@ -28,12 +28,15 @@ (and suspend (not resume)) (throw (IllegalArgumentException. "suspendable state should have a resume function (i.e. missing :resume fn)")))) +(defn- with-ns [ns name] + (str ns "/" name)) + (defmacro defstate [state & body] (let [[state params] (macro/name-with-attributes state body) {:keys [start stop suspend resume] :as lifecycle} (apply hash-map params)] (validate lifecycle) (let [s-meta (cond-> {:mount-state mount-state - :order (make-state-seq state) + :order (make-state-seq (with-ns *ns* state)) :start `(fn [] ~start) :status #{:stopped}} stop (assoc :stop `(fn [] ~stop)) From ead12a7e883901338459fb4445542f1c4bc27af6 Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 30 Nov 2015 13:00:45 -0500 Subject: [PATCH 06/16] [#18]: cleaning up stale states stale running instances are going into limbo on redefs caused by recompiling namespaces with defstates, tn/refresh, tn/refresh-all, etc. cleaning those stale instances with their stop functions before redefining their states --- src/mount/core.clj | 26 ++++++++++++++++++++++-- test/check/cleanup_dirty_states_test.clj | 13 ++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 test/check/cleanup_dirty_states_test.clj diff --git a/src/mount/core.clj b/src/mount/core.clj index ba777f6..19125be 100644 --- a/src/mount/core.clj +++ b/src/mount/core.clj @@ -5,6 +5,7 @@ (defonce ^:private -args (atom :no-args)) ;; mostly for command line args and external files (defonce ^:private state-seq (atom 0)) (defonce ^:private state-order (atom {})) +(defonce ^:private running (atom {})) ;; to clean dirty states on redefs ;; supporting tools.namespace: (disable-reload!) (alter-meta! *ns* assoc ::load false) ;; to exclude the dependency @@ -31,15 +32,34 @@ (defn- with-ns [ns name] (str ns "/" name)) +(defn- pounded? [f] + (let [pound "(fn* [] "] ;;TODO: think of a better (i.e. typed) way to distinguish #(f params) from (fn [params] (...))) + (.startsWith (str f) pound))) + +(defn- unpound [f] + (if (pounded? f) + (nth f 2) ;; magic 2 is to get the body => ["fn*" "[]" "(fn body)"] + f)) + +(defn- cleanup-if-dirty + "in case a namespace is recompiled without calling (mount/stop), + a running state instance will still be running. + this function stops this 'lost' state instance. + it is meant to be called by defstate before defining a new state" + [state] + (when-let [stop (@running state)] + (stop))) + (defmacro defstate [state & body] (let [[state params] (macro/name-with-attributes state body) {:keys [start stop suspend resume] :as lifecycle} (apply hash-map params)] (validate lifecycle) + (cleanup-if-dirty (with-ns *ns* state)) (let [s-meta (cond-> {:mount-state mount-state :order (make-state-seq (with-ns *ns* state)) :start `(fn [] ~start) :status #{:stopped}} - stop (assoc :stop `(fn [] ~stop)) + stop (assoc :stop `(fn [] ~(unpound stop))) suspend (assoc :suspend `(fn [] ~suspend)) resume (assoc :resume `(fn [] ~resume)))] `(defonce ~(with-meta state (merge (meta state) s-meta)) @@ -50,7 +70,7 @@ (swap! done conj (ns-resolve ns name)) state)) -(defn- up [var {:keys [ns name start resume status] :as state} done] +(defn- up [var {:keys [ns name start stop resume status] :as state} done] (when-not (:started status) (let [s (try (if (:suspended status) (record! state resume done) @@ -58,6 +78,7 @@ (catch Throwable t (throw (RuntimeException. (str "could not start [" name "] due to") t))))] (intern ns (symbol name) s) + (swap! running assoc (with-ns ns name) stop) (alter-meta! var assoc :status #{:started})))) (defn- down [var {:keys [ns name stop status] :as state} done] @@ -68,6 +89,7 @@ (catch Throwable t (throw (RuntimeException. (str "could not stop [" name "] due to") t))))) (intern ns (symbol name) (NotStartedState. name)) ;; (!) if a state does not have :stop when _should_ this might leak + (swap! running dissoc (with-ns ns name)) (alter-meta! var assoc :status #{:stopped}))) (defn- sigstop [var {:keys [ns name suspend resume status] :as state} done] diff --git a/test/check/cleanup_dirty_states_test.clj b/test/check/cleanup_dirty_states_test.clj new file mode 100644 index 0000000..73fd0a8 --- /dev/null +++ b/test/check/cleanup_dirty_states_test.clj @@ -0,0 +1,13 @@ +(ns check.cleanup_dirty_states_test + (:require [mount.core :as mount] + [app] + [clojure.test :refer :all])) + +(deftest cleanup-dirty-states + (let [_ (mount/start)] + (is (not (.isClosed (:server-socket app/nrepl)))) + (require 'app :reload) + (mount/start) ;; should not result in "BindException Address already in use" since the clean up will stop the previous instance + (is (not (.isClosed (:server-socket app/nrepl)))) + (mount/stop) + (is (instance? mount.core.NotStartedState app/nrepl)))) From 0e4e5073430899d6ef3749259923fcca1b6b42df Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 30 Nov 2015 13:12:44 -0500 Subject: [PATCH 07/16] unpounding lifecycle fns --- README.md | 6 +++--- doc/runtime-arguments.md | 4 ++-- doc/uberjar.md | 4 ++-- test/app/app.clj | 4 ++-- test/app/config.clj | 2 +- test/app/nyse.clj | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e1990a1..8e78c8d 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ In case this state needs to be cleaned / destryed between reloads, there is also ```clojure (defstate conn :start create-conn - :stop #(disconnect conn)) + :stop (disconnect conn)) ``` That is pretty much it. But wait, there is more.. this state is _a top level being_, which means it can be simply @@ -146,7 +146,7 @@ There are of course direct dependecies that `mount` respects: (:require [mount.core :refer [defstate]])) (defstate app-config - :start #(load-config "test/resources/config.edn")) + :start (load-config "test/resources/config.edn")) ``` this `app-config`, being top level, can be used in other namespaces, including the ones that create states: @@ -156,7 +156,7 @@ this `app-config`, being top level, can be used in other namespaces, including t (:require [mount.core :refer [defstate]] [app.config :refer [app-config]])) -(defstate conn :start #(create-connection app-config)) +(defstate conn :start (create-connection app-config)) ``` [here](https://github.com/tolitius/mount/blob/master/test/app/nyse.clj) diff --git a/doc/runtime-arguments.md b/doc/runtime-arguments.md index b4d206b..93dbd14 100644 --- a/doc/runtime-arguments.md +++ b/doc/runtime-arguments.md @@ -63,8 +63,8 @@ For the example sake the app reads arguments in two places: * [inside](https://github.com/tolitius/mount/blob/with-args/test/app/nyse.clj#L17) a `defstate` ```clojure -(defstate conn :start #(new-connection (mount/args)) - :stop #(disconnect (mount/args) conn)) +(defstate conn :start (new-connection (mount/args)) + :stop (disconnect (mount/args) conn)) ``` * and from "any" [other place](https://github.com/tolitius/mount/blob/with-args/test/app/config.clj#L8) within a function: diff --git a/doc/uberjar.md b/doc/uberjar.md index 40b2012..25db9f4 100644 --- a/doc/uberjar.md +++ b/doc/uberjar.md @@ -44,8 +44,8 @@ and the reloadable state: (run-jetty {:join? false :port (:port www)}))) -(defstate nyse-app :start #(start-nyse app-config) - :stop #(.stop nyse-app)) ;; it's a "org.eclipse.jetty.server.Server" at this point +(defstate nyse-app :start (start-nyse app-config) + :stop (.stop nyse-app)) ;; it's a "org.eclipse.jetty.server.Server" at this point ``` In order not to block, and being reloadable, the Jetty server is started in the "`:join? false`" mode which starts the server, diff --git a/test/app/app.clj b/test/app/app.clj index 037269f..f529c95 100644 --- a/test/app/app.clj +++ b/test/app/app.clj @@ -11,8 +11,8 @@ (start-server :bind host :port port)) ;; nREPL is just another simple state -(defstate nrepl :start #(start-nrepl (:nrepl app-config)) - :stop #(stop-server nrepl)) +(defstate nrepl :start (start-nrepl (:nrepl app-config)) + :stop (stop-server nrepl)) ;; datomic schema (defn create-schema [conn] diff --git a/test/app/config.clj b/test/app/config.clj index 74303a5..1e18a59 100644 --- a/test/app/config.clj +++ b/test/app/config.clj @@ -10,4 +10,4 @@ edn/read-string)) (defstate app-config - :start #(load-config "test/resources/config.edn")) + :start (load-config "test/resources/config.edn")) diff --git a/test/app/nyse.clj b/test/app/nyse.clj index 7de455f..649dfad 100644 --- a/test/app/nyse.clj +++ b/test/app/nyse.clj @@ -17,5 +17,5 @@ (.release conn) ;; usually it's not released, here just to illustrate the access to connection on (stop) (d/delete-database uri))) -(defstate conn :start #(new-connection app-config) - :stop #(disconnect app-config conn)) +(defstate conn :start (new-connection app-config) + :stop (disconnect app-config conn)) From db510832be2c4009d5c7d9fda721ef6c0674286e Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 30 Nov 2015 10:28:30 -0500 Subject: [PATCH 08/16] [docs]: updating branch build link (thanks @xhh) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e78c8d..2989373 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt module | branch | status ----------|----------|---------- mount | `master` | [![Circle CI](https://circleci.com/gh/tolitius/mount/tree/master.png?style=svg)](https://circleci.com/gh/tolitius/mount/tree/master) - mount | `0.1.5` | [![Circle CI](https://circleci.com/gh/tolitius/mount/tree/master.png?style=svg)](https://circleci.com/gh/tolitius/mount/tree/0.1.5) + mount | `0.1.5` | [![Circle CI](https://circleci.com/gh/tolitius/mount/tree/0.1.5.png?style=svg)](https://circleci.com/gh/tolitius/mount/tree/0.1.5) [![Clojars Project](http://clojars.org/mount/latest-version.svg)](http://clojars.org/mount) From 20e230b5dd31a718478a8a1ddc9e608bf359af72 Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 30 Nov 2015 17:23:03 -0500 Subject: [PATCH 09/16] [#20, #21]: removing trampoline to take functions as values --- src/mount/core.clj | 2 +- test/check/fun_with_values_test.clj | 39 +++++++++++++++++++++++++++++ test/check/start_with_test.clj | 3 ++- test/check/suspend_resume_test.clj | 20 ++++++++------- 4 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 test/check/fun_with_values_test.clj diff --git a/src/mount/core.clj b/src/mount/core.clj index 19125be..6ca57c4 100644 --- a/src/mount/core.clj +++ b/src/mount/core.clj @@ -66,7 +66,7 @@ (NotStartedState. ~(str state)))))) (defn- record! [{:keys [ns name]} f done] - (let [state (trampoline f)] + (let [state (f)] (swap! done conj (ns-resolve ns name)) state)) diff --git a/test/check/fun_with_values_test.clj b/test/check/fun_with_values_test.clj new file mode 100644 index 0000000..765793d --- /dev/null +++ b/test/check/fun_with_values_test.clj @@ -0,0 +1,39 @@ +(ns check.fun-with-values-test + (:require [mount.core :as mount :refer [defstate]] + [clojure.test :refer :all])) + +(defn f [n] + (fn [m] + (+ n m))) + +(defn g [a b] + (+ a b)) + +(defstate scalar :start 42) +(defstate fun :start #(inc 41)) +(defstate with-fun :start (inc 41)) +(defstate with-partial :start (partial g 41)) +(defstate f-in-f :start (f 41)) +(defstate f-value :start (g 41 1)) + +(defn with-fun-and-values [f] + (mount/start #'check.fun-with-values-test/scalar + #'check.fun-with-values-test/fun + #'check.fun-with-values-test/with-fun + #'check.fun-with-values-test/with-partial + #'check.fun-with-values-test/f-in-f + #'check.fun-with-values-test/f-value) + (f) + (mount/stop)) + +(use-fixtures :each with-fun-and-values) + +(deftest fun-with-values + (is (= scalar 42)) + (is (= (fun) 42)) + (is (= with-fun 42)) + (is (= (with-partial 1) 42)) + (is (= (f-in-f 1) 42)) + (is (= f-value 42))) + +(run-tests) diff --git a/test/check/start_with_test.clj b/test/check/start_with_test.clj index 5256f70..8e362c0 100644 --- a/test/check/start_with_test.clj +++ b/test/check/start_with_test.clj @@ -8,7 +8,7 @@ (defstate test-conn :start 42 :stop #(constantly 0)) -(defstate test-nrepl :start vector) +(defstate test-nrepl :start []) (deftest start-with @@ -44,3 +44,4 @@ (is (instance? mount.core.NotStartedState test-conn)) (is (instance? mount.core.NotStartedState test-nrepl)) (mount/stop)))) + diff --git a/test/check/suspend_resume_test.clj b/test/check/suspend_resume_test.clj index 6e2b9a6..e5d022f 100644 --- a/test/check/suspend_resume_test.clj +++ b/test/check/suspend_resume_test.clj @@ -15,17 +15,17 @@ (defn suspend [s] (koncat s :suspended)) (defn resume [s] (koncat s :resumed)) -(defstate web-server :start #(start :w) - :stop #(stop :w) - :suspend #(suspend :w) - :resume #(resume :w)) +(defstate web-server :start (start :w) + :stop (stop :w) + :suspend (suspend :w) + :resume (resume :w)) -(defstate q-listener :start #(start :q) - :stop #(stop :q) - :suspend #(suspend :q) - :resume #(resume :q)) +(defstate q-listener :start (start :q) + :stop (stop :q) + :suspend (suspend :q) + :resume (resume :q)) -(defstate randomizer :start #(rand-int 42)) +(defstate randomizer :start (rand-int 42)) (deftest suspendable-lifecycle @@ -139,3 +139,5 @@ (is (= q-listener :q-suspended)) (is (= web-server :w-suspended)) (mount/stop)))) + +(run-tests) From f2f4c1f3ea3819d07a60c74ace8406396f80b09d Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 30 Nov 2015 17:33:33 -0500 Subject: [PATCH 10/16] removing 'replled' (run-tests) --- test/check/fun_with_values_test.clj | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/check/fun_with_values_test.clj b/test/check/fun_with_values_test.clj index 765793d..80a8e64 100644 --- a/test/check/fun_with_values_test.clj +++ b/test/check/fun_with_values_test.clj @@ -35,5 +35,3 @@ (is (= (with-partial 1) 42)) (is (= (f-in-f 1) 42)) (is (= f-value 42))) - -(run-tests) From 325eecf0c9251f26d943821255a4a39c65950ed2 Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 30 Nov 2015 21:13:30 -0500 Subject: [PATCH 11/16] [#21]: test for lifecycle private fn with args --- test/check/fun_with_values_test.clj | 9 +++++++++ test/check/private_fun_test.clj | 14 ++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 test/check/private_fun_test.clj diff --git a/test/check/fun_with_values_test.clj b/test/check/fun_with_values_test.clj index 80a8e64..96eecf1 100644 --- a/test/check/fun_with_values_test.clj +++ b/test/check/fun_with_values_test.clj @@ -9,12 +9,17 @@ (defn g [a b] (+ a b)) +(defn- pf [n] + (+ 41 n)) + (defstate scalar :start 42) (defstate fun :start #(inc 41)) (defstate with-fun :start (inc 41)) (defstate with-partial :start (partial g 41)) (defstate f-in-f :start (f 41)) +(defstate f-args :start g) (defstate f-value :start (g 41 1)) +(defstate private-f :start pf) (defn with-fun-and-values [f] (mount/start #'check.fun-with-values-test/scalar @@ -22,6 +27,8 @@ #'check.fun-with-values-test/with-fun #'check.fun-with-values-test/with-partial #'check.fun-with-values-test/f-in-f + #'check.fun-with-values-test/f-args + #'check.fun-with-values-test/private-f #'check.fun-with-values-test/f-value) (f) (mount/stop)) @@ -34,4 +41,6 @@ (is (= with-fun 42)) (is (= (with-partial 1) 42)) (is (= (f-in-f 1) 42)) + (is (= (f-args 41 1) 42)) + (is (= (private-f 1) 42)) (is (= f-value 42))) diff --git a/test/check/private_fun_test.clj b/test/check/private_fun_test.clj new file mode 100644 index 0000000..2f2c413 --- /dev/null +++ b/test/check/private_fun_test.clj @@ -0,0 +1,14 @@ +(ns check.private-fun-test + (:require [mount.core :as mount :refer [defstate]] + [check.fun-with-values-test :refer [private-f]] + [clojure.test :refer :all])) + +(defn with-fun-and-values [f] + (mount/start #'check.fun-with-values-test/private-f) + (f) + (mount/stop)) + +(use-fixtures :each with-fun-and-values) + +(deftest fun-with-valuesj + (is (= (private-f 1) 42))) From 958d7e345c9ad0983d30d44af9d852fe8d2d0bcc Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 30 Nov 2015 21:19:58 -0500 Subject: [PATCH 12/16] dev.clj: replacing start-without with start --- dev/dev.clj | 9 +++------ test/check/suspend_resume_test.clj | 2 -- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/dev/dev.clj b/dev/dev.clj index 4da3e15..e74d05b 100644 --- a/dev/dev.clj +++ b/dev/dev.clj @@ -19,12 +19,9 @@ (defn start [] (with-logging-status) - (mount/start-without #'check.start-with-test/test-conn - #'check.start-with-test/test-nrepl - #'check.parts-test/should-not-start - #'check.suspend-resume-test/randomizer - #'check.suspend-resume-test/web-server - #'check.suspend-resume-test/q-listener)) ;; example on how to start app without certain states + (mount/start #'app.config/app-config + #'app.nyse/conn + #'app/nrepl)) ;; example on how to start app with certain states (defn stop [] (mount/stop)) diff --git a/test/check/suspend_resume_test.clj b/test/check/suspend_resume_test.clj index e5d022f..eb37ace 100644 --- a/test/check/suspend_resume_test.clj +++ b/test/check/suspend_resume_test.clj @@ -139,5 +139,3 @@ (is (= q-listener :q-suspended)) (is (= web-server :w-suspended)) (mount/stop)))) - -(run-tests) From f404bb4420d7c202685abe856b93f24eaa6de0c1 Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 30 Nov 2015 22:05:44 -0500 Subject: [PATCH 13/16] [logging]: status vs. started? suspendes?.. --- test/app/utils/logging.clj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/app/utils/logging.clj b/test/app/utils/logging.clj index 7a1307d..dc0216c 100644 --- a/test/app/utils/logging.clj +++ b/test/app/utils/logging.clj @@ -17,13 +17,13 @@ "mount.core$sigcont" :resume :noop))) -(defn whatcha-doing? [{:keys [started? suspended? suspend]} action] +(defn whatcha-doing? [{:keys [status suspend]} action] (case action - :up (if suspended? ">> resuming" - (if-not started? ">> starting")) - :down (if (or started? suspended?) "<< stopping") - :suspend (if (and started? suspend) "<< suspending") - :resume (if suspended? ">> resuming"))) + :up (if (status :suspended) ">> resuming" + (if-not (status :started) ">> starting")) + :down (if (or (status :started) (status :suspended)) "<< stopping") + :suspend (if (and (status :started) suspend) "<< suspending") + :resume (if (status :suspended) ">> resuming"))) (defn log-status [f & args] (let [{:keys [ns name] :as state} (second args) From 31c1501b0ac1997d1fd43c6bc81867787464780a Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 30 Nov 2015 22:36:11 -0500 Subject: [PATCH 14/16] [docs]: expanding on value of values --- README.md | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 2989373..41a6d85 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt - [Differences from Component](#differences-from-component) - [How](#how) - [Creating State](#creating-state) - - [Value of Values](#value-of-values) - [Using State](#using-state) - [Dependencies](#dependencies) - [Talking States](#talking-states) +- [Value of Values](#value-of-values) - [The Importance of Being Reloadable](#the-importance-of-being-reloadable) - [Start and Stop Order](#start-and-stop-order) - [Start and Stop Parts of Application](#start-and-stop-parts-of-application) @@ -103,14 +103,6 @@ dev=> conn #object[datomic.peer.LocalConnection 0x1661a4eb "datomic.peer.LocalConnection@1661a4eb"] ``` -#### Value of values - -Lifecycle functions start/stop/suspend/resume can take both functions and values. This is "valuable" and also works: - -```clojure -(mount/defstate answer-to-the-ultimate-question-of-life-the-universe-and-everything :start 42) -``` - ### Using State For example let's say an `app` needs a connection above. No problem: @@ -162,6 +154,39 @@ this `app-config`, being top level, can be used in other namespaces, including t [here](https://github.com/tolitius/mount/blob/master/test/app/nyse.clj) is an example of a Datomic connection that "depends" on a similar `app-config`. +## Value of values + +Lifecycle functions start/stop/suspend/resume can take both functions and values. This is "valuable" and also works: + +```clojure +(mount/defstate answer-to-the-ultimate-question-of-life-the-universe-and-everything :start 42) +``` + +Besides scalar values, lifecycle functions can take anonymous functions, partial functions, function references, etc.. Here are some examples: + +```clojure +(defn f [n] + (fn [m] + (+ n m))) + +(defn g [a b] + (+ a b)) + +(defn- pf [n] + (+ 41 n)) + +(defstate scalar :start 42) +(defstate fun :start #(inc 41)) +(defstate with-fun :start (inc 41)) +(defstate with-partial :start (partial g 41)) +(defstate f-in-f :start (f 41)) +(defstate f-args :start g) +(defstate f-value :start (g 41 1)) +(defstate private-f :start pf) +``` + +Check out [fun-with-values-test](https://github.com/tolitius/mount/blob/958d7e345c9ad0983d30d44af9d852fe8d2d0bcc/test/check/fun_with_values_test.clj) for more details. + ## The Importance of Being Reloadable `mount` has start and stop functions that will walk all the states created with `defstate` and start / stop them From bf5294edb73208044c52fb9c36176ff3212d1833 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Tue, 1 Dec 2015 00:28:36 -0500 Subject: [PATCH 15/16] [docs value of values]: removing "mount" prefix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41a6d85..20ca43c 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ is an example of a Datomic connection that "depends" on a similar `app-config`. Lifecycle functions start/stop/suspend/resume can take both functions and values. This is "valuable" and also works: ```clojure -(mount/defstate answer-to-the-ultimate-question-of-life-the-universe-and-everything :start 42) +(defstate answer-to-the-ultimate-question-of-life-the-universe-and-everything :start 42) ``` Besides scalar values, lifecycle functions can take anonymous functions, partial functions, function references, etc.. Here are some examples: From 7f720403eaf6caa9be3e855fe65a57da7bb8c712 Mon Sep 17 00:00:00 2001 From: anatoly Date: Tue, 1 Dec 2015 08:46:43 -0500 Subject: [PATCH 16/16] [#21]: examples of "fn" vs. "(fn)" for a lifecycle fns --- README.md | 7 ++++++- test/check/fun_with_values_test.clj | 9 +++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 20ca43c..7a3660b 100644 --- a/README.md +++ b/README.md @@ -175,17 +175,22 @@ Besides scalar values, lifecycle functions can take anonymous functions, partial (defn- pf [n] (+ 41 n)) +(defn fna [] + 42) + (defstate scalar :start 42) (defstate fun :start #(inc 41)) (defstate with-fun :start (inc 41)) (defstate with-partial :start (partial g 41)) (defstate f-in-f :start (f 41)) +(defstate f-no-args-value :start (fna)) +(defstate f-no-args :start fna) (defstate f-args :start g) (defstate f-value :start (g 41 1)) (defstate private-f :start pf) ``` -Check out [fun-with-values-test](https://github.com/tolitius/mount/blob/958d7e345c9ad0983d30d44af9d852fe8d2d0bcc/test/check/fun_with_values_test.clj) for more details. +Check out [fun-with-values-test](https://github.com/tolitius/mount/blob/0.1.5/test/check/fun_with_values_test.clj) for more details. ## The Importance of Being Reloadable diff --git a/test/check/fun_with_values_test.clj b/test/check/fun_with_values_test.clj index 96eecf1..f1f4772 100644 --- a/test/check/fun_with_values_test.clj +++ b/test/check/fun_with_values_test.clj @@ -12,11 +12,16 @@ (defn- pf [n] (+ 41 n)) +(defn fna [] + 42) + (defstate scalar :start 42) (defstate fun :start #(inc 41)) (defstate with-fun :start (inc 41)) (defstate with-partial :start (partial g 41)) (defstate f-in-f :start (f 41)) +(defstate f-no-args-value :start (fna)) +(defstate f-no-args :start fna) (defstate f-args :start g) (defstate f-value :start (g 41 1)) (defstate private-f :start pf) @@ -28,6 +33,8 @@ #'check.fun-with-values-test/with-partial #'check.fun-with-values-test/f-in-f #'check.fun-with-values-test/f-args + #'check.fun-with-values-test/f-no-args-value + #'check.fun-with-values-test/f-no-args #'check.fun-with-values-test/private-f #'check.fun-with-values-test/f-value) (f) @@ -41,6 +48,8 @@ (is (= with-fun 42)) (is (= (with-partial 1) 42)) (is (= (f-in-f 1) 42)) + (is (= f-no-args-value 42)) + (is (= (f-no-args) 42)) (is (= (f-args 41 1) 42)) (is (= (private-f 1) 42)) (is (= f-value 42)))