removing dependency on var meta

This commit is contained in:
anatoly 2015-12-05 20:31:17 -05:00
parent e8b04318e3
commit fb45dbbb4e
2 changed files with 66 additions and 62 deletions

View file

@ -4,17 +4,15 @@
(defonce ^:private mount-state 42) (defonce ^:private mount-state 42)
(defonce ^:private -args (atom :no-args)) ;; mostly for command line args and external files (defonce ^:private -args (atom :no-args)) ;; mostly for command line args and external files
(defonce ^:private state-seq (atom 0)) (defonce ^:private state-seq (atom 0))
(defonce ^:private state-order (atom {})) (defonce ^:private meta-state (atom {}))
(defonce ^:private running (atom {})) ;; to clean dirty states on redefs (defonce ^:private running (atom {})) ;; to clean dirty states on redefs
;; supporting tools.namespace: (disable-reload!) ;; supporting tools.namespace: (disable-reload!)
(alter-meta! *ns* assoc ::load false) ;; to exclude the dependency (alter-meta! *ns* assoc ::load false) ;; to exclude the dependency
(defn- make-state-seq [state] (defn- make-state-seq [state]
(or (@state-order state) (or (:order (@meta-state state))
(let [nseq (swap! state-seq inc)] (swap! state-seq inc)))
(swap! state-order assoc state nseq)
nseq)))
(deftype NotStartedState [state] (deftype NotStartedState [state]
Object Object
@ -30,13 +28,13 @@
(IllegalArgumentException. "suspendable state should have a resume function (i.e. missing :resume fn)")))) (IllegalArgumentException. "suspendable state should have a resume function (i.e. missing :resume fn)"))))
(defn- with-ns [ns name] (defn- with-ns [ns name]
(str ns "/" name)) (str "#'" ns "/" name))
(defn- pounded? [f] (defn- pounded? [f]
(let [pound "(fn* [] "] ;;TODO: think of a better (i.e. typed) way to distinguish #(f params) from (fn [params] (...))) (let [pound "(fn* [] "] ;;TODO: think of a better (i.e. typed) way to distinguish #(f params) from (fn [params] (...)))
(.startsWith (str f) pound))) (.startsWith (str f) pound)))
(defn- unpound [f] (defn unpound [f]
(if (pounded? 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)) f))
@ -50,88 +48,91 @@
(when-let [stop (@running state)] (when-let [stop (@running state)]
(stop))) (stop)))
;; (!) TODO: this should be private (needs thinking)
;; it is public now, can be called by "defstate" on macro expansion
(defn update-meta! [path v]
(swap! meta-state assoc-in path v))
(defmacro defstate [state & body] (defmacro defstate [state & body]
(let [[state params] (macro/name-with-attributes state body) (let [[state params] (macro/name-with-attributes state body)
{:keys [start stop suspend resume] :as lifecycle} (apply hash-map params)] {: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) (validate lifecycle)
(cleanup-if-dirty (with-ns *ns* state)) (cleanup-if-dirty state-name)
(let [s-meta (cond-> {:mount-state mount-state (let [s-meta (cond-> {:mount-state mount-state
:order (make-state-seq (with-ns *ns* state)) :order order
:start `(fn [] ~start) :start `(fn [] ~start)
:status #{:stopped}} :status #{:stopped}}
stop (assoc :stop `(fn [] ~(unpound stop))) stop (assoc :stop `(fn [] ~stop))
suspend (assoc :suspend `(fn [] ~suspend)) suspend (assoc :suspend `(fn [] ~suspend))
resume (assoc :resume `(fn [] ~resume)))] resume (assoc :resume `(fn [] ~resume)))]
`(defonce ~(with-meta state (merge (meta state) s-meta)) `(do
(NotStartedState. ~(str state)))))) (defonce ~state (NotStartedState. ~state-name))
(update-meta! [~state-name] (assoc ~s-meta :var (var ~state)))
(var ~state)))))
(defn- record! [{:keys [ns name]} f done] (defn- record! [state-name f done]
(let [state (f)] (let [state (f)]
(swap! done conj (ns-resolve ns name)) (swap! done conj state-name)
state)) state))
(defn- up [var {:keys [ns name start stop resume status] :as state} done] (defn- up [state {:keys [var start stop resume status]} done]
(when-not (:started status) (when-not (:started status)
(let [s (try (if (:suspended status) (let [s (try (if (:suspended status)
(record! state resume done) (record! state resume done)
(record! state start done)) (record! state start done))
(catch Throwable t (catch Throwable t
(throw (RuntimeException. (str "could not start [" name "] due to") t))))] (throw (RuntimeException. (str "could not start [" state "] due to") t))))]
(intern ns (symbol name) s) (alter-var-root var (constantly s))
(swap! running assoc (with-ns ns name) stop) (swap! running assoc state stop)
(alter-meta! var assoc :status #{:started})))) (update-meta! [state :status] #{:started}))))
(defn- down [var {:keys [ns name stop status] :as state} done] (defn- down [state {:keys [var stop status]} done]
(when (some status #{:started :suspended}) (when (some status #{:started :suspended})
(when stop (when stop
(try (try
(record! state stop done) (record! state stop done)
(catch Throwable t (catch Throwable t
(throw (RuntimeException. (str "could not stop [" name "] due to") t))))) (throw (RuntimeException. (str "could not stop [" state "] due to") t)))))
(intern ns (symbol name) (NotStartedState. name)) ;; (!) if a state does not have :stop when _should_ this might leak (alter-var-root var (constantly (NotStartedState. state))) ;; (!) if a state does not have :stop when _should_ this might leak
(swap! running dissoc (with-ns ns name)) (swap! running dissoc state)
(alter-meta! var assoc :status #{:stopped}))) (update-meta! [state :status] #{:stopped})))
(defn- sigstop [var {:keys [ns name suspend resume status] :as state} done] (defn- sigstop [state {:keys [var resume suspend status]} done]
(when (and (:started status) resume) ;; can't have suspend without resume, but the reverse is possible (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?) (when suspend ;; don't suspend if there is only resume function (just mark it :suspended?)
(let [s (try (record! state suspend done) (let [s (try (record! state suspend done)
(catch Throwable t (catch Throwable t
(throw (RuntimeException. (str "could not suspend [" name "] due to") t))))] (throw (RuntimeException. (str "could not suspend [" state "] due to") t))))]
(intern ns (symbol name) s))) (alter-var-root var (constantly s))))
(alter-meta! var assoc :status #{:suspended}))) (update-meta! [state :status] #{:suspended})))
(defn- sigcont [var {:keys [ns name start resume status] :as state} done] (defn- sigcont [state {:keys [var resume status]} done]
(when (instance? NotStartedState var) (when (instance? NotStartedState var)
(throw (RuntimeException. (str "could not resume [" name "] since it is stoppped (i.e. not suspended)")))) (throw (RuntimeException. (str "could not resume [" state "] since it is stoppped (i.e. not suspended)"))))
(when (:suspended status) (when (:suspended status)
(let [s (try (record! state resume done) (let [s (try (record! state resume done)
(catch Throwable t (catch Throwable t
(throw (RuntimeException. (str "could not resume [" name "] due to") t))))] (throw (RuntimeException. (str "could not resume [" state "] due to") t))))]
(intern ns (symbol name) s) (alter-var-root var (constantly s))
(alter-meta! var assoc :status #{:started})))) (update-meta! [state :status] #{:started}))))
;;TODO args might need more thinking ;;TODO args might need more thinking
(defn args [] @-args) (defn args [] @-args)
(defn mount-state? [var] (defn- find-all-states []
(= (-> var meta :mount-state) (keys @meta-state))
mount-state))
(defn find-all-states []
(->> (all-ns)
(mapcat ns-interns)
(map second)
(filter mount-state?)))
;;TODO ns based for now. need to be _state_ based ;;TODO ns based for now. need to be _state_ based
(defn- add-deps [{:keys [ns] :as state} all] #_(defn- add-deps [{:keys [ns] :as state} all]
(let [refers (ns-refers ns) (let [refers (ns-refers ns)
any (set all) any (set all)
deps (filter (comp any val) refers)] deps (filter (comp any val) refers)]
(assoc state :deps deps))) (assoc state :deps deps)))
(defn states-with-deps [] #_(defn states-with-deps []
(let [all (find-all-states)] (let [all (find-all-states)]
(->> (map (comp #(add-deps % all) (->> (map (comp #(add-deps % all)
#(select-keys % [:name :order :ns :status]) #(select-keys % [:name :order :ns :status])
@ -141,10 +142,11 @@
(defn- bring [states fun order] (defn- bring [states fun order]
(let [done (atom [])] (let [done (atom [])]
(->> states (as-> states $
(sort-by (comp :order meta) order) (map str $)
(map #(fun % (meta %) done)) (select-keys @meta-state $)
dorun) (sort-by (comp :order val) order $)
(doseq [[k v] $] (fun k v done)))
@done)) @done))
(defn- merge-lifecycles (defn- merge-lifecycles
@ -159,23 +161,23 @@
:start start :stop stop :suspend suspend :resume resume))) :start start :stop stop :suspend suspend :resume resume)))
(defn- rollback! [state] (defn- rollback! [state]
(let [{:keys [origin]} (meta state)] (let [{:keys [origin] :as sub} (@meta-state state)]
(when origin (when origin
(alter-meta! state #(merge-lifecycles % origin))))) (update-meta! [state] (merge-lifecycles sub origin)))))
(defn- substitute! [state with] (defn- substitute! [state with]
(let [lifecycle-fns #(select-keys % [:start :stop :suspend :resume :status]) (let [lifecycle-fns #(select-keys % [:start :stop :suspend :resume :status])
origin (meta state) origin (@meta-state state)
sub (meta with)] sub (@meta-state with)]
(alter-meta! with assoc :sub? true) (update-meta! [with :sub?] true)
(alter-meta! state #(merge-lifecycles % (lifecycle-fns origin) sub)))) (update-meta! [state] (merge-lifecycles origin (lifecycle-fns origin) sub))))
(defn- unsub [state] (defn- unsub [state]
(when (-> (meta state) :sub?) (when (-> (@meta-state state) :sub?)
(alter-meta! state dissoc :sub?))) (update-meta! [state :sub?] nil)))
(defn- all-without-subs [] (defn- all-without-subs []
(remove (comp :sub? meta) (find-all-states))) (remove (comp :sub? @meta-state) (find-all-states)))
(defn start [& states] (defn start [& states]
(let [states (or (seq states) (all-without-subs))] (let [states (or (seq states) (all-without-subs))]
@ -190,6 +192,7 @@
(defn stop-except [& states] (defn stop-except [& states]
(let [all (set (find-all-states)) (let [all (set (find-all-states))
states (map str states)
states (remove (set states) all)] states (remove (set states) all)]
(apply stop states))) (apply stop states)))
@ -201,12 +204,13 @@
(defn start-with [with] (defn start-with [with]
(doseq [[from to] with] (doseq [[from to] with]
(substitute! from to)) (substitute! (str from) (str to)))
(start)) (start))
(defn start-without [& states] (defn start-without [& states]
(if (first states) (if (first states)
(let [app (set (all-without-subs)) (let [app (set (all-without-subs))
states (map str states)
without (remove (set states) app)] without (remove (set states) app)]
(apply start without)) (apply start without))
(start))) (start)))

View file

@ -26,10 +26,10 @@
:resume (if (status :suspended) ">> resuming"))) :resume (if (status :suspended) ">> resuming")))
(defn log-status [f & args] (defn log-status [f & args]
(let [{:keys [ns name] :as state} (second args) (let [{:keys [var] :as state} (second args)
action (f-to-action f)] action (f-to-action f)]
(when-let [taking-over-the-world (whatcha-doing? state action)] (when-let [taking-over-the-world (whatcha-doing? state action)]
(info (str taking-over-the-world ".. " (ns-resolve ns name)))) (info (str taking-over-the-world ".. " var)))
(apply f args))) (apply f args)))
(defonce lifecycle-fns (defonce lifecycle-fns