Created
December 29, 2021 01:05
-
-
Save kurtharriger/3097bdde3f50cc83645fa16b8e848f49 to your computer and use it in GitHub Desktop.
dime app helper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns user | |
(:require | |
[app.server :as server] | |
[dime.core :as di] | |
[dime.var :as dv :refer [defconst]])) | |
(def app-namespaces | |
['app.ynab | |
'app.budget | |
'app.parser | |
'app.server]) | |
;; todo: maybe services can specify a stop method on | |
;; metadata or implement a lifecycle protocol | |
(defn stop [app] | |
(when-let [server (:app/server app)] | |
((:stop server)))) | |
(def ^:dynamic *quiet* false) | |
(defn notify-starting [] | |
(when-not *quiet* (prn "Starting..."))) | |
(defn notify-started [] | |
(when-not *quiet* (prn "Started."))) | |
(defn notify-stopping [] | |
(when-not *quiet* (prn "Stopping."))) | |
(defn notify-stopped [] | |
(when-not *quiet* (prn "Stopped."))) | |
(defn start* [] | |
(notify-starting) | |
(let [graph (dv/ns-vars->graph app-namespaces) | |
app (with-meta (di/inject-all graph {}) {:graph graph})] | |
(notify-started) | |
app)) | |
(defn stop* [app] | |
(when-let [server (:app.server/server app)] | |
(notify-stopping) | |
((:stop server)) | |
(notify-stopped) | |
nil)) | |
(defonce app-state (atom nil)) | |
(defn start! [] | |
(swap! app-state #(or % (start*)))) | |
(defn stop! [] | |
(swap! app-state stop*)) | |
(defn restart! [] | |
(stop!) | |
(start!) | |
nil) | |
(defprotocol AppAction | |
(run [this app args])) | |
(extend-protocol AppAction | |
clojure.lang.IFn | |
(run [this app args] (apply this (cons app args)))) | |
(defn uget [map key] | |
(let [value (get map key ::not-found)] | |
(if (and (= value ::not-found) (not (qualified-keyword? key))) | |
(if-let [matches (get (group-by name (keys map)) (name key) ::not-found)] | |
(if (= (count matches) 1) | |
(get map (first matches) ::not-found) | |
::ambiguous)) | |
value))) | |
(extend-protocol AppAction | |
clojure.lang.Keyword | |
(run [this app args] | |
(when-let [value (uget app this)] | |
(if (fn? value) | |
(apply value args) | |
value)))) | |
(defn app | |
"Starts application if it is not currenlty running. | |
If no action is specified starts app and returns nil | |
(to prevent printing lots of data to repl) | |
If an action is specified then it will be run on the | |
started application. | |
action can be a function such as: | |
(app keys) to list exposed objects | |
(app identity) to return application map (since no action returns nil) | |
(app meta) to return metadata and access the graph | |
If the action is a keyword then it will lookup the value from the app | |
and if that value is a function it will invoke it with the specified args | |
(app :app.ynab/get-budgets) | |
(app :app.ynab/get-budgets-by-id budget-id) | |
or if you are already in the a app.ynab namespace you can just do (user/app ::get-budget-by-id ...) | |
which is almost as easy as calling the function directly (and possibly much easier when you need to have the dependencies injected) | |
If the keyword is unquafied it will attempt to find a qualified keyword with same name and use that as long as there is only one match | |
If you want to retrieve the partial function without invoking it then apply a function such as get or uget for search using unqualified keyword | |
(app get key) or (app uget key)" | |
[& [action & args]] | |
;; todo: maybe also create an AppAtomAction | |
;; that could be applied to the atom | |
;; so app becomes the facade for everthing | |
;; (app start) instead of (user/start!) | |
;; or maybe something like (app *ns*) | |
;; to reload and restart services in that | |
;; namespace... | |
(when-let [app (swap! app-state #(or % (start*)))] | |
(when action | |
(run action app args)))) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment