Compare commits
No commits in common. "master" and "0.1.12" have entirely different histories.
50 changed files with 210 additions and 607 deletions
|
|
@ -1 +0,0 @@
|
||||||
{:config-paths ["../resources/clj-kondo.exports/mount/mount/"]}
|
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -1,10 +1,7 @@
|
||||||
/target
|
/target
|
||||||
/classes
|
/classes
|
||||||
/checkouts
|
/checkouts
|
||||||
.cpcache/
|
pom.xml
|
||||||
.rebel_readline_history
|
|
||||||
cljs-test-runner-out
|
|
||||||
node_modules
|
|
||||||
pom.xml.asc
|
pom.xml.asc
|
||||||
.repl*
|
.repl*
|
||||||
dev/resources/public/js/*
|
dev/resources/public/js/*
|
||||||
|
|
@ -21,4 +18,3 @@ doo-index.html
|
||||||
/.nrepl-history
|
/.nrepl-history
|
||||||
.cljs_rhino_repl/
|
.cljs_rhino_repl/
|
||||||
out/
|
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
|
|
||||||
35
CHANGELOG.md
35
CHANGELOG.md
|
|
@ -1,38 +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
|
## 0.1.11
|
||||||
###### Fri Dec 2 18:09:51 2016 -0600
|
###### Fri Dec 2 18:09:51 2016 -0600
|
||||||
|
|
||||||
|
|
|
||||||
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
|
|
||||||
95
README.md
95
README.md
|
|
@ -1,13 +1,19 @@
|
||||||
> I think that it's _extraordinarily important_ that we in computer science keep fun in computing
|
> 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">
|
# mount <img src="doc/img/mount-logo.png" width="70px">
|
||||||
[](https://github.com/tolitius/mount/releases)
|
|
||||||
[](https://clojars.org/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)
|
||||||
|
|
||||||
|
[](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)*
|
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
||||||
|
|
||||||
- [Why?](#why)
|
- [Why?](#why)
|
||||||
|
|
@ -28,8 +34,6 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt
|
||||||
- [Swapping States with States](#swapping-states-with-states)
|
- [Swapping States with States](#swapping-states-with-states)
|
||||||
- [Stop an Application Except Certain States](#stop-an-application-except-certain-states)
|
- [Stop an Application Except Certain States](#stop-an-application-except-certain-states)
|
||||||
- [ClojureScript is Clojure](doc/clojurescript.md#managing-state-in-clojurescript)
|
- [ClojureScript is Clojure](doc/clojurescript.md#managing-state-in-clojurescript)
|
||||||
- [cljc mode](#cljc-mode)
|
|
||||||
- [Disable Lazy Start](#disable-lazy-start)
|
|
||||||
- [Packaging](#packaging)
|
- [Packaging](#packaging)
|
||||||
- [Affected States](#affected-states)
|
- [Affected States](#affected-states)
|
||||||
- [Recompiling Namespaces with Running States](#recompiling-namespaces-with-running-states)
|
- [Recompiling Namespaces with Running States](#recompiling-namespaces-with-running-states)
|
||||||
|
|
@ -90,7 +94,7 @@ Creating state is easy:
|
||||||
(defstate conn :start (create-conn))
|
(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`
|
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:
|
`required` by other namespaces or in REPL:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
dev=> (require '[app.db :refer [conn]])
|
dev=> (require '[app.nyse :refer [conn]])
|
||||||
nil
|
nil
|
||||||
dev=> conn
|
dev=> conn
|
||||||
#object[datomic.peer.LocalConnection 0x1661a4eb "datomic.peer.LocalConnection@1661a4eb"]
|
#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.
|
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
|
## Dependencies
|
||||||
|
|
||||||
If the whole app is one big application context (or `system`), cross dependencies with a solid dependency graph
|
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))
|
(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`.
|
[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)_
|
|
||||||
|
|
||||||
## Value of values
|
## Value of values
|
||||||
|
|
||||||
|
|
@ -405,7 +392,7 @@ The `start-with-states` function takes values in a form of `{:start fn :stop fn}
|
||||||
```
|
```
|
||||||
|
|
||||||
`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
|
`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
|
||||||
substituted with `#(connect test-config)` function, which could end up being anything, a map, an in memory DB, etc.
|
substituted with `#(connect test-config)` function, which could endup being anything, a map, an in memory DB, etc.
|
||||||
|
|
||||||
The `:stop` functions of substitutes can be anything, and could refer to the original state references. As in the example above: `db` and `publisher`
|
The `:stop` functions of substitutes can be anything, and could refer to the original state references. As in the example above: `db` and `publisher`
|
||||||
are real references. They would need to be accessible from the namespace of course, so you might need to `(:require [app.neo :refer [db]])`
|
are real references. They would need to be accessible from the namespace of course, so you might need to `(:require [app.neo :refer [db]])`
|
||||||
|
|
@ -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)
|
<< 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
|
## 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.
|
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.
|
||||||
|
|
@ -861,7 +798,7 @@ The documentation is [here](doc/runtime-arguments.md#passing-runtime-arguments).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright © 2020 tolitius
|
Copyright © 2017 tolitius
|
||||||
|
|
||||||
Distributed under the Eclipse Public License either version 1.0 or (at
|
Distributed under the Eclipse Public License either version 1.0 or (at
|
||||||
your option) any later version.
|
your option) any later version.
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
BOOT_VERSION=2.7.1
|
|
||||||
BOOT_CLOJURE_VERSION=1.10.1
|
|
||||||
25
build.boot
25
build.boot
|
|
@ -1,18 +1,8 @@
|
||||||
(def +version+ "0.1.18")
|
(def +version+ "0.1.12")
|
||||||
|
|
||||||
;; -- 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
|
|
||||||
|
|
||||||
(set-env!
|
(set-env!
|
||||||
:source-paths #{"src"}
|
: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
|
;; mount dev, examples apps and tests
|
||||||
|
|
||||||
[org.clojure/clojure "1.8.0" :scope "provided"]
|
[org.clojure/clojure "1.8.0" :scope "provided"]
|
||||||
|
|
@ -23,7 +13,6 @@
|
||||||
[cheshire "5.5.0" :scope "provided"]
|
[cheshire "5.5.0" :scope "provided"]
|
||||||
[hiccups "0.3.0" :scope "provided" :exclusions [org.clojure/clojurescript]]
|
[hiccups "0.3.0" :scope "provided" :exclusions [org.clojure/clojurescript]]
|
||||||
[com.andrewmcveigh/cljs-time "0.3.14" :scope "provided"]
|
[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"]
|
[ch.qos.logback/logback-classic "1.1.3" :scope "provided"]
|
||||||
[org.clojure/tools.logging "0.3.1" :scope "provided"]
|
[org.clojure/tools.logging "0.3.1" :scope "provided"]
|
||||||
[robert/hooke "1.3.0" :scope "provided"]
|
[robert/hooke "1.3.0" :scope "provided"]
|
||||||
|
|
@ -44,12 +33,11 @@
|
||||||
|
|
||||||
;; boot cljs
|
;; boot cljs
|
||||||
[adzerk/boot-cljs "1.7.228-1" :scope "test"]
|
[adzerk/boot-cljs "1.7.228-1" :scope "test"]
|
||||||
[adzerk/boot-cljs-repl "0.4.0" :scope "test"]
|
[adzerk/boot-cljs-repl "0.3.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"]
|
|
||||||
[pandeiro/boot-http "0.7.1-SNAPSHOT" :scope "test"]
|
[pandeiro/boot-http "0.7.1-SNAPSHOT" :scope "test"]
|
||||||
[tolitius/boot-stripper "0.1.0-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"]
|
[adzerk/boot-reload "0.4.8" :scope "test"]
|
||||||
[crisptrutski/boot-cljs-test "0.2.1-SNAPSHOT" :scope "test"]])
|
[crisptrutski/boot-cljs-test "0.2.1-SNAPSHOT" :scope "test"]])
|
||||||
|
|
||||||
|
|
@ -144,8 +132,7 @@
|
||||||
|
|
||||||
(task-options!
|
(task-options!
|
||||||
tcs/test-cljs {:js-env :phantom}
|
tcs/test-cljs {:js-env :phantom}
|
||||||
push {; :ensure-clean nil
|
push {:ensure-branch nil}
|
||||||
:ensure-branch nil}
|
|
||||||
pom {:project 'mount
|
pom {:project 'mount
|
||||||
:version +version+
|
:version +version+
|
||||||
:description "managing Clojure and ClojureScript app state since (reset)"
|
:description "managing Clojure and ClojureScript app state since (reset)"
|
||||||
|
|
|
||||||
17
circle.yml
Normal file
17
circle.yml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
machine:
|
||||||
|
java:
|
||||||
|
version: oraclejdk8
|
||||||
|
environment:
|
||||||
|
_JAVA_OPTIONS: "-Xms512m -Xmx2048m"
|
||||||
|
|
||||||
|
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
|
(ns dev
|
||||||
(:require [clojure.pprint :refer [pprint]]
|
(:require [clojure.pprint :refer [pprint]]
|
||||||
[clojure.tools.namespace.repl :as tn]
|
[clojure.tools.namespace.repl :as tn]
|
||||||
|
[boot.core :refer [load-data-readers!]]
|
||||||
[mount.core :as mount :refer [defstate]]
|
[mount.core :as mount :refer [defstate]]
|
||||||
[mount.tools.graph :refer [states-with-deps]]
|
[mount.tools.graph :refer [states-with-deps]]
|
||||||
[app.utils.logging :refer [with-logging-status]]
|
[app.utils.logging :refer [with-logging-status]]
|
||||||
|
|
@ -40,11 +41,4 @@
|
||||||
(tn/refresh :after 'dev/go))
|
(tn/refresh :after 'dev/go))
|
||||||
|
|
||||||
(mount/in-clj-mode)
|
(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!)
|
(load-data-readers!)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns proto-play
|
(ns proto-play
|
||||||
(:require [mount.tools.graph :as mg]
|
(:require [mount.tools.graph :as mount]
|
||||||
[proto-repl-charts.graph :as proto]))
|
[proto-repl-charts.graph :as proto]))
|
||||||
|
|
||||||
(defn mount->proto [graph]
|
(defn mount->proto [graph]
|
||||||
|
|
@ -10,6 +10,6 @@
|
||||||
{}
|
{}
|
||||||
graph))
|
graph))
|
||||||
|
|
||||||
(->> (mg/states-with-deps)
|
(->> (mount/states-with-deps)
|
||||||
mount->proto
|
mount->proto
|
||||||
(proto/graph "a proto graph of mount states"))
|
(proto/graph "a proto graph of mount states"))
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ the same with or without Mount: there are no extra mental steps.
|
||||||
### Objects vs. Namespaces
|
### Objects vs. Namespaces
|
||||||
|
|
||||||
One thing that feels a bit "unClojure" about Component is "Objects". Objects everywhere, and Objects for everything.
|
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:
|
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.
|
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.
|
Mount uses namespaces and vars where Component uses records and protocols.
|
||||||
|
|
||||||
Component manages records and protocols,
|
Component manages protocols and records, and in order to do that it requires a whole app buyin, which makes it a _framework_.
|
||||||
and in order to do that it requires a whole app buy-in,
|
|
||||||
which makes it a _framework_.
|
|
||||||
|
|
||||||
Mount does not need to manage namespaces and vars,
|
Mount does not need to manage namespaces and vars, since it is very well managed by the Clojure Compiler, which makes it a _library_.
|
||||||
since it is very well managed by the Clojure Compiler,
|
|
||||||
which makes it a _library_.
|
|
||||||
|
|
||||||
## What Component does better
|
## 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_.
|
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:
|
Testing is not alien to Mount and it knows how to do a thing or two:
|
||||||
|
|
||||||
|
|
|
||||||
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.12-SNAPSHOT"
|
||||||
:description "managing Clojure and ClojureScript app state since (reset)"
|
:description "managing Clojure and ClojureScript app state since (reset)"
|
||||||
:url "https://github.com/tolitius/mount"
|
:url "https://github.com/tolitius/mount"
|
||||||
:license {:name "Eclipse Public License"
|
:license {:name "Eclipse Public License"
|
||||||
|
|
@ -8,12 +8,9 @@
|
||||||
|
|
||||||
:dependencies [] ;; for visual clarity
|
: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"]
|
:profiles {:dev {:source-paths ["dev" "dev/clj" "test/clj"]
|
||||||
:dependencies [[org.clojure/clojure "1.8.0"]
|
:dependencies [[org.clojure/clojure "1.7.0"]
|
||||||
[org.clojure/clojurescript "1.9.946"]; :classifier "aot"]
|
[org.clojure/clojurescript "1.7.170"]; :classifier "aot"]
|
||||||
[datascript "0.13.3"]
|
[datascript "0.13.3"]
|
||||||
[compojure "1.4.0"]
|
[compojure "1.4.0"]
|
||||||
[ring/ring-jetty-adapter "1.1.0"]
|
[ring/ring-jetty-adapter "1.1.0"]
|
||||||
|
|
@ -30,8 +27,7 @@
|
||||||
:plugins [[lein-cljsbuild "1.1.1"]
|
:plugins [[lein-cljsbuild "1.1.1"]
|
||||||
[lein-doo "0.1.6"]
|
[lein-doo "0.1.6"]
|
||||||
[lein-figwheel "0.5.0-2"]
|
[lein-figwheel "0.5.0-2"]
|
||||||
[test2junit "1.1.3"]
|
[test2junit "1.1.3"]]
|
||||||
[lein-tach "1.0.0"]]
|
|
||||||
|
|
||||||
:test2junit-output-dir ~(or (System/getenv "CIRCLE_TEST_REPORTS") "target/test2junit")
|
: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
|
(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]
|
#?(:clj (:require [mount.tools.macro :refer [on-error throw-runtime] :as macro]
|
||||||
[mount.tools.macrovich :refer [deftime]]
|
|
||||||
[mount.tools.logger :refer [log]]
|
[mount.tools.logger :refer [log]]
|
||||||
[clojure.set :refer [intersection]]
|
[clojure.set :refer [intersection]]
|
||||||
[clojure.string :as s])
|
[clojure.string :as s])
|
||||||
:cljs (:require [mount.tools.macro]
|
:cljs (:require [mount.tools.macro :as macro]
|
||||||
[clojure.set :refer [intersection]]
|
[clojure.set :refer [intersection]]
|
||||||
[mount.tools.logger :refer [log]]))
|
[mount.tools.logger :refer [log]]))
|
||||||
#?(:cljs (:require-macros [mount.core]
|
#?(:cljs (:require-macros [mount.core]
|
||||||
[mount.tools.macro :refer [on-error throw-runtime]]
|
[mount.tools.macro :refer [if-clj on-error throw-runtime]])))
|
||||||
[mount.tools.macrovich :refer [deftime]])))
|
|
||||||
|
|
||||||
(defonce ^:private -args (atom {})) ;; mostly for command line args and external files
|
(defonce ^:private -args (atom {})) ;; mostly for command line args and external files
|
||||||
(defonce ^:private state-seq (atom 0))
|
(defonce ^:private state-seq (atom 0))
|
||||||
|
|
@ -18,6 +15,10 @@
|
||||||
(defonce ^:private meta-state (atom {}))
|
(defonce ^:private meta-state (atom {}))
|
||||||
(defonce ^:private running (atom {})) ;; to clean dirty states on redefs
|
(defonce ^:private running (atom {})) ;; to clean dirty states on redefs
|
||||||
|
|
||||||
|
;; supporting tools.namespace: (disable-reload!)
|
||||||
|
#?(:clj
|
||||||
|
(alter-meta! *ns* assoc ::load false)) ;; to exclude the dependency
|
||||||
|
|
||||||
(defn- make-state-seq [state]
|
(defn- make-state-seq [state]
|
||||||
(or (:order (@meta-state state))
|
(or (:order (@meta-state state))
|
||||||
(swap! state-seq inc)))
|
(swap! state-seq inc)))
|
||||||
|
|
@ -57,6 +58,17 @@
|
||||||
(stop))
|
(stop))
|
||||||
(swap! running dissoc state)))
|
(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
|
#?(:clj
|
||||||
(defn alter-state! [{:keys [var inst]} value]
|
(defn alter-state! [{:keys [var inst]} value]
|
||||||
(if (= @mode :cljc)
|
(if (= @mode :cljc)
|
||||||
|
|
@ -98,8 +110,8 @@
|
||||||
:fail? false)
|
:fail? false)
|
||||||
:f-failed)]
|
:f-failed)]
|
||||||
(log cause :error) ;; this would mostly be useful in REPL / browser console
|
(log cause :error) ;; this would mostly be useful in REPL / browser console
|
||||||
(alter-state! current (->NotStartedState state)))
|
(alter-state! current (NotStartedState. state)))
|
||||||
(alter-state! current (->NotStartedState state))) ;; (!) if a state does not have :stop when _should_ this might leak
|
(alter-state! current (NotStartedState. state))) ;; (!) if a state does not have :stop when _should_ this might leak
|
||||||
(swap! running dissoc state)
|
(swap! running dissoc state)
|
||||||
(update-meta! [state :status] #{:stopped})))
|
(update-meta! [state :status] #{:stopped})))
|
||||||
|
|
||||||
|
|
@ -112,12 +124,9 @@
|
||||||
(#?(:clj deref
|
(#?(:clj deref
|
||||||
:cljs -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)
|
(when-not (:started status)
|
||||||
(if (= :throw (-> var meta :on-lazy-start))
|
(up name state (atom #{})))
|
||||||
(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))
|
@inst))
|
||||||
#?(:clj clojure.lang.IPending
|
#?(:clj clojure.lang.IPending
|
||||||
:cljs IPending)
|
:cljs IPending)
|
||||||
|
|
@ -126,17 +135,6 @@
|
||||||
[_]
|
[_]
|
||||||
(boolean ((running-states) name))))
|
(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)))
|
|
||||||
|
|
||||||
(defn on-reload-meta [s-var]
|
(defn on-reload-meta [s-var]
|
||||||
(or (-> s-var meta :on-reload)
|
(or (-> s-var meta :on-reload)
|
||||||
:restart)) ;; restart by default on ns reload
|
:restart)) ;; restart by default on ns reload
|
||||||
|
|
@ -151,7 +149,7 @@
|
||||||
;;TODO: make private after figuring out the inconsistency betwen cljs compile stages
|
;;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)
|
;; (i.e. _sometimes_ this, if private, is not seen by expanded "defmacro" on cljs side)
|
||||||
(defn mount-it [s-var s-name s-meta]
|
(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)
|
:var s-var)
|
||||||
on-reload (on-reload-meta s-var)
|
on-reload (on-reload-meta s-var)
|
||||||
existing? (when-not (= :noop on-reload)
|
existing? (when-not (= :noop on-reload)
|
||||||
|
|
@ -161,15 +159,13 @@
|
||||||
(log (str ">> starting.. " s-name " (namespace was recompiled)"))
|
(log (str ">> starting.. " s-name " (namespace was recompiled)"))
|
||||||
(up s-name with-inst (atom #{})))))
|
(up s-name with-inst (atom #{})))))
|
||||||
|
|
||||||
(deftime
|
#?(:clj
|
||||||
|
(defmacro defstate
|
||||||
(defmacro defstate
|
"Defines a state. Restarts on recompilation.
|
||||||
"defines a state (a.k.a. a stateful component).
|
Pass ^{:on-reload :noop} to prevent auto-restart
|
||||||
restarts on recompilation.
|
on ns recompilation, or :stop to stop on recompilation."
|
||||||
pass ^{:on-reload :noop} to prevent auto-restart on ns recompilation,
|
|
||||||
or ^{:on-reload :stop} to stop on recompilation."
|
|
||||||
[state & body]
|
[state & body]
|
||||||
(let [[state params] (mount.tools.macro/name-with-attributes state body)
|
(let [[state params] (macro/name-with-attributes state body)
|
||||||
{:keys [start stop] :as lifecycle} (apply hash-map params)
|
{:keys [start stop] :as lifecycle} (apply hash-map params)
|
||||||
state-name (with-ns *ns* state)
|
state-name (with-ns *ns* state)
|
||||||
order (make-state-seq state-name)]
|
order (make-state-seq state-name)]
|
||||||
|
|
@ -183,20 +179,19 @@
|
||||||
;; only create/redefine a new state iff this is not a running ^{:on-reload :noop}
|
;; only create/redefine a new state iff this is not a running ^{:on-reload :noop}
|
||||||
(if-not (running-noop? ~state-name)
|
(if-not (running-noop? ~state-name)
|
||||||
(do
|
(do
|
||||||
(~'defonce ~state (->DerefableState ~state-name))
|
(~'defonce ~state (DerefableState. ~state-name))
|
||||||
(mount-it (~'var ~state) ~state-name ~s-meta))
|
(mount-it (~'var ~state) ~state-name ~s-meta))
|
||||||
(~'defonce ~state (current-state ~state-name)))
|
(~'defonce ~state (current-state ~state-name)))
|
||||||
(~'var ~state)))))
|
(~'var ~state))))))
|
||||||
|
|
||||||
(defmacro defstate! [state & {:keys [start! stop!]}]
|
#?(:clj
|
||||||
|
(defmacro defstate! [state & {:keys [start! stop!]}]
|
||||||
(let [state-name (with-ns *ns* state)]
|
(let [state-name (with-ns *ns* state)]
|
||||||
`(defstate ~state
|
`(defstate ~state
|
||||||
:start (~'let [~state (mount.core/current-state ~state-name)]
|
:start (~'let [~state (mount/current-state ~state-name)]
|
||||||
~start!)
|
~start!)
|
||||||
:stop (~'let [~state (mount.core/current-state ~state-name)]
|
:stop (~'let [~state (mount/current-state ~state-name)]
|
||||||
~stop!))))
|
~stop!)))))
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
(defn in-cljc-mode []
|
(defn in-cljc-mode []
|
||||||
(reset! mode :cljc))
|
(reset! mode :cljc))
|
||||||
|
|
@ -284,7 +279,7 @@
|
||||||
(if-not (empty? fs) ;; (mount/start) vs. (mount/start #{}) vs. (mount/start #{1 2 3})
|
(if-not (empty? fs) ;; (mount/start) vs. (mount/start #{}) vs. (mount/start #{1 2 3})
|
||||||
(apply start fs)
|
(apply start fs)
|
||||||
{:started #{}})
|
{:started #{}})
|
||||||
(let [states (or (->> states (map var-to-str) seq)
|
(let [states (or (seq states)
|
||||||
(all-without-subs))]
|
(all-without-subs))]
|
||||||
{:started (bring states up <)}))))
|
{:started (bring states up <)}))))
|
||||||
|
|
||||||
|
|
@ -294,7 +289,7 @@
|
||||||
(if-not (empty? fs) ;; (mount/stop) vs. (mount/stop #{}) vs. (mount/stop #{1 2 3})
|
(if-not (empty? fs) ;; (mount/stop) vs. (mount/stop #{}) vs. (mount/stop #{1 2 3})
|
||||||
(apply stop fs)
|
(apply stop fs)
|
||||||
{:stopped #{}})
|
{:stopped #{}})
|
||||||
(let [states (or (->> states (map var-to-str) seq)
|
(let [states (or (seq states)
|
||||||
(find-all-states))
|
(find-all-states))
|
||||||
_ (dorun (map unsub states)) ;; unmark substitutions marked by "start-with" / "swap-states"
|
_ (dorun (map unsub states)) ;; unmark substitutions marked by "start-with" / "swap-states"
|
||||||
stopped (bring states down >)]
|
stopped (bring states down >)]
|
||||||
|
|
@ -308,8 +303,8 @@
|
||||||
set))
|
set))
|
||||||
|
|
||||||
(defn only
|
(defn only
|
||||||
([these]
|
([states]
|
||||||
(only (find-all-states) these))
|
(only (find-all-states) states))
|
||||||
([states these]
|
([states these]
|
||||||
(intersection (mapset var-to-str these)
|
(intersection (mapset var-to-str these)
|
||||||
(mapset var-to-str states))))
|
(mapset var-to-str states))))
|
||||||
|
|
@ -322,8 +317,8 @@
|
||||||
states))
|
states))
|
||||||
|
|
||||||
(defn except
|
(defn except
|
||||||
([these]
|
([states]
|
||||||
(except (find-all-states) these))
|
(except (find-all-states) states))
|
||||||
([states these]
|
([states these]
|
||||||
(remove (mapset var-to-str these)
|
(remove (mapset var-to-str these)
|
||||||
(mapset var-to-str states))))
|
(mapset var-to-str states))))
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
(:import [goog.debug Console])]))
|
(:import [goog.debug Console])]))
|
||||||
|
|
||||||
#?(:cljs
|
#?(:cljs
|
||||||
(defonce ^:dynamic *logger*
|
(defonce *logger*
|
||||||
(do
|
(do
|
||||||
(.setCapturing (Console.) true)
|
(.setCapturing (Console.) true)
|
||||||
(glog/getLogger "mount" nil))))
|
(glog/getLogger "mount"))))
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defn log [msg & _]
|
(defn log [msg & _]
|
||||||
|
|
@ -15,6 +15,6 @@
|
||||||
#?(:cljs
|
#?(:cljs
|
||||||
(defn log [msg & level]
|
(defn log [msg & level]
|
||||||
(case (first level)
|
(case (first level)
|
||||||
:error (glog/error *logger* msg nil)
|
:error (glog/error *logger* msg)
|
||||||
(glog/info *logger* msg nil))))
|
(glog/info *logger* msg))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,31 @@
|
||||||
(ns mount.tools.macro
|
(ns mount.tools.macro
|
||||||
(:refer-clojure :exclude [case])
|
#?(:cljs (:require-macros [mount.tools.macro])))
|
||||||
#?(:cljs (:require-macros [mount.tools.macrovich :refer [deftime]])
|
|
||||||
:clj (:require [mount.tools.macrovich :refer [deftime]])))
|
|
||||||
|
|
||||||
(deftime
|
#?(:clj
|
||||||
|
(defmacro if-clj [then else]
|
||||||
|
(if (-> &env :ns not)
|
||||||
|
then
|
||||||
|
else)))
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
(defmacro on-error [msg f & {:keys [fail?]
|
(defmacro on-error [msg f & {:keys [fail?]
|
||||||
:or {fail? true}}]
|
:or {fail? true}}]
|
||||||
`(mount.tools.macrovich/case
|
`(if-clj
|
||||||
:clj (try ~f
|
(try ~f
|
||||||
(catch Throwable t#
|
(catch Throwable t#
|
||||||
(if ~fail?
|
(if ~fail?
|
||||||
(throw (RuntimeException. ~msg t#))
|
(throw (RuntimeException. ~msg t#))
|
||||||
{:f-failed (ex-info ~msg {} t#)})))
|
{:f-failed (ex-info ~msg {} t#)})))
|
||||||
:cljs (try ~f
|
(try ~f
|
||||||
(catch :default t#
|
(catch :default t#
|
||||||
(if ~fail?
|
(if ~fail?
|
||||||
(throw (js/Error (~'str ~msg " " t#)))
|
(throw (~'str ~msg " " t#))
|
||||||
{:f-failed (ex-info ~msg {} t#)})))))
|
{:f-failed (ex-info ~msg {} t#)}))))))
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
(defmacro throw-runtime [msg]
|
(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
|
;; this is a one to one copy from https://github.com/clojure/tools.macro
|
||||||
;; to avoid a lib dependency for a single function
|
;; 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]
|
(defn entity [conn id]
|
||||||
(d/entity (d/db conn) id))
|
(d/entity (d/db conn) id))
|
||||||
|
|
||||||
(defn touch
|
(defn touch [conn results]
|
||||||
"takes 'entity ids' results from a query
|
"takes 'entity ids' results from a query
|
||||||
e.g. '#{[272678883689461] [272678883689462] [272678883689459] [272678883689457]}'"
|
e.g. '#{[272678883689461] [272678883689462] [272678883689459] [272678883689457]}'"
|
||||||
[conn results]
|
|
||||||
(let [e (partial entity conn)]
|
(let [e (partial entity conn)]
|
||||||
(map #(-> % first e d/touch) results)))
|
(map #(-> % first e d/touch) results)))
|
||||||
|
|
|
||||||
|
|
@ -109,16 +109,6 @@
|
||||||
(is (= 42 (dval conn)))
|
(is (= 42 (dval conn)))
|
||||||
(mount/stop)))
|
(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"
|
(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}
|
(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
|
states (swap-states t-states {#'tapp.nyse/conn swap-conn
|
||||||
|
|
|
||||||
|
|
@ -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