diff --git a/README.md b/README.md index 5b36726..d0160b9 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,9 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt - [Plugging into (reset)](#plugging-into-reset) - [Suspendable Example Application](#suspendable-example-application) - [Affected States](#affected-states) + +- [ClojureScript is Clojure](doc/clojurescript.md) + - [Logging](#logging) - [Mount and Develop!](#mount-and-develop) - [Running New York Stock Exchange](#running-new-york-stock-exchange) diff --git a/doc/clojurescript.md b/doc/clojurescript.md new file mode 100644 index 0000000..54b199c --- /dev/null +++ b/doc/clojurescript.md @@ -0,0 +1,120 @@ +## Managing state in ClojureScript + +- [The "Why"](#the-why) +- [Mount Modes](#mount-modes) + - [Just Clojure Mode](#just-clojure-mode) + - [Clojure and ClojureScript Mode](#clojure-_and_-clojurescript-mode) +- [Mounting that ClojureScript](#mounting-that-clojurescript) + - [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. +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_ :)). + +### 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 +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. + +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 +(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 +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. + +### Mount Modes + +Mount has two modes `clj` and `cljc`. + +#### Just Clojure Mode + +`clj` mode is _default_, and all the APIs are exactly the same as they are in the mount Clojure [documentation](../README.md). + +#### Clojure _and_ ClojureScript Mode + +`cljc` mode is is not default, but it is easy to switch to it: + +```clojure +(mount/in-cljc-mode) +``` + +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_" + +which means in order to use them, you'd need to `@` it. That's where the difference between two modes end. + +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 +* 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... + +### Mounting that ClojureScript + +Let's look at the example [ClojureScript app](../dev/cljs/app) that uses mount to manage several states: + +* [Datascript](https://github.com/tonsky/datascript) Database +* Websocket Connection +* Configuration + +In order to run it, just compile `cljs` (in `:advanced` mode, because why not? :)) with: + +```clojure +$ lein do clean, cljsbuild once prod +Compiling ClojureScript... +Compiling "dev/resources/public/js/compiled/mount.js" from ["src" "dev/cljs"]... +Successfully compiled "dev/resources/public/js/compiled/mount.js" in 23.966 seconds. +``` + +And just open a browser at `file:///[path-to-mount]/mount/dev/resources/public/index.html`: + + + +The flow behind the app is quite simple: + +* load config +* open a WebSocket connection +* keep an audit log in Datascript +* call `(mount/stop)` to disconnect + +#### Using States + +A good example of derefing state is here in [websockets.cljs](https://github.com/tolitius/mount/blob/0825ad2ed085b73b7ae989b4382ce4e0376e4be3/dev/cljs/app/websockets.cljs#L21): + +```clojure + +(ns app.websockets + (:require [app.conf :refer [config]] + [app.audit-log :refer [audit log]]) + (:require-macros [mount.core :refer [defstate]])) + +;; ... + +(defstate system-a :start (connect (get-in @config [:system-a :uri])) + :stop (disconnect system-a)) +``` + +notice how config is deferef'ed `@config` in order to use its state. It of course does not have to be deref'ed here, and +can be just passed along to the `connect` function to be `@`ed there instead. + +### Thanks + +I'd like to thank these good people for brainstorming and supporting the idea of Mount in ClojureScript universe: + +[@DomKM](https://github.com/DomKM), [@yogthos](https://github.com/yogthos) and [@edvorg](https://github.com/edvorg) diff --git a/doc/img/mount.cljs.example.png b/doc/img/mount.cljs.example.png new file mode 100644 index 0000000..5edad72 Binary files /dev/null and b/doc/img/mount.cljs.example.png differ