From 05f7c357122b7ea5c3e3da035cd86a4005c22810 Mon Sep 17 00:00:00 2001 From: anatoly Date: Sun, 13 Dec 2015 21:35:35 -0500 Subject: [PATCH 01/18] on to 0.1.7-SNAPSHOT --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index d316dda..f855e29 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject mount "0.1.6" +(defproject mount "0.1.7-SNAPSHOT" :description "managing Clojure app state since (reset)" :url "https://github.com/tolitius/mount" :license {:name "Eclipse Public License" From d342b43f233fe6499ac89a065e4cd18e007a421b Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 14 Dec 2015 20:41:14 -0500 Subject: [PATCH 02/18] #22: dissoc from running states on recompile --- src/mount/core.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mount/core.cljc b/src/mount/core.cljc index 59da9ec..ac11e27 100644 --- a/src/mount/core.cljc +++ b/src/mount/core.cljc @@ -50,7 +50,8 @@ it is meant to be called by defstate before defining a new state" [state] (when-let [stop (@running state)] - (stop))) + (stop) + (swap! running dissoc state))) #?(:clj (defn current-state [state] From dc5b6c2614d19855f403299d6ca0341df14e011e Mon Sep 17 00:00:00 2001 From: anatoly Date: Tue, 15 Dec 2015 00:27:57 -0500 Subject: [PATCH 03/18] cleaning macros (thx @DomKM) removing reader conditionals from defstate, since this part of the macro is compiled by clojure (not cljs) adding ":require-macros [mount.core :refer [defstate]]" to itself (mount.core) to obviate a need for mount clients to do ":require-macros" --- src/mount/core.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mount/core.cljc b/src/mount/core.cljc index ac11e27..5b54fc4 100644 --- a/src/mount/core.cljc +++ b/src/mount/core.cljc @@ -2,7 +2,8 @@ #?(:clj (:require [mount.tools.macro :refer [on-error throw-runtime] :as macro]) :cljs (:require [mount.tools.macro :as macro] [mount.tools.cljs :as cljs])) - #?(:cljs (:require-macros [mount.tools.macro :refer [on-error throw-runtime]]))) + #?(:cljs (:require-macros [mount.tools.macro :refer [on-error throw-runtime]] + [mount.core :refer [defstate]]))) (defonce ^:private -args (atom :no-args)) ;; mostly for command line args and external files (defonce ^:private state-seq (atom 0)) @@ -130,8 +131,7 @@ (defmacro defstate [state & body] (let [[state params] (macro/name-with-attributes state body) {:keys [start stop suspend resume] :as lifecycle} (apply hash-map params) - state-name (with-ns #?(:clj *ns* - :cljs (cljs/this-ns)) state) ;; on cljs side (cljs.analyzer/*cljs-ns*) may do it, but still might not be good for :advanced + state-name (with-ns *ns* state) order (make-state-seq state-name) sym (str state)] (validate lifecycle) From c1be3c377ea4800a4a053077eeab0f46f408ee6f Mon Sep 17 00:00:00 2001 From: anatoly Date: Tue, 15 Dec 2015 09:41:09 -0500 Subject: [PATCH 04/18] #23: cleanup up most of AOT cljs warnings.. [still have some] --- .gitignore | 1 + project.clj | 2 +- src/mount/core.cljc | 55 +++++++++++++++++++------------------- src/mount/tools/cljs.cljs | 9 ------- src/mount/tools/macro.cljc | 25 +++++++++-------- 5 files changed, 43 insertions(+), 49 deletions(-) delete mode 100644 src/mount/tools/cljs.cljs diff --git a/.gitignore b/.gitignore index 1f14e58..a215468 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ pom.xml.asc dev/resources/public/js/* figwheel_server.log build.xml +doo-index.html *.jar *.class /.lein-* diff --git a/project.clj b/project.clj index f855e29..9d424ae 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :profiles {:dev {:source-paths ["dev" "dev/clj"] :dependencies [[org.clojure/clojure "1.7.0"] - [org.clojure/clojurescript "1.7.170"] + [org.clojure/clojurescript "1.7.170"]; :classifier "aot"] [datascript "0.13.3"] [hiccups "0.3.0"] [com.andrewmcveigh/cljs-time "0.3.14"] diff --git a/src/mount/core.cljc b/src/mount/core.cljc index 5b54fc4..a78353e 100644 --- a/src/mount/core.cljc +++ b/src/mount/core.cljc @@ -1,9 +1,8 @@ (ns mount.core #?(:clj (:require [mount.tools.macro :refer [on-error throw-runtime] :as macro]) - :cljs (:require [mount.tools.macro :as macro] - [mount.tools.cljs :as cljs])) - #?(:cljs (:require-macros [mount.tools.macro :refer [on-error throw-runtime]] - [mount.core :refer [defstate]]))) + :cljs (:require [mount.tools.macro :as macro])) + #?(:cljs (:require-macros [mount.core] + [mount.tools.macro :refer [on-error throw-runtime]]))) (defonce ^:private -args (atom :no-args)) ;; mostly for command line args and external files (defonce ^:private state-seq (atom 0)) @@ -35,13 +34,12 @@ (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] (...))) - #?(:clj (.startsWith (str f) pound) - :cljs (cljs/starts-with? (str f) pound)))) + (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)"] + (nth f 2) ;; magic 2 is to get the body => ["fn*" "[]" "(fn body)"] f)) (defn- cleanup-if-dirty @@ -128,25 +126,26 @@ (up name state (atom #{}))) @inst))) -(defmacro defstate [state & body] - (let [[state params] (macro/name-with-attributes state body) - {:keys [start stop suspend resume] :as lifecycle} (apply hash-map params) - state-name (with-ns *ns* state) - order (make-state-seq state-name) - sym (str state)] - (validate lifecycle) - (cleanup-if-dirty state-name) - (let [s-meta (cond-> {:order order - :start `(fn [] ~start) - :status #{:stopped}} - stop (assoc :stop `(fn [] ~stop)) - suspend (assoc :suspend `(fn [] ~suspend)) - resume (assoc :resume `(fn [] ~resume)))] - `(do - (def ~state (DerefableState. ~state-name)) - ((var update-meta!) [~state-name] (assoc ~s-meta :inst (atom (NotStartedState. ~state-name)) - :var (var ~state))) - (var ~state))))) +#?(:clj + (defmacro defstate [state & body] + (let [[state params] (macro/name-with-attributes state body) + {:keys [start stop suspend resume] :as lifecycle} (apply hash-map params) + state-name (with-ns *ns* state) + order (make-state-seq state-name) + sym (str state)] + (validate lifecycle) + (cleanup-if-dirty state-name) + (let [s-meta (cond-> {:order order + :start `(fn [] ~start) + :status #{:stopped}} + stop (assoc :stop `(fn [] ~stop)) + suspend (assoc :suspend `(fn [] ~suspend)) + resume (assoc :resume `(fn [] ~resume)))] + `(do + (def ~state (DerefableState. ~state-name)) + ((var update-meta!) [~state-name] (assoc ~s-meta :inst (atom (NotStartedState. ~state-name)) + :var (var ~state))) + (var ~state)))))) (defn in-cljc-mode [] (reset! mode :cljc)) @@ -182,7 +181,7 @@ #?(:cljs (defn var-to-str [v] - (if (var? v) + (if (instance? cljs.core.Var v) (let [{:keys [ns name]} (meta v)] (with-ns ns name)) v))) diff --git a/src/mount/tools/cljs.cljs b/src/mount/tools/cljs.cljs deleted file mode 100644 index 35f1fc3..0000000 --- a/src/mount/tools/cljs.cljs +++ /dev/null @@ -1,9 +0,0 @@ -(ns mount.tools.cljs - (:require [cljs.analyzer :as ana] - [goog.string :as gstring])) - -(defn this-ns [] - ana/*cljs-ns*) - -(defn starts-with? [s pre] - (gstring/startsWith s pre)) diff --git a/src/mount/tools/macro.cljc b/src/mount/tools/macro.cljc index 22961aa..7177726 100644 --- a/src/mount/tools/macro.cljc +++ b/src/mount/tools/macro.cljc @@ -1,16 +1,19 @@ -(ns mount.tools.macro) +(ns mount.tools.macro + #?(:cljs (:require-macros [mount.tools.macro]))) -(defmacro on-error [msg f] - `(try - ~f - (catch #?(:clj Throwable - :cljs :default) t# - (throw #?(:clj (RuntimeException. ~msg t#) - :cljs (js/Error (str ~msg (.-stack t#)))))))) +#?(:clj + (defmacro on-error [msg f] + `(try + ~f + (catch #?(:clj Throwable + :cljs :default) t# + (throw #?(:clj (RuntimeException. ~msg t#) + :cljs (js/Error (str ~msg (.-stack t#))))))))) -(defmacro throw-runtime [msg] - `(throw #?(:clj (RuntimeException. ~msg) - :cljs (js/Error (str ~msg))))) +#?(:clj + (defmacro throw-runtime [msg] + `(throw #?(:clj (RuntimeException. ~msg) + :cljs (js/Error (str ~msg)))))) ;; this is a one to one copy from https://github.com/clojure/tools.macro ;; to avoid a lib dependency for a single function From eeda08032279528322c8bd140bdf43e6774820e0 Mon Sep 17 00:00:00 2001 From: anatoly Date: Tue, 15 Dec 2015 11:47:21 -0500 Subject: [PATCH 05/18] adding "defstate!" wrapper (per @DomKM) --- src/mount/core.cljc | 73 +++++++++++++++++++++----------------- src/mount/tools/macro.cljc | 20 +++++------ 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/src/mount/core.cljc b/src/mount/core.cljc index a78353e..245e01f 100644 --- a/src/mount/core.cljc +++ b/src/mount/core.cljc @@ -53,25 +53,25 @@ (swap! running dissoc state))) #?(:clj - (defn current-state [state] - (let [{:keys [inst var]} (@meta-state state)] - (if (= @mode :cljc) - @inst - (var-get var)))) + (defn current-state [state] + (let [{:keys [inst var]} (@meta-state state)] + (if (= @mode :cljc) + @inst + (var-get var)))) :cljs - (defn current-state [state] - (-> (@meta-state state) :inst deref))) + (defn current-state [state] + (-> (@meta-state state) :inst deref))) #?(:clj - (defn alter-state! [{:keys [var inst]} value] - (if (= @mode :cljc) - (reset! inst value) - (alter-var-root var (constantly value)))) + (defn alter-state! [{:keys [var inst]} value] + (if (= @mode :cljc) + (reset! inst value) + (alter-var-root var (constantly value)))) :cljs - (defn alter-state! [{:keys [inst]} value] - (reset! inst value))) + (defn alter-state! [{:keys [inst]} value] + (reset! inst value))) (defn- update-meta! [path v] (swap! meta-state assoc-in path v)) @@ -127,25 +127,34 @@ @inst))) #?(:clj - (defmacro defstate [state & body] - (let [[state params] (macro/name-with-attributes state body) - {:keys [start stop suspend resume] :as lifecycle} (apply hash-map params) - state-name (with-ns *ns* state) - order (make-state-seq state-name) - sym (str state)] - (validate lifecycle) - (cleanup-if-dirty state-name) - (let [s-meta (cond-> {:order order - :start `(fn [] ~start) - :status #{:stopped}} - stop (assoc :stop `(fn [] ~stop)) - suspend (assoc :suspend `(fn [] ~suspend)) - resume (assoc :resume `(fn [] ~resume)))] - `(do - (def ~state (DerefableState. ~state-name)) - ((var update-meta!) [~state-name] (assoc ~s-meta :inst (atom (NotStartedState. ~state-name)) - :var (var ~state))) - (var ~state)))))) + (defmacro defstate [state & body] + (let [[state params] (macro/name-with-attributes state body) + {:keys [start stop suspend resume] :as lifecycle} (apply hash-map params) + state-name (with-ns *ns* state) + order (make-state-seq state-name) + sym (str state)] + (validate lifecycle) + (cleanup-if-dirty state-name) + (let [s-meta (cond-> {:order order + :start `(fn [] ~start) + :status #{:stopped}} + stop (assoc :stop `(fn [] ~stop)) + suspend (assoc :suspend `(fn [] ~suspend)) + resume (assoc :resume `(fn [] ~resume)))] + `(do + (def ~state (DerefableState. ~state-name)) + ((var update-meta!) [~state-name] (assoc ~s-meta :inst (atom (NotStartedState. ~state-name)) + :var (var ~state))) + (var ~state)))))) + +#?(:clj + (defmacro defstate! [state & {:keys [start! stop!]}] + (let [state-name (with-ns *ns* state)] + `(defstate ~state + :start (~'let [~state (mount/current-state ~state-name)] + ~start!) + :stop (~'let [~state (mount/current-state ~state-name)] + ~stop!))))) (defn in-cljc-mode [] (reset! mode :cljc)) diff --git a/src/mount/tools/macro.cljc b/src/mount/tools/macro.cljc index 7177726..f7449d0 100644 --- a/src/mount/tools/macro.cljc +++ b/src/mount/tools/macro.cljc @@ -2,18 +2,18 @@ #?(:cljs (:require-macros [mount.tools.macro]))) #?(:clj - (defmacro on-error [msg f] - `(try - ~f - (catch #?(:clj Throwable - :cljs :default) t# - (throw #?(:clj (RuntimeException. ~msg t#) - :cljs (js/Error (str ~msg (.-stack t#))))))))) + (defmacro on-error [msg f] + `(try + ~f + (catch #?(:clj Throwable + :cljs :default) t# + (throw #?(:clj (RuntimeException. ~msg t#) + :cljs (js/Error (str ~msg (.-stack t#))))))))) #?(:clj - (defmacro throw-runtime [msg] - `(throw #?(:clj (RuntimeException. ~msg) - :cljs (js/Error (str ~msg)))))) + (defmacro throw-runtime [msg] + `(throw #?(:clj (RuntimeException. ~msg) + :cljs (js/Error (str ~msg)))))) ;; this is a one to one copy from https://github.com/clojure/tools.macro ;; to avoid a lib dependency for a single function From 4fb6f6849e540d5e479cb4f0c690b27b01b1a11c Mon Sep 17 00:00:00 2001 From: anatoly Date: Tue, 15 Dec 2015 12:25:00 -0500 Subject: [PATCH 06/18] #22: a note on state cleaning when ns is recompiled --- src/mount/core.cljc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mount/core.cljc b/src/mount/core.cljc index 245e01f..30ba572 100644 --- a/src/mount/core.cljc +++ b/src/mount/core.cljc @@ -49,6 +49,7 @@ it is meant to be called by defstate before defining a new state" [state] (when-let [stop (@running state)] + (prn (str "<< stopping.. " state " (namespace was recompiled)")) (stop) (swap! running dissoc state))) From 5478d4298bdb8d78b2ed6285edd626ee47db62fd Mon Sep 17 00:00:00 2001 From: Oleksandr Manenko Date: Wed, 16 Dec 2015 18:24:48 +0200 Subject: [PATCH 07/18] Fix a typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d4584e..3ca5448 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ Creating state is easy: 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` +In case this state needs to be cleaned / destroyed between reloads, there is also `:stop` ```clojure (defstate conn :start create-conn From 5aac6198d05be1ff0c86f256fa03781612a52ed0 Mon Sep 17 00:00:00 2001 From: anatoly Date: Wed, 16 Dec 2015 18:16:17 -0500 Subject: [PATCH 08/18] refactoring example app: + www --- dev/clj/app/db.clj | 50 +++++++++++++++++++++ dev/clj/app/example.clj | 53 ++--------------------- dev/clj/app/nyse.clj | 33 +++++++------- dev/clj/app/www.clj | 31 +++++++++++++ dev/dev.clj | 7 ++- dev/resources/config.edn | 2 + project.clj | 7 ++- src/mount/core.cljc | 2 +- test/clj/{app => tapp}/conf.clj | 4 +- test/clj/{app => tapp}/example.clj | 10 +++-- test/clj/{app => tapp}/nyse.clj | 6 ++- test/clj/{app => tapp}/utils/datomic.clj | 4 +- test/clj/{app => tapp}/utils/logging.clj | 2 +- test/cljs/tapp/audit_log.cljs | 25 +++++++++++ test/cljs/tapp/conf.cljs | 9 ++++ test/cljs/tapp/example.cljs | 26 +++++++++++ test/cljs/tapp/websockets.cljs | 22 ++++++++++ test/mount/test.cljc | 2 + test/mount/test/cleanup_dirty_states.cljc | 18 ++++---- test/mount/test/fun_with_values.cljc | 2 + test/mount/test/helper.cljc | 2 + test/mount/test/parts.cljc | 14 +++--- test/mount/test/private_fun.cljc | 2 + test/mount/test/start_with.cljc | 24 +++++----- test/mount/test/start_without.cljc | 18 ++++---- test/mount/test/stop_except.cljc | 22 +++++----- test/mount/test/suspend_resume.cljc | 24 +++++----- test/mount/test/var/fun_with_values.clj | 2 + test/mount/test/var/private_fun.clj | 2 + 29 files changed, 290 insertions(+), 135 deletions(-) create mode 100644 dev/clj/app/db.clj create mode 100644 dev/clj/app/www.clj rename test/clj/{app => tapp}/conf.clj (85%) rename test/clj/{app => tapp}/example.clj (92%) rename test/clj/{app => tapp}/nyse.clj (88%) rename test/clj/{app => tapp}/utils/datomic.clj (83%) rename test/clj/{app => tapp}/utils/logging.clj (95%) create mode 100644 test/cljs/tapp/audit_log.cljs create mode 100644 test/cljs/tapp/conf.cljs create mode 100644 test/cljs/tapp/example.cljs create mode 100644 test/cljs/tapp/websockets.cljs diff --git a/dev/clj/app/db.clj b/dev/clj/app/db.clj new file mode 100644 index 0000000..dfcb6f5 --- /dev/null +++ b/dev/clj/app/db.clj @@ -0,0 +1,50 @@ +(ns app.db + (:require [mount.core :refer [defstate]] + [datomic.api :as d] + [clojure.tools.logging :refer [info]] + [app.conf :refer [config]])) + +(defn- new-connection [conf] + (info "conf: " conf) + (let [uri (get-in conf [:datomic :uri])] + (info "creating a connection to datomic:" uri) + (d/create-database uri) + (d/connect uri))) + +(defn disconnect [conf conn] + (let [uri (get-in conf [:datomic :uri])] + (info "disconnecting from " uri) + (.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 config) + :stop (disconnect config conn)) + +;; datomic schema (staging as an example) +(defn create-schema [conn] + (let [schema [{:db/id #db/id [:db.part/db] + :db/ident :order/symbol + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one + :db/index true + :db.install/_attribute :db.part/db} + + {:db/id #db/id [:db.part/db] + :db/ident :order/bid + :db/valueType :db.type/bigdec + :db/cardinality :db.cardinality/one + :db.install/_attribute :db.part/db} + + {:db/id #db/id [:db.part/db] + :db/ident :order/qty + :db/valueType :db.type/long + :db/cardinality :db.cardinality/one + :db.install/_attribute :db.part/db} + + {:db/id #db/id [:db.part/db] + :db/ident :order/offer + :db/valueType :db.type/bigdec + :db/cardinality :db.cardinality/one + :db.install/_attribute :db.part/db}]] + + @(d/transact conn schema))) diff --git a/dev/clj/app/example.clj b/dev/clj/app/example.clj index 81ffb49..ab425eb 100644 --- a/dev/clj/app/example.clj +++ b/dev/clj/app/example.clj @@ -1,10 +1,9 @@ (ns app.example - (:require [datomic.api :as d] - [clojure.tools.nrepl.server :refer [start-server stop-server]] + (:require [clojure.tools.nrepl.server :refer [start-server stop-server]] [mount.core :as mount :refer [defstate]] - [app.utils.datomic :refer [touch]] [app.conf :refer [config]] - [app.nyse :as nyse])) + [app.www]) + (:gen-class)) ;; for -main / uberjar (no need in dev) ;; example on creating a network REPL (defn- start-nrepl [{:keys [host port]}] @@ -14,52 +13,6 @@ (defstate nrepl :start (start-nrepl (:nrepl config)) :stop (stop-server nrepl)) -;; datomic schema -(defn create-schema [conn] - (let [schema [{:db/id #db/id [:db.part/db] - :db/ident :order/symbol - :db/valueType :db.type/string - :db/cardinality :db.cardinality/one - :db/index true - :db.install/_attribute :db.part/db} - - {:db/id #db/id [:db.part/db] - :db/ident :order/bid - :db/valueType :db.type/bigdec - :db/cardinality :db.cardinality/one - :db.install/_attribute :db.part/db} - - {:db/id #db/id [:db.part/db] - :db/ident :order/qty - :db/valueType :db.type/long - :db/cardinality :db.cardinality/one - :db.install/_attribute :db.part/db} - - {:db/id #db/id [:db.part/db] - :db/ident :order/offer - :db/valueType :db.type/bigdec - :db/cardinality :db.cardinality/one - :db.install/_attribute :db.part/db}]] - - @(d/transact conn schema))) - -(defn add-order [ticker bid offer qty] ;; can take connection as param - @(d/transact nyse/conn [{:db/id (d/tempid :db.part/user) - :order/symbol ticker - :order/bid bid - :order/offer offer - :order/qty qty}])) - - -(defn find-orders [ticker] ;; can take connection as param - (let [orders (d/q '[:find ?e :in $ ?ticker - :where [?e :order/symbol ?ticker]] - (d/db nyse/conn) ticker)] - (touch nyse/conn orders))) - -(defn create-nyse-schema [] - (create-schema nyse/conn)) - ;; example of an app entry point (defn -main [& args] (mount/start)) diff --git a/dev/clj/app/nyse.clj b/dev/clj/app/nyse.clj index f6a8ede..6cd1147 100644 --- a/dev/clj/app/nyse.clj +++ b/dev/clj/app/nyse.clj @@ -1,21 +1,20 @@ (ns app.nyse - (:require [mount.core :as mount :refer [defstate]] - [datomic.api :as d] - [clojure.tools.logging :refer [info]] - [app.conf :refer [config]])) + (:require [datomic.api :as d] + [app.db :refer [create-schema] :as db] + [app.utils.datomic :refer [touch]])) -(defn- new-connection [conf] - (info "conf: " conf) - (let [uri (get-in conf [:datomic :uri])] - (info "creating a connection to datomic:" uri) - (d/create-database uri) - (d/connect uri))) +(defn add-order [ticker bid offer qty] ;; can take connection as param + @(d/transact db/conn [{:db/id (d/tempid :db.part/user) + :order/symbol ticker + :order/bid bid + :order/offer offer + :order/qty qty}])) -(defn disconnect [conf conn] - (let [uri (get-in conf [:datomic :uri])] - (info "disconnecting from " uri) - (.release conn) ;; usually it's not released, here just to illustrate the access to connection on (stop) - (d/delete-database uri))) +(defn find-orders [ticker] ;; can take connection as param + (let [orders (d/q '[:find ?e :in $ ?ticker + :where [?e :order/symbol ?ticker]] + (d/db db/conn) ticker)] + (touch db/conn orders))) -(defstate conn :start (new-connection config) - :stop (disconnect config conn)) +(defn create-nyse-schema [] + (create-schema db/conn)) diff --git a/dev/clj/app/www.clj b/dev/clj/app/www.clj new file mode 100644 index 0000000..2103d4e --- /dev/null +++ b/dev/clj/app/www.clj @@ -0,0 +1,31 @@ +(ns app.www + (:require [app.nyse :refer [add-order find-orders create-nyse-schema]] + [app.conf :refer [config]] + [mount.core :refer [defstate]] + [cheshire.core :refer [generate-string]] + [compojure.core :refer [routes defroutes GET POST]] + [compojure.handler :as handler] + [ring.adapter.jetty :refer [run-jetty]])) + +(defroutes mount-example-routes + + (GET "/" [] "welcome to mount sample app!") + (GET "/nyse/orders/:ticker" [ticker] + (generate-string (find-orders ticker))) + + (POST "/nyse/orders" [ticker qty bid offer] + (add-order ticker (bigdec bid) (bigdec offer) (Integer/parseInt qty)) + (generate-string {:added {:ticker ticker + :qty qty + :bid bid + :offer offer}}))) + +(defn start-nyse [{:keys [www]}] + (create-nyse-schema) ;; creating schema (usually done long before the app is started..) + (-> (routes mount-example-routes) + (handler/site) + (run-jetty {:join? false + :port (:port www)}))) + +(defstate nyse-app :start (start-nyse config) + :stop (.stop nyse-app)) ;; it's a "org.eclipse.jetty.server.Server" at this point diff --git a/dev/dev.clj b/dev/dev.clj index d9c493e..f2facf1 100644 --- a/dev/dev.clj +++ b/dev/dev.clj @@ -12,12 +12,15 @@ [clojure.tools.namespace.repl :as tn] [mount.core :as mount] [app.utils.logging :refer [with-logging-status]] - [app.example :refer [create-nyse-schema find-orders add-order]])) ;; <<<< replace this your "app" namespace(s) you want to be available at REPL time + [app.www] + [app.example] + [app.nyse :refer [create-nyse-schema find-orders add-order]])) ;; <<<< replace this your "app" namespace(s) you want to be available at REPL time (defn start [] (with-logging-status) (mount/start #'app.conf/config - #'app.nyse/conn + #'app.db/conn + #'app.www/nyse-app #'app.example/nrepl)) ;; example on how to start app with certain states (defn stop [] diff --git a/dev/resources/config.edn b/dev/resources/config.edn index e927032..dd8d3fd 100644 --- a/dev/resources/config.edn +++ b/dev/resources/config.edn @@ -1,6 +1,8 @@ {:datomic {:uri "datomic:mem://mount"} + :www {:port 4242} + :h2 {:classname "org.h2.Driver" :subprotocol "h2" diff --git a/project.clj b/project.clj index 9d424ae..5cad57a 100644 --- a/project.clj +++ b/project.clj @@ -8,10 +8,13 @@ :dependencies [] ;; for visual clarity - :profiles {:dev {:source-paths ["dev" "dev/clj"] + :profiles {:dev {:source-paths ["dev" "dev/clj" "test/clj"] :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/clojurescript "1.7.170"]; :classifier "aot"] [datascript "0.13.3"] + [compojure "1.4.0"] + [ring/ring-jetty-adapter "1.1.0"] + [cheshire "5.5.0"] [hiccups "0.3.0"] [com.andrewmcveigh/cljs-time "0.3.14"] [ch.qos.logback/logback-classic "1.1.3"] @@ -58,4 +61,4 @@ :optimizations :advanced :pretty-print false}}}}} - :test {:source-paths ["dev" "test/clj" "test"]}}) + :test {:source-paths ["test" "test/clj" "test/cljs"]}}) diff --git a/src/mount/core.cljc b/src/mount/core.cljc index 30ba572..5027239 100644 --- a/src/mount/core.cljc +++ b/src/mount/core.cljc @@ -144,7 +144,7 @@ resume (assoc :resume `(fn [] ~resume)))] `(do (def ~state (DerefableState. ~state-name)) - ((var update-meta!) [~state-name] (assoc ~s-meta :inst (atom (NotStartedState. ~state-name)) + ((var mount.core/update-meta!) [~state-name] (assoc ~s-meta :inst (atom (NotStartedState. ~state-name)) :var (var ~state))) (var ~state)))))) diff --git a/test/clj/app/conf.clj b/test/clj/tapp/conf.clj similarity index 85% rename from test/clj/app/conf.clj rename to test/clj/tapp/conf.clj index 3273a95..6c821ba 100644 --- a/test/clj/app/conf.clj +++ b/test/clj/tapp/conf.clj @@ -1,8 +1,10 @@ -(ns app.conf +(ns tapp.conf (:require [mount.core :as mount :refer [defstate]] [clojure.edn :as edn] [clojure.tools.logging :refer [info]])) +(alter-meta! *ns* assoc ::load false) + (defn load-config [path] (info "loading config from" path) (-> path diff --git a/test/clj/app/example.clj b/test/clj/tapp/example.clj similarity index 92% rename from test/clj/app/example.clj rename to test/clj/tapp/example.clj index 028d406..721d2da 100644 --- a/test/clj/app/example.clj +++ b/test/clj/tapp/example.clj @@ -1,10 +1,12 @@ -(ns app.example +(ns tapp.example (:require [datomic.api :as d] [clojure.tools.nrepl.server :refer [start-server stop-server]] [mount.core :as mount :refer [defstate]] - [app.utils.datomic :refer [touch]] - [app.conf :refer [config]] - [app.nyse :as nyse])) + [tapp.utils.datomic :refer [touch]] + [tapp.conf :refer [config]] + [tapp.nyse :as nyse])) + +(alter-meta! *ns* assoc ::load false) ;; example on creating a network REPL (defn- start-nrepl [{:keys [host port]}] diff --git a/test/clj/app/nyse.clj b/test/clj/tapp/nyse.clj similarity index 88% rename from test/clj/app/nyse.clj rename to test/clj/tapp/nyse.clj index 374cba7..3a2800c 100644 --- a/test/clj/app/nyse.clj +++ b/test/clj/tapp/nyse.clj @@ -1,8 +1,10 @@ -(ns app.nyse +(ns tapp.nyse (:require [mount.core :as mount :refer [defstate]] [datomic.api :as d] [clojure.tools.logging :refer [info]] - [app.conf :refer [config]])) + [tapp.conf :refer [config]])) + +(alter-meta! *ns* assoc ::load false) (defn- new-connection [conf] (info "conf: " conf) diff --git a/test/clj/app/utils/datomic.clj b/test/clj/tapp/utils/datomic.clj similarity index 83% rename from test/clj/app/utils/datomic.clj rename to test/clj/tapp/utils/datomic.clj index 836f9b9..321f0d7 100644 --- a/test/clj/app/utils/datomic.clj +++ b/test/clj/tapp/utils/datomic.clj @@ -1,6 +1,8 @@ -(ns app.utils.datomic +(ns tapp.utils.datomic (:require [datomic.api :as d])) +(alter-meta! *ns* assoc ::load false) + (defn entity [conn id] (d/entity (d/db conn) id)) diff --git a/test/clj/app/utils/logging.clj b/test/clj/tapp/utils/logging.clj similarity index 95% rename from test/clj/app/utils/logging.clj rename to test/clj/tapp/utils/logging.clj index 2d8c64d..c888838 100644 --- a/test/clj/app/utils/logging.clj +++ b/test/clj/tapp/utils/logging.clj @@ -1,4 +1,4 @@ -(ns app.utils.logging ;; << change to your namespace/path +(ns tapp.utils.logging ;; << change to your namespace/path (:require [mount.core] [robert.hooke :refer [add-hook clear-hooks]] [clojure.string :refer [split]] diff --git a/test/cljs/tapp/audit_log.cljs b/test/cljs/tapp/audit_log.cljs new file mode 100644 index 0000000..0eac430 --- /dev/null +++ b/test/cljs/tapp/audit_log.cljs @@ -0,0 +1,25 @@ +(ns tapp.audit-log + (:require [datascript.core :as d] + [cljs-time.core :refer [now]]) + (:require-macros [mount.core :refer [defstate]])) + +(defstate log :start (d/create-conn {})) + +(defn audit [db source & msg] + (d/transact! @db [{:db/id -1 + :source source + :timestamp (now) + :msg (apply str msg)}])) + +(defn find-source-logs [db source] + (d/q '{:find [?t ?msg] + :in [$ ?s] + :where [[?e :source ?s] + [?e :timestamp ?t] + [?e :msg ?msg]]} + @@db source)) + +(defn find-all-logs [db] + (->> (map :e (d/datoms @@db :aevt :timestamp)) + dedupe + (d/pull-many @@db '[:timestamp :source :msg]))) diff --git a/test/cljs/tapp/conf.cljs b/test/cljs/tapp/conf.cljs new file mode 100644 index 0000000..8439412 --- /dev/null +++ b/test/cljs/tapp/conf.cljs @@ -0,0 +1,9 @@ +(ns tapp.conf + (:require [tapp.audit-log :refer [audit log]]) + (:require-macros [mount.core :refer [defstate]])) + +(defn load-config [path] + (audit log :app-conf "loading config from '" path "' (at least pretending)") + {:system-a {:uri "ws://echo.websocket.org/"}}) + +(defstate config :start (load-config "resources/config.end")) diff --git a/test/cljs/tapp/example.cljs b/test/cljs/tapp/example.cljs new file mode 100644 index 0000000..5f8808a --- /dev/null +++ b/test/cljs/tapp/example.cljs @@ -0,0 +1,26 @@ +(ns tapp.example + (:require [mount.core :as mount] + [tapp.conf] + [tapp.websockets] + [tapp.audit-log :refer [log find-all-logs]] + [cljs-time.format :refer [unparse formatters]] + [hiccups.runtime :as hiccupsrt]) + (:require-macros [hiccups.core :as hiccups :refer [html]])) + +(defn format-log-event [{:keys [timestamp source msg]}] + (str (unparse (formatters :date-hour-minute-second-fraction) timestamp) + " → [" (name source) "]: " msg)) + +(defn show-log [] + (.write js/document + (html [:ul (doall (for [e (find-all-logs log)] + [:li (format-log-event e)]))]))) + +(mount/start) + +;; time to establish a websocket connection before disconnecting +(js/setTimeout #(mount/stop-except "#'tapp.audit-log/log") 500) + +;; time to close a connection to show it in audit +(js/setTimeout #(show-log) 1000) + diff --git a/test/cljs/tapp/websockets.cljs b/test/cljs/tapp/websockets.cljs new file mode 100644 index 0000000..71adc07 --- /dev/null +++ b/test/cljs/tapp/websockets.cljs @@ -0,0 +1,22 @@ +(ns tapp.websockets + (:require [tapp.conf :refer [config]] + [tapp.audit-log :refer [audit log]]) + (:require-macros [mount.core :refer [defstate]])) + +(defn ws-status [ws] + {:url (.-url ws) :ready-state (.-readyState ws)}) + +(defn connect [uri] + (let [ws (js/WebSocket. uri)] + (audit log :system-a "connecting to " (ws-status ws)) + (set! (.-onopen ws) #(audit log :system-a "opened " (ws-status ws))) + (set! (.-onclose ws) #(audit log :system-a "closed " (ws-status ws))) + ws)) + +(defn disconnect [ws] + (audit log :system-a "closing " (ws-status @ws)) + (.close @ws) + (audit log :system-a "disconnecting " (ws-status @ws))) + +(defstate system-a :start (connect (get-in @config [:system-a :uri])) + :stop (disconnect system-a)) diff --git a/test/mount/test.cljc b/test/mount/test.cljc index b24368d..fa06e33 100644 --- a/test/mount/test.cljc +++ b/test/mount/test.cljc @@ -15,6 +15,8 @@ mount.test.suspend-resume )) +#?(:clj (alter-meta! *ns* assoc ::load false)) + (mount.core/in-cljc-mode) #?(:cljs diff --git a/test/mount/test/cleanup_dirty_states.cljc b/test/mount/test/cleanup_dirty_states.cljc index c4fced9..88986bc 100644 --- a/test/mount/test/cleanup_dirty_states.cljc +++ b/test/mount/test/cleanup_dirty_states.cljc @@ -2,23 +2,25 @@ (:require #?@(:cljs [[cljs.test :as t :refer-macros [is are deftest testing use-fixtures]] [mount.core :as mount :refer-macros [defstate]] - [app.websockets :refer [system-a]] - [app.conf :refer [config]] - [app.audit-log :refer [log]]] + [tapp.websockets :refer [system-a]] + [tapp.conf :refer [config]] + [tapp.audit-log :refer [log]]] :clj [[clojure.test :as t :refer [is are deftest testing use-fixtures]] [mount.core :as mount :refer [defstate]] - [app.example]]) + [tapp.example]]) [mount.test.helper :refer [dval helper forty-two]])) +#?(:clj (alter-meta! *ns* assoc ::load false)) + #?(:clj (deftest cleanup-dirty-states (let [_ (mount/start)] - (is (not (.isClosed (:server-socket (dval app.example/nrepl))))) - (require 'app.example :reload) + (is (not (.isClosed (:server-socket (dval tapp.example/nrepl))))) + (require 'tapp.example :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 (dval app.example/nrepl))))) + (is (not (.isClosed (:server-socket (dval tapp.example/nrepl))))) (mount/stop) - (is (instance? mount.core.NotStartedState (dval app.example/nrepl)))))) + (is (instance? mount.core.NotStartedState (dval tapp.example/nrepl)))))) #?(:cljs (deftest cleanup-dirty-states diff --git a/test/mount/test/fun_with_values.cljc b/test/mount/test/fun_with_values.cljc index 61cf200..667f216 100644 --- a/test/mount/test/fun_with_values.cljc +++ b/test/mount/test/fun_with_values.cljc @@ -5,6 +5,8 @@ :clj [[clojure.test :as t :refer [is are deftest testing use-fixtures]] [mount.core :as mount :refer [defstate]]]))) +#?(:clj (alter-meta! *ns* assoc ::load false)) + (defn f [n] (fn [m] (+ n m))) diff --git a/test/mount/test/helper.cljc b/test/mount/test/helper.cljc index ecc0cff..e02bf32 100644 --- a/test/mount/test/helper.cljc +++ b/test/mount/test/helper.cljc @@ -3,6 +3,8 @@ #?@(:cljs [[mount.core :as mount :refer-macros [defstate]]] :clj [[mount.core :as mount :refer [defstate]]]))) +#?(:clj (alter-meta! *ns* assoc ::load false)) + (defn dval "returns a value of DerefableState without deref'ing it" [d] diff --git a/test/mount/test/parts.cljc b/test/mount/test/parts.cljc index 20a503f..0a76a91 100644 --- a/test/mount/test/parts.cljc +++ b/test/mount/test/parts.cljc @@ -2,24 +2,26 @@ (:require #?@(:cljs [[cljs.test :as t :refer-macros [is are deftest testing use-fixtures]] [mount.core :as mount :refer-macros [defstate]] - [app.websockets :refer [system-a]] - [app.conf :refer [config]] - [app.audit-log :refer [log]]] + [tapp.websockets :refer [system-a]] + [tapp.conf :refer [config]] + [tapp.audit-log :refer [log]]] :clj [[clojure.test :as t :refer [is are deftest testing use-fixtures]] [mount.core :as mount :refer [defstate]] - [app.nyse :refer [conn]]]) + [tapp.nyse :refer [conn]]]) [mount.test.helper :refer [dval]])) +#?(:clj (alter-meta! *ns* assoc ::load false)) + (defstate should-not-start :start (constantly 42)) #?(:clj (defn with-parts [f] - (mount/start #'app.conf/config #'app.nyse/conn) + (mount/start #'tapp.conf/config #'tapp.nyse/conn) (f) (mount/stop))) (use-fixtures :once - #?(:cljs {:before #(mount/start #'app.conf/config #'app.audit-log/log) + #?(:cljs {:before #(mount/start #'tapp.conf/config #'tapp.audit-log/log) :after mount/stop} :clj with-parts)) diff --git a/test/mount/test/private_fun.cljc b/test/mount/test/private_fun.cljc index 47ef9a4..e487e54 100644 --- a/test/mount/test/private_fun.cljc +++ b/test/mount/test/private_fun.cljc @@ -7,6 +7,8 @@ [mount.test.fun-with-values :refer [private-f]])) +#?(:clj (alter-meta! *ns* assoc ::load false)) + (use-fixtures :once #?(:cljs {:before #(mount/start #'mount.test.fun-with-values/private-f) :after mount/stop} diff --git a/test/mount/test/start_with.cljc b/test/mount/test/start_with.cljc index 7e7a7bd..d8ef8b8 100644 --- a/test/mount/test/start_with.cljc +++ b/test/mount/test/start_with.cljc @@ -2,16 +2,18 @@ (:require #?@(:cljs [[cljs.test :as t :refer-macros [is are deftest testing use-fixtures]] [mount.core :as mount :refer-macros [defstate]] - [app.websockets :refer [system-a]] - [app.conf :refer [config]] - [app.audit-log :refer [log]]] + [tapp.websockets :refer [system-a]] + [tapp.conf :refer [config]] + [tapp.audit-log :refer [log]]] :clj [[clojure.test :as t :refer [is are deftest testing use-fixtures]] [mount.core :as mount :refer [defstate]] - [app.conf :refer [config]] - [app.nyse :refer [conn]] - [app.example :refer [nrepl]]]) + [tapp.conf :refer [config]] + [tapp.nyse :refer [conn]] + [tapp.example :refer [nrepl]]]) [mount.test.helper :refer [dval helper]])) +#?(:clj (alter-meta! *ns* assoc ::load false)) + (defstate test-conn :start 42 :stop (constantly 0)) @@ -21,7 +23,7 @@ (deftest start-with (testing "should start with substitutes" - (let [_ (mount/start-with {#'app.websockets/system-a #'mount.test.start-with/test-conn + (let [_ (mount/start-with {#'tapp.websockets/system-a #'mount.test.start-with/test-conn #'mount.test.helper/helper #'mount.test.start-with/test-nrepl})] (is (map? (dval config))) (is (vector? (dval helper))) @@ -30,7 +32,7 @@ (mount/stop))) (testing "should not start the substitute itself" - (let [_ (mount/start-with {#'app.websockets/system-a #'mount.test.start-with/test-conn})] + (let [_ (mount/start-with {#'tapp.websockets/system-a #'mount.test.start-with/test-conn})] (is (instance? mount.core.NotStartedState (dval test-conn))) (is (= 42 (dval system-a))) (mount/stop))) @@ -60,15 +62,15 @@ (deftest start-with (testing "should start with substitutes" - (let [_ (mount/start-with {#'app.nyse/conn #'mount.test.start-with/test-conn - #'app.example/nrepl #'mount.test.start-with/test-nrepl})] + (let [_ (mount/start-with {#'tapp.nyse/conn #'mount.test.start-with/test-conn + #'tapp.example/nrepl #'mount.test.start-with/test-nrepl})] (is (map? (dval config))) (is (vector? (dval nrepl))) (is (= (dval conn) 42)) (mount/stop))) (testing "should not start the substitute itself" - (let [_ (mount/start-with {#'app.nyse/conn #'mount.test.start-with/test-conn})] + (let [_ (mount/start-with {#'tapp.nyse/conn #'mount.test.start-with/test-conn})] (is (instance? mount.core.NotStartedState (dval test-conn))) (is (= (dval conn) 42)) (mount/stop))) diff --git a/test/mount/test/start_without.cljc b/test/mount/test/start_without.cljc index f6a68d1..a678e16 100644 --- a/test/mount/test/start_without.cljc +++ b/test/mount/test/start_without.cljc @@ -2,24 +2,26 @@ (:require #?@(:cljs [[cljs.test :as t :refer-macros [is are deftest testing use-fixtures]] [mount.core :as mount :refer-macros [defstate]] - [app.websockets :refer [system-a]] - [app.conf :refer [config]] - [app.audit-log :refer [log]]] + [tapp.websockets :refer [system-a]] + [tapp.conf :refer [config]] + [tapp.audit-log :refer [log]]] :clj [[clojure.test :as t :refer [is are deftest testing use-fixtures]] [mount.core :as mount :refer [defstate]] - [app.conf :refer [config]] - [app.nyse :refer [conn]] - [app.example :refer [nrepl]]]) + [tapp.conf :refer [config]] + [tapp.nyse :refer [conn]] + [tapp.example :refer [nrepl]]]) [mount.test.helper :refer [dval helper]])) +#?(:clj (alter-meta! *ns* assoc ::load false)) + #?(:clj (defn without [f] - (mount/start-without #'app.nyse/conn #'app.example/nrepl) + (mount/start-without #'tapp.nyse/conn #'tapp.example/nrepl) (f) (mount/stop))) (use-fixtures :once - #?(:cljs {:before #(mount/start-without #'mount.test.helper/helper #'app.websockets/system-a) + #?(:cljs {:before #(mount/start-without #'mount.test.helper/helper #'tapp.websockets/system-a) :after mount/stop} :clj without)) diff --git a/test/mount/test/stop_except.cljc b/test/mount/test/stop_except.cljc index 3e61f97..26fc54c 100644 --- a/test/mount/test/stop_except.cljc +++ b/test/mount/test/stop_except.cljc @@ -2,22 +2,24 @@ (:require #?@(:cljs [[cljs.test :as t :refer-macros [is are deftest testing use-fixtures]] [mount.core :as mount :refer-macros [defstate]] - [app.websockets :refer [system-a]] - [app.conf :refer [config]] - [app.audit-log :refer [log]]] + [tapp.websockets :refer [system-a]] + [tapp.conf :refer [config]] + [tapp.audit-log :refer [log]]] :clj [[clojure.test :as t :refer [is are deftest testing use-fixtures]] [mount.core :as mount :refer [defstate]] - [app.conf :refer [config]] - [app.nyse :refer [conn]] - [app.example :refer [nrepl]]]) + [tapp.conf :refer [config]] + [tapp.nyse :refer [conn]] + [tapp.example :refer [nrepl]]]) [mount.test.helper :refer [dval helper]])) +#?(:clj (alter-meta! *ns* assoc ::load false)) + #?(:cljs (deftest stop-except (testing "should stop all except nrepl" (let [_ (mount/start) - _ (mount/stop-except #'app.audit-log/log #'mount.test.helper/helper)] + _ (mount/stop-except #'tapp.audit-log/log #'mount.test.helper/helper)] (is (= :started (dval helper))) (is (instance? datascript.db/DB @(dval log))) (is (instance? mount.core.NotStartedState (dval config))) @@ -33,7 +35,7 @@ (testing "should stop all normally after stop-except" (let [_ (mount/start) - _ (mount/stop-except #'app.audit-log/log #'mount.test.helper/helper) + _ (mount/stop-except #'tapp.audit-log/log #'mount.test.helper/helper) _ (mount/stop)] (is (instance? mount.core.NotStartedState (dval config))) (is (instance? mount.core.NotStartedState (dval log))) @@ -44,7 +46,7 @@ (testing "should stop all except nrepl" (let [_ (mount/start) - _ (mount/stop-except #'app.nyse/conn #'app.conf/config)] + _ (mount/stop-except #'tapp.nyse/conn #'tapp.conf/config)] (is (map? (dval config))) (is (instance? datomic.peer.LocalConnection (dval conn))) (is (instance? mount.core.NotStartedState (dval nrepl))) @@ -59,7 +61,7 @@ (testing "should stop all normally after stop-except" (let [_ (mount/start) - _ (mount/stop-except #'app.nyse/conn #'app.conf/config) + _ (mount/stop-except #'tapp.nyse/conn #'tapp.conf/config) _ (mount/stop)] (is (instance? mount.core.NotStartedState (dval config))) (is (instance? mount.core.NotStartedState (dval conn))) diff --git a/test/mount/test/suspend_resume.cljc b/test/mount/test/suspend_resume.cljc index ea1fe36..c3503ec 100644 --- a/test/mount/test/suspend_resume.cljc +++ b/test/mount/test/suspend_resume.cljc @@ -2,16 +2,18 @@ (:require #?@(:cljs [[cljs.test :as t :refer-macros [is are deftest testing use-fixtures]] [mount.core :as mount :refer-macros [defstate]] - [app.websockets :refer [system-a]] - [app.conf :refer [config]] - [app.audit-log :refer [log]]] + [tapp.websockets :refer [system-a]] + [tapp.conf :refer [config]] + [tapp.audit-log :refer [log]]] :clj [[clojure.test :as t :refer [is are deftest testing use-fixtures]] [mount.core :as mount :refer [defstate]] - [app.conf :refer [config]] - [app.nyse :refer [conn]] - [app.example :refer [nrepl]]]) + [tapp.conf :refer [config]] + [tapp.nyse :refer [conn]] + [tapp.example :refer [nrepl]]]) [mount.test.helper :refer [dval]])) +#?(:clj (alter-meta! *ns* assoc ::load false)) + (defn koncat [k s] (-> (name k) (str "-" (name s)) @@ -48,7 +50,7 @@ (testing "should resume _only suspendable_ states that are currently suspended" (let [_ (mount/start) - _ (mount/stop #'app.websockets/system-a) + _ (mount/stop #'tapp.websockets/system-a) _ (mount/suspend) _ (mount/resume)] (is (map? (dval config))) @@ -82,7 +84,7 @@ (testing "when replacing a non suspendable state with a suspendable one, the later should be able to suspend/resume, the original should not be suspendable after resume and preserve its lifecycle fns after rollback/stop" - (let [_ (mount/start-with {#'app.websockets/system-a #'mount.test.suspend-resume/web-server}) + (let [_ (mount/start-with {#'tapp.websockets/system-a #'mount.test.suspend-resume/web-server}) _ (mount/suspend)] (is (= (dval system-a) :w-suspended)) (is (instance? mount.core.NotStartedState (dval web-server))) @@ -107,7 +109,7 @@ (testing "should resume _only suspendable_ states that are currently suspended" (let [_ (mount/start) - _ (mount/stop #'app.example/nrepl) + _ (mount/stop #'tapp.example/nrepl) _ (mount/suspend) _ (mount/resume)] (is (map? (dval config))) @@ -142,7 +144,7 @@ (testing "when replacing a non suspendable state with a suspendable one, the later should be able to suspend/resume, the original should not be suspendable after resume and preserve its lifecycle fns after rollback/stop" - (let [_ (mount/start-with {#'app.example/nrepl #'mount.test.suspend-resume/web-server}) + (let [_ (mount/start-with {#'tapp.example/nrepl #'mount.test.suspend-resume/web-server}) _ (mount/suspend)] (is (= (dval nrepl) :w-suspended)) (is (instance? mount.core.NotStartedState (dval web-server))) @@ -176,7 +178,7 @@ the original should still be suspended and preserve its lifecycle fns after the rollback/stop" (let [_ (mount/start) _ (mount/suspend) - _ (mount/start-with {#'mount.test.suspend-resume/web-server #'app.nyse/conn}) ;; TODO: good to WARN on started states during "start-with" + _ (mount/start-with {#'mount.test.suspend-resume/web-server #'tapp.nyse/conn}) ;; TODO: good to WARN on started states during "start-with" _ (mount/suspend)] (is (instance? datomic.peer.LocalConnection (dval conn))) (is (= (dval web-server) :w-suspended)) ;; since the "conn" does not have a resume method, so web-server was not started diff --git a/test/mount/test/var/fun_with_values.clj b/test/mount/test/var/fun_with_values.clj index f6a09a1..155f318 100644 --- a/test/mount/test/var/fun_with_values.clj +++ b/test/mount/test/var/fun_with_values.clj @@ -2,6 +2,8 @@ (:require [clojure.test :as t :refer [is are deftest testing use-fixtures]] [mount.core :as mount :refer [defstate]])) +(alter-meta! *ns* assoc ::load false) + (defn f [n] (fn [m] (+ n m))) diff --git a/test/mount/test/var/private_fun.clj b/test/mount/test/var/private_fun.clj index f00a37d..6c91456 100644 --- a/test/mount/test/var/private_fun.clj +++ b/test/mount/test/var/private_fun.clj @@ -3,6 +3,8 @@ [mount.core :as mount :refer [defstate]] [mount.test.var.fun-with-values :refer [private-f]])) +(alter-meta! *ns* assoc ::load false) + (defn in-clj-mode [f] (mount/in-clj-mode) (require :reload 'mount.test.var.fun-with-values 'mount.test.var.private-fun) From ca45d22111ffdf98c709f6f7040a9869147e019c Mon Sep 17 00:00:00 2001 From: anatoly Date: Sun, 20 Dec 2015 01:28:34 -0500 Subject: [PATCH 09/18] making mount boot'iful... [the beginning] --- .gitignore | 2 +- build.boot | 56 +++++++++++++++++++++++++++++++++++++++++++ dev/{ => clj}/dev.clj | 11 +-------- project.clj | 2 +- 4 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 build.boot rename dev/{ => clj}/dev.clj (66%) diff --git a/.gitignore b/.gitignore index a215468..68279ea 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,5 @@ doo-index.html *.iml /.idea /.lein-repl-history - +/.nrepl-history diff --git a/build.boot b/build.boot new file mode 100644 index 0000000..5f92dd3 --- /dev/null +++ b/build.boot @@ -0,0 +1,56 @@ +(set-env! + :source-paths #{"src"} + :dependencies '[;; dev / examples / test + [org.clojure/clojure "1.7.0" :scope "provided"] + [org.clojure/clojurescript "1.7.170" :scope "provided"] + [datascript "0.13.3" :scope "provided"] + [compojure "1.4.0" :scope "provided"] + [ring/ring-jetty-adapter "1.1.0" :scope "provided"] + [cheshire "5.5.0" :scope "provided"] + [hiccups "0.3.0" :scope "provided"] + [com.andrewmcveigh/cljs-time "0.3.14" :scope "provided"] + [ch.qos.logback/logback-classic "1.1.3" :scope "provided"] + [org.clojure/tools.logging "0.3.1" :scope "provided"] + [robert/hooke "1.3.0" :scope "provided"] + [org.clojure/tools.namespace "0.2.11" :scope "provided"] + [org.clojure/tools.nrepl "0.2.11" :scope "provided"] + [com.datomic/datomic-free "0.9.5327" :scope "provided" :exclusions [joda-time]] + + ;; boot + [boot/core "2.5.1" :scope "provided"] + [adzerk/bootlaces "0.1.13" :scope "test"] + [adzerk/boot-test "1.0.6" :scope "test"]]) + +(require '[adzerk.bootlaces :refer :all] + '[adzerk.boot-test :as bt]) + +(def +version+ "0.1.7-SNAPSHOT") + +(bootlaces! +version+) + +(deftask dev [] + + (set-env! :source-paths #(conj % "dev/clj")) + + (defn in [] + (load-data-readers!) + (require 'dev) + (in-ns 'dev)) + + (comp + (watch) + (repl)) + identity) + +(deftask test [] + (set-env! :source-paths #(conj % "test" "test/clj")) ;; (!) :source-paths must not overlap. + (bt/test)) + +(task-options! + pom {:project 'mount + :version +version+ + :description "managing Clojure and ClojureScript app state since (reset)" + :url "https://github.com/tolitius/mount" + :scm {:url "https://github.com/tolitius/mount"} + :license {"Eclipse Public License" + "http://www.eclipse.org/legal/epl-v10.html"}}) diff --git a/dev/dev.clj b/dev/clj/dev.clj similarity index 66% rename from dev/dev.clj rename to dev/clj/dev.clj index f2facf1..69eb5c6 100644 --- a/dev/dev.clj +++ b/dev/clj/dev.clj @@ -1,14 +1,5 @@ (ns dev - "Tools for interactive development with the REPL. This file should - not be included in a production build of the application." - (:require [clojure.java.io :as io] - [clojure.java.javadoc :refer [javadoc]] - [clojure.pprint :refer [pprint]] - [clojure.reflect :refer [reflect]] - [clojure.repl :refer [apropos dir doc find-doc pst source]] - [clojure.set :as set] - [clojure.string :as str] - [clojure.test :as test] + (:require [clojure.pprint :refer [pprint]] [clojure.tools.namespace.repl :as tn] [mount.core :as mount] [app.utils.logging :refer [with-logging-status]] diff --git a/project.clj b/project.clj index 5cad57a..691833c 100644 --- a/project.clj +++ b/project.clj @@ -1,5 +1,5 @@ (defproject mount "0.1.7-SNAPSHOT" - :description "managing Clojure app state since (reset)" + :description "managing Clojure and ClojureScript app state since (reset)" :url "https://github.com/tolitius/mount" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} From d67f318ef16514ecb3625ed8ccf141346284b1c4 Mon Sep 17 00:00:00 2001 From: anatoly Date: Sun, 20 Dec 2015 02:03:03 -0500 Subject: [PATCH 10/18] [boot]: dissoc ensure snapshot branch --- build.boot | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.boot b/build.boot index 5f92dd3..4585925 100644 --- a/build.boot +++ b/build.boot @@ -39,14 +39,14 @@ (comp (watch) - (repl)) - identity) + (repl))) (deftask test [] (set-env! :source-paths #(conj % "test" "test/clj")) ;; (!) :source-paths must not overlap. (bt/test)) (task-options! + push #(-> (into {} %) (assoc :ensure-branch nil)) pom {:project 'mount :version +version+ :description "managing Clojure and ClojureScript app state since (reset)" From 8f9f44e3cc20079c9cf7ea165e643c6007456ef3 Mon Sep 17 00:00:00 2001 From: anatoly Date: Sun, 20 Dec 2015 19:16:48 -0500 Subject: [PATCH 11/18] [boot]: adding logging and t.namespace --- build.boot | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/build.boot b/build.boot index 4585925..071de0f 100644 --- a/build.boot +++ b/build.boot @@ -17,29 +17,39 @@ [com.datomic/datomic-free "0.9.5327" :scope "provided" :exclusions [joda-time]] ;; boot - [boot/core "2.5.1" :scope "provided"] - [adzerk/bootlaces "0.1.13" :scope "test"] - [adzerk/boot-test "1.0.6" :scope "test"]]) + [boot/core "2.5.1" :scope "provided"] + [adzerk/bootlaces "0.1.13" :scope "test"] + [adzerk/boot-logservice "1.0.1" :scope "test"] + [adzerk/boot-test "1.0.6" :scope "test"]]) (require '[adzerk.bootlaces :refer :all] - '[adzerk.boot-test :as bt]) + '[adzerk.boot-test :as bt] + '[adzerk.boot-logservice :as log-service] + '[clojure.tools.logging :as log] + '[clojure.tools.namespace.repl :refer [set-refresh-dirs]]) (def +version+ "0.1.7-SNAPSHOT") (bootlaces! +version+) +(def log-config + [:configuration + [:appender {:name "STDOUT" :class "ch.qos.logback.core.ConsoleAppender"} + [:encoder [:pattern "%-5level %logger{36} - %msg%n"]]] + [:root {:level "TRACE"} + [:appender-ref {:ref "STDOUT"}]]]) + (deftask dev [] (set-env! :source-paths #(conj % "dev/clj")) - (defn in [] - (load-data-readers!) - (require 'dev) - (in-ns 'dev)) + (alter-var-root #'log/*logger-factory* + (constantly (log-service/make-factory log-config))) + (apply set-refresh-dirs (get-env :directories)) + (load-data-readers!) - (comp - (watch) - (repl))) + (require 'dev) + (in-ns 'dev)) (deftask test [] (set-env! :source-paths #(conj % "test" "test/clj")) ;; (!) :source-paths must not overlap. From a75b087b8f04365a275eaeed5ca4103849d9ffea Mon Sep 17 00:00:00 2001 From: anatoly Date: Sun, 20 Dec 2015 19:22:17 -0500 Subject: [PATCH 12/18] [boot]: moving tests to test/core to avoid "boot => The :source-paths, :resource-paths, and :asset-paths must not overlap." --- build.boot | 2 +- project.clj | 2 +- test/{ => core}/mount/test.cljc | 0 test/{ => core}/mount/test/cleanup_dirty_states.cljc | 0 test/{ => core}/mount/test/fun_with_values.cljc | 0 test/{ => core}/mount/test/helper.cljc | 0 test/{ => core}/mount/test/parts.cljc | 0 test/{ => core}/mount/test/private_fun.cljc | 0 test/{ => core}/mount/test/start_with.cljc | 0 test/{ => core}/mount/test/start_without.cljc | 0 test/{ => core}/mount/test/stop_except.cljc | 0 test/{ => core}/mount/test/suspend_resume.cljc | 0 test/{ => core}/mount/test/var/fun_with_values.clj | 0 test/{ => core}/mount/test/var/private_fun.clj | 0 14 files changed, 2 insertions(+), 2 deletions(-) rename test/{ => core}/mount/test.cljc (100%) rename test/{ => core}/mount/test/cleanup_dirty_states.cljc (100%) rename test/{ => core}/mount/test/fun_with_values.cljc (100%) rename test/{ => core}/mount/test/helper.cljc (100%) rename test/{ => core}/mount/test/parts.cljc (100%) rename test/{ => core}/mount/test/private_fun.cljc (100%) rename test/{ => core}/mount/test/start_with.cljc (100%) rename test/{ => core}/mount/test/start_without.cljc (100%) rename test/{ => core}/mount/test/stop_except.cljc (100%) rename test/{ => core}/mount/test/suspend_resume.cljc (100%) rename test/{ => core}/mount/test/var/fun_with_values.clj (100%) rename test/{ => core}/mount/test/var/private_fun.clj (100%) diff --git a/build.boot b/build.boot index 071de0f..6455c49 100644 --- a/build.boot +++ b/build.boot @@ -52,7 +52,7 @@ (in-ns 'dev)) (deftask test [] - (set-env! :source-paths #(conj % "test" "test/clj")) ;; (!) :source-paths must not overlap. + (set-env! :source-paths #(conj % "test/core" "test/clj")) ;; (!) :source-paths must not overlap. (bt/test)) (task-options! diff --git a/project.clj b/project.clj index 691833c..6d4b759 100644 --- a/project.clj +++ b/project.clj @@ -61,4 +61,4 @@ :optimizations :advanced :pretty-print false}}}}} - :test {:source-paths ["test" "test/clj" "test/cljs"]}}) + :test {:source-paths ["test/core" "test/clj" "test/cljs"]}}) diff --git a/test/mount/test.cljc b/test/core/mount/test.cljc similarity index 100% rename from test/mount/test.cljc rename to test/core/mount/test.cljc diff --git a/test/mount/test/cleanup_dirty_states.cljc b/test/core/mount/test/cleanup_dirty_states.cljc similarity index 100% rename from test/mount/test/cleanup_dirty_states.cljc rename to test/core/mount/test/cleanup_dirty_states.cljc diff --git a/test/mount/test/fun_with_values.cljc b/test/core/mount/test/fun_with_values.cljc similarity index 100% rename from test/mount/test/fun_with_values.cljc rename to test/core/mount/test/fun_with_values.cljc diff --git a/test/mount/test/helper.cljc b/test/core/mount/test/helper.cljc similarity index 100% rename from test/mount/test/helper.cljc rename to test/core/mount/test/helper.cljc diff --git a/test/mount/test/parts.cljc b/test/core/mount/test/parts.cljc similarity index 100% rename from test/mount/test/parts.cljc rename to test/core/mount/test/parts.cljc diff --git a/test/mount/test/private_fun.cljc b/test/core/mount/test/private_fun.cljc similarity index 100% rename from test/mount/test/private_fun.cljc rename to test/core/mount/test/private_fun.cljc diff --git a/test/mount/test/start_with.cljc b/test/core/mount/test/start_with.cljc similarity index 100% rename from test/mount/test/start_with.cljc rename to test/core/mount/test/start_with.cljc diff --git a/test/mount/test/start_without.cljc b/test/core/mount/test/start_without.cljc similarity index 100% rename from test/mount/test/start_without.cljc rename to test/core/mount/test/start_without.cljc diff --git a/test/mount/test/stop_except.cljc b/test/core/mount/test/stop_except.cljc similarity index 100% rename from test/mount/test/stop_except.cljc rename to test/core/mount/test/stop_except.cljc diff --git a/test/mount/test/suspend_resume.cljc b/test/core/mount/test/suspend_resume.cljc similarity index 100% rename from test/mount/test/suspend_resume.cljc rename to test/core/mount/test/suspend_resume.cljc diff --git a/test/mount/test/var/fun_with_values.clj b/test/core/mount/test/var/fun_with_values.clj similarity index 100% rename from test/mount/test/var/fun_with_values.clj rename to test/core/mount/test/var/fun_with_values.clj diff --git a/test/mount/test/var/private_fun.clj b/test/core/mount/test/var/private_fun.clj similarity index 100% rename from test/mount/test/var/private_fun.clj rename to test/core/mount/test/var/private_fun.clj From c7bb4d6a26baafc487ab8d68f8e44d4fd8e2e49d Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 21 Dec 2015 14:41:06 -0500 Subject: [PATCH 13/18] [boot]: plugging in cljs --- build.boot | 52 +++++++++++++++++++++++++-------- dev/clj/dev.clj | 2 ++ dev/resources/mount.cljs.edn | 1 + dev/resources/public/index.html | 2 +- 4 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 dev/resources/mount.cljs.edn diff --git a/build.boot b/build.boot index 6455c49..c007259 100644 --- a/build.boot +++ b/build.boot @@ -1,50 +1,66 @@ (set-env! :source-paths #{"src"} - :dependencies '[;; dev / examples / test + :dependencies '[;; mount brings _no dependencies_, everything here is for + ;; mount dev, examples apps and tests + [org.clojure/clojure "1.7.0" :scope "provided"] - [org.clojure/clojurescript "1.7.170" :scope "provided"] + [org.clojure/clojurescript "1.7.189" :scope "provided" :classifier "aot"] [datascript "0.13.3" :scope "provided"] [compojure "1.4.0" :scope "provided"] [ring/ring-jetty-adapter "1.1.0" :scope "provided"] [cheshire "5.5.0" :scope "provided"] - [hiccups "0.3.0" :scope "provided"] + [hiccups "0.3.0" :scope "provided" :exclusions [org.clojure/clojurescript]] [com.andrewmcveigh/cljs-time "0.3.14" :scope "provided"] [ch.qos.logback/logback-classic "1.1.3" :scope "provided"] [org.clojure/tools.logging "0.3.1" :scope "provided"] [robert/hooke "1.3.0" :scope "provided"] [org.clojure/tools.namespace "0.2.11" :scope "provided"] - [org.clojure/tools.nrepl "0.2.11" :scope "provided"] + [org.clojure/tools.nrepl "0.2.12" :scope "provided"] [com.datomic/datomic-free "0.9.5327" :scope "provided" :exclusions [joda-time]] - ;; boot + ;; boot clj [boot/core "2.5.1" :scope "provided"] [adzerk/bootlaces "0.1.13" :scope "test"] [adzerk/boot-logservice "1.0.1" :scope "test"] - [adzerk/boot-test "1.0.6" :scope "test"]]) + [adzerk/boot-test "1.0.6" :scope "test"] + + ;; boot cljs + [adzerk/boot-cljs "1.7.170-3" :scope "test"] + [adzerk/boot-cljs-repl "0.3.0" :scope "test"] + [pandeiro/boot-http "0.7.1-SNAPSHOT" :scope "test"] + [com.cemerick/piggieback "0.2.1" :scope "test" :exclusions [org.clojure/clojurescript]] + [weasel "0.7.0" :scope "test" :exclusions [org.clojure/clojurescript]] + [adzerk/boot-reload "0.4.2" :scope "test"] + [crisptrutski/boot-cljs-test "0.2.1-SNAPSHOT" :scope "test"]]) (require '[adzerk.bootlaces :refer :all] '[adzerk.boot-test :as bt] '[adzerk.boot-logservice :as log-service] - '[clojure.tools.logging :as log] + '[adzerk.boot-cljs :refer [cljs]] + '[adzerk.boot-cljs-repl :refer [cljs-repl start-repl]] + '[adzerk.boot-reload :refer [reload]] + '[pandeiro.boot-http :refer :all] + '[crisptrutski.boot-cljs-test :refer [test-cljs]] + '[clojure.tools.logging :as log] '[clojure.tools.namespace.repl :refer [set-refresh-dirs]]) (def +version+ "0.1.7-SNAPSHOT") (bootlaces! +version+) -(def log-config +(def log4b [:configuration [:appender {:name "STDOUT" :class "ch.qos.logback.core.ConsoleAppender"} [:encoder [:pattern "%-5level %logger{36} - %msg%n"]]] [:root {:level "TRACE"} [:appender-ref {:ref "STDOUT"}]]]) -(deftask dev [] - - (set-env! :source-paths #(conj % "dev/clj")) +(deftask dev [] + (set-env! :source-paths #(conj % "dev/clj" "dev/cljs")) + (set-env! :resource-paths #{"dev/resources"}) (alter-var-root #'log/*logger-factory* - (constantly (log-service/make-factory log-config))) + (constantly (log-service/make-factory log4b))) (apply set-refresh-dirs (get-env :directories)) (load-data-readers!) @@ -55,6 +71,18 @@ (set-env! :source-paths #(conj % "test/core" "test/clj")) ;; (!) :source-paths must not overlap. (bt/test)) +(deftask cljs-example + "mount cljs example" + [] + (set-env! :source-paths #(conj % "dev/clj" "dev/cljs")) + (set-env! :resource-paths #{"dev/resources"}) + + (comp + (wait) + (serve :dir "dev/resources/public/") + (cljs-repl) + (cljs :optimizations :advanced :ids #{"mount"}))) + (task-options! push #(-> (into {} %) (assoc :ensure-branch nil)) pom {:project 'mount diff --git a/dev/clj/dev.clj b/dev/clj/dev.clj index 69eb5c6..252108f 100644 --- a/dev/clj/dev.clj +++ b/dev/clj/dev.clj @@ -1,6 +1,7 @@ (ns dev (:require [clojure.pprint :refer [pprint]] [clojure.tools.namespace.repl :as tn] + [boot.core :refer [load-data-readers!]] [mount.core :as mount] [app.utils.logging :refer [with-logging-status]] [app.www] @@ -38,3 +39,4 @@ (tn/refresh :after 'dev/go)) (mount/in-clj-mode) +(load-data-readers!) diff --git a/dev/resources/mount.cljs.edn b/dev/resources/mount.cljs.edn new file mode 100644 index 0000000..b75e4b2 --- /dev/null +++ b/dev/resources/mount.cljs.edn @@ -0,0 +1 @@ +{:require [app.example]} diff --git a/dev/resources/public/index.html b/dev/resources/public/index.html index f7ca4d9..ebacc81 100644 --- a/dev/resources/public/index.html +++ b/dev/resources/public/index.html @@ -1,6 +1,6 @@ - + From c7a737126f214792ee687ad014debc7ee667b8fe Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 21 Dec 2015 16:49:13 -0500 Subject: [PATCH 14/18] [boot]: plugging in boot-cljs-test --- build.boot | 15 ++++++++++++++- test/resources/mount.cljs.edn | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 test/resources/mount.cljs.edn diff --git a/build.boot b/build.boot index c007259..2ba881f 100644 --- a/build.boot +++ b/build.boot @@ -40,7 +40,7 @@ '[adzerk.boot-cljs-repl :refer [cljs-repl start-repl]] '[adzerk.boot-reload :refer [reload]] '[pandeiro.boot-http :refer :all] - '[crisptrutski.boot-cljs-test :refer [test-cljs]] + '[crisptrutski.boot-cljs-test :as tcs] '[clojure.tools.logging :as log] '[clojure.tools.namespace.repl :refer [set-refresh-dirs]]) @@ -71,6 +71,19 @@ (set-env! :source-paths #(conj % "test/core" "test/clj")) ;; (!) :source-paths must not overlap. (bt/test)) +(deftask test-cljs [] + (set-env! :source-paths #(conj % "test/core" "test/cljs")) + (set-env! :resource-paths #{"test/resources"}) + + (comp + (tcs/test-cljs :out-file "mount.js"))) + +(deftask test-cljs-advanced [] + (set-env! :source-paths #(conj % "dev/clj" "dev/cljs")) + (set-env! :resource-paths #{"dev/resources"}) + + (cljs :optimizations :advanced :ids #{"mount"})) + (deftask cljs-example "mount cljs example" [] diff --git a/test/resources/mount.cljs.edn b/test/resources/mount.cljs.edn new file mode 100644 index 0000000..9e3acae --- /dev/null +++ b/test/resources/mount.cljs.edn @@ -0,0 +1 @@ +{:require [mount.test]} From 9d23f190b344f5b7ce3d0bbbde5107c45ed2648c Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 21 Dec 2015 20:04:05 -0500 Subject: [PATCH 15/18] [boot]: plugging into circle --- build.boot | 6 ++++-- circle.yml | 11 ++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/build.boot b/build.boot index 2ba881f..edb8422 100644 --- a/build.boot +++ b/build.boot @@ -76,13 +76,15 @@ (set-env! :resource-paths #{"test/resources"}) (comp - (tcs/test-cljs :out-file "mount.js"))) + (tcs/test-cljs ;; :optimizations :advanced + :out-file "mount.js"))) (deftask test-cljs-advanced [] (set-env! :source-paths #(conj % "dev/clj" "dev/cljs")) (set-env! :resource-paths #{"dev/resources"}) - (cljs :optimizations :advanced :ids #{"mount"})) + (comp + (cljs :optimizations :advanced :ids #{"mount"}))) (deftask cljs-example "mount cljs example" diff --git a/circle.yml b/circle.yml index a07a0ea..e3cccfc 100644 --- a/circle.yml +++ b/circle.yml @@ -2,9 +2,14 @@ machine: java: version: oraclejdk8 +dependencies: + pre: + - wget https://github.com/boot-clj/boot-bin/releases/download/2.4.2/boot.sh + - mv boot.sh boot && chmod a+x boot && sudo mv boot /usr/local/bin + test: override: - - lein do clean, test - - lein do clean, doo phantom test once - - lein do clean, cljsbuild once prod + - boot test + - boot test-cljs + - boot test-cljs-advanced - lein test2junit From a0bc17073ee6aab167b8fbca737ee508e51d1103 Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 21 Dec 2015 20:12:47 -0500 Subject: [PATCH 16/18] #22: ns-recompile png --- doc/img/ns-recompile.png | Bin 0 -> 30953 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/img/ns-recompile.png diff --git a/doc/img/ns-recompile.png b/doc/img/ns-recompile.png new file mode 100644 index 0000000000000000000000000000000000000000..f8391a19c0c5f44a18aa57c06f3a90f602c46e23 GIT binary patch literal 30953 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 literal 0 HcmV?d00001 From d66832aaf18fdb89dd6b1aa5744564c4c704befa Mon Sep 17 00:00:00 2001 From: anatoly Date: Mon, 21 Dec 2015 20:45:15 -0500 Subject: [PATCH 17/18] [boot]: removing dev/resources from dev task tools.namespace refreshes the whole world with them. will either figure out how to provide a safe list of dirs automagically or (preferable option is to) rip tools.namespace out --- build.boot | 1 - 1 file changed, 1 deletion(-) diff --git a/build.boot b/build.boot index edb8422..b9c2b0d 100644 --- a/build.boot +++ b/build.boot @@ -57,7 +57,6 @@ (deftask dev [] (set-env! :source-paths #(conj % "dev/clj" "dev/cljs")) - (set-env! :resource-paths #{"dev/resources"}) (alter-var-root #'log/*logger-factory* (constantly (log-service/make-factory log4b))) From f936726b34da647e08592150f1a18ae513d5a48e Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 21 Dec 2015 20:42:29 -0500 Subject: [PATCH 18/18] [docs]: about ns state recompilation --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ca5448..08604d9 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,9 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt - [Suspendable Lifecycle](#suspendable-lifecycle) - [Plugging into (reset)](#plugging-into-reset) - [Suspendable Example Application](#suspendable-example-application) -- [Affected States](#affected-states) - [ClojureScript is Clojure](doc/clojurescript.md#managing-state-in-clojurescript) +- [Affected States](#affected-states) +- [Recompiling Namespaces with Running States](#recompiling-namespaces-with-running-states) - [Logging](#logging) - [Mount and Develop!](#mount-and-develop) - [Running New York Stock Exchange](#running-new-york-stock-exchange) @@ -411,6 +412,23 @@ $ git checkout suspendable Switched to branch 'suspendable' ``` +## Recompiling Namespaces with Running States + +At the development time, whenever you "recompile" a namespace, depending on your setup, new versions of its recompiled classes would get reloaded. + +In case this namespace included a state reference (i.e. `(defstate ...)`), mount will check if this state is running at the point of recompilation, and if it is, _it will stop it_. Since after the recompilation an old reference to this state will be lost. Mount will also let you know if the state was stopped during recompilation: + + + +The state of course can be started again: + +```clojure +dev=> (mount/start #'app.example/nrepl) + +INFO app.utils.logging - >> starting.. #'app.example/nrepl +{:started ["#'app.example/nrepl"]} +``` + ## Affected States Every time a lifecycle function (start/stop/suspend/resume) is called mount will return all the states that were affected: