{"id":13746122,"url":"https://github.com/tolitius/mount","last_synced_at":"2025-05-13T17:05:24.987Z","repository":{"id":1770550,"uuid":"44562759","full_name":"tolitius/mount","owner":"tolitius","description":"managing Clojure and ClojureScript app state since (reset)","archived":false,"fork":false,"pushed_at":"2025-01-31T17:58:57.000Z","size":1647,"stargazers_count":1236,"open_issues_count":31,"forks_count":87,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-05-03T00:38:58.221Z","etag":null,"topics":["clojure","clojurescript","state-management"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tolitius.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-10-19T20:41:45.000Z","updated_at":"2025-04-11T02:15:20.000Z","dependencies_parsed_at":"2024-08-03T06:01:41.757Z","dependency_job_id":"5fa00807-a196-4ae2-af19-7d3bb0690572","html_url":"https://github.com/tolitius/mount","commit_stats":{"total_commits":400,"total_committers":29,"mean_commits":"13.793103448275861","dds":0.12,"last_synced_commit":"58e2ded43056180171ccacf2663571bf398fa690"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Fmount","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Fmount/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Fmount/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Fmount/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tolitius","download_url":"https://codeload.github.com/tolitius/mount/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253990460,"owners_count":21995774,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","clojurescript","state-management"],"created_at":"2024-08-03T06:00:47.309Z","updated_at":"2025-05-13T17:05:24.961Z","avatar_url":"https://github.com/tolitius.png","language":"Clojure","funding_links":[],"categories":["Clojure","Dependency injection","Awesome ClojureScript"],"sub_categories":["State Management"],"readme":"\u003e I think that it's _extraordinarily important_ that we in computer science keep fun in computing\n\n_**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](https://web.mit.edu/6.001/6.037/sicp.pdf)_\n\n# mount \u003cimg src=\"doc/img/mount-logo.png\" width=\"70px\"\u003e\n[![\u003c! release](https://img.shields.io/badge/dynamic/json.svg?label=release\u0026url=https%3A%2F%2Fclojars.org%2Fmount%2Flatest-version.json\u0026query=version\u0026colorB=blue)](https://github.com/tolitius/mount/releases)\n[![\u003c! clojars](https://img.shields.io/clojars/v/mount.svg)](https://clojars.org/mount)\n\n###### _any_ questions or feedback: [`#mount`](https://clojurians.slack.com/messages/mount/) clojurians slack channel \u003cimg src=\"doc/img/slack-icon.png\" width=\"15px\"\u003e (or just [open an issue](https://github.com/tolitius/mount/issues))\n\n**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*\n\n- [Why?](#why)\n  - [Differences from Component](#differences-from-component)\n- [How](#how)\n  - [Creating State](#creating-state)\n  - [Using State](#using-state)\n- [Dependencies](#dependencies)\n  - [Talking States](#talking-states)\n- [Value of Values](#value-of-values)\n- [The Importance of Being Reloadable](#the-importance-of-being-reloadable)\n- [Start and Stop Order](#start-and-stop-order)\n- [Composing States](#composing-states)\n- [Start and Stop Parts of Application](#start-and-stop-parts-of-application)\n- [Start an Application Without Certain States](#start-an-application-without-certain-states)\n- [Swapping Alternate Implementations](#swapping-alternate-implementations)\n  - [Swapping States with Values](#swapping-states-with-values)\n  - [Swapping States with States](#swapping-states-with-states)\n- [Stop an Application Except Certain States](#stop-an-application-except-certain-states)\n- [ClojureScript is Clojure](doc/clojurescript.md#managing-state-in-clojurescript)\n- [cljc mode](#cljc-mode)\n  - [Disable Lazy Start](#disable-lazy-start)\n- [Packaging](#packaging)\n- [Affected States](#affected-states)\n- [Recompiling Namespaces with Running States](#recompiling-namespaces-with-running-states)\n  - [:on-reload](#on-reload)\n- [Cleaning up Deleted States](#cleaning-up-deleted-states)\n- [Logging](#logging)\n  - [mount-up](#mount-up)\n  - [Manual AOP](#manual-aop)\n- [Exception Handling](#exception-handling)\n- [Clojure Version](#clojure-version)\n- [Mount and Develop!](#mount-and-develop)\n  - [Running New York Stock Exchange](#running-new-york-stock-exchange)\n  - [New York Stock Exchange Maintenance](#new-york-stock-exchange-maintenance)\n- [Web and Uberjar](#web-and-uberjar)\n- [Runtime Arguments](#runtime-arguments)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Why?\n\nClojure is\n\n* powerful\n* simple\n* and _fun_\n\nDepending on how application state is managed during development, the above three superpowers can either stay,\ngo somewhat, or go completely.\n\nIf Clojure REPL (i.e. `lein repl`, `boot repl`) fired up instantly, the need to reload application state\ninside the REPL would go away. But at the moment, and for some time in the future, managing state by making it\nreloadable within the same REPL session is important to retain all the Clojure superpowers.\n\nHere is a good [breakdown](http://blog.ndk.io/clojure-bootstrapping.html) on the Clojure REPL\nstartup time, and it is [not because of JVM](http://blog.ndk.io/jvm-slow-startup.html).\n\n`mount` is here to preserve all the Clojure superpowers while making _the application state_ enjoyably reloadable.\n\nThere is another Clojure superpower that `mount` is made to retain: Clojure community.\nPull request away, let's solve this thing!\n\n### Differences from Component\n\n`mount` is an alternative to the [component](https://github.com/stuartsierra/component) approach with notable [differences](doc/differences-from-component.md#differences-from-component).\n\n## How\n\n```clojure\n(require '[mount.core :refer [defstate]])\n```\n\n### Creating State\n\nCreating state is easy:\n\n```clojure\n(defstate conn :start (create-conn))\n```\n\nwhere the `create-conn` function creates a connection (for example to a database) and is defined elsewhere, can be right above it.\n\nIn case this state needs to be cleaned / destroyed between reloads, there is also `:stop`\n\n```clojure\n(defstate conn :start (create-conn)\n               :stop (disconnect conn))\n```\n\nThat is pretty much it. But wait, there is more.. this state is _a top level being_, which means it can be simply\n`required` by other namespaces or in REPL:\n\n```clojure\ndev=\u003e (require '[app.db :refer [conn]])\nnil\ndev=\u003e conn\n#object[datomic.peer.LocalConnection 0x1661a4eb \"datomic.peer.LocalConnection@1661a4eb\"]\n```\n\n### Using State\n\nFor example let's say an `app` needs a connection above. No problem:\n\n```clojure\n(ns app\n  (:require [above :refer [conn]]))\n```\n\nwhere `above` is an arbitrary namespace that defines the above state / connection.\n\n### Documentation String\n\nAs in any definition (i.e. `def`, `defn`) a documentation string can be added to better describe a state:\n\n```clojure\n(defstate answer\n  \"answer to the ultimate question of life universe and everything\"\n  :start (+ 1 41))\n```\n```clojure\n(doc answer)\n-------------------------\ndev/answer\n  answer to the ultimate question of life universe and everything\n```\n\n## Dependencies\n\nIf the whole app is one big application context (or `system`), cross dependencies with a solid dependency graph\nis an integral part of the system.\n\nBut if a state is a simple top level being, these beings can coexist with each other and with other\nnamespaces by being `required` instead.\n\nIf a managing state library requires a whole app buy-in, where everything is a bean or a component,\nit is a framework, and  dependency graph is usually quite large and complex,\nsince it has _everything_ (every piece of the application) in it.\n\nBut if stateful things are kept lean and low level (i.e. I/O, queues, threads, connections, etc.), dependency graphs are simple and small, and everything else is just namespaces and functions: the way it should be.\n\n### Talking States\n\nThere are of course direct dependencies that `mount` respects:\n\n```clojure\n(ns app.config\n  (:require [mount.core :refer [defstate]]))\n\n(defstate config\n  :start (load-config \"test/resources/config.edn\"))\n```\n\nthis `config`, being top level, can be used in other namespaces, including the ones that create states:\n\n```clojure\n(ns app.database\n  (:require [mount.core :refer [defstate]]\n            [app.config :refer [config]]))\n\n(defstate conn :start (create-connection config))\n```\n\n[here](dev/clj/app/www.clj#L32) is an example of a web server that \"depends\" on a similar `config`.\n\n###### _(the example `load-config` function above comes from [cprop](https://github.com/tolitius/cprop), but could of course be a custom function that loads configuration from a file)_\n\n## Value of values\n\nLifecycle functions start/stop can take both functions and values. This is \"valuable\" and also works:\n\n```clojure\n(defstate answer-to-the-ultimate-question-of-life-the-universe-and-everything :start 42)\n```\n\nWhile it would be useful in REPL and for testing, real application states would usually have start / stop logic, in other words, the real lifecycle.\n\nBesides scalar values, lifecycle functions can take anonymous functions, partial functions, function references, etc.. Here are some examples:\n\n```clojure\n(defn f [n]\n  (fn [m]\n    (+ n m)))\n\n(defn g [a b]\n  (+ a b))\n\n(defn- pf [n]\n  (+ 41 n))\n\n(defn fna []\n  42)\n\n(defstate scalar :start 42)\n(defstate fun :start #(inc 41))\n(defstate with-fun :start (inc 41))\n(defstate with-partial :start (partial g 41))\n(defstate f-in-f :start (f 41))\n(defstate f-no-args-value :start (fna))\n(defstate f-no-args :start fna)\n(defstate f-args :start g)\n(defstate f-value :start (g 41 1))\n(defstate private-f :start pf)\n```\n\nCheck out [fun-with-values-test](test/core/mount/test/fun_with_values.cljc) for more details.\n\n## The Importance of Being Reloadable\n\n`mount` has start and stop functions that will walk all the states created with `defstate` and start / stop them\naccordingly: i.e. will call their `:start` and `:stop` defined functions. Hence the whole application state can be reloaded in REPL e.g.:\n\n```\ndev=\u003e (require '[mount.core :as mount])\n\ndev=\u003e (mount/stop)\ndev=\u003e (mount/start)\n```\n\nWhile it is not always necessary, mount lifecycle can be easily hooked up to [tools.namespace](https://github.com/clojure/tools.namespace),\nto make the whole application reloadable with refreshing the app namespaces.\nHere is a [dev.clj](dev/clj/dev.clj) as an example, that sums up to:\n\n```clojure\n(defn go []\n  (start)\n  :ready)\n\n(defn reset []\n  (stop)\n  (tn/refresh :after 'dev/go))\n```\n\nthe `(reset)` is then used in REPL to restart / reload application state without the need to restart the REPL itself.\n\n## Start and Stop Order\n\nSince dependencies are \"injected\" by `require`ing on the namespace level, `mount` **trusts the Clojure compiler** to\nmaintain the start and stop order for all the `defstates`.\n\nThe \"start\" order is then recorded and replayed on each `(reset)`.\n\nThe \"stop\" order is simply `(reverse \"start order\")`:\n\n```clojure\ndev=\u003e (reset)\n08:21:39.430 [nREPL-worker-1] DEBUG mount - \u003c\u003c stopping..  nrepl\n08:21:39.431 [nREPL-worker-1] DEBUG mount - \u003c\u003c stopping..  conn\n08:21:39.432 [nREPL-worker-1] DEBUG mount - \u003c\u003c stopping..  config\n\n:reloading (app.config app.nyse app.utils.datomic app)\n\n08:21:39.462 [nREPL-worker-1] DEBUG mount - \u003e\u003e starting..  config\n08:21:39.463 [nREPL-worker-1] DEBUG mount - \u003e\u003e starting..  conn\n08:21:39.481 [nREPL-worker-1] DEBUG mount - \u003e\u003e starting..  nrepl\n:ready\n```\n\nYou can see examples of start and stop flows in the [example app](README.md#mount-and-develop).\n\n## Composing States\n\nBesides calling `(mount/start)` there are other useful ways to start an application:\n\n* [starting parts of an application](README.md#start-and-stop-parts-of-application)\n* [starting an application without certain states](README.md#start-an-application-without-certain-states)\n* [swapping alternate implementations](README.md#swapping-alternate-implementations)\n* [passing runtime arguments](README.md#runtime-arguments)\n\nWhile all of these are great by themselves, sometimes it is really handy to compose these super powers. For example to start an application with _only_ certain states, _swapping_ a couple of them for new values, while passing runtime _arguments_.\n\n### Composer's Toolbox\n\nEach \"tool\" has a single responsibility and can be composed with other tools in _any_ combination and order.\n\n* `only` will return _only_ states that it is given + exist (seen by mount) in the application\n* `except` will return all the states that it is given _except_ a given set\n* `swap` will take a map with keys as states and values as their substitute values\n* `swap-states` will take a map with keys as states and values with `{:start fn :stop fn}` as their substitute states\n* `with-args` will take a map that could later be accessed by `(mount/args)`\n\nAll these functions take one or two arguments. If called with two arguments, the first one will be treated as the universe of states to work with. If called with one argument, it will work with _all known_ to mount states.\n\nNone of these functions start or stop the application states, they merely serve as transformations from the initial set of states to the one that will later be passed to `(mount/start)`.\n\n### Be Composing\n\nAll of the above is much easier to understand by looking at examples:\n\n```clojure\n(-\u003e (only #{#'foo/a\n            #'foo/b\n            #'foo/c\n            #'bar/d\n            #'baz/e})\n    (except [#'foo/c\n             #'bar/d])\n    (with-args {:a 42})\n    mount/start)\n```\n\nThis would start off from 5 states, even though the whole application may have many more states available. It would then exclude two states (i.e. `#'foo/c` and `#'bar/d`), then it will pass runtime arguments `{:a 42}`, and finally it will start the remaining three states: `#'foo/a`, `#'foo/b`, `#'baz/e`.\n\nYou may notice that `only` takes a set, while `except` takes a vector in this example. This is done intentionally to demonstrate that both these functions can take any collection of states. `set` would make more sense for most cases though.\n\nHere is a more \"involved\" example:\n\n```clojure\n(-\u003e (only #{#'foo/a\n            #'foo/b\n            #'foo/c\n            #'bar/d\n            #'baz/e})\n    (with-args {:a 42})\n    (except [#'foo/c\n             #'bar/d])\n    (swap-states {#'foo/a {:start #(create-connection test-conf)\n                           :stop #(disconnect a)}})\n    (swap {#'baz/e {:datomic {:uri \"datomic:mem://composable-mount\"}}})\n    mount/start)\n```\n\nThis will do the same thing as the previous example plus it would swap `#'foo/a` with alternative `:start` and `:stop` functions and `#'baz/e` with `{:datomic {:uri \"datomic:mem://composable-mount\"}}` value before starting the application.\n\n## Start and Stop Parts of Application\n\nIn REPL or during testing it is often very useful to work with / start / stop _only a part_ of an application, i.e. \"only these two states\".\n\n`mount`'s lifecycle functions, i.e. start/stop, can _optionally_ take states as vars (i.e. prefixed with their namespaces):\n\n```clojure\n(mount/start #'app.config/config #'app.nyse/conn)\n...\n(mount/stop #'app.config/config #'app.nyse/conn)\n```\n\nwhich will _only_ start/stop `config` and `conn` (won't start/stop any other states).\n\nHere is an [example](test/core/mount/test/parts.cljc) test that uses only two namespaces checking that the third one is not started.\n\n## Start an Application Without Certain States\n\nWhether it is in REPL or during testing, it is often useful to start an application _without_ certain states. These can be queue listeners that are not needed at REPL time, or a subset of an application to test.\n\nThe `start-without` function can do just that:\n\n```clojure\n(mount/start-without #'app.feeds/feed-listener\n                     #'app/nrepl)\n```\n\nwhich will start an application without starting `feed-listener` and `nrepl` states.\n\nHere is an [example](test/core/mount/test/start_without.cljc) test that excludes Datomic connection and nREPL from an application on start.\n\n## Swapping Alternate Implementations\n\nDuring testing it is often very useful to mock/stub certain states. For example running a test against an in memory database vs. the real one, running with a publisher that publishes to a test core.async channel vs. the real remote queue, etc.\n\n### Swapping States with Values\n\nThe `start-with` function takes values as substitutes.\n\nSay we have a `send-sms` state:\n\n```clojure\n(ns app.sms)\n;; ...\n(defstate send-sms :start (create-sms-sender\n                            (:sms config)))\n```\n\nWhen running tests it would be great _not_ to send the real text messages, but rather send them all to a local core.async channel instead:\n\n```clojure\n(let [sms-ch (chan)\n      send-sms (fn [sms] (go (\u003e! sms-ch sms)))]\n  (mount/start-with {#'app.sms/send-sms send-sms})   ;; \u003c\u003c\u003c\u003c swapping the \"send-sms\" state with a test function\n  ;; testing.. checking \"sms-ch\" channel\n  (mount/stop))\n```\n\n`start-with` takes a map of states with their substitutes. For example `#'app.sms/send-sms` here is the real deal SMS sender that is being substituted with a `send-sms` test function.\n\n### Swapping States with States\n\nThe `start-with-states` function takes values in a form of `{:start fn :stop fn}` as substitutes:\n\n```clojure\n(mount/start-with-states {#'app.neo/db        {:start #(connect test-config)\n                                               :stop #(disconnect db)}\n                          #'app.neo/publisher {:start #(create-pub test-config)\n                                               :stop #(close-pub publisher)}})\n```\n\n`start-with-states` takes a map of states with their substitutes. For example `#'app.nyse/db` here is the real deal (remote) DB that is being\nsubstituted with `#(connect test-config)` function, which could end up being anything, a map, an in memory DB, etc.\n\nThe `:stop` functions of substitutes can be anything, and could refer to the original state references. As in the example above: `db` and `publisher`\nare real references. They would need to be accessible from the namespace of course, so you might need to `(:require [app.neo :refer [db]])`\nin order to use `db` in `:stop #(disconnect db)` example above.\n\n--\n\nOne thing to note is whenever\n\n```clojure\n(mount/stop)\n```\n\nis run after `start-with`/`start-with-states`, it rolls back to an original \"state of states\", i.e. `#'app.neo/db` is `#'app.neo/db` again. So subsequent calls to `(mount/start)` or even to `(mount/start-with {something else})` will start from a clean slate.\n\nHere is an [example](test/core/mount/test/start_with_states.cljc) test that starts an app with mocking Datomic connection and nREPL.\n\n## Stop an Application Except Certain States\n\nCalling `(mount/stop)` will stop all the application states. In case everything needs to be stopped _besides certain ones_, it can be done with `(mount/stop-except)`.\n\nHere is an example of restarting the application without bringing down `#'app.www/nyse-app`:\n\n```clojure\ndev=\u003e (mount/start)\n14:34:10.813 [nREPL-worker-0] INFO  mount.core - \u003e\u003e starting..  config\n14:34:10.814 [nREPL-worker-0] INFO  mount.core - \u003e\u003e starting..  conn\n14:34:10.814 [nREPL-worker-0] INFO  app.db - creating a connection to datomic: datomic:mem://mount\n14:34:10.838 [nREPL-worker-0] INFO  mount.core - \u003e\u003e starting..  nyse-app\n14:34:10.843 [nREPL-worker-0] DEBUG o.e.j.u.component.AbstractLifeCycle - STARTED SelectChannelConnector@0.0.0.0:4242\n14:34:10.843 [nREPL-worker-0] DEBUG o.e.j.u.component.AbstractLifeCycle - STARTED org.eclipse.jetty.server.Server@194f37af\n14:34:10.844 [nREPL-worker-0] INFO  mount.core - \u003e\u003e starting..  nrepl\n:started\n\ndev=\u003e (mount/stop-except #'app.www/nyse-app)\n14:34:47.766 [nREPL-worker-0] INFO  mount.core - \u003c\u003c stopping..  nrepl\n14:34:47.766 [nREPL-worker-0] INFO  mount.core - \u003c\u003c stopping..  conn\n14:34:47.766 [nREPL-worker-0] INFO  app.db - disconnecting from  datomic:mem://mount\n14:34:47.766 [nREPL-worker-0] INFO  mount.core - \u003c\u003c stopping..  config\n:stopped\ndev=\u003e\n\ndev=\u003e (mount/start)\n14:34:58.673 [nREPL-worker-0] INFO  mount.core - \u003e\u003e starting..  config\n14:34:58.674 [nREPL-worker-0] INFO  app.config - loading config from test/resources/config.edn\n14:34:58.674 [nREPL-worker-0] INFO  mount.core - \u003e\u003e starting..  conn\n14:34:58.674 [nREPL-worker-0] INFO  app.db - creating a connection to datomic: datomic:mem://mount\n14:34:58.693 [nREPL-worker-0] INFO  mount.core - \u003e\u003e starting..  nrepl\n:started\n```\n\nNotice that the `nyse-app` is not started the second time (hence no more accidental `java.net.BindException: Address already in use`). It is already up and running.\n\n## Recompiling Namespaces with Running States\n\nMount will detect when a namespace with states (i.e. with `(defstate ...)`) was reloaded/recompiled,\nand will check every state in this namespace whether it was running at the point of recompilation. If it was, _it will restart it_:\n\n* if a state has a `:stop` function, mount will invoke it on the old version of state (i.e. cleanup)\n* it will call a \"new\" `:start` function _after_ this state is recompiled/redefined\n\nMount won't keep it a secret, it'll tell you about all the states that had to be restarted during namespace reload/recompilation:\n\n\u003cimg src=\"doc/img/ns-recompile.png\" width=\"500px\"\u003e\n\nsame is true for recompiling and reloading (figwheel, boot-reload, etc.) namespaces in ClojureScript:\n\n\u003cimg src=\"doc/img/cljs-ns-reload.png\" width=\"500px\"\u003e\n\nProviding a `:stop` function _is_ optional, but in case a state needs to be cleaned between restarts or on a system shutdown,\n`:stop` is highly recommended.\n\n### :on-reload\n\nBy default a state will be restarted on its redefinition or a namespace recompilation. However it is not always a desired behavior. Sometimes it's ok to have stale references during REPL sessions / development, other times all that is needed is not a \"restart\", but just a \"stop\".\n\nThis behavior could be controlled with an optional `:on-reload` meta attribute when defining a state.\n\nIn case _nothing_ needs to be done to a running state on reload / recompile / redef, set `:on-reload` to `:noop`:\n\n```clojure\n(defstate ^{:on-reload :noop}\n          mem-db :start (connect config)\n                 :stop (disconnect mem-db))\n```\n\nWhen a running state needs to be just \"stopped\" on reload, set `:on-reload` to `:stop`:\n\n```clojure\n(defstate ^{:on-reload :stop}\n          mem-db :start (connect config)\n                 :stop (disconnect mem-db))\n```\n\nAgain, by default, if no `:on-reload` meta is added, internally it would be set to `:restart`, in which case a running state will be restarted on a redef / a namespace reload.\n\nNote that `^{:on-reload :noop}` will disable stopping or starting the state on namespace recompilation but it will still obey `(mount/start)` / `(mount/stop)` calls. This means that if any of the namespaces with `(mount/start)` / `(mount/stop)` calls are reloaded or these calls are explicitely executed (i.e. somewhere in the `dev` namespace or in an `:after` clause), the state's start/stop functions will still be called.\n\n## Cleaning up Deleted States\n\nMount will detect when a state was renamed/deleted from a namespace, and will do two things:\n\n* if a state had a `:stop` function, mount will invoke it on the old version of state (i.e. cleanup)\n* will remove any knowledge of this state internally\n\nHere is an example:\n\n```clojure\ndev=\u003e (defstate won't-be-here-long :start (println \"I am starting... \")\n                                   :stop (println \"I am stopping... \"))\n#'dev/won't-be-here-long\ndev=\u003e\n\ndev=\u003e (mount/start #'dev/won't-be-here-long)\nINFO  app.utils.logging - \u003e\u003e starting..  #'dev/won't-be-here-long\nI am starting...\n{:started [\"#'dev/won't-be-here-long\"]}\ndev=\u003e\n```\n\n\"deleting\" it from REPL, and starting all the states:\n\n```clojure\ndev=\u003e (ns-unmap 'dev 'won't-be-here-long)\nnil\ndev=\u003e (mount/start)\n\n\"\u003c\u003c stopping.. #'dev/won't-be-here-long (it was deleted)\"\nI am stopping...\n\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.conf/config\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.db/conn\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.www/nyse-app\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.example/nrepl\n{:started [\"#'app.conf/config\" \"#'app.db/conn\" \"#'app.www/nyse-app\" \"#'app.example/nrepl\"]}\n```\n\nMount detected that `#'dev/won't-be-here-long` was deleted, hence:\n```clojure\n\u003c\u003c stopping.. #'dev/won't-be-here-long (it was deleted)\n```\n\n## `cljc` mode\n\nBy default mount states are kept under var references. While it works for Clojure, it falls short in the land of ClojureScript since, especially during an `:advanced` compilation, var names get compressed + ClojureScript does not support reified vars.\n\nTo support both Clojure and ClojureScript mount has a `cljc` mode which is well documented in [here](doc/clojurescript.md#managing-state-in-clojurescript), and can be enabled by `(mount/in-cljc-mode)`.\n\n### Disable Lazy Start\n\nWhen in `cljc` mode, mount states that are not started by `(mount/start a b c)`, or that are not transitive states: i.e. not `:require`d at the time `(mount/start)` is called, will start lazily whenever they are dereferenced:\n\n```clojure\n=\u003e (mount/in-cljc-mode)\n:cljc\n\n=\u003e (defstate db-connection :start (println \"connecting\")\n                           :stop (println \"disconnecting...\"))\n\n=\u003e db-connection\n#object[mount.core.DerefableState 0x546b9d51 {:status :pending, :val nil}]\n\ndev=\u003e (mount/running-states)\n#{}\n\ndev=\u003e @db-connection   ;;   db-connection will start here when deref'ed even though it was not started explicitly\nconnecting\n\ndev=\u003e (mount/running-states)\n#{\"#'dev/db-connection\"}\n```\n\nThis can be quite handy as it allows certain app states to start lazily.\n\nHowever there are cases when it is best to fail in case a certain state is deref'ed while it was not yet started. This is possible by marking such states with `^{:on-lazy-start :throw}` metadata:\n\n```clojure\n=\u003e (defstate ^{:on-lazy-start :throw} db-connection :start (do (println \"connecting\") 42)\n                                                    :stop (println \"disconnecting...\"))\n\n=\u003e @db-connection   ;;   this will throw since db connection is deref'ed before it was started\n\njava.lang.RuntimeException: :on-lazy-start is set to :throw i.e. (defstate {:on-lazy-start :throw} #'dev/db-connection...) and #'dev/db-connection state was not explicitly started before it was deref'ed (i.e. @#'dev/db-connection)\n\n=\u003e (mount/start #'dev/db-connection)\nconnecting\n{:started [\"#'dev/db-connection\"]}\n\n=\u003e @db-connection\n42\n```\n\n## Packaging\n\nSince `mount` relies on the Clojure/Script Compiler to learn about all the application states, before `mount/start` is called all the namespaces that have `defstate`s need to be compiled.\n\nAt the development time this requirement is mostly transparent, since these namespaces are compiled with nREPL, or refreshed with \"tools.namespace\", etc. But it becomes important when _packaging_ an application or when starting a web application via [lein-ring](https://github.com/weavejester/lein-ring#general-options)'s or [boot-http](https://github.com/pandeiro/boot-http#-i----init-and--c----cleanup)'s `:init` hooks.\n\nDepending on a structure and a kind of an application, this means that these namespaces need to be _`:required`_ prior to a call to `mount/start` when packaging the app as a stand alone JAR or a WAR.\n\nThis can be easily done with choosing an application entry point, which could be a web handler namespace with routes or just an arbitrary app namespace (i.e. `my.app`). In this app entry point namespace all other namespaces that have `defstate` would be `:require`d and a call to the `mount/start` function would be defined:\n\n```clojure\n(ns my.app\n  (:require [a]\n            [b]\n            [c]\n            [mount.core :as mount]))\n\n(defn rock-n-roll []                   ;; or (defn -main [args].. )\n  (mount/start))\n```\n\nthis would ensure that at the time `(rock-n-roll)` is called, all the namespaces with states were compiled (i.e. mount knows about them). `(rock-n-roll)` can be used in/as a -main function or as a web hook such as `:init`.\n\nIn practice only a few namespaces need to be `:require`d, since others will be brought in transitively (i.e. by already required namespaces). From the `my.app` example above, say we had namespaces `d`, `e` and `f` that are required by `a`, and `g` and `h` that are required by `b`. They (`d`, `e`, `f`, `g` and `h`) _won't_ need to be required by `my.app`, since `a` and `b` would \"bring\" them in.\n\n## Affected States\n\nEvery time a lifecycle function (start/stop) is called mount will return all the states that were affected:\n\n```clojure\ndev=\u003e (mount/start)\n{:started [#'app.config/config\n           #'app.nyse/conn\n           #'app/nrepl]}\n```\n```clojure\ndev=\u003e (mount/stop)\n{:stopped [#'app/nrepl\n           #'app.nyse/conn\n           #'app.config/config]}\n```\n\nAn interesting bit here is a vector vs. a set: all the states are returned _in the order they were affected_.\n\n## Logging\n\n\u003e All the mount examples have `\u003e\u003e starting..` / `\u003c\u003c stopping..` logging messages, but when I develop an application with mount I don't see them.\n\nValid question. It was a [conscious choice](https://github.com/tolitius/mount/issues/15) not to depend on any particular logging library, since there are few to select from, and this decision is best left to the developer who may choose to use mount.\n\nSince mount is a _library_ it should _not_ bring any dependencies unless its functionality directly depends on them.\n\n\u003e _But I still these logging statements in the examples..._\n\n### mount-up\n\nOne way to do that would be using \"[mount-up](https://github.com/tolitius/mount-up)\" that \"watches mount's ups and downs\":\n\n```clojure\n=\u003e (require '[mount-up.core :as mu])\n\n=\u003e (mu/on-upndown :info mu/log :before)\n\n=\u003e (mount/start)\nINFO  mount-up.core - \u003e\u003e starting.. #'boot.user/server\n{:started [\"#'boot.user/server\"]}\n\n=\u003e (mount/stop)\nINFO  mount-up.core - \u003c\u003c stopping.. #'boot.user/server\n{:stopped [\"#'boot.user/server\"]}\n```\n\n### Manual AOP\n\nAnother, a more manual way, would be to do it via an excellent [robert hooke](https://github.com/technomancy/robert-hooke/). Example applications live in `test`, so does the [utility](test/clj/tapp/utils/logging.clj#L42) that adds logging to all the mount's lifecycle functions on start in [dev.clj](https://github.com/tolitius/mount/blob/75d7cdc610ce38623d4d3aea1da3170d1c9a3b4b/dev/dev.clj#L21).\n\n## Exception Handling\n\nOne way to handle exceptions on start/stop would be to simply wrap start/stop functions in `try/catch`.\n\nAnother way would be to use a custom [mount-up](https://github.com/tolitius/mount-up/blob/master/README.md#wrapping) wrapper.\n\n## Clojure Version\n\nSince mount [supports both](doc/clojurescript.md#managing-state-in-clojurescript) Clojure and ClojureScript, it relies on [Reader Conditionals](http://clojure.org/reader#The%20Reader--Reader%20Conditionals) that were introduced in `Clojure 1.7`. mount's code is not precompiled (i.e. AOT) and distributed in `.cljc` sources, hence it currently requires `Clojure 1.7` and above.\n\n## Mount and Develop!\n\nBesides a [a collection](https://github.com/tolitius/stater) of sample mount applications, mount _sources_ come with two sample apps:\n\n* Clojure [app](dev/clj/app)\n* ClojureScript [app](doc/clojurescript.md#mounting-that-clojurescript)\n\nYou can clone mount, jump into a REPL and start playing with these built in apps.\n\nBelow is an example of the Clojure app that comes with mount.\n\nThe app has 4 states:\n\n* `config`, loaded from the files and refreshed on each `(reset)`\n* `datomic connection` that uses the config to create itself\n* `nyse web app` which is a web server with compojure routes (i.e. the actual app)\n* `nrepl` that uses config to bind to host/port\n\n### Running New York Stock Exchange\n\nTo try it out, clone `mount`, get to REPL (`boot repl` or `lein repl`) and switch to `(dev)`:\n\n```clojure\n$ boot repl\n\nuser=\u003e (dev)\n#object[clojure.lang.Namespace 0xcf1a0cc \"dev\"]\n```\n\nstart/restart/reset everything using `(reset)`:\n\n```clojure\ndev=\u003e (reset)\n\n:reloading (mount.tools.macro mount.core app.utils.logging app.conf app.db app.utils.datomic app.nyse app.www app.example dev)\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.conf/config\nINFO  app.conf - loading config from dev/resources/config.edn\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.db/conn\nINFO  app.db - conf:  {:datomic {:uri datomic:mem://mount}, :www {:port 4242}, :h2 {:classname org.h2.Driver, :subprotocol h2, :subname jdbc:h2:mem:mount, :user sa, :password }, :rabbit {:api-port 15672, :password guest, :queue r-queue, :username guest, :port 5672, :node jabit, :exchange-type direct, :host 192.168.1.1, :vhost /captoman, :auto-delete-q? true, :routing-key , :exchange foo}, :nrepl {:host 0.0.0.0, :port 7878}}\nINFO  app.db - creating a connection to datomic: datomic:mem://mount\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.www/nyse-app\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.example/nrepl\ndev=\u003e\n```\n\neverything is started and can be played with:\n\n```clojure\ndev=\u003e (add-order conn {:ticker \"GOOG\" :bid 665.51M :offer 665.59M :qty 100})\ndev=\u003e (add-order conn {:ticker \"GOOG\" :bid 665.50M :offer 665.58M :qty 300})\n\ndev=\u003e (find-orders conn \"GOOG\")\n({:db/id 17592186045418, :order/symbol \"GOOG\", :order/bid 665.51M, :order/qty 100, :order/offer 665.59M}\n {:db/id 17592186045420, :order/symbol \"GOOG\", :order/bid 665.50M, :order/qty 300, :order/offer 665.58M})\n```\n\nsince there is also a web server running, we can add orders with HTTP POST (from a different terminal window):\n\n```clojure\n$ curl -X POST -d \"ticker=TSLA\u0026qty=100\u0026bid=232.38\u0026offer=232.43\" \"http://localhost:4242/nyse/orders\"\n\n{\"added\":{\"ticker\":\"TSLA\",\"qty\":\"100\",\"bid\":\"232.38\",\"offer\":\"232.43\"}}\n```\n\n```clojure\ndev=\u003e (find-orders conn \"TSLA\")\n({:db/id 17592186045422, :order/symbol \"TSLA\", :order/bid 232.38M, :order/qty 100, :order/offer 232.43M})\n```\n\nonce something is changed in the code, or you just need to reload everything, do `(reset)`.\n\n_note: a simple `(mount/stop)` / `(mount/start)` will also work, `(reset)` is for \"convenience + ns refresh\":_\n\n```clojure\ndev=\u003e (reset)\nINFO  app.utils.logging - \u003c\u003c stopping..  #'app.example/nrepl\nINFO  app.utils.logging - \u003c\u003c stopping..  #'app.www/nyse-app\nINFO  app.utils.logging - \u003c\u003c stopping..  #'app.db/conn\nINFO  app.db - disconnecting from  datomic:mem://mount\nINFO  app.utils.logging - \u003c\u003c stopping..  #'app.conf/config\n\n:reloading (app.conf app.db app.nyse app.www app.example dev)\n\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.conf/config\nINFO  app.conf - loading config from dev/resources/config.edn\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.db/conn\nINFO  app.db - conf:  {:datomic {:uri datomic:mem://mount}, :www {:port 4242}, :h2 {:classname org.h2.Driver, :subprotocol h2, :subname jdbc:h2:mem:mount, :user sa, :password }, :rabbit {:api-port 15672, :password guest, :queue r-queue, :username guest, :port 5672, :node jabit, :exchange-type direct, :host 192.168.1.1, :vhost /captoman, :auto-delete-q? true, :routing-key , :exchange foo}, :nrepl {:host 0.0.0.0, :port 7878}}\nINFO  app.db - creating a connection to datomic: datomic:mem://mount\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.www/nyse-app\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.example/nrepl\n:ready\n```\n\nnotice that it stopped and started again.\n\nIn `app.db` connection `:stop` calls a `disconnect` function where a [database is deleted](https://github.com/tolitius/mount/blob/e3066fe024f89420bd4463a433c5d3b893b7b315/dev/clj/app/db.clj#L18). Hence after `(reset)` was called the app was brought its starting point: [database was created](https://github.com/tolitius/mount/blob/e3066fe024f89420bd4463a433c5d3b893b7b315/dev/clj/app/db.clj#L11) by the\n`:start` that calls a `new-connection` function, and db schema is [created](https://github.com/tolitius/mount/blob/e3066fe024f89420bd4463a433c5d3b893b7b315/dev/clj/app/www.clj#L26) by `nyse.app`.\n\nBut again no orders:\n\n```clojure\ndev=\u003e (find-orders conn \"GOOG\")\n()\ndev=\u003e (find-orders conn \"TSLA\")\n()\n```\n\nhence the app is in its \"clean\" state, and ready to rock and roll as right after the REPL started:\n\n```clojure\ndev=\u003e (add-order conn {:ticker \"TSLA\" :bid 232.381M :offer 232.436M :qty 250})\n\ndev=\u003e (find-orders conn \"TSLA\")\n({:db/id 17592186045418, :order/symbol \"TSLA\", :order/bid 232.381M, :order/qty 250, :order/offer 232.436M})\n```\n\n### New York Stock Exchange Maintenance\n\nSay we want to leave the exchange functioning, but would like to make sure that no one can hit it from the web. Easy, just stop the web server:\n\n```clojure\ndev=\u003e (mount/stop #'app.www/nyse-app)\nINFO  app.utils.logging - \u003c\u003c stopping..  #'app.www/nyse-app\n{:stopped [\"#'app.www/nyse-app\"]}\ndev=\u003e\n```\n```bash\n$ curl localhost:4242\ncurl: (7) Failed to connect to localhost port 4242: Connection refused\n```\n\neverything but the web server works as before:\n\n```clojure\ndev=\u003e (find-orders conn \"TSLA\")\n({:db/id 17592186045420, :order/symbol \"TSLA\", :order/bid 232.381M, :order/qty 250, :order/offer 232.436M})\ndev=\u003e\n```\n\nonce we found who `DDoS`ed us on `:4242`, and punished them, we can restart the web server:\n\n```clojure\ndev=\u003e (mount/start #'app.www/nyse-app)\nINFO  app.utils.logging - \u003e\u003e starting..  #'app.www/nyse-app\n{:started [\"#'app.www/nyse-app\"]}\ndev=\u003e\n```\n\n```clojure\n$ curl localhost:4242\nwelcome to the mount sample app!\n```\n\n## Web and Uberjar\n\nThere is an `uberjar` branch with an example webapp and it's uberjar sibling. Before trying it:\n\n```clojure\n$ git checkout uberjar\nSwitched to branch 'uberjar'\n```\n\nThe documentation is [here](doc/uberjar.md#creating-reloadable-uberjarable-app).\n\n## Runtime Arguments\n\nThere is an `with-args` branch with an example app that takes command line params\n\n```clojure\n$ git checkout with-args\nSwitched to branch 'with-args'\n```\n\nThe documentation is [here](doc/runtime-arguments.md#passing-runtime-arguments).\n\n## License\n\nCopyright © 2020 tolitius\n\nDistributed under the Eclipse Public License either version 1.0 or (at\nyour option) any later version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftolitius%2Fmount","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftolitius%2Fmount","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftolitius%2Fmount/lists"}