Merge branch 'master' into 0.1.9-SNAPSHOT

This commit is contained in:
anatoly 2016-01-11 20:01:02 -05:00
commit 07eef10a77
3 changed files with 77 additions and 38 deletions

View file

@ -139,7 +139,7 @@ But if stateful things are kept lean and low level (i.e. I/O, queues, threads, c
### Talking States
There are of course direct dependecies that `mount` respects:
There are of course direct dependencies that `mount` respects:
```clojure
(ns app.config
@ -159,8 +159,8 @@ this `config`, being top level, can be used in other namespaces, including the o
(defstate conn :start (create-connection config))
```
[here](dev/clj/app/nyse.clj)
is an example of a Datomic connection that "depends" on a similar `config`.
[here](dev/clj/app/www.clj#L30)
is an example of a web server that "depends" on a similar `config`.
## Value of values
@ -203,7 +203,7 @@ Check out [fun-with-values-test](test/core/mount/test/fun_with_values.cljc) for
## The Importance of Being Reloadable
`mount` has start and stop functions that will walk all the states created with `defstate` and start / stop them
accordingly: i.e. will call their `:start` and `:stop` defined functions. Hence the whole applicatoin state can be reloaded in REPL e.g.:
accordingly: i.e. will call their `:start` and `:stop` defined functions. Hence the whole application state can be reloaded in REPL e.g.:
```
dev=> (require '[mount.core :as mount])
@ -212,7 +212,7 @@ dev=> (mount/stop)
dev=> (mount/start)
```
While it is not always necessary, mount lificycle can be easily hooked up to [tools.namespace](https://github.com/clojure/tools.namespace),
While it is not always necessary, mount lifecycle can be easily hooked up to [tools.namespace](https://github.com/clojure/tools.namespace),
to make the whole application reloadable with refreshing the app namespaces.
Here is a [dev.clj](dev/clj/dev.clj) as an example, that sums up to:
@ -286,7 +286,7 @@ Here is an [example](test/core/mount/test/start_without.cljc) test that excludes
## Swapping Alternate Implementations
During testing it is often very useful to mock/stub certain states. For example runnig a test against an in memory database vs. the real one, running with a publisher that publishes to a test core.async channel vs. the real remote queue, etc.
During testing it is often very useful to mock/stub certain states. For example running a test against an in memory database vs. the real one, running with a publisher that publishes to a test core.async channel vs. the real remote queue, etc.
The `start-with` function can do just that:
@ -355,7 +355,7 @@ and some other use cases.
### Suspendable Lifecycle
In additiong to `start` / `stop` functions, a state can also have `resume` and, if needed, `suspend` ones:
In addition to `start` / `stop` functions, a state can also have `resume` and, if needed, `suspend` ones:
```clojure
(defstate web-server :start start-server
@ -500,7 +500,7 @@ The way this is done is via an excellent [robert hooke](https://github.com/techn
## Clojure Version
Since mount [supports both](doc/clojurescript.md#managing-state-in-clojurescript) Clojure and CljoureScript, it relies on [Reader Conditionals](http://clojure.org/reader#The%20Reader--Reader%20Conditionals) that were introduced in `Clojure 1.7`. mount's code is not precompiled (i.e. AOT) and disributed in `.cljc` sources, hence it currently requires `Clojure 1.7` and above.
Since mount [supports both](doc/clojurescript.md#managing-state-in-clojurescript) Clojure and CljoureScript, it relies on [Reader Conditionals](http://clojure.org/reader#The%20Reader--Reader%20Conditionals) that were introduced in `Clojure 1.7`. mount's code is not precompiled (i.e. AOT) and distributed in `.cljc` sources, hence it currently requires `Clojure 1.7` and above.
## Mount and Develop!
@ -609,7 +609,7 @@ dev=> (find-orders "TSLA")
### New York Stock Exchange Maintenance
Say we want to leave the exchage functioning, but would like to make sure that noone can hit it from the web. Easy, just stop the web server:
Say we want to leave the exchange functioning, but would like to make sure that noone can hit it from the web. Easy, just stop the web server:
```clojure
dev=> (mount/stop #'app.www/nyse-app)

View file

@ -22,7 +22,7 @@ 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),
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,
it is quite importand to [compress](https://github.com/clojure/clojurescript/wiki/Advanced-Compilation) the final result.
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
@ -53,17 +53,17 @@ from the default `clj` mode:
> all states are "_derefable_"
which means in order to use them, you'd need to `@` it. That's where the difference between two modes end.
which means in order to use them, you'd need to `@` it. That's where the difference between the two modes ends.
Again, `cljc` mode API is _consistent across both_ Clojure and ClojureScript.
While initially it may sound strange, this approach has very nice properties:
* Mentally something that you defer (`@`) is associated with a state behind it
* Mentally something that you deref (`@`) is associated with a state behind it
* The whole system may start lazily without an explicit call `(mount/start)`
* States may have watchers which is just an idea at this point, but it could be quite useful
Now as the theory is laid out...
Now that the theory is laid out...
### Mounting that ClojureScript

View file

@ -22,13 +22,14 @@ The not so hidden benefit is REPL time reloadability that it brings to the table
- [Then why "mount"!?](#then-why-mount)
- [So what are the differences?](#so-what-are-the-differences)
- [Objects vs. Namespaces](#objects-vs-namespaces)
- [Start and Stop Order](#start-and-stop-order)
- [Component requires whole app buy in](#component-requires-whole-app-buy-in)
- [Start and Stop Order](#start-and-stop-order)
- [Refactoring an existing application](#refactoring-an-existing-application)
- [Code navigation](#code-navigation)
- [Objects vs. Namespaces](#objects-vs-namespaces)
- [Starting and stopping parts of an application](#starting-and-stopping-parts-of-an-application)
- [Boilerplate code](#boilerplate-code)
- [Library vs. Framework](#library-vs-framework)
- [What Component does better](#what-component-does-better)
- [Swapping alternate implementations](#swapping-alternate-implementations)
- [Uberjar / Packaging](#uberjar--packaging)
@ -51,26 +52,6 @@ Before moving on to differences, [here](https://news.ycombinator.com/item?id=246
## So what are the differences?
### 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 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.
Mount relies on Clojure namespaces to clear the boundaries. No change from Clojure here: `defstate` in one namespace
can be easily `:require`d in another.
### Start and Stop Order
Component relies on a cool [dependency](https://github.com/stuartsierra/dependency) library to build
a graph of dependencies, and start/stop them via topological sort based on the dependencies in this graph.
Since Mount relies on Clojure namespaces and `:require`/`:use`, the order of states
and their dependencies are revealed by the Clojure Compiler itself. Mount just records that order and replays
it back and forth on stop and start.
### Component requires whole app buy in
Component really only works if you build your entire app around its model: application is fully based on Components
@ -89,6 +70,15 @@ than to
Again this is mostly a personal preference: the code works in both cases.
### Start and Stop Order
Component relies on a cool [dependency](https://github.com/stuartsierra/dependency) library to build
a graph of dependencies, and start/stop them via topological sort based on the dependencies in this graph.
Since Mount relies on Clojure namespaces and `:require`/`:use`, the order of states
and their dependencies are revealed by the Clojure Compiler itself. Mount just records that order and replays
it back and forth on stop and start.
### Refactoring an existing application
Since to get the most benefits of Component the approach is "all or nothing", to rewrite an existing application
@ -103,6 +93,17 @@ Component changes the way the code is structured. Depending on the size of the c
Since Mount relies on Clojure namespaces (`:require`/`:use`), navigation across functions / states is exactly
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 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.
Mount relies on Clojure namespaces to clear the boundaries. No change from Clojure here: `defstate` in one namespace
can be easily `:require`d in another.
### Starting and stopping _parts_ of an application
Component can't really start and stop parts of an application within the same "system". Other sub systems can be
@ -143,13 +144,21 @@ Mount is pretty much:
no "ceremony".
### Library vs. Framework
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_.
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
### Swapping alternate implementations
This is someting that is very useful for testing and is very easy to do in Component by simply assoc'ing onto a map.
Mount can do it to: https://github.com/tolitius/mount#swapping-alternate-implementations
Mount can do it too: https://github.com/tolitius/mount#swapping-alternate-implementations
The reason it is in "Component does it better" section is because, while result is the same, merging maps is a bit simpler than:
@ -174,11 +183,11 @@ will be brought transitively. Here is more about mount [packaging](https://githu
On the flip side, Component _system_ usually requires all the `:require`s, since in order to be built, it needs to "see" all the top level states.
###### _conclusion: it's simple in Mount as well, but requires an additional step._
###### _conclusion: it's simple in Mount as well, but is left upto developer to require what's needed._
### Multiple separate systems
With Component multiple separate systems can be started _in the same Clojure runtime_ with different settings. Which is very useful for testing.
With Component multiple separate systems can be started _in the same Clojure runtime_ with different settings. Which might be useful for testing.
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".
@ -192,6 +201,8 @@ Testing is not alien to Mount and it knows how to do a thing or two:
But running two apps in the same JVM side by side with "same but different" states, is not something Mount can do at the moment.
After [booting mount](http://www.dotkam.com/2015/12/22/the-story-of-booting-mount/) I am secretly thinking of achieving multiple separate systems by running them in different [Boot Pods](https://github.com/boot-clj/boot/wiki/Pods), but for now it remains to be a secret hypothesis.
###### _conclusion: needs more thinking._
### Visualizing dependency graph
@ -201,3 +212,31 @@ Having this visualization is really helpful, especially during code discusions b
Mount does not have this at the moment. It does have all the data to create such a visualization, perhaps even
by building a graph out of the data it has just for this purpose.
There is a [`(states-with-deps)`](https://github.com/tolitius/mount/blob/master/src/mount/tools/graph.cljc#L20) function that can help out:
```clojure
dev=> (require '[mount.tools.graph :as graph])
dev=> (graph/states-with-deps)
({:name "#'app.conf/config",
:order 1,
:status #{:started},
:deps #{}}
{:name "#'app.db/conn",
:order 2,
:status #{:started},
:deps #{"#'app.conf/config"}}
{:name "#'app.www/nyse-app",
:order 3,
:status #{:started},
:deps #{"#'app.conf/config"}}
{:name "#'app.example/nrepl",
:order 4,
:status #{:started},
:deps #{"#'app.www/nyse-app" "#'app.conf/config"}})
```
But it does not draw :), and it currently only supports Clojure, not ClojureScript.
###### _conclusion: needs more thinking._