Compare commits
No commits in common. "master" and "0.1.11" have entirely different histories.
53 changed files with 223 additions and 724 deletions
|
|
@ -1 +0,0 @@
|
|||
{:config-paths ["../resources/clj-kondo.exports/mount/mount/"]}
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -1,10 +1,7 @@
|
|||
/target
|
||||
/classes
|
||||
/checkouts
|
||||
.cpcache/
|
||||
.rebel_readline_history
|
||||
cljs-test-runner-out
|
||||
node_modules
|
||||
pom.xml
|
||||
pom.xml.asc
|
||||
.repl*
|
||||
dev/resources/public/js/*
|
||||
|
|
@ -21,4 +18,3 @@ doo-index.html
|
|||
/.nrepl-history
|
||||
.cljs_rhino_repl/
|
||||
out/
|
||||
.clj-kondo/.cache
|
||||
|
|
|
|||
26
.travis.yml
26
.travis.yml
|
|
@ -1,26 +0,0 @@
|
|||
sudo: false
|
||||
language: java
|
||||
script:
|
||||
- boot test
|
||||
- boot test-cljs
|
||||
- boot test-cljs-advanced
|
||||
install:
|
||||
- mkdir -p ~/bin
|
||||
- export PATH=~/bin:$PATH
|
||||
|
||||
- curl -L https://github.com/boot-clj/boot-bin/releases/download/latest/boot.sh -o ~/bin/boot
|
||||
- chmod +x ~/bin/boot
|
||||
jdk: openjdk8
|
||||
env:
|
||||
matrix:
|
||||
- BOOT_CLOJURE_VERSION=1.8.0
|
||||
global:
|
||||
- JAVA_OPTS="-Xms512m -Xmx2048m"
|
||||
jdk:
|
||||
- openjdk8
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
- $HOME/.boot/cache/bin
|
||||
- $HOME/.boot/cache/lib
|
||||
- $HOME/bin
|
||||
46
CHANGELOG.md
46
CHANGELOG.md
|
|
@ -1,49 +1,3 @@
|
|||
## 0.1.19
|
||||
###### Wed Aug 21 22:43:40 2024 -0400
|
||||
|
||||
* add `clojure-kondo`
|
||||
|
||||
## 0.1.17
|
||||
###### Mon Dec 19 15:17:21 2022 -0500
|
||||
|
||||
* 2d050e9 fix: swap-states non string rollback (thanks to [egg-juxt](https://github.com/egg-juxt))
|
||||
|
||||
## 0.1.16
|
||||
###### Mon Jan 28 10:34:26 2019 -0500
|
||||
|
||||
* fb52f79 prevent reloading of mount.core ns ([106](https://github.com/tolitius/mount/issues/106))
|
||||
* c5f3e4c current-state should return Derefable on :cljc ([104](https://github.com/tolitius/mount/issues/104))
|
||||
* c2687d1 silent `*logger*` warning in latest ClojureScript([101](https://github.com/tolitius/mount/issues/101))
|
||||
* bb23747 switch form circle to travis
|
||||
|
||||
## 0.1.14
|
||||
###### Thu Oct 25 18:34:22 2018 -0400
|
||||
|
||||
* cljs: throw js/Error not just a string ([#100](https://github.com/tolitius/mount/issues/100))
|
||||
* add ^{:on-lazy-start :throw} ([#95](https://github.com/tolitius/mount/issues/95) and [#99](https://github.com/tolitius/mount/issues/99))
|
||||
* self hosted ClojureScript support ([#85](https://github.com/tolitius/mount/issues/85) and [#97](https://github.com/tolitius/mount/issues/97))
|
||||
|
||||
## 0.1.12
|
||||
###### Sat Feb 10 14:53:04 2018 -0500
|
||||
|
||||
* remove ref to old state alias in `swap-states` ([#89](https://github.com/tolitius/mount/issues/89))
|
||||
* `:on-reload :noop` should not leave stale references
|
||||
* `stop-except` & `start-without` to take all states ([#81](https://github.com/tolitius/mount/issues/81))
|
||||
* goog.log/error for logging cljs errors ([#63](https://github.com/tolitius/mount/issues/63))
|
||||
* on failure (down) reports and cleans up vs. just "throw" ([#63](https://github.com/tolitius/mount/issues/63))
|
||||
* do not start a `DerefableState` as a side-effect of printing ([#76](https://github.com/tolitius/mount/issues/76))
|
||||
|
||||
## 0.1.11
|
||||
###### Fri Dec 2 18:09:51 2016 -0600
|
||||
|
||||
* opening `(find-all-states)`
|
||||
* stop accepts a collection of states (in addition to varargs) ([#69](https://github.com/tolitius/mount/issues/69))
|
||||
* swap-states and start-with-states take values ([#68](https://github.com/tolitius/mount/issues/68))
|
||||
* adding `(running-states)` that returns a set
|
||||
* `(mount/start #{})` is a noop ([#65](https://github.com/tolitius/mount/issues/65))
|
||||
* removing log on state discovery
|
||||
* adding a restart listener with watchers
|
||||
|
||||
## 0.1.10
|
||||
###### Sun Feb 28 18:20:52 2016 -0500
|
||||
|
||||
|
|
|
|||
31
Makefile
31
Makefile
|
|
@ -1,31 +0,0 @@
|
|||
.PHONY: clean test jar tag outdated install deploy tree repl
|
||||
|
||||
clean:
|
||||
rm -rf target
|
||||
rm -rf classes
|
||||
|
||||
jar: clean test tag
|
||||
clojure -A:jar
|
||||
|
||||
test: clean
|
||||
clojure -X:test :patterns '[".*"]' # clojure tests
|
||||
# clojure -Atest-cljs -r ".*test.self.host.*" # clojure script tests
|
||||
# run "j8; boot test-cljs" until running cljs tests via deps.edn is fixed
|
||||
|
||||
outdated:
|
||||
clojure -M:outdated
|
||||
|
||||
tag:
|
||||
clojure -A:tag
|
||||
|
||||
install: jar
|
||||
clojure -A:install
|
||||
|
||||
deploy: jar
|
||||
clojure -A:deploy
|
||||
|
||||
tree:
|
||||
mvn dependency:tree
|
||||
|
||||
repl:
|
||||
clojure -A:dev -A:repl
|
||||
133
README.md
133
README.md
|
|
@ -1,13 +1,22 @@
|
|||
> I think that it's _extraordinarily important_ that we in computer science keep fun in computing
|
||||
|
||||
_**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](https://web.mit.edu/6.001/6.037/sicp.pdf)_
|
||||
_**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-3.html)_
|
||||
|
||||
# mount <img src="doc/img/mount-logo.png" width="70px">
|
||||
[](https://github.com/tolitius/mount/releases)
|
||||
[](https://clojars.org/mount)
|
||||
# mount
|
||||
|
||||
###### _any_ questions or feedback: [`#mount`](https://clojurians.slack.com/messages/mount/) clojurians slack channel <img src="doc/img/slack-icon.png" width="15px"> (or just [open an issue](https://github.com/tolitius/mount/issues))
|
||||
module | branch | status
|
||||
----------|----------|----------
|
||||
mount | `master` | [](https://circleci.com/gh/tolitius/mount/tree/master)
|
||||
mount | `0.1.11-SNAPSHOT` | [](https://circleci.com/gh/tolitius/mount/tree/0.1.11)
|
||||
|
||||
[](http://clojars.org/mount)
|
||||
|
||||
> <img src="doc/img/slack-icon.png" width="30px"> _any_ questions or feedback: [`#mount`](https://clojurians.slack.com/messages/mount/) clojurians slack channel (or just [open an issue](https://github.com/tolitius/mount/issues))
|
||||
|
||||
-
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
||||
|
||||
- [Why?](#why)
|
||||
|
|
@ -28,17 +37,12 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt
|
|||
- [Swapping States with States](#swapping-states-with-states)
|
||||
- [Stop an Application Except Certain States](#stop-an-application-except-certain-states)
|
||||
- [ClojureScript is Clojure](doc/clojurescript.md#managing-state-in-clojurescript)
|
||||
- [cljc mode](#cljc-mode)
|
||||
- [Disable Lazy Start](#disable-lazy-start)
|
||||
- [Packaging](#packaging)
|
||||
- [Affected States](#affected-states)
|
||||
- [Recompiling Namespaces with Running States](#recompiling-namespaces-with-running-states)
|
||||
- [:on-reload](#on-reload)
|
||||
- [Cleaning up Deleted States](#cleaning-up-deleted-states)
|
||||
- [Logging](#logging)
|
||||
- [mount-up](#mount-up)
|
||||
- [Manual AOP](#manual-aop)
|
||||
- [Exception Handling](#exception-handling)
|
||||
- [Clojure Version](#clojure-version)
|
||||
- [Mount and Develop!](#mount-and-develop)
|
||||
- [Running New York Stock Exchange](#running-new-york-stock-exchange)
|
||||
|
|
@ -90,7 +94,7 @@ Creating state is easy:
|
|||
(defstate conn :start (create-conn))
|
||||
```
|
||||
|
||||
where the `create-conn` function creates a connection (for example to a database) and is defined elsewhere, can be right above it.
|
||||
where the `create-conn` function is defined elsewhere, can be right above it.
|
||||
|
||||
In case this state needs to be cleaned / destroyed between reloads, there is also `:stop`
|
||||
|
||||
|
|
@ -103,7 +107,7 @@ That is pretty much it. But wait, there is more.. this state is _a top level bei
|
|||
`required` by other namespaces or in REPL:
|
||||
|
||||
```clojure
|
||||
dev=> (require '[app.db :refer [conn]])
|
||||
dev=> (require '[app.nyse :refer [conn]])
|
||||
nil
|
||||
dev=> conn
|
||||
#object[datomic.peer.LocalConnection 0x1661a4eb "datomic.peer.LocalConnection@1661a4eb"]
|
||||
|
|
@ -120,22 +124,6 @@ For example let's say an `app` needs a connection above. No problem:
|
|||
|
||||
where `above` is an arbitrary namespace that defines the above state / connection.
|
||||
|
||||
### Documentation String
|
||||
|
||||
As in any definition (i.e. `def`, `defn`) a documentation string can be added to better describe a state:
|
||||
|
||||
```clojure
|
||||
(defstate answer
|
||||
"answer to the ultimate question of life universe and everything"
|
||||
:start (+ 1 41))
|
||||
```
|
||||
```clojure
|
||||
(doc answer)
|
||||
-------------------------
|
||||
dev/answer
|
||||
answer to the ultimate question of life universe and everything
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
If the whole app is one big application context (or `system`), cross dependencies with a solid dependency graph
|
||||
|
|
@ -172,9 +160,8 @@ this `config`, being top level, can be used in other namespaces, including the o
|
|||
(defstate conn :start (create-connection config))
|
||||
```
|
||||
|
||||
[here](dev/clj/app/www.clj#L32) is an example of a web server that "depends" on a similar `config`.
|
||||
|
||||
###### _(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)_
|
||||
[here](dev/clj/app/www.clj#L32)
|
||||
is an example of a web server that "depends" on a similar `config`.
|
||||
|
||||
## Value of values
|
||||
|
||||
|
|
@ -331,7 +318,7 @@ Here is a more "involved" example:
|
|||
mount/start)
|
||||
```
|
||||
|
||||
This 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.
|
||||
This will do the same thing as the previous example plus it would swap `#'foo/a` with `#'test/a` state and `#'baz/e` with `{:datomic {:uri "datomic:mem://composable-mount"}}` value before starting the application.
|
||||
|
||||
## Start and Stop Parts of Application
|
||||
|
||||
|
|
@ -548,56 +535,6 @@ Mount detected that `#'dev/won't-be-here-long` was deleted, hence:
|
|||
<< stopping.. #'dev/won't-be-here-long (it was deleted)
|
||||
```
|
||||
|
||||
## `cljc` mode
|
||||
|
||||
By 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.
|
||||
|
||||
To 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)`.
|
||||
|
||||
### Disable Lazy Start
|
||||
|
||||
When 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:
|
||||
|
||||
```clojure
|
||||
=> (mount/in-cljc-mode)
|
||||
:cljc
|
||||
|
||||
=> (defstate db-connection :start (println "connecting")
|
||||
:stop (println "disconnecting..."))
|
||||
|
||||
=> db-connection
|
||||
#object[mount.core.DerefableState 0x546b9d51 {:status :pending, :val nil}]
|
||||
|
||||
dev=> (mount/running-states)
|
||||
#{}
|
||||
|
||||
dev=> @db-connection ;; db-connection will start here when deref'ed even though it was not started explicitly
|
||||
connecting
|
||||
|
||||
dev=> (mount/running-states)
|
||||
#{"#'dev/db-connection"}
|
||||
```
|
||||
|
||||
This can be quite handy as it allows certain app states to start lazily.
|
||||
|
||||
However 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:
|
||||
|
||||
```clojure
|
||||
=> (defstate ^{:on-lazy-start :throw} db-connection :start (do (println "connecting") 42)
|
||||
:stop (println "disconnecting..."))
|
||||
|
||||
=> @db-connection ;; this will throw since db connection is deref'ed before it was started
|
||||
|
||||
java.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)
|
||||
|
||||
=> (mount/start #'dev/db-connection)
|
||||
connecting
|
||||
{:started ["#'dev/db-connection"]}
|
||||
|
||||
=> @db-connection
|
||||
42
|
||||
```
|
||||
|
||||
## Packaging
|
||||
|
||||
Since `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.
|
||||
|
|
@ -650,35 +587,9 @@ Valid question. It was a [conscious choice](https://github.com/tolitius/mount/is
|
|||
|
||||
Since mount is a _library_ it should _not_ bring any dependencies unless its functionality directly depends on them.
|
||||
|
||||
> _But I still these logging statements in the examples..._
|
||||
> But I still these logging statements in the examples.
|
||||
|
||||
### mount-up
|
||||
|
||||
One way to do that would be using "[mount-up](https://github.com/tolitius/mount-up)" that "watches mount's ups and downs":
|
||||
|
||||
```clojure
|
||||
=> (require '[mount-up.core :as mu])
|
||||
|
||||
=> (mu/on-upndown :info mu/log :before)
|
||||
|
||||
=> (mount/start)
|
||||
INFO mount-up.core - >> starting.. #'boot.user/server
|
||||
{:started ["#'boot.user/server"]}
|
||||
|
||||
=> (mount/stop)
|
||||
INFO mount-up.core - << stopping.. #'boot.user/server
|
||||
{:stopped ["#'boot.user/server"]}
|
||||
```
|
||||
|
||||
### Manual AOP
|
||||
|
||||
Another, 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).
|
||||
|
||||
## Exception Handling
|
||||
|
||||
One way to handle exceptions on start/stop would be to simply wrap start/stop functions in `try/catch`.
|
||||
|
||||
Another way would be to use a custom [mount-up](https://github.com/tolitius/mount-up/blob/master/README.md#wrapping) wrapper.
|
||||
The way this is done is 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).
|
||||
|
||||
## Clojure Version
|
||||
|
||||
|
|
@ -861,7 +772,7 @@ The documentation is [here](doc/runtime-arguments.md#passing-runtime-arguments).
|
|||
|
||||
## License
|
||||
|
||||
Copyright © 2020 tolitius
|
||||
Copyright © 2016 tolitius
|
||||
|
||||
Distributed under the Eclipse Public License either version 1.0 or (at
|
||||
your option) any later version.
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
BOOT_VERSION=2.7.1
|
||||
BOOT_CLOJURE_VERSION=1.10.1
|
||||
29
build.boot
29
build.boot
|
|
@ -1,18 +1,8 @@
|
|||
(def +version+ "0.1.18")
|
||||
|
||||
;; -- since boot is no more, this file will go away
|
||||
;; -- once deps.edn "learns" how to run all cljs tests
|
||||
|
||||
;; j8; boot test
|
||||
;; j8; boot test-cljs
|
||||
;; j8; boot test-cljs-advanced
|
||||
;; j8; boot -v build-jar push-snapshot
|
||||
;; j8; boot -v build-jar push-release
|
||||
(def +version+ "0.1.11-SNAPSHOT")
|
||||
|
||||
(set-env!
|
||||
:source-paths #{"src"}
|
||||
:resource-paths #{"resources"}
|
||||
:dependencies '[;; mount brings _NO DEPENDENCIES_, everything here is for
|
||||
:dependencies '[;; mount brings _no dependencies_, everything here is for
|
||||
;; mount dev, examples apps and tests
|
||||
|
||||
[org.clojure/clojure "1.8.0" :scope "provided"]
|
||||
|
|
@ -23,7 +13,6 @@
|
|||
[cheshire "5.5.0" :scope "provided"]
|
||||
[hiccups "0.3.0" :scope "provided" :exclusions [org.clojure/clojurescript]]
|
||||
[com.andrewmcveigh/cljs-time "0.3.14" :scope "provided"]
|
||||
[tolitius/yang "0.1.9" :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"]
|
||||
|
|
@ -36,20 +25,19 @@
|
|||
[proto-repl-charts "0.3.2" :scope "provided"]
|
||||
|
||||
;; boot clj
|
||||
[boot/core "2.7.1" :scope "provided"]
|
||||
[boot/core "2.6.0" :scope "provided"]
|
||||
[adzerk/bootlaces "0.1.13" :scope "test"]
|
||||
[adzerk/boot-logservice "1.2.0" :scope "test"]
|
||||
[adzerk/boot-logservice "1.0.1" :scope "test"]
|
||||
[adzerk/boot-test "1.1.1" :scope "test"]
|
||||
[tolitius/boot-check "0.1.2" :scope "test"]
|
||||
|
||||
;; boot cljs
|
||||
[adzerk/boot-cljs "1.7.228-1" :scope "test"]
|
||||
[adzerk/boot-cljs-repl "0.4.0" :scope "test"]
|
||||
[cider/piggieback "0.3.9" :scope "test" :exclusions [org.clojure/clojurescript]]
|
||||
[weasel "0.7.0" :scope "test" :exclusions [org.clojure/clojurescript]]
|
||||
[nrepl "0.4.5" :scope "test"]
|
||||
[adzerk/boot-cljs-repl "0.3.0" :scope "test"]
|
||||
[pandeiro/boot-http "0.7.1-SNAPSHOT" :scope "test"]
|
||||
[tolitius/boot-stripper "0.1.0-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.8" :scope "test"]
|
||||
[crisptrutski/boot-cljs-test "0.2.1-SNAPSHOT" :scope "test"]])
|
||||
|
||||
|
|
@ -144,8 +132,7 @@
|
|||
|
||||
(task-options!
|
||||
tcs/test-cljs {:js-env :phantom}
|
||||
push {; :ensure-clean nil
|
||||
:ensure-branch nil}
|
||||
push {:ensure-branch nil}
|
||||
pom {:project 'mount
|
||||
:version +version+
|
||||
:description "managing Clojure and ClojureScript app state since (reset)"
|
||||
|
|
|
|||
15
circle.yml
Normal file
15
circle.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
machine:
|
||||
java:
|
||||
version: oraclejdk8
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- wget https://github.com/boot-clj/boot-bin/releases/download/latest/boot.sh
|
||||
- mv boot.sh boot && chmod a+x boot && sudo mv boot /usr/local/bin
|
||||
|
||||
test:
|
||||
override:
|
||||
- boot test
|
||||
- boot test-cljs
|
||||
- boot test-cljs-advanced
|
||||
- lein do clean, test2junit
|
||||
52
deps.edn
52
deps.edn
|
|
@ -1,52 +0,0 @@
|
|||
{:paths ["src" "resources"]
|
||||
|
||||
:deps {} ;; deps no deps
|
||||
|
||||
:aliases {:dev {:extra-deps {metosin/jsonista {:mvn/version "0.3.8"}
|
||||
com.datomic/datomic-free {:mvn/version "0.9.5359"
|
||||
:exclusions [joda-time/joda-time]}
|
||||
org.clojure/tools.nrepl {:mvn/version "0.2.12"}
|
||||
org.clojure/tools.namespace {:mvn/version "0.2.11"}
|
||||
cheshire/cheshire {:mvn/version "5.5.0"}
|
||||
compojure/compojure {:mvn/version "1.5.0"}
|
||||
ring/ring-jetty-adapter {:mvn/version "1.1.0"}
|
||||
robert/hooke {:mvn/version "1.3.0"}
|
||||
proto-repl/proto-repl {:mvn/version "0.3.1"}
|
||||
proto-repl-charts/proto-repl-charts {:mvn/version "0.3.2"}
|
||||
nrepl/nrepl {:mvn/version "0.7.0"}}}
|
||||
:test {:extra-paths ["test/core" "test/clj" "test/cljs" "test/resources"]
|
||||
:extra-deps {com.datomic/datomic-free {:mvn/version "0.9.5359"
|
||||
:exclusions [joda-time/joda-time]}
|
||||
org.clojure/tools.nrepl {:mvn/version "0.2.12"}
|
||||
robert/hooke {:mvn/version "1.3.0"}
|
||||
org.clojure/tools.logging {:mvn/version "1.3.0"}
|
||||
io.github.cognitect-labs/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git"
|
||||
:sha "e7660458ce25bc4acb4ccc3e2415aae0a4907198"}}
|
||||
:main-opts ["-m" "cognitect.test-runner"]
|
||||
:exec-fn cognitect.test-runner.api/test}
|
||||
:test-cljs {:extra-paths ["test/core" "test/cljs" "test/resources"]
|
||||
:extra-deps {org.clojure/clojure {:mvn/version "1.8.0"}
|
||||
org.clojure/clojurescript {:mvn/version "1.7.228"}
|
||||
com.andrewmcveigh/cljs-time {:mvn/version "0.3.14"}
|
||||
hiccups/hiccups {:mvn/version "0.3.0"}
|
||||
datascript/datascript {:mvn/version "0.15.0"}
|
||||
olical/cljs-test-runner {:mvn/version "3.8.1"}}
|
||||
:main-opts ["-m" "cljs-test-runner.main"]}
|
||||
:repl {:extra-paths ["dev/clj"]
|
||||
:extra-deps {cider/cider-nrepl {:mvn/version "0.22.4"}
|
||||
org.clojure/tools.logging {:mvn/version "1.2.4"}
|
||||
com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
|
||||
:main-opts ["-e" "(require 'dev)(in-ns 'dev)"
|
||||
"-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware]"
|
||||
"-i" "-f" "rebel-readline.main/-main"]}
|
||||
:outdated {:extra-deps {olical/depot {:mvn/version "2.0.1"}}
|
||||
:main-opts ["-m" "depot.outdated.main" "-a" "outdated"]}
|
||||
:tag {:extra-deps {tolitius/tag {:mvn/version "0.1.7"}}
|
||||
:main-opts ["-m" "tag.core" "tolitius/mount" "managing Clojure and ClojureScript app state since (reset)"]}
|
||||
:jar {:extra-deps {seancorfield/depstar {:mvn/version "1.1.128"}}
|
||||
:extra-paths ["target/about"]
|
||||
:main-opts ["-m" "hf.depstar.jar" "target/mount.jar" "--exclude" "clojure/core/specs/alpha.*"]}
|
||||
:deploy {:extra-deps {deps-deploy/deps-deploy {:mvn/version "RELEASE"}}
|
||||
:main-opts ["-m" "deps-deploy.deps-deploy" "deploy" "target/mount.jar"]}
|
||||
:install {:extra-deps {deps-deploy/deps-deploy {:mvn/version "RELEASE"}}
|
||||
:main-opts ["-m" "deps-deploy.deps-deploy" "install" "target/mount.jar"]}}}
|
||||
|
|
@ -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 :refer [defstate]]
|
||||
[mount.tools.graph :refer [states-with-deps]]
|
||||
[app.utils.logging :refer [with-logging-status]]
|
||||
|
|
@ -40,11 +41,4 @@
|
|||
(tn/refresh :after 'dev/go))
|
||||
|
||||
(mount/in-clj-mode)
|
||||
|
||||
(defn load-data-readers!
|
||||
"Refresh *data-readers* with readers from newly acquired dependencies."
|
||||
[]
|
||||
(#'clojure.core/load-data-readers)
|
||||
(set! *data-readers* (.getRawRoot #'*data-readers*)))
|
||||
|
||||
(load-data-readers!)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
(ns proto-play
|
||||
(:require [mount.tools.graph :as mg]
|
||||
(:require [mount.tools.graph :as mount]
|
||||
[proto-repl-charts.graph :as proto]))
|
||||
|
||||
(defn mount->proto [graph]
|
||||
|
|
@ -10,6 +10,6 @@
|
|||
{}
|
||||
graph))
|
||||
|
||||
(->> (mg/states-with-deps)
|
||||
(->> (mount/states-with-deps)
|
||||
mount->proto
|
||||
(proto/graph "a proto graph of mount states"))
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ Mount has two modes `clj` and `cljc`.
|
|||
|
||||
#### Clojure _and_ ClojureScript Mode
|
||||
|
||||
`cljc` is not a default mode, but it is easy to switch to:
|
||||
`cljc` mode is is not default, but it is easy to switch to:
|
||||
|
||||
To switch Mount into this mode do:
|
||||
|
||||
|
|
@ -83,16 +83,14 @@ Let's look at the example [ClojureScript app](../dev/cljs/app) that uses mount t
|
|||
|
||||
In order to run it, just compile `cljs` (in `:advanced` mode, because why not? :)) with:
|
||||
|
||||
```
|
||||
$ boot cljs-example
|
||||
Started Jetty on http://localhost:3000
|
||||
nREPL server started on port 64412 on host 127.0.0.1 - nrepl://127.0.0.1:64412
|
||||
Adding :require adzerk.boot-cljs-repl to mount.cljs.edn...
|
||||
```clojure
|
||||
$ lein do clean, cljsbuild once prod
|
||||
Compiling ClojureScript...
|
||||
• mount.js
|
||||
Compiling "dev/resources/public/js/compiled/mount.js" from ["src" "dev/cljs"]...
|
||||
Successfully compiled "dev/resources/public/js/compiled/mount.js" in 23.966 seconds.
|
||||
```
|
||||
|
||||
And just open a browser at [http://localhost:3000](http://localhost:3000):
|
||||
And just open a browser at `file:///[path-to-mount]/mount/dev/resources/public/index.html`:
|
||||
|
||||
<img src="img/mount.cljs.example.png" width="700">
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ Before moving on to differences, [here](https://news.ycombinator.com/item?id=246
|
|||
Component really only works if you build your entire app around its model: application is fully based on Components
|
||||
where every Component is an Object.
|
||||
|
||||
Mount does not require you to "buy anything at all", it is free :) Just create a `defstate` whenever/wherever
|
||||
Mount does not require you to "buy anything at all", it is free :) Just create a `defstate` whenever/whereever
|
||||
you need it and use it.
|
||||
|
||||
This one was a big deal for all the projects we used Component with, "the whole app buy in" converts an "_open_" application
|
||||
|
|
@ -94,7 +94,7 @@ the same with or without Mount: there are no extra mental steps.
|
|||
### Objects vs. Namespaces
|
||||
|
||||
One thing that feels a bit "unClojure" about Component is "Objects". Objects everywhere, and Objects for everything.
|
||||
This is how Component "separates explicit dependencies" and "clears the boundaries".
|
||||
This is how Component "separates explicit dependencies" and "clears the bounaries".
|
||||
|
||||
This is also how an Object Oriented language does it, which does not leave a lot of room for functions:
|
||||
with Component most of the functions are _methods_ which is an important distinction.
|
||||
|
|
@ -146,13 +146,9 @@ no "ceremony".
|
|||
|
||||
Mount uses namespaces and vars where Component uses records and protocols.
|
||||
|
||||
Component manages records and protocols,
|
||||
and in order to do that it requires a whole app buy-in,
|
||||
which makes it a _framework_.
|
||||
Component manages protocols and records, and in order to do that it requires a whole app buyin, which makes it a _framework_.
|
||||
|
||||
Mount does not need to manage namespaces and vars,
|
||||
since it is very well managed by the Clojure Compiler,
|
||||
which makes it a _library_.
|
||||
Mount does not need to manage namespaces and vars, since it is very well managed by the Clojure Compiler, which makes it a _library_.
|
||||
|
||||
## What Component does better
|
||||
|
||||
|
|
@ -160,9 +156,9 @@ which makes it a _library_.
|
|||
|
||||
With Component multiple separate systems can be started _in the same Clojure runtime_ with different settings. Which _might_ be useful for testing, i.e. if you need to have `dev db` and `test db` started in the _same_ REPL, to _run tests within the same REPL you develop in_.
|
||||
|
||||
Development workflows vary and tend to be a subjective / preference based more than a true recipe, but I believe it is much cleaner to run tests in the _separate_ REPL / process. Moreover run them continuously: i.e. `boot watch speak test`: this way you don't event need to look at that other REPL / terminal, Boot will _tell_ you whether the tests pass or fail after any file is changed.
|
||||
Development workflows vary and tend to be a subjective / preference based more than a true recipe, but I believe it is much cleaner to run tests in the _separate_ REPL / process. Moreover run them continuesly: i.e. `boot watch speak test`: this way you don't event need to look at that other REPL / terminal, Boot will _tell_ you whether the tests pass or fail after any file is changed.
|
||||
|
||||
Mount keeps states in namespaces, hence the app becomes "[The One](https://en.wikipedia.org/wiki/Neo_(The_Matrix))", and there can't be "multiples The Ones". In practice, if we are talking about stateful external resources, there is trully only _one_ of them with a given configuration. Different configuration => different state. It's that simple.
|
||||
Mount keeps states in namespaces, hence the app becomes "[The One](https://en.wikipedia.org/wiki/Neo_(The_Matrix))", and there can't be "multiples The Ones". In practice, if we are talking about stateful external resources, there is trully only _one_ of them with a given configuration. Different configuration => different state. It's is that simple.
|
||||
|
||||
Testing is not alien to Mount and it knows how to do a thing or two:
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 215 KiB |
23
package.json
23
package.json
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"name": "@tolitius/mount",
|
||||
"version": "0.1.16",
|
||||
"license": "EPL-1.0",
|
||||
"homepage": "https://github.com/tolitius/mount",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tolitius/mount"
|
||||
},
|
||||
"author": {
|
||||
"name": "tolitius",
|
||||
"url": "http://www.dotkam.com"
|
||||
},
|
||||
"files": [
|
||||
"src/*"
|
||||
],
|
||||
"directories": {
|
||||
"lib": "src"
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": "^8.16.0"
|
||||
}
|
||||
}
|
||||
45
pom.xml
45
pom.xml
|
|
@ -1,45 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
<groupId>mount</groupId>
|
||||
<artifactId>mount</artifactId>
|
||||
<version>0.1.23</version>
|
||||
<name>mount</name>
|
||||
<description>managing Clojure and ClojureScript app state since (reset)</description>
|
||||
<url>https://github.com/tolitius/mount</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Eclipse Public License</name>
|
||||
<url>http://www.eclipse.org/legal/epl-v10.html</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<name>tolitius</name>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<url>https://github.com/tolitius/mount</url>
|
||||
<connection>scm:git:git://github.com/tolitius/mount.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/tolitius/mount.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.clojure</groupId>
|
||||
<artifactId>clojure</artifactId>
|
||||
<version>1.11.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<sourceDirectory>src</sourceDirectory>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>clojars</id>
|
||||
<url>https://repo.clojars.org/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
</project>
|
||||
12
project.clj
12
project.clj
|
|
@ -1,4 +1,4 @@
|
|||
(defproject mount "0.1.17"
|
||||
(defproject mount "0.1.10"
|
||||
:description "managing Clojure and ClojureScript app state since (reset)"
|
||||
:url "https://github.com/tolitius/mount"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
@ -8,12 +8,9 @@
|
|||
|
||||
:dependencies [] ;; for visual clarity
|
||||
|
||||
:tach {:test-runner-ns 'mount.test-self-host
|
||||
:source-paths ["test/core"]}
|
||||
|
||||
:profiles {:dev {:source-paths ["dev" "dev/clj" "test/clj"]
|
||||
:dependencies [[org.clojure/clojure "1.8.0"]
|
||||
[org.clojure/clojurescript "1.9.946"]; :classifier "aot"]
|
||||
: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"]
|
||||
|
|
@ -30,8 +27,7 @@
|
|||
:plugins [[lein-cljsbuild "1.1.1"]
|
||||
[lein-doo "0.1.6"]
|
||||
[lein-figwheel "0.5.0-2"]
|
||||
[test2junit "1.1.3"]
|
||||
[lein-tach "1.0.0"]]
|
||||
[test2junit "1.1.3"]]
|
||||
|
||||
:test2junit-output-dir ~(or (System/getenv "CIRCLE_TEST_REPORTS") "target/test2junit")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
{:linters {:mount/defstate {:level :warning}}
|
||||
:hooks {:analyze-call {mount.core/defstate hooks.defstate/defstate
|
||||
mount.core/defstate! hooks.defstate/defstate}}}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
(ns hooks.defstate
|
||||
(:require [clj-kondo.hooks-api :as api]))
|
||||
|
||||
(defn defstate [{:keys [node]}]
|
||||
(let [[n & args] (next (:children node))
|
||||
[docs args] (if (string? (api/sexpr (first args)))
|
||||
[(first args) (next args)]
|
||||
[nil args])
|
||||
m (when-let [m (first (:meta n))]
|
||||
(api/sexpr m))
|
||||
m (if (map? m) m {})
|
||||
ks (cond-> (take 1 args)
|
||||
(> (count args) 2) (conj (nth args 2)))
|
||||
invalid-key (first (remove (comp (partial contains? #{:start :stop}) api/sexpr) ks))]
|
||||
(cond
|
||||
invalid-key
|
||||
(api/reg-finding!
|
||||
{:message (str "lifecycle functions can only contain `:start` and `:stop`. illegal function found: " (api/sexpr invalid-key))
|
||||
:type :mount/defstate
|
||||
:row (:row (meta invalid-key))
|
||||
:col (:col (meta invalid-key))})
|
||||
(not (contains? (set (map api/sexpr ks)) :start))
|
||||
(throw (ex-info "lifecycle functions must include `:start`" {}))
|
||||
((complement contains?) #{2 4} (count args))
|
||||
(throw (ex-info "lifecycle functions must consist of no more than 2 pair forms: `:start` and `:stop`" {}))
|
||||
(and (contains? m :on-reload) (not (contains? #{:noop :stop} (:on-reload m))))
|
||||
(api/reg-finding!
|
||||
{:message "metadata `:on-reload` key can only have value of `noop` or `stop`"
|
||||
:type :mount/defstate
|
||||
:row (:row (meta n))
|
||||
:col (:col (meta n))})
|
||||
:else
|
||||
{:node (api/list-node
|
||||
(cond-> [(api/token-node 'def) n]
|
||||
docs (conj docs)
|
||||
true (conj (api/list-node
|
||||
(list*
|
||||
(api/token-node 'do)
|
||||
args)))))})))
|
||||
|
|
@ -1,16 +1,13 @@
|
|||
(ns mount.core
|
||||
#?(:clj {:clojure.tools.namespace.repl/load false}) ; prevent reloading of this ns
|
||||
#?(:clj (:require [mount.tools.macro :refer [on-error throw-runtime] :as macro]
|
||||
[mount.tools.macrovich :refer [deftime]]
|
||||
[mount.tools.logger :refer [log]]
|
||||
[clojure.set :refer [intersection]]
|
||||
[clojure.string :as s])
|
||||
:cljs (:require [mount.tools.macro]
|
||||
:cljs (:require [mount.tools.macro :as macro]
|
||||
[clojure.set :refer [intersection]]
|
||||
[mount.tools.logger :refer [log]]))
|
||||
#?(:cljs (:require-macros [mount.core]
|
||||
[mount.tools.macro :refer [on-error throw-runtime]]
|
||||
[mount.tools.macrovich :refer [deftime]])))
|
||||
[mount.tools.macro :refer [if-clj on-error throw-runtime]])))
|
||||
|
||||
(defonce ^:private -args (atom {})) ;; mostly for command line args and external files
|
||||
(defonce ^:private state-seq (atom 0))
|
||||
|
|
@ -18,6 +15,10 @@
|
|||
(defonce ^:private meta-state (atom {}))
|
||||
(defonce ^:private running (atom {})) ;; to clean dirty states on redefs
|
||||
|
||||
;; supporting tools.namespace: (disable-reload!)
|
||||
#?(:clj
|
||||
(alter-meta! *ns* assoc ::load false)) ;; to exclude the dependency
|
||||
|
||||
(defn- make-state-seq [state]
|
||||
(or (:order (@meta-state state))
|
||||
(swap! state-seq inc)))
|
||||
|
|
@ -57,6 +58,17 @@
|
|||
(stop))
|
||||
(swap! running dissoc state)))
|
||||
|
||||
#?(:clj
|
||||
(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)))
|
||||
|
||||
#?(:clj
|
||||
(defn alter-state! [{:keys [var inst]} value]
|
||||
(if (= @mode :cljc)
|
||||
|
|
@ -83,75 +95,34 @@
|
|||
(swap! running assoc state {:stop stop})
|
||||
(update-meta! [state :status] #{:started}))))
|
||||
|
||||
(defn- down
|
||||
"brings a state down by
|
||||
* calling its 'stop' function if it is defined
|
||||
* if not defined, state will still become a 'NotStartedState'
|
||||
* in case of a failure on 'stop', state is still marked as :stopped, and the error is logged / printed
|
||||
* dissoc'ing it from the running states
|
||||
* marking it as :stopped"
|
||||
[state {:keys [stop status] :as current} done]
|
||||
(defn- down [state {:keys [stop status] :as current} done]
|
||||
(when (some status #{:started})
|
||||
(if stop
|
||||
(if-let [cause (-> (on-error (str "could not stop [" state "] due to")
|
||||
(record! state stop done)
|
||||
:fail? false)
|
||||
:f-failed)]
|
||||
(log cause :error) ;; this would mostly be useful in REPL / browser console
|
||||
(alter-state! current (->NotStartedState state)))
|
||||
(alter-state! current (->NotStartedState state))) ;; (!) if a state does not have :stop when _should_ this might leak
|
||||
(when stop
|
||||
(on-error (str "could not stop [" state "] due to")
|
||||
(record! state stop done)))
|
||||
(alter-state! current (NotStartedState. state)) ;; (!) if a state does not have :stop when _should_ this might leak
|
||||
(swap! running dissoc state)
|
||||
(update-meta! [state :status] #{:stopped})))
|
||||
|
||||
(defn running-states []
|
||||
(set (keys @running)))
|
||||
|
||||
(deftype DerefableState [name]
|
||||
#?(:clj clojure.lang.IDeref
|
||||
:cljs IDeref)
|
||||
(#?(:clj deref
|
||||
:cljs -deref)
|
||||
[_]
|
||||
(let [{:keys [status var inst] :as state} (@meta-state name)]
|
||||
(let [{:keys [status inst] :as state} (@meta-state name)]
|
||||
(when-not (:started status)
|
||||
(if (= :throw (-> var meta :on-lazy-start))
|
||||
(throw-runtime (str ":on-lazy-start is set to :throw i.e. (defstate {:on-lazy-start :throw} " name "...) "
|
||||
"and " name " state was not explicitly started before it was deref'ed (i.e. @" name ")"))
|
||||
(up name state (atom #{}))))
|
||||
@inst))
|
||||
#?(:clj clojure.lang.IPending
|
||||
:cljs IPending)
|
||||
(#?(:clj isRealized
|
||||
:cljs -realized?)
|
||||
[_]
|
||||
(boolean ((running-states) name))))
|
||||
|
||||
#?(:clj
|
||||
(defn current-state [state]
|
||||
(let [{:keys [var]} (@meta-state state)]
|
||||
(if (= @mode :cljc)
|
||||
(->DerefableState state)
|
||||
(var-get var))))
|
||||
|
||||
:cljs
|
||||
(defn current-state [state]
|
||||
(-> (@meta-state state) :inst deref)))
|
||||
(up name state (atom #{})))
|
||||
@inst)))
|
||||
|
||||
(defn on-reload-meta [s-var]
|
||||
(or (-> s-var meta :on-reload)
|
||||
:restart)) ;; restart by default on ns reload
|
||||
|
||||
(defn running-noop? [s-name]
|
||||
(let [{:keys [var status]} (@meta-state s-name)
|
||||
on-reload (-> var meta :on-reload)]
|
||||
(when status
|
||||
(and (status :started)
|
||||
(= :noop on-reload)))))
|
||||
|
||||
;;TODO: make private after figuring out the inconsistency betwen cljs compile stages
|
||||
;; (i.e. _sometimes_ this, if private, is not seen by expanded "defmacro" on cljs side)
|
||||
(defn mount-it [s-var s-name s-meta]
|
||||
(let [with-inst (assoc s-meta :inst (atom (->NotStartedState s-name))
|
||||
(let [with-inst (assoc s-meta :inst (atom (NotStartedState. s-name))
|
||||
:var s-var)
|
||||
on-reload (on-reload-meta s-var)
|
||||
existing? (when-not (= :noop on-reload)
|
||||
|
|
@ -161,15 +132,9 @@
|
|||
(log (str ">> starting.. " s-name " (namespace was recompiled)"))
|
||||
(up s-name with-inst (atom #{})))))
|
||||
|
||||
(deftime
|
||||
|
||||
(defmacro defstate
|
||||
"defines a state (a.k.a. a stateful component).
|
||||
restarts on recompilation.
|
||||
pass ^{:on-reload :noop} to prevent auto-restart on ns recompilation,
|
||||
or ^{:on-reload :stop} to stop on recompilation."
|
||||
[state & body]
|
||||
(let [[state params] (mount.tools.macro/name-with-attributes state body)
|
||||
#?(:clj
|
||||
(defmacro defstate [state & body]
|
||||
(let [[state params] (macro/name-with-attributes state body)
|
||||
{:keys [start stop] :as lifecycle} (apply hash-map params)
|
||||
state-name (with-ns *ns* state)
|
||||
order (make-state-seq state-name)]
|
||||
|
|
@ -180,23 +145,18 @@
|
|||
stop (assoc :stop `(fn [] ~stop)))]
|
||||
`(do
|
||||
;; (log (str "|| mounting... " ~state-name))
|
||||
;; only create/redefine a new state iff this is not a running ^{:on-reload :noop}
|
||||
(if-not (running-noop? ~state-name)
|
||||
(do
|
||||
(~'defonce ~state (->DerefableState ~state-name))
|
||||
(mount-it (~'var ~state) ~state-name ~s-meta))
|
||||
(~'defonce ~state (current-state ~state-name)))
|
||||
(~'var ~state)))))
|
||||
(~'defonce ~state (DerefableState. ~state-name))
|
||||
(mount-it (~'var ~state) ~state-name ~s-meta)
|
||||
(~'var ~state))))))
|
||||
|
||||
#?(:clj
|
||||
(defmacro defstate! [state & {:keys [start! stop!]}]
|
||||
(let [state-name (with-ns *ns* state)]
|
||||
`(defstate ~state
|
||||
:start (~'let [~state (mount.core/current-state ~state-name)]
|
||||
:start (~'let [~state (mount/current-state ~state-name)]
|
||||
~start!)
|
||||
:stop (~'let [~state (mount.core/current-state ~state-name)]
|
||||
~stop!))))
|
||||
|
||||
)
|
||||
:stop (~'let [~state (mount/current-state ~state-name)]
|
||||
~stop!)))))
|
||||
|
||||
(defn in-cljc-mode []
|
||||
(reset! mode :cljc))
|
||||
|
|
@ -221,6 +181,9 @@
|
|||
(with-ns ns name))
|
||||
v)))
|
||||
|
||||
(defn running-states []
|
||||
(set (keys @running)))
|
||||
|
||||
(defn- unvar-state [s]
|
||||
(->> s (drop 2) (apply str))) ;; magic 2 is removing "#'" in state name
|
||||
|
||||
|
|
@ -284,7 +247,7 @@
|
|||
(if-not (empty? fs) ;; (mount/start) vs. (mount/start #{}) vs. (mount/start #{1 2 3})
|
||||
(apply start fs)
|
||||
{:started #{}})
|
||||
(let [states (or (->> states (map var-to-str) seq)
|
||||
(let [states (or (seq states)
|
||||
(all-without-subs))]
|
||||
{:started (bring states up <)}))))
|
||||
|
||||
|
|
@ -294,7 +257,7 @@
|
|||
(if-not (empty? fs) ;; (mount/stop) vs. (mount/stop #{}) vs. (mount/stop #{1 2 3})
|
||||
(apply stop fs)
|
||||
{:stopped #{}})
|
||||
(let [states (or (->> states (map var-to-str) seq)
|
||||
(let [states (or (seq states)
|
||||
(find-all-states))
|
||||
_ (dorun (map unsub states)) ;; unmark substitutions marked by "start-with" / "swap-states"
|
||||
stopped (bring states down >)]
|
||||
|
|
@ -308,8 +271,8 @@
|
|||
set))
|
||||
|
||||
(defn only
|
||||
([these]
|
||||
(only (find-all-states) these))
|
||||
([states]
|
||||
(only (find-all-states) states))
|
||||
([states these]
|
||||
(intersection (mapset var-to-str these)
|
||||
(mapset var-to-str states))))
|
||||
|
|
@ -322,8 +285,8 @@
|
|||
states))
|
||||
|
||||
(defn except
|
||||
([these]
|
||||
(except (find-all-states) these))
|
||||
([states]
|
||||
(except (find-all-states) states))
|
||||
([states these]
|
||||
(remove (mapset var-to-str these)
|
||||
(mapset var-to-str states))))
|
||||
|
|
@ -378,9 +341,7 @@
|
|||
(let [all (set (find-all-states))
|
||||
states (map var-to-str states)
|
||||
states (remove (set states) all)]
|
||||
(if-not (empty? states)
|
||||
(apply stop states)
|
||||
{:stopped #{}})))
|
||||
(apply stop states)))
|
||||
|
||||
(defn start-with-args [xs & states]
|
||||
(reset! -args xs)
|
||||
|
|
@ -405,7 +366,5 @@
|
|||
(let [app (set (all-without-subs))
|
||||
states (map var-to-str states)
|
||||
without (remove (set states) app)]
|
||||
(if-not (empty? without)
|
||||
(apply start without)
|
||||
{:started #{}}))
|
||||
(apply start without))
|
||||
(start)))
|
||||
|
|
|
|||
|
|
@ -3,18 +3,16 @@
|
|||
(:import [goog.debug Console])]))
|
||||
|
||||
#?(:cljs
|
||||
(defonce ^:dynamic *logger*
|
||||
(defonce *logger*
|
||||
(do
|
||||
(.setCapturing (Console.) true)
|
||||
(glog/getLogger "mount" nil))))
|
||||
(glog/getLogger "mount"))))
|
||||
|
||||
#?(:clj
|
||||
(defn log [msg & _]
|
||||
(defn log [msg]
|
||||
(prn msg)))
|
||||
|
||||
#?(:cljs
|
||||
(defn log [msg & level]
|
||||
(case (first level)
|
||||
:error (glog/error *logger* msg nil)
|
||||
(glog/info *logger* msg nil))))
|
||||
(defn log [msg]
|
||||
(glog/info *logger* msg)))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,28 +1,26 @@
|
|||
(ns mount.tools.macro
|
||||
(:refer-clojure :exclude [case])
|
||||
#?(:cljs (:require-macros [mount.tools.macrovich :refer [deftime]])
|
||||
:clj (:require [mount.tools.macrovich :refer [deftime]])))
|
||||
#?(:cljs (:require-macros [mount.tools.macro])))
|
||||
|
||||
(deftime
|
||||
#?(:clj
|
||||
(defmacro if-clj [then else]
|
||||
(if (-> &env :ns not)
|
||||
then
|
||||
else)))
|
||||
|
||||
(defmacro on-error [msg f & {:keys [fail?]
|
||||
:or {fail? true}}]
|
||||
`(mount.tools.macrovich/case
|
||||
:clj (try ~f
|
||||
#?(:clj
|
||||
(defmacro on-error [msg f]
|
||||
`(if-clj
|
||||
(try ~f
|
||||
(catch Throwable t#
|
||||
(if ~fail?
|
||||
(throw (RuntimeException. ~msg t#))
|
||||
{:f-failed (ex-info ~msg {} t#)})))
|
||||
:cljs (try ~f
|
||||
(throw (RuntimeException. ~msg t#))))
|
||||
(try ~f
|
||||
(catch :default t#
|
||||
(if ~fail?
|
||||
(throw (js/Error (~'str ~msg " " t#)))
|
||||
{:f-failed (ex-info ~msg {} t#)})))))
|
||||
(throw (~'str ~msg " " t#)))))))
|
||||
|
||||
#?(:clj
|
||||
(defmacro throw-runtime [msg]
|
||||
`(throw (mount.tools.macrovich/case :clj (RuntimeException. ~msg) :cljs (~'str ~msg))))
|
||||
|
||||
)
|
||||
`(throw (if-clj (RuntimeException. ~msg)
|
||||
(~'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
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
(ns ^{:doc "From https://github.com/cgrand/macrovich. Licensed under EPL v1.
|
||||
Copyright Cristophe Grand." }
|
||||
mount.tools.macrovich
|
||||
(:refer-clojure :exclude [case]))
|
||||
|
||||
(defmacro deftime
|
||||
"This block will only be evaluated at the correct time for macro definition, at other times its content
|
||||
are removed.
|
||||
For Clojure it always behaves like a `do` block.
|
||||
For Clojurescript/JVM the block is only visible to Clojure.
|
||||
For self-hosted Clojurescript the block is only visible when defining macros in the pseudo-namespace."
|
||||
[& body]
|
||||
(when #?(:clj (not (:ns &env)) :cljs (re-matches #".*\$macros" (name (ns-name *ns*))))
|
||||
`(do ~@body)))
|
||||
|
||||
(defmacro case [& {:keys [cljs clj]}]
|
||||
(if (contains? &env '&env)
|
||||
`(if (:ns ~'&env) ~cljs ~clj)
|
||||
(if #?(:clj (:ns &env) :cljs true)
|
||||
cljs
|
||||
clj)))
|
||||
|
|
@ -6,9 +6,8 @@
|
|||
(defn entity [conn id]
|
||||
(d/entity (d/db conn) id))
|
||||
|
||||
(defn touch
|
||||
(defn touch [conn results]
|
||||
"takes 'entity ids' results from a query
|
||||
e.g. '#{[272678883689461] [272678883689462] [272678883689459] [272678883689457]}'"
|
||||
[conn results]
|
||||
(let [e (partial entity conn)]
|
||||
(map #(-> % first e d/touch) results)))
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
mount.test.start-without
|
||||
mount.test.start-with
|
||||
mount.test.start-with-states
|
||||
mount.test.printing
|
||||
))
|
||||
|
||||
#?(:clj (alter-meta! *ns* assoc ::load false))
|
||||
|
|
@ -32,7 +31,6 @@
|
|||
'mount.test.start-without
|
||||
'mount.test.start-with
|
||||
'mount.test.start-with-states
|
||||
'mount.test.printing
|
||||
))
|
||||
|
||||
(defn run-tests []
|
||||
|
|
|
|||
|
|
@ -109,16 +109,6 @@
|
|||
(is (= 42 (dval conn)))
|
||||
(mount/stop)))
|
||||
|
||||
(testing "swap-states should swap states on start and rollback on stop"
|
||||
(let [states (swap-states {#'tapp.nyse/conn swap-conn})]
|
||||
(is (= states (#'mount.core/find-all-states)))
|
||||
(mount/start #'tapp.nyse/conn)
|
||||
(is (= 42 (dval conn)))
|
||||
(mount/stop #'tapp.nyse/conn)
|
||||
(mount/start #'tapp.nyse/conn)
|
||||
(is (instance? datomic.peer.LocalConnection (dval conn)))
|
||||
(mount/stop)))
|
||||
|
||||
(testing "swap-states should swap states with states and return only states that it is given"
|
||||
(let [t-states #{"#'is.not/here" #'mount.test.composable-fns/test-conn #'tapp.nyse/conn}
|
||||
states (swap-states t-states {#'tapp.nyse/conn swap-conn
|
||||
|
|
|
|||
|
|
@ -35,9 +35,8 @@
|
|||
(require 'mount.test.on-reload-helper :reload)
|
||||
|
||||
;; "a" is marked as :noop on reload
|
||||
;; previous behavior left a stale reference =>>> ;; (is (instance? mount.core.NotStartedState (dval a))) ;; (!) stale reference of old a is still there somewhere
|
||||
(is (= :started (dval a))) ;; make sure a still has the same instance as before reload
|
||||
(is (= (-> pre-reload :a) ;; and the start was not called: the counter did not change
|
||||
(is (instance? mount.core.NotStartedState (dval a))) ;; (!) stale reference of old a is still there somewhere
|
||||
(is (= (-> pre-reload :a)
|
||||
(-> @counter :a)))
|
||||
|
||||
;; "b" is marked as :stop on reload
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
(ns mount.test.printing
|
||||
(:require
|
||||
#?@(:cljs [[cljs.test :as t :refer-macros [is are deftest testing use-fixtures]]
|
||||
[mount.core :as mount :refer-macros [defstate]]]
|
||||
: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))
|
||||
|
||||
(defstate foo
|
||||
:start (do (println "Starting!") 42))
|
||||
|
||||
(deftest test-printing-has-no-side-effects
|
||||
;; Test that printing an unstarted DerefableState does not have the
|
||||
;; side-effect of starting it
|
||||
(println foo)
|
||||
(is (not= 42 foo)))
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
(ns mount.test-self-host
|
||||
(:require
|
||||
[cljs.test :as t]
|
||||
|
||||
mount.test.fun-with-values
|
||||
mount.test.private-fun
|
||||
mount.test.printing
|
||||
mount.test.parts
|
||||
mount.test.cleanup-dirty-states
|
||||
mount.test.stop-except
|
||||
mount.test.start-without
|
||||
mount.test.start-with
|
||||
mount.test.start-with-states
|
||||
))
|
||||
|
||||
(t/run-tests
|
||||
'mount.test.fun-with-values
|
||||
'mount.test.private-fun
|
||||
'mount.test.printing
|
||||
'mount.test.parts
|
||||
'mount.test.cleanup-dirty-states
|
||||
;; 'mount.test.stop-except ;; TODO: can't run with deps.edn (due to "WebSocket is not defined")
|
||||
;; 'mount.test.start-with ;; boot, lein have no problems
|
||||
;; 'mount.test.start-with-states ;; most likely somm misconfigured in node..
|
||||
'mount.test.start-without
|
||||
)
|
||||
|
||||
(defn run-tests []
|
||||
(t/run-all-tests #"mount.test.*"))
|
||||
Loading…
Reference in a new issue