Compare commits

...

81 commits

Author SHA1 Message Date
anatoly
fcdff7577a on to 0.1.23 2025-05-04 16:56:19 -04:00
Anatoly
984979b347
Merge pull request #139 from jwr/fixed-logging
fix parameters of goog.log functions to prevent compiler warnings
2025-05-04 16:48:36 -04:00
Jan Rychter
2f4d1a3581 fix parameters of goog.log functions to prevent compiler warnings 2025-04-30 15:36:35 +09:00
anatoly
2bca9089b8 including clj-kondo fix 2025-01-31 12:58:10 -05:00
Anatoly
656af38802
Merge pull request #138 from NoahTheDuke/nb/fix-clj-kondo-config-3
Fix clj-kondo config, part 3: Remove wrapping atom from clj-kondo hook
2025-01-31 12:56:51 -05:00
Noah Bogart
bfd7adf98f Remove wrapping atom from clj-kondo hook 2025-01-29 14:58:39 -05:00
anatoly
08173bc6a1 0.1.20
includes clj-kondo config fix #137
2024-11-04 10:22:00 -05:00
Anatoly
3fc6c58cd6
Merge pull request #137 from NoahTheDuke/nb/fix-clj-kondo-config-2
Fix clj-kondo config, part 2
2024-11-04 10:19:54 -05:00
Noah Bogart
ccaf99910c clj-kondo: Fix docstrings 2024-10-23 15:12:07 -04:00
Noah Bogart
9c588f68c2 clj-kondo: lint defstate as atom 2024-10-23 14:53:41 -04:00
Anatoly
f16d7004ee
Merge pull request #134 from tolitius/dependabot/maven/org.clojure-clojure-1.11.2
Bump org.clojure:clojure from 1.11.1 to 1.11.2
2024-08-22 21:29:37 -04:00
dependabot[bot]
3caea10121
Bump org.clojure:clojure from 1.11.1 to 1.11.2
Bumps [org.clojure:clojure](https://github.com/clojure/clojure) from 1.11.1 to 1.11.2.
- [Changelog](https://github.com/clojure/clojure/blob/master/changes.md)
- [Commits](https://github.com/clojure/clojure/compare/clojure-1.11.1...clojure-1.11.2)

---
updated-dependencies:
- dependency-name: org.clojure:clojure
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-22 02:54:08 +00:00
anatoly
fb09fef496 on to 0.1.19 2024-08-21 22:53:31 -04:00
Anatoly
b89b930ec2
Merge pull request #133 from NoahTheDuke/nb/fix-clj-kondo-config
Update defstate clj-kondo hook to return a var
2024-08-21 22:43:40 -04:00
Noah Bogart
4065f5df36 Update defstate clj-kondo hook to return a var 2024-07-11 11:41:52 -04:00
Anatoly
632977ff41
update the "Structure and Interpretation of Computer Programs" link 2024-07-01 13:45:07 -04:00
anatoly
5275023b38 add deps.edn 2024-03-05 22:49:19 -05:00
anatoly
ad6ca6fb5e up to 0.1.18 2024-03-04 18:43:40 -05:00
Anatoly
eff4c04f21
Merge pull request #130 from rajcspsg/issue_128
Add clojure-kondo for mount.defstate
2024-03-04 18:39:28 -05:00
Rajkumar Natarajan
5df6c941f7 incorporate the review comments for .clj-kondo 2024-03-02 15:15:55 -08:00
Rajkumar Natarajan
098b56d14f Add clojure-kondo for mount.defstate 2023-05-30 13:47:07 -07:00
Anatoly
58e2ded430
[doc]: remove the broken travis ci link 2023-04-15 23:00:17 -04:00
anatoly
8a3fc385a2 update changelog: 0.1.17 2022-12-19 15:20:16 -05:00
anatoly
2d050e9055 fix: swap-states non string rollback
thanks to @egg-juxt for bringing it up
2022-12-19 15:17:38 -05:00
Anatoly
b77f504cfd
Merge pull request #122 from rgkirch/patch-1
Update README.md
2021-11-24 16:23:40 -05:00
rgkirch
5ac52b725b
Update README.md 2021-11-20 16:46:43 -05:00
Anatoly
c85da6149c
docs: update license year 2020-09-10 20:12:54 -04:00
anatoly
6cf5390a44 up cljs-repl version for examples 2020-07-31 20:27:04 -04:00
anatoly
bc3924aedf add yang dep for examples 2020-07-30 16:30:29 -04:00
Anatoly
5d992042e4
Merge pull request #114 from dijonkitchen/patch-1
docs: make wording clearer
2019-12-26 09:39:10 -05:00
JC
83542e56a7
docs: make wording clearer
Uses same order 

Uses semantic linefeed for the section: https://rhodesmill.org/brandon/2012/one-sentence-per-line/
2019-12-25 06:16:53 -08:00
anatoly
232df7a8a3 .travis: force openjdk8 2019-08-20 23:19:55 -04:00
Anatoly
10dbbaa40b
Merge pull request #110 from dgr/tweak-composables
Update `only` and `except` single-arity parameter names.
2019-08-20 23:09:53 -04:00
Dave Roberts
5b26fb6092 Update only and except single-arity parameter names.
The single-arity parameter names for the `only` and `except` functions
did not match the same parameters in the dual-arity versions of the
same functions. This makes both arities use the same name for the same
parameter.
2019-08-13 10:38:22 -05:00
Anatoly
e9f9dfca14
[docs]: add clarification about "load-config" fn 2019-07-09 16:28:22 -04:00
Anatoly
348297ee11
[docs]: (create-conn) rephrase clarification 2019-07-09 10:28:49 -04:00
Anatoly
52831f6b3e
[docs]: add fn "create-conn" clarification 2019-07-09 09:51:44 -04:00
Anatoly
9666d3f372
Merge pull request #109 from cloojure/patch-1
Update differences-from-component.md
2019-04-11 20:12:43 -04:00
Alan Thompson
5564e367e7
Update differences-from-component.md
small typo
2019-04-11 15:36:53 -07:00
anatoly
f65ed6a266 add change log for 0.1.16 2019-01-28 10:43:58 -05:00
anatoly
6dafae195d release 0.1.16 2019-01-28 10:36:02 -05:00
Anatoly
5fab543501
Merge pull request #107 from krajj7/fix_106
prevent reloading of mount.core ns - fix https://github.com/tolitius/mount/issues/106
2019-01-28 10:32:51 -05:00
krajj7
2ba3c60995 synced project.clj version with build.boot 2019-01-26 13:13:28 +01:00
krajj7
fb52f79396 prevent reloading of mount.core ns - fix https://github.com/tolitius/mount/issues/106 2019-01-26 13:13:28 +01:00
anatoly
c5f3e4cdf8 #104: current-state should return Derefable on :cljc 2019-01-04 13:00:53 -05:00
anatoly
76e9a71a13 release 0.1.15 2018-12-04 12:15:38 -05:00
Anatoly
8de6b09989
Merge pull request #101 from arichiardi/silent-new-cljs-warning
Silent *logger* warning in latest ClojureScript
2018-11-03 22:05:14 -04:00
Andrea Richiardi
884a2b2d87
Fix project.clj version to match boot's 2018-11-03 17:10:03 -07:00
Andrea Richiardi
c2687d1b9f
Silent *logger* warning in latest ClojureScript
The latest version is a bit stricter and emits a warning for ear-muffed vars
with no ^:dynamic.
2018-11-03 17:07:15 -07:00
Anatoly
17112646f1
[docs]: <! ed badge tags 2018-11-01 18:16:43 -04:00
Anatoly
d9c4f621cd
[docs]: update a link to Alan J. Perlis quote 2018-10-26 11:48:41 -04:00
anatoly
ed1c9944f6 npm: update package.json version
also remove circle.yml
2018-10-26 08:54:42 -04:00
anatoly
e07d7d6aad onto 0.1.15-SNAPSHOT 2018-10-25 22:36:17 -04:00
Anatoly
686b79e03f
[docs]: change feedback channels format 2018-10-25 22:35:25 -04:00
Anatoly
2ff032c8b3
[docs]: add clojar shileds badge 2018-10-25 22:32:26 -04:00
Anatoly
244d834c46
[docs]: reduce slack icon 2018-10-25 22:23:57 -04:00
Anatoly
0ff5fe106c
[docs]: add travis and release badges 2018-10-25 21:52:27 -04:00
anatoly
bb23747273 switch form circle to travis 2018-10-25 19:48:40 -04:00
anatoly
2d5566ae86 release 0.1.14 2018-10-25 18:35:55 -04:00
anatoly
1acd4feb4c #100 cljs: throw js/Error not just a string 2018-10-25 15:45:41 -04:00
Anatoly
c210cc486c
docs: toc add "Disable Lazy Start" sub section 2018-10-12 14:39:01 -04:00
Anatoly
4f8384763b
docs: toc link to "cljc mode" 2018-10-12 14:33:50 -04:00
anatoly
6e848d1ee4 #99 and #95: add ^{:on-lazy-start :throw} 2018-10-12 14:22:49 -04:00
anatoly
db38d8cacd onto 0.1.14-SNAPSHOT 2018-08-07 23:28:29 -04:00
anatoly
594cc58b71 releae 0.1.13 2018-08-07 23:27:21 -04:00
Anatoly
e956dd4de5
Merge pull request #98 from dijonkitchen/patch-1
[docs]: fix spelling
2018-07-18 18:00:57 -04:00
Jonathan Chen
0a208bd275
Docs: Fix spelling 2018-07-18 14:16:41 -07:00
anatoly
5e0a4c5bfc #97: self hosted 0.1.13-SNAPSHOT 2018-05-06 20:56:57 -04:00
anatoly
562340f4dc #83: [docs] add doc string 2018-04-24 15:48:32 -04:00
anatoly
b3f3f49d64 scoping npm package: @tolitius/mount 2018-04-09 12:07:06 -04:00
Anatoly
a4552fc6ed
Merge pull request #97 from arichiardi/self-host-support
self-host ClojureScript support
2018-04-08 22:56:58 -04:00
Andrea Richiardi
697775ef45 Use more modern clojure and clojurescript in the tests 2018-04-06 22:29:43 -07:00
Andrea Richiardi
75d05e3158 Add package.json for npmjs.com publishing 2018-04-06 22:29:43 -07:00
Andrea Richiardi
dc59f3ec98 Add minimal self-host testing 2018-04-06 22:29:43 -07:00
Andrea Richiardi
1ccff026de Move macrovich macro in separate namespace and tweak
This patch makes sure we use a separate namespace for macrovich and that macros
are correctly loaded. It avoids having warnings under :advanced.  It also puts
vars back where they were before as they look in the right order.
2018-04-06 22:29:43 -07:00
Vadim Platonov
be6e9be246 Works under self-hosted Clojurescript 2018-04-06 21:50:17 -07:00
Anatoly
52e360f3ab
Merge pull request #96 from hipitihop/patch-1
Update differences-from-component.md
2018-04-05 11:30:13 -04:00
Denis Johnson
fdb85e0fe2
Update differences-from-component.md
Removed redundant `is`
2018-04-05 14:04:49 +10:00
anatoly
e247636be6 reformat all with linter 2018-03-15 23:50:49 -04:00
Anatoly
e032a6ef54
[changelog]: formatting 2018-02-10 14:57:58 -05:00
anatoly
dc7dbdd51e update changelog for 0.1.12 2018-02-10 14:56:07 -05:00
50 changed files with 607 additions and 210 deletions

1
.clj-kondo/config.edn Normal file
View file

@ -0,0 +1 @@
{:config-paths ["../resources/clj-kondo.exports/mount/mount/"]}

6
.gitignore vendored
View file

@ -1,7 +1,10 @@
/target
/classes
/checkouts
pom.xml
.cpcache/
.rebel_readline_history
cljs-test-runner-out
node_modules
pom.xml.asc
.repl*
dev/resources/public/js/*
@ -18,3 +21,4 @@ doo-index.html
/.nrepl-history
.cljs_rhino_repl/
out/
.clj-kondo/.cache

26
.travis.yml Normal file
View file

@ -0,0 +1,26 @@
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

View file

@ -1,3 +1,38 @@
## 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
@ -19,7 +54,7 @@
## 0.1.9
###### Sun Jan 31 15:47:19 2016 -0500
* `:on-reload` #{:noop :stop :restart} ([#36](https://github.com/tolitius/mount/issues/36))
* `:on-reload` #{:noop :stop :restart} ([#36](https://github.com/tolitius/mount/issues/36))
* swapping states with values ([#45](https://github.com/tolitius/mount/issues/45))
* `(mount.core/system)` experiment is refactored to [Yurt](https://github.com/tolitius/yurt)
* cleaning up deleted states ([#42](https://github.com/tolitius/mount/issues/42))

31
Makefile Normal file
View file

@ -0,0 +1,31 @@
.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

View file

@ -1,19 +1,13 @@
> 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://mitpress.mit.edu/sicp/full-text/book/book-Z-H-3.html)_
_**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](https://web.mit.edu/6.001/6.037/sicp.pdf)_
# mount <img src="doc/img/mount-logo.png" width="70px">
[![<! release](https://img.shields.io/badge/dynamic/json.svg?label=release&url=https%3A%2F%2Fclojars.org%2Fmount%2Flatest-version.json&query=version&colorB=blue)](https://github.com/tolitius/mount/releases)
[![<! clojars](https://img.shields.io/clojars/v/mount.svg)](https://clojars.org/mount)
module | branch | status
----------|----------|----------
mount | `master` | [![Circle CI](https://circleci.com/gh/tolitius/mount/tree/master.png?style=svg)](https://circleci.com/gh/tolitius/mount/tree/master)
###### _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))
[![Clojars Project](http://clojars.org/mount/latest-version.svg)](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)
@ -34,6 +28,8 @@ _**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)
@ -94,7 +90,7 @@ Creating state is easy:
(defstate conn :start (create-conn))
```
where the `create-conn` function is defined elsewhere, can be right above it.
where the `create-conn` function creates a connection (for example to a database) and is defined elsewhere, can be right above it.
In case this state needs to be cleaned / destroyed between reloads, there is also `:stop`
@ -107,7 +103,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.nyse :refer [conn]])
dev=> (require '[app.db :refer [conn]])
nil
dev=> conn
#object[datomic.peer.LocalConnection 0x1661a4eb "datomic.peer.LocalConnection@1661a4eb"]
@ -124,6 +120,22 @@ 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
@ -160,8 +172,9 @@ 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`.
[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
@ -392,7 +405,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
substituted with `#(connect test-config)` function, which could endup being anything, a map, an in memory DB, etc.
substituted with `#(connect test-config)` function, which could end up 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`
are real references. They would need to be accessible from the namespace of course, so you might need to `(:require [app.neo :refer [db]])`
@ -535,6 +548,56 @@ 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.
@ -798,7 +861,7 @@ The documentation is [here](doc/runtime-arguments.md#passing-runtime-arguments).
## License
Copyright © 2017 tolitius
Copyright © 2020 tolitius
Distributed under the Eclipse Public License either version 1.0 or (at
your option) any later version.

2
boot.properties Normal file
View file

@ -0,0 +1,2 @@
BOOT_VERSION=2.7.1
BOOT_CLOJURE_VERSION=1.10.1

View file

@ -1,8 +1,18 @@
(def +version+ "0.1.12")
(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
(set-env!
:source-paths #{"src"}
:dependencies '[;; mount brings _no dependencies_, everything here is for
:resource-paths #{"resources"}
:dependencies '[;; mount brings _NO DEPENDENCIES_, everything here is for
;; mount dev, examples apps and tests
[org.clojure/clojure "1.8.0" :scope "provided"]
@ -13,6 +23,7 @@
[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"]
@ -33,11 +44,12 @@
;; boot cljs
[adzerk/boot-cljs "1.7.228-1" :scope "test"]
[adzerk/boot-cljs-repl "0.3.0" :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"]
[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"]])
@ -132,7 +144,8 @@
(task-options!
tcs/test-cljs {:js-env :phantom}
push {:ensure-branch nil}
push {; :ensure-clean nil
:ensure-branch nil}
pom {:project 'mount
:version +version+
:description "managing Clojure and ClojureScript app state since (reset)"

View file

@ -1,17 +0,0 @@
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 Normal file
View file

@ -0,0 +1,52 @@
{: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"]}}}

View file

@ -5,9 +5,9 @@
(defn load-config [path]
(info "loading config from" path)
(-> path
slurp
(-> path
slurp
edn/read-string))
(defstate config
(defstate config
:start (load-config "dev/resources/config.edn"))

View file

@ -1,4 +1,4 @@
(ns app.db
(ns app.db
(:require [mount.core :refer [defstate]]
[datomic.api :as d]
[clojure.tools.logging :refer [info]]
@ -34,7 +34,7 @@
:db/valueType :db.type/bigdec
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
{:db/id #db/id [:db.part/db]
:db/ident :order/qty
:db/valueType :db.type/long

View file

@ -11,6 +11,6 @@
(defn find-orders [conn ticker]
(let [orders (d/q '[:find ?e :in $ ?ticker
:where [?e :order/symbol ?ticker]]
:where [?e :order/symbol ?ticker]]
(d/db conn) ticker)]
(touch conn orders)))

View file

@ -1,4 +1,4 @@
(ns app.utils.logging ;; << change to your namespace/path
(ns app.utils.logging ;; << change to your namespace/path
(:require [mount.core]
[robert.hooke :refer [add-hook clear-hooks]]
[clojure.string :refer [split]]
@ -21,9 +21,9 @@
:down "<< stopping"
false))
(defn log-status [f & args]
(defn log-status [f & args]
(let [[state-name state] args
action (f-to-action f state)]
action (f-to-action f state)]
(when-let [taking-over-the-world (whatcha-doing? action)]
(info (str taking-over-the-world ".. " state-name)))
(apply f args)))

View file

@ -14,10 +14,10 @@
(GET "/nyse/orders/:ticker" [ticker]
(generate-string (find-orders conn ticker)))
(POST "/nyse/orders" [ticker qty bid offer]
(let [order {:ticker ticker
:bid (bigdec bid)
:offer (bigdec offer)
(POST "/nyse/orders" [ticker qty bid offer]
(let [order {:ticker ticker
:bid (bigdec bid)
:offer (bigdec offer)
:qty (Integer/parseInt qty)}]
(add-order conn order)
(generate-string {:added order}))))

View file

@ -1,7 +1,6 @@
(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]]
@ -41,4 +40,11 @@
(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!)

View file

@ -1,5 +1,5 @@
(ns proto-play
(:require [mount.tools.graph :as mount]
(:require [mount.tools.graph :as mg]
[proto-repl-charts.graph :as proto]))
(defn mount->proto [graph]
@ -10,6 +10,6 @@
{}
graph))
(->> (mount/states-with-deps)
(->> (mg/states-with-deps)
mount->proto
(proto/graph "a proto graph of mount states"))

View file

@ -13,13 +13,13 @@
(defn find-source-logs [db source]
(d/q '{:find [?t ?msg]
:in [$ ?s]
:in [$ ?s]
:where [[?e :source ?s]
[?e :timestamp ?t]
[?e :msg ?msg]]}
@@db source))
(defn find-all-logs [db]
(->> (map :e (d/datoms @@db :aevt :timestamp))
(->> (map :e (d/datoms @@db :aevt :timestamp))
dedupe
(d/pull-many @@db '[:timestamp :source :msg])))

View file

@ -12,7 +12,7 @@
" &#8594; [" (name source) "]: " msg))
(defn show-log []
(.write js/document
(.write js/document
(html [:ul (doall (for [e (find-all-logs log)]
[:li (format-log-event e)]))])))

View file

@ -1,4 +1,4 @@
{:datomic
{:datomic
{:uri "datomic:mem://mount"}
:www {:port 4242}

View file

@ -8,7 +8,7 @@
- [Using States](#using-states)
- [Thanks](#thanks)
In case you need to manage state in ClojureScript using mount, _all_ the mount Clojure features are supported in ClojureScript.
In case you need to manage state in ClojureScript using mount, _all_ the mount Clojure features are supported in ClojureScript.
Which means all the mount Clojure [documentation](../README.md) is the mount ClojureScript documentation.
With a slight change in [_mode_](clojurescript.md#mount-modes) ( no change in _mood_ though, just the _mode_ :)).
@ -16,19 +16,19 @@ With a slight change in [_mode_](clojurescript.md#mount-modes) ( no change in _m
### The "Why"
Since [reader conditionals](http://clojure.org/reader#The%20Reader--Reader%20Conditionals) were added in Clojure 1.7,
it became a lot easier to target both platforms with lots of code reuse. You might have noticed
it became a lot easier to target both platforms with lots of code reuse. You might have noticed
that most of mount code lives in `.cljc` files.
The way mount is designed it "mounts" itself to a solid Clojure [namespace API](http://clojure.org/namespaces),
The way mount is designed it "mounts" itself to a solid Clojure [namespace API](http://clojure.org/namespaces),
and while `.cljc` helps a lot with targeting Clojure and ClojureScript, JavaScript VM is vastly different from JVM.
Since JavaScript mostly tagrets browsers, mobile devices and IoT,
Since JavaScript mostly tagrets browsers, mobile devices and IoT,
it is quite important to [compress](https://github.com/clojure/clojurescript/wiki/Advanced-Compilation) the final result.
Which means that Clojure namespaces API are not that well supported in ClojureScript, since they get renamed and optimized
during compilation + of course no native namespace support on the JavaScript side
during compilation + of course no native namespace support on the JavaScript side
(but that is somewhat solved with [Google Closure](https://closure-library.googlecode.com/git-history/docs/local_closure_goog_base.js.source.html#line428)).
But. When developing an application in Clojure and ClojureScript, it would only make sense if the API for any library
But. When developing an application in Clojure and ClojureScript, it would only make sense if the API for any library
would be _identical_ for both platforms. It should be transparent for developers whether they use a library in Clojure or ClojureScript.
It is not possible for all libraries (i.e. concurrency, reified Vars, etc.), but we should try to make it possible for most.
@ -52,7 +52,7 @@ To switch Mount into this mode do:
anywhere before a call to `(mount/start)`, usually at the entry point of an app: in the `-main`, web handler, etc.
This sets mount into the `cljc` mode. In this mode mount supports _both_: Clojure and ClojureScript with one difference
This sets mount into the `cljc` mode. In this mode mount supports _both_: Clojure and ClojureScript with one difference
from the default `clj` mode:
> all states are "_derefable_"

View file

@ -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 bounaries".
This is how Component "separates explicit dependencies" and "clears the boundaries".
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,9 +146,13 @@ no "ceremony".
Mount uses namespaces and vars where Component uses 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_.
Component manages records and protocols,
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, 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
@ -156,9 +160,9 @@ Mount does not need to manage namespaces and vars, since it is very well managed
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 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.
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.
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.
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.
Testing is not alien to Mount and it knows how to do a thing or two:

23
package.json Normal file
View file

@ -0,0 +1,23 @@
{
"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 Normal file
View file

@ -0,0 +1,45 @@
<?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>

View file

@ -1,4 +1,4 @@
(defproject mount "0.1.12-SNAPSHOT"
(defproject mount "0.1.17"
:description "managing Clojure and ClojureScript app state since (reset)"
:url "https://github.com/tolitius/mount"
:license {:name "Eclipse Public License"
@ -7,10 +7,13 @@
:source-paths ["src"]
: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.7.0"]
[org.clojure/clojurescript "1.7.170"]; :classifier "aot"]
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.946"]; :classifier "aot"]
[datascript "0.13.3"]
[compojure "1.4.0"]
[ring/ring-jetty-adapter "1.1.0"]
@ -27,7 +30,8 @@
:plugins [[lein-cljsbuild "1.1.1"]
[lein-doo "0.1.6"]
[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")

View file

@ -0,0 +1,3 @@
{:linters {:mount/defstate {:level :warning}}
:hooks {:analyze-call {mount.core/defstate hooks.defstate/defstate
mount.core/defstate! hooks.defstate/defstate}}}

View file

@ -0,0 +1,39 @@
(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)))))})))

View file

@ -1,13 +1,16 @@
(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 :as macro]
:cljs (:require [mount.tools.macro]
[clojure.set :refer [intersection]]
[mount.tools.logger :refer [log]]))
#?(:cljs (:require-macros [mount.core]
[mount.tools.macro :refer [if-clj on-error throw-runtime]])))
[mount.tools.macro :refer [on-error throw-runtime]]
[mount.tools.macrovich :refer [deftime]])))
(defonce ^:private -args (atom {})) ;; mostly for command line args and external files
(defonce ^:private state-seq (atom 0))
@ -15,10 +18,6 @@
(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)))
@ -58,17 +57,6 @@
(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)
@ -110,8 +98,8 @@
: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
(alter-state! current (->NotStartedState state)))
(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})))
@ -124,9 +112,12 @@
(#?(:clj deref
:cljs -deref)
[_]
(let [{:keys [status inst] :as state} (@meta-state name)]
(let [{:keys [status var inst] :as state} (@meta-state name)]
(when-not (:started status)
(up name state (atom #{})))
(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)
@ -135,6 +126,17 @@
[_]
(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]
(or (-> s-var meta :on-reload)
:restart)) ;; restart by default on ns reload
@ -146,10 +148,10 @@
(and (status :started)
(= :noop on-reload)))))
;;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)
(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)
@ -159,39 +161,42 @@
(log (str ">> starting.. " s-name " (namespace was recompiled)"))
(up s-name with-inst (atom #{})))))
#?(:clj
(defmacro defstate
"Defines a state. Restarts on recompilation.
Pass ^{:on-reload :noop} to prevent auto-restart
on ns recompilation, or :stop to stop on recompilation."
[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)]
(validate lifecycle)
(let [s-meta (cond-> {:order order
:start `(fn [] ~start)
:status #{:stopped}}
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))))))
(deftime
#?(:clj
(defmacro defstate! [state & {:keys [start! stop!]}]
(let [state-name (with-ns *ns* state)]
`(defstate ~state
:start (~'let [~state (mount/current-state ~state-name)]
~start!)
:stop (~'let [~state (mount/current-state ~state-name)]
~stop!)))))
(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)
{:keys [start stop] :as lifecycle} (apply hash-map params)
state-name (with-ns *ns* state)
order (make-state-seq state-name)]
(validate lifecycle)
(let [s-meta (cond-> {:order order
:start `(fn [] ~start)
:status #{:stopped}}
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)))))
(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!)
:stop (~'let [~state (mount.core/current-state ~state-name)]
~stop!))))
)
(defn in-cljc-mode []
(reset! mode :cljc))
@ -279,7 +284,7 @@
(if-not (empty? fs) ;; (mount/start) vs. (mount/start #{}) vs. (mount/start #{1 2 3})
(apply start fs)
{:started #{}})
(let [states (or (seq states)
(let [states (or (->> states (map var-to-str) seq)
(all-without-subs))]
{:started (bring states up <)}))))
@ -289,7 +294,7 @@
(if-not (empty? fs) ;; (mount/stop) vs. (mount/stop #{}) vs. (mount/stop #{1 2 3})
(apply stop fs)
{:stopped #{}})
(let [states (or (seq states)
(let [states (or (->> states (map var-to-str) seq)
(find-all-states))
_ (dorun (map unsub states)) ;; unmark substitutions marked by "start-with" / "swap-states"
stopped (bring states down >)]
@ -303,22 +308,22 @@
set))
(defn only
([states]
(only (find-all-states) states))
([these]
(only (find-all-states) these))
([states these]
(intersection (mapset var-to-str these)
(mapset var-to-str states))))
(defn with-args
(defn with-args
([args]
(with-args (find-all-states) args))
([states args]
(reset! -args args) ;; TODO localize
states))
(defn except
([states]
(except (find-all-states) states))
(defn except
([these]
(except (find-all-states) these))
([states these]
(remove (mapset var-to-str these)
(mapset var-to-str states))))

View file

@ -3,10 +3,10 @@
(:import [goog.debug Console])]))
#?(:cljs
(defonce *logger*
(defonce ^:dynamic *logger*
(do
(.setCapturing (Console.) true)
(glog/getLogger "mount"))))
(glog/getLogger "mount" nil))))
#?(:clj
(defn log [msg & _]
@ -15,6 +15,6 @@
#?(:cljs
(defn log [msg & level]
(case (first level)
:error (glog/error *logger* msg)
(glog/info *logger* msg))))
:error (glog/error *logger* msg nil)
(glog/info *logger* msg nil))))

View file

@ -1,31 +1,28 @@
(ns mount.tools.macro
#?(:cljs (:require-macros [mount.tools.macro])))
(:refer-clojure :exclude [case])
#?(:cljs (:require-macros [mount.tools.macrovich :refer [deftime]])
:clj (:require [mount.tools.macrovich :refer [deftime]])))
#?(:clj
(defmacro if-clj [then else]
(if (-> &env :ns not)
then
else)))
(deftime
#?(:clj
(defmacro on-error [msg f & {:keys [fail?]
:or {fail? true}}]
`(if-clj
(try ~f
(catch Throwable t#
(if ~fail?
(throw (RuntimeException. ~msg t#))
{:f-failed (ex-info ~msg {} t#)})))
(try ~f
(catch :default t#
(if ~fail?
(throw (~'str ~msg " " t#))
{:f-failed (ex-info ~msg {} t#)}))))))
(defmacro on-error [msg f & {:keys [fail?]
:or {fail? true}}]
`(mount.tools.macrovich/case
:clj (try ~f
(catch Throwable t#
(if ~fail?
(throw (RuntimeException. ~msg t#))
{:f-failed (ex-info ~msg {} t#)})))
:cljs (try ~f
(catch :default t#
(if ~fail?
(throw (js/Error (~'str ~msg " " t#)))
{:f-failed (ex-info ~msg {} t#)})))))
#?(:clj
(defmacro throw-runtime [msg]
`(throw (if-clj (RuntimeException. ~msg)
(~'str ~msg)))))
(defmacro throw-runtime [msg]
`(throw (mount.tools.macrovich/case :clj (RuntimeException. ~msg) :cljs (~'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

View file

@ -0,0 +1,21 @@
(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)))

View file

@ -7,9 +7,9 @@
(defn load-config [path]
(info "loading config from" path)
(-> path
slurp
(-> path
slurp
edn/read-string))
(defstate config
(defstate config
:start (load-config "dev/resources/config.edn"))

View file

@ -30,7 +30,7 @@
:db/valueType :db.type/bigdec
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
{:db/id #db/id [:db.part/db]
:db/ident :order/qty
:db/valueType :db.type/long
@ -51,11 +51,11 @@
:order/bid bid
:order/offer offer
:order/qty qty}]))
(defn find-orders [ticker] ;; can take connection as param
(let [orders (d/q '[:find ?e :in $ ?ticker
:where [?e :order/symbol ?ticker]]
:where [?e :order/symbol ?ticker]]
(d/db @nyse/conn) ticker)]
(touch @nyse/conn orders)))

View file

@ -6,8 +6,9 @@
(defn entity [conn id]
(d/entity (d/db conn) id))
(defn touch [conn results]
(defn touch
"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)))

View file

@ -1,4 +1,4 @@
(ns tapp.utils.logging ;; << change to your namespace/path
(ns tapp.utils.logging ;; << change to your namespace/path
(:require [mount.core]
[robert.hooke :refer [add-hook clear-hooks]]
[clojure.string :refer [split]]
@ -21,9 +21,9 @@
:down "<< stopping"
false))
(defn log-status [f & args]
(defn log-status [f & args]
(let [[state-name state] args
action (f-to-action f state)]
action (f-to-action f state)]
(when-let [taking-over-the-world (whatcha-doing? action)]
(info (str taking-over-the-world ".. " state-name)))
(apply f args)))

View file

@ -13,13 +13,13 @@
(defn find-source-logs [db source]
(d/q '{:find [?t ?msg]
:in [$ ?s]
:in [$ ?s]
:where [[?e :source ?s]
[?e :timestamp ?t]
[?e :msg ?msg]]}
@@db source))
(defn find-all-logs [db]
(->> (map :e (d/datoms @@db :aevt :timestamp))
(->> (map :e (d/datoms @@db :aevt :timestamp))
dedupe
(d/pull-many @@db '[:timestamp :source :msg])))

View file

@ -12,7 +12,7 @@
" &#8594; [" (name source) "]: " msg))
(defn show-log []
(.write js/document
(.write js/document
(html [:ul (doall (for [e (find-all-logs log)]
[:li (format-log-event e)]))])))

View file

@ -11,7 +11,7 @@
[mount.test.helper :refer [dval helper forty-two]]))
(def status (atom :a-not-started))
(defstate a :start (reset! status :a-started)
(defstate a :start (reset! status :a-started)
:stop (reset! status :a-stopped))
#?(:clj (alter-meta! *ns* assoc ::load false))
@ -30,11 +30,11 @@
(testing "should cleanup/stop a state after it was deleted from ns"
(is (empty? (:started (mount/start)))) ;; on any mount op (not necessarily on "stop")
(is (= :a-stopped @status))
(is (not (some #{"#'mount.test.cleanup-deleted-states/a"}
(is (not (some #{"#'mount.test.cleanup-deleted-states/a"}
(keys @@#'mount.core/meta-state)))))
(testing "should not stop it again on stop (should not be there by this point)")
(is (not (some #{"#'mount.test.cleanup-deleted-states/a"}
(is (not (some #{"#'mount.test.cleanup-deleted-states/a"}
(-> (mount/stop) :stopped set))))))
;; (t/run-tests)

View file

@ -48,7 +48,7 @@
(.require js/goog "mount.test.helper") ;; should have run :stop of `helper`
;; (is (= :cleaned @forty-two)) ;; TODO: figure out how to reload a namespace properly
;; (is (instance? mount.core.NotStartedState (dval helper)))
(mount/start #'mount.test.helper/helper)
(mount/start #'mount.test.helper/helper)
(is (= :started (dval helper)))
(mount/stop)
(is (instance? mount.core.NotStartedState (dval helper))))))

View file

@ -31,14 +31,14 @@
#?(:clj
(deftest only-states
(testing "only should only return given states.
(testing "only should only return given states.
if source set of states is not provided, it should use all the states to select from"
(is (= #{"#'mount.test.composable-fns/test-conn" "#'tapp.example/nrepl" "#'tapp.nyse/conn"}
(is (= #{"#'mount.test.composable-fns/test-conn" "#'tapp.example/nrepl" "#'tapp.nyse/conn"}
(only #{"#'is.not/here" #'mount.test.composable-fns/test-conn #'tapp.example/nrepl #'tapp.nyse/conn}))))
(testing "only should only return given states"
(is (= #{"#'mount.test.composable-fns/test-conn" "#'tapp.example/nrepl"}
(only [#'mount.test.composable-fns/test-conn #'tapp.example/nrepl #'tapp.nyse/conn]
(is (= #{"#'mount.test.composable-fns/test-conn" "#'tapp.example/nrepl"}
(only [#'mount.test.composable-fns/test-conn #'tapp.example/nrepl #'tapp.nyse/conn]
#{"#'is.not/here" #'mount.test.composable-fns/test-conn #'tapp.example/nrepl}))))))
#?(:clj
@ -109,6 +109,16 @@
(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

View file

@ -43,7 +43,7 @@
#'mount.test.fun-with-values/private-f
#'mount.test.fun-with-values/f-value))
(use-fixtures :once
(use-fixtures :once
#?(:cljs {:before start-states
:after mount/stop}
:clj #((start-states) (%) (mount/stop))))

View file

@ -5,10 +5,10 @@
#?(:clj (alter-meta! *ns* assoc ::load false))
(defn dval
(defn dval
"returns a value of DerefableState without deref'ing it"
[d]
(-> (@@(var mount.core/meta-state)
(-> (@@(var mount.core/meta-state)
#?(:clj (.name d)
:cljs (.-name d)))
:inst

View file

@ -7,7 +7,7 @@
[tapp.audit-log :refer [log]]]
:clj [[clojure.test :as t :refer [is are deftest testing use-fixtures]]
[mount.core :as mount :refer [defstate]]
[tapp.example]])
[tapp.example]])
[mount.test.helper :refer [dval helper forty-two counter inc-counter]]
[mount.test.on-reload-helper :refer [a b c]]))
@ -42,14 +42,14 @@
;; "b" is marked as :stop on reload
(is (instance? mount.core.NotStartedState (dval b)))
(is (= (-> pre-reload :b :started)
(is (= (-> pre-reload :b :started)
(-> @counter :b :started)))
(is (= (inc (-> pre-reload :b :stopped))
(is (= (inc (-> pre-reload :b :stopped))
(-> @counter :b :stopped)))
;; "c" is not marked on reload, using "restart" as default
(is (= :started (dval c)))
(is (= (inc (-> pre-reload :c :started))
(-> @counter :c :started)))
(is (= (inc (-> pre-reload :c :stopped))
(is (= (inc (-> pre-reload :c :stopped))
(-> @counter :c :stopped))))))

View file

@ -20,18 +20,18 @@
(f)
(mount/stop)))
(use-fixtures :once
(use-fixtures :once
#?(:cljs {:before #(mount/start #'tapp.conf/config #'tapp.audit-log/log)
:after mount/stop}
:clj with-parts))
#?(:clj
(deftest start-only-parts
(deftest start-only-parts
(is (instance? datomic.peer.LocalConnection (dval conn)))
(is (instance? mount.core.NotStartedState (dval should-not-start)))))
#?(:cljs
(deftest start-only-parts
(deftest start-only-parts
(is (instance? datascript.db/DB @(dval log)))
(is (map? (dval config)))
(is (instance? mount.core.NotStartedState (dval should-not-start)))

View file

@ -9,11 +9,11 @@
#?(:clj (alter-meta! *ns* assoc ::load false))
(use-fixtures :once
(use-fixtures :once
#?(:cljs {:before #(mount/start #'mount.test.fun-with-values/private-f)
:after mount/stop}
:clj #((mount/start #'mount.test.fun-with-values/private-f)
(%)
:clj #((mount/start #'mount.test.fun-with-values/private-f)
(%)
(mount/stop))))
(deftest fun-with-values

View file

@ -51,7 +51,7 @@
(is (instance? mount.core.NotStartedState (dval test-conn)))
(is (instance? mount.core.NotStartedState (dval test-nrepl)))
(mount/stop)))))
#?(:clj
(deftest start-with
@ -62,7 +62,7 @@
(is (= :nrepl-sub (dval nrepl)))
(is (= "conn-sub" (dval conn)))
(mount/stop)))
(testing "should start normally after start-with"
(let [_ (mount/start)]
(is (map? (dval config)))

View file

@ -67,7 +67,7 @@
(is (instance? mount.core.NotStartedState (dval test-conn)))
(is (instance? mount.core.NotStartedState (dval test-nrepl)))
(mount/stop)))))
#?(:clj
(deftest start-with-states
@ -78,7 +78,7 @@
(is (instance? clojure.tools.nrepl.server.Server (dval nrepl)))
(is (= (dval conn) 42))
(mount/stop)))
#_(testing "should not start the substitute itself" ;; was true when subbing with exsiting states
(let [_ (mount/start-with-states {#'tapp.nyse/conn swap-conn})]
(is (instance? mount.core.NotStartedState (dval test-conn)))

View file

@ -20,7 +20,7 @@
(f)
(mount/stop)))
(use-fixtures :once
(use-fixtures :once
#?(:cljs {:before #(mount/start-without #'mount.test.helper/helper #'tapp.websockets/system-a)
:after mount/stop}
:clj without))

View file

@ -25,7 +25,7 @@
(is (instance? mount.core.NotStartedState (dval config)))
(is (instance? mount.core.NotStartedState (dval system-a)))
(mount/stop)))
(testing "should start normally after stop-except"
(let [_ (mount/start)]
(is (map? (dval config)))
@ -51,7 +51,7 @@
(is (instance? datomic.peer.LocalConnection (dval conn)))
(is (instance? mount.core.NotStartedState (dval nrepl)))
(mount/stop)))
(testing "should start normally after stop-except"
(let [_ (mount/start)]
(is (map? (dval config)))

View file

@ -0,0 +1,29 @@
(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.*"))