Anton Astashov's blog

Would be nice to write something clever here.

Dart Apps With Unidirectional Flow and Persistent Data Structures

| Comments

Recently in JavaScript, and especially in ClojureScript world, one approach of developing front-end apps became pretty popular – using unidirectional app flow and persistent data structures. Something pretty close to Flux, but not really exactly like it. I tried that approach in Textik, and was really happy how it went.

The idea

Here’s the idea – in your app, you have only one place where you store ALL of your application state, some sort of global structure, containing persistent vectors and maps. It is important to use persistent data structures there, as you will see soon, they provide some very important features we use to speed up our app.

So, you have one global data structure, where the state of all your application is stored. Everything is there – what tooltips are shown, what buttons are disabled, all the content of the app, etc. Then, you pass that global data structure to your rendering engine, which renders the whole app, from top to bottom, for the first time. It also sets up various HTML events listeners to the DOM elements, and attaches event handlers, which basically do only one thing – they generate parameters payload, and pass it to Dispatcher.

Dispatcher receives that payload, and figures out what to do with it. It calls various “mutators” – models, which actually will change the global state of the app. Then, Dispatcher calls the rendering engine again, and passes the updated global state to it. The rendering engine rerenders the whole app again, from top to the bottom. But now, it can easily figure out what parts, or what components of the app should be rerendered. We pass particular subtrees of our global state tree to the components while rendering the whole app, and because these are persistent data structures, we can very quickly find out if some particular subtree was changed since last render. So, the rendering engine, using that knowledge, rerenders only parts of the app, which were changed, and because of that – it does that really fast.

And then everything happens in the same way again. We get another event, send the payload to Dispatcher, Dispatcher calls mutators, mutators change the global state, and Dispatcher calls the rendering engine to rerender the app again.

I.e., this way:

1
2
3
4
5
  main -------> render view --------> dispatcher
                    ^                      |
                    |                      |
                    |                      v
                    +------------ change global state

So, this way we get a very simple data flow, all our events go through the same message bus to Dispatcher, the app structure becomes so simple, it is even ridiculous a bit :)

Implementing TodoMVC in Dart

So, I decided to try the same approach with Dart. I took ReactJS as the rendering engine, since it works really close to what I want from it, and there is a nice Dart wrapper for it from Clean Team – react. As for persistent data structures, there is a pub package from VacuumLabs – persistent (but unfortunately it is not in Pub, you should get it from their forked repo from GitHub).

You can check the source code here: https://github.com/astashov/todomvc/tree/master/labs/architecture-examples/react-dart-uniflow/web/dart, and you can play with it here: http://astashov.s3-us-west-2.amazonaws.com/todomvc/index.html (if you want to check it in Dart, use Dartium). This is just usual TodoMVC app.

Let me quickly guide you through the code.

Everything starts with initializing the global state.

1
2
3
4
5
6
7
8
9
10
// data.dart

var _appData = new Data(persist({
    'autoincrement': 0,
    'new-input': '',
    'list': [],
    'filter': 'all',
    'edit': null}));

Data get appData => _appData;

appData will be globally available, and we’ll get the current value of the global state with appData.value. There are a bunch of methods-mutators in the Data class, which will overwrite the value property of appData with the new value. They won’t really mutate the existing value, but recreate the new one, reusing unchanged parts of the old value.

Then, we initialize the app, entering our main() function:

1
2
3
4
5
6
// app.dart

void main() {
  setClientConfiguration();
  rerender();
}

setClientConfiguration() initializes the react library, and then we render the app for the first time. It happens in dispatcher.dart:

1
2
3
4
5
// dispatcher.dart

void rerender() {
  renderComponent(todoAppComponent({'value': appData.value}), querySelector('#todoapp'));
}

Here we call renderComponent, which is a function of the react package, and pass the React component we are going to render and the selector which we are going to append it to.

The components are defined in the components.dart file.

All our components are not just default React components, but instead inherited from our custom _Component class, which we instrumented with the shouldComponentUpdate hook. There we check if the new passed value (our subtree of the global state) was changed since the last render. Again, this check should be very fast because of the persistent data structures properties. Also, as a convention, we are going to send our subtrees as the ‘value’ prop.

Let’s have a look at todoAppComponent – our entry component. All it does is just renders three subcomponents – header, footer and main, and pass parts of the received global tree to them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// components.dart

class TodoApp extends _Component {
  render() {
    return
      div({'id': 'todo-app', 'className': value['filter']}, [
        headerComponent({'value': value["new-input"]}),
        mainComponent({'value': persist({
          'list': value["list"],
          'edit': value["edit"]})}),
        footerComponent({'value': persist({
          'count': value["list"].length,
          'filter': value["filter"],
          'countCompleted': value["list"].where((i) => i["isCompleted"]).length})})]);
  }
}
var todoAppComponent = registerComponent(() => new TodoApp());

Now, let’s have a look e.g. at headerComponent, which renders the input for creating new tasks, and also adds event handlers to the input. It only receives the appData.value['new-input'] value, which defines what should be the value of the input.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// components.dart

class Header extends _Component {

  // ...

  void _inputChange(event) {
    dispatch({'action': 'new-input', 'value': event.target.value});
    setState({'value': event.target.value});
  }

  // ...

  render() {
    return
      header({'id': 'header'}, [
        h1({}, 'todos'),
        input({
          'id': 'new-todo', 'placeholder': 'What needs to be done?', 'autofocus': 'autofocus',
          'value': value, 'onChange': _inputChange, 'onKeyDown': _onKeyDown})]);
  }
}
var headerComponent = registerComponent(() => new Header());

Every single time we enter/remove a character in the input, we fire an event, and its handler calls the dispatch function of Dispatcher. We send the payload to it, which looks like {'action': 'new-input', 'value': input.value}.

There, in Dispatcher, we handle it. For simplicity, I mutate the global state right in Dispatcher, but in a larger application you probably would want to delegate that to a model.

And then, we rerender the app again.

1
2
3
4
5
6
7
8
9
10
11
12
// dispatcher.dart

void dispatch(Map payload) {
  switch (payload["action"]) {
    // ...
    case 'new-input':
      appData.update("new-input", payload['value']);
      break;
    // ...
  }
  rerender();
}

Summary

So, that’s actually it. Of course there are also other functionality – editing items, removing items, marking as completed, etc, but it works just in the same way as I just described. For items, there is the Item model – items.dart, which provides API for CRUD operations for items. But all that stuff works in the same way I just described.

That’s the whole lifecycle of the application. Very simple, very declarative – you basically only need to work with the global state, changing it accordingly, and you don’t really need to care about the view – React will do a great job with rendering the app using the global state.

Another cool side-effect of having the global state – you can always easily reproduce your user’s problems – debugging becomes way easier! If you use something like Airbrake or Rollbar to aggregate exceptions from your users, then you could just attach the global state in JSON to the exception you are about to send to e.g. Rollbar, and then you can easily reproduce the user’s problem just by applying that JSON on your machine, so you’d get exactly the same state of the app where the user was before the exception happened. Kinda cool, huh? :)

There are a bunch of other cool things (e.g. simple undo – you can just save the full state of the app (or part of the state) in a vector every time the change happens, and it will be stored efficiently because of the nature of persistent data structures).

I’m really excited about this approach so far.

Isn’t that like Rails?

If you ever worked with Rails, you can actually see a lot of similar things in this approach. In Rails, we pass our request through router, then router figures out (from URLs and GET params) what controller this request should be sent to, controller calls various models, which change the database, and the render the response. In this case,

  • Dispatcher is router
  • ‘mutators’ are models
  • appData is our database

In models, we also don’t have our internal state, models work with the database, retrieving and saving data into it. Same as in ActiveRecord :)

And the whole lifecycle of the front-end app like this one and a Rails app looks extremely familiar. :)


There are still some things, that could be improved, and some additional persistent data structures, that could be written, like e.g. currently PersistentVector doesn’t efficiently handle inserting/removing elements in the middle of a vector. But still, you already can do a lot with the Dart ecosystem and these 2 packages I was talking about – react and persistent.

So, please try it out, hopefully you’ll find it useful :)

Related links

  • React
  • Persistent – btw, there is a great explanation why persistent data structures are cool, make sure you’ve read that!
  • Source code
  • Demo (Dartium only)
  • Slides of my Textik talk – where I was talking about the Textik’s architecture, which is very close to what I described dhere.

Perfect ClojureScript Development Environment With Vim

| Comments

When I just started to learn ClojureScript, I had hard time trying to find some tutorials which could explain how to set up a nice dev environment for ClojureScript. The good environment is extremely important especially when you just started to play around with the language, it is very useful to get immediate feedback to your changes in the code. But it’s also helpful when you already know what you do, and allows you to iterate quickly on your project.

Later, while working on Textik (check it out, or check the sources), I was able to build something close enough to the ideal environment, which could allow me to iterate very quickly. To simplify life of ClojureScript beginners, here’s a tutorial how to set up that kind of environment.

So, here is a detailed explanation what you need to do from the scratch, or you could just go and clone/fork the skeleton project on Github, if you feel more like tl;dr.

I used Vim for development, but most of this tutorial (except the Vim part) is IDE-agnostic.

To start, let’s create a new Clojure project, called ‘perfection’:

1
$ lein new perfection

It will create a skeleton of a Clojure project. Then, open project.clj file, it should look something like that:

1
2
3
4
5
6
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]])

Add some lines to turn the project into a ClojureScript project:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2277"]] ;; <- Adding ClojureScript

  :jvm-opts ["-Xmx1G"] ;; <- Sometimes ClojureScript compilation fails because available
                       ;; JVM heap space it too low. This line gives it more space

  :plugins [[lein-cljsbuild "1.0.3"]] ;; <- While you can build ClojureScript just by running
                                      ;;    'cljsc', there is a waaay more convenient way -
                                      ;;    using cljsbuild lein plugin

  ;; This is the list of builds 'cljsbuild' will use to compile our ClojureScript
  ;; code into JavaScript. Here we add first build "dev", it will put resulting main
  ;; entry for JavaScript code to resources/public/perfection.js, and all dependencies
  ;; for it to resources/public/out
  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/perfection"]
              :compiler {
                :output-to "resources/public/perfection.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}]})

Now, run

1
$ lein deps

And lein will install all the new dependencies we just added.

Next, delete the file src/perfection/core.clj, create a new file src/perfection/core.cljs, and put simple “Hello World” code into it:

1
2
3
(ns perfection.core)

(.log js/console "Hello world!")

Now we need something to run our compiled JavaScript code. Let’s create the index.html file, which will include the generated JavaScript file. Create resources/public/index.html file and add something like this to it:

1
2
3
4
5
6
7
8
9
10
11
<html>
  <head>
    <title>Perfection - perfect dev environment for ClojureScript</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <script src="out/goog/base.js" type="text/javascript"></script>
    <script src="perfection.js" type="text/javascript"></script>
    <script type="text/javascript">goog.require("perfection.core");</script>
  </body>
</html>

Let’s also add some styles, create the resources/public/css/styles.css CSS file with this content:

1
2
3
body {
  background: silver;
}

Let’s try to run compilation, via ‘cljsbuild’ lein plugin:

1
2
3
4
$ lein cljsbuild auto dev
Compiling ClojureScript.
Compiling "resources/public/perfection.js" from ["src"]...
Successfully compiled "resources/public/perfection.js" in 6.829 seconds.

Now we need some server to serve our files. I usually prefer to use Python’s SimpleHTTPServer:

1
$ python -m SimpleHTTPServer 8000

Next, open a browser and go to http://localhost:8000/resources/public/index.html. Check the console, you should see “Hello World!” there.

Figwheel

Now, you have some running ClojureScript environment, which will automatically and instantly (less than for a second) recompile your files when you change any files in ‘src/perfection’ directory. Sweet. But we want more.

Meet lein-figwheel, amazing lein plugin, which will reload the changed JavaScript files after recompilation in the browser without reloading the page! That is so cool. Let’s add it to our project:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2277"]
                 [figwheel "0.1.3-SNAPSHOT"]]) ;; <- Adding Figwheel

  :jvm-opts ["-Xmx1G"]

  :plugins [[lein-cljsbuild "1.0.3"]
            [lein-figwheel "0.1.3-SNAPSHOT"]] ;; <- Adding also a lein plugin for it

  ;; Figwheel settings. We are going to use 'resource/public' directory as our root
  ;; directory for serving the files by the figwheel server, and also we specify
  ;; where our CSS styles live, so Figwheel could reload them without reloading the
  ;; page when we change them as well!
  :figwheel {
    :http-server-root "public"
    :port 3449
    :css-dirs ["resources/public/css"]}

  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/perfection" "src/figwheel"] ;; <- adding source path where
                                                              ;;    figwheel code will live
              :compiler {
                :output-to "resources/public/perfection.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}]})

We will place the client code for figwheel not in src/perfection, but in a separate dir, because we don’t want other builds (like ‘test’ or ‘release’) to include it.

Now, create the file src/figwheel/perfecton_figwheel.cljs, and put this code there:

1
2
3
4
(ns perfection-figwheel
  (:require [figwheel.client :as fw :include-macros true]))

(fw/watch-and-reload)

And require it from resources/public/index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
  <head>
    <title>Perfection - perfect dev environment for ClojureScript</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <script src="out/goog/base.js" type="text/javascript"></script>
    <script src="perfection.js" type="text/javascript"></script>
    <script type="text/javascript">goog.require("perfection.core");</script>
    <!-- Requiring figwheel -->
    <script type="text/javascript">goog.require("perfection_figwheel");</script>
  </body>
</html>

After that, let’s shut down our python server, but run

1
$ lein figwheel dev

instead, then open localhost:3449/index.html in the browser, and check the console. It should look like:

1
2
3
Hello world!
Figwheel: trying to open cljs reload socket
Figwheel: socket connection established

Now go to src/perfection/core.cljs, and change it to something else, e.g. change “Hello World!” to “Hello World 2!”. You should see in the console, that we reloaded our files, and the new “Hello World 2!” output:

1
2
3
4
Figwheel: loading files
Hello world 2!
Figwheel: loaded these files
("/out/perfection/core.js")

Figwheel will also track the changes in CSS and also reload them. Change the background in the CSS file from silver to blue (resources/public/css/style.css file):

1
2
3
body {
  background: blue;
}

and you’ll see in the browser console:

1
2
Figwheel: loaded CSS files
("/css/style.css")

Tests

I like unit tests. They give me confidence. But it really sucks to manually run them every time, and it especially sucks to run them in Clojure/ClojureScript, because you have to wait for the full JVM start, which takes several seconds. Not really perfect instant experience we are looking for. Thankfully, you can run it once, and then restart the tests on every change of the source or test files, via the same ‘lein clsbuild auto’ mechanism.

First thing, make sure you have PhantomJS installed. If not, install it with HomeBrew:

1
$ brew install phantomjs

Then, let’s add the ‘test’ build for cljsbuild, in project.clj:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2277"]
                 [figwheel "0.1.3-SNAPSHOT"]])

  :jvm-opts ["-Xmx1G"]

  :plugins [[lein-cljsbuild "1.0.3"]
            [lein-figwheel "0.1.3-SNAPSHOT"]
            [com.cemerick/clojurescript.test "0.3.1"]] ;; <- Adding lein plugin for tests

  :figwheel {
    :http-server-root "public"
    :port 3449
    :css-dirs ["resources/public/css"]}

  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/perfection" "src/figwheel"]
              :compiler {
                :output-to "resources/public/perfection.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}
             ;; Adding the new build 'test'. Note the :notify-command setting,
             ;; it will run the tests when the files change
             {:id "test"
              :source-paths ["src/perfection" "test"]
              :notify-command ["phantomjs" :cljs.test/runner "perfection_test.js"]
              :compiler {
                :output-to "perfection_test.js"
                :optimizations :whitespace}}]})

Now, let’s add some function to test (src/perfection/core.cljs):

1
2
3
4
5
6
7
(ns perfection.core)

(.log js/console "Hello world 2!")

;; Our new function
(defn add [a b]
  (+ a b))

Then, remove the currently existing test/perfection/core_test.clj, and create a new file test/perfection/core_test.cljs, which will contain our new first failing test:

1
2
3
4
5
6
7
(ns perfection.core-test
  (:require-macros [cemerick.cljs.test :refer (is deftest)])
  (:require [cemerick.cljs.test :as test]
            [perfection.core :as pn]))

(deftest ex1
  (is (= (pn/add 1 2) 4)))

Let’s try to run it. It should fail:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ lein cljsbuild auto test
Compiling ClojureScript.
Compiling "perfection_test.js" from ["src/perfection" "test"]...
SyntaxError: Parse error


Testing perfection.core-test

FAIL in (perfection.core-test/ex1) (:)
expected: (= (pn/add 1 2) 4)
  actual: (not (= 3 4))

Ran 1 tests containing 1 assertions.
Testing complete: 1 failures, 0 errors.

Ran 1 tests containing 1 assertions.
Testing complete: 1 failures, 0 errors.
Successfully compiled "perfection_test.js" in 4.203 seconds.

Change (is (= (pn/add 1 2) 4)) to (is (= (pn/add 1 2) 3)), then save the file, and the test should rerun automatically, and now it should pass:

1
2
3
4
5
6
7
8
9
10
11
12
Compiling "perfection_test.js" from ["src/perfection" "test"]...
SyntaxError: Parse error


Testing perfection.core-test

Ran 1 tests containing 1 assertions.
Testing complete: 0 failures, 0 errors.

Ran 1 tests containing 1 assertions.
Testing complete: 0 failures, 0 errors.
Successfully compiled "perfection_test.js" in 0.719 seconds.

W00t! Now we have an autorunning test in our app.

Some notes: PhantomJS is pretty old, it lacks a lot of features existing in modern browsers, like Function.bind or requestAnimationFrame. So, for tests you may need shims, you can write them in separate JS files, and then add to :notify-command in project.clj for the “test” build, e.g. like that:

1
2
3
4
5
:notify-command ["phantomjs"
                 :cljs.test/runner
                 "resources/public/js/function-bind-shim.js"
                 "resources/public/js/request-animation-frame-shim.js"
                 "perfection_test.js"]

Integration with Vim

We already have a lot, but we are not going to stop :). We would also want to quickly search docs for the functions we use from Vim, be able to jump to definitions of the functions, and even execute some code right in the browser’s context, right in the context of our running application, straight from Vim. Oh yes, that would be super cool. And that is totally possible, all thanks to famous Vim master Tim Pope and his beautiful ‘vim-fireplace’ plugin.

So, let’s first configure our app so it could accept connections from repl. We need to install brepl to it. As usual, we first need to add some changes to project.clj:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2277"]
                 [figwheel "0.1.3-SNAPSHOT"]])

  :jvm-opts ["-Xmx1G"]

  :plugins [[lein-cljsbuild "1.0.3"]
            [lein-figwheel "0.1.3-SNAPSHOT"]
            [com.cemerick/clojurescript.test "0.3.1"]
            [com.cemerick/austin "0.1.4"]] ;; <- Adds nice support for ClojureScript REPL.
                                           ;;    Also has Piggieback as a dependency, which
                                           ;;    we need for vim-fireplace

  :figwheel {
    :http-server-root "public"
    :port 3449
    :css-dirs ["resources/public/css"]}

  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/perfection" "src/figwheel" "src/brepl"]  ;; <- adding source path
                                                                           ;;    where brepl code
                                                                           ;;    will live
              :compiler {
                :output-to "resources/public/perfection.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}
             {:id "test"
              :source-paths ["src/perfection" "test"]
              :notify-command ["phantomjs" :cljs.test/runner "perfection_test.js"]
              :compiler {
                :output-to "perfection_test.js"
                :optimizations :whitespace}}]})

Same thing as for figwheel – we will place the client code for brepl not in src/perfection, but in a separate dir, because we don’t want other builds (like ‘test’ or ‘release’) to include it.

Now, create src/brepl/perfection_brepl.cljs:

1
2
3
4
(ns perfection-brepl
  (:require [clojure.browser.repl :as repl]))

(repl/connect "http://localhost:9000/repl")

And add its requiring to resources/public/index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
  <head>
    <title>Perfection - perfect dev environment for ClojureScript</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <script src="out/goog/base.js" type="text/javascript"></script>
    <script src="perfection.js" type="text/javascript"></script>
    <script type="text/javascript">goog.require("perfection.core");</script>
    <script type="text/javascript">goog.require("perfection_figwheel");</script>
    <!-- Requiring brepl -->
    <script type="text/javascript">goog.require("perfection_brepl");</script>
  </body>
</html>

After that, restart lein figwheel dev, then run lein repl.

Now, it’s time to install the vim-fireplace plugin for Vim. I use pathogen, so I just simply put the plugin to ~/.vim/bundle/vim-fireplace.

Next, restart Vim, open src/perfection/core.cljs, and in Vim run :Piggieback 9000. It will hang for several seconds.

Then reload localhost:3449/index.html in browser.

You may see the exception in the browser console, like:

1
2
3
Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement':
Blocked a frame with origin "http://localhost:3449" from accessing a frame with origin
"http://localhost:9000". Protocols, domains, and ports must match.

but that’s totally fine, and will work anyway.

Go back to src/perfection/core.cljs in Vim, and try to run something like :Eval (js/alert 123). The alert window should appear right in the browser. Cool, huh?

You can do a lot of things with vim-fireplace, just check the docs. It converts Vim to a pretty solid ClojureScript IDE, which now is quite close even to LightTable, but with all that well-known Vim stuff you and me love so much :)

Release

Now we have everything we need for comfortable development process, but I highly recommend that you also add another build, which will generate the “release” version of the app, which will eventually go to production. It is very important to do that right from the beginning, because sometimes there are issues with “advanced” optimizations of Google Closure (which ClojureScript uses under the hood for compilation). You should really always check that your app works with :advanced optimizations, because debugging may be difficult there.

So, add this to your project.clj:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2277"]
                 [figwheel "0.1.3-SNAPSHOT"]])

  :jvm-opts ["-Xmx1G"]

  :plugins [[lein-cljsbuild "1.0.3"]
            [lein-figwheel "0.1.3-SNAPSHOT"]
            [com.cemerick/clojurescript.test "0.3.1"]
            [com.cemerick/austin "0.1.4"]]

  :figwheel {
    :http-server-root "public"
    :port 3449
    :css-dirs ["resources/public/css"]}

  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/perfection" "src/figwheel" "src/brepl"]
              :compiler {
                :output-to "resources/public/perfection.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}
             {:id "test"
              :source-paths ["src/perfection" "test"]
              :notify-command ["phantomjs" :cljs.test/runner "perfection_test.js"]
              :compiler {
                :output-to "perfection_test.js"
                :optimizations :whitespace}}
             ;; Our release build. The main difference - it uses :optimizations :advanced.
             ;; It also doesn't include src/figwheel or src/brepl - we don't need that in production
             {:id "release"
              :source-paths ["src/perfection"]
              :compiler {
                :output-to "resources/public/perfection_prod.js"
                :output-dir "resources/public/prod-out"
                :optimizations :advanced
                :pretty-print false
                :source-map "resources/public/perfection_prod.js.map"}}]})

Now create resources/public/index_prod.html, it’s going to be way shorter than our dev one:

1
2
3
4
5
6
7
8
9
<html>
  <head>
    <title>Perfection - perfect dev environment for ClojureScript</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <script src="perfection_prod.js" type="text/javascript"></script>
  </body>
</html>

And run:

1
$ lein cljsbuild auto release

Now you can open http://localhost:3449/index_prod.html, and make sure the site still works (by checking for “Hello World 2!” in the console.

BTW, now if you open resources/public/perfection_prod.js, all you will see there is:

1
2
3
4
5
;(function(){
console.log("Hello world 2!");
})();

//# sourceMappingURL=perfection_prod.js.map

That’s the power of :advanced optimizations. It got rid of everything which is not used (all the ClojureScript libs, Google Closure libs, thousands and thousands of lines of code), and left only what matters.

Summary

So, I usually have 4 things running simultaneously:

1
2
3
4
lein repl
lein figwheel dev
lein cljsbuild auto test
lein cljsbuild auto release

And whenever I make a change in the source code, I’ll get updated version of the app in the browser, without page refresh, the tests will run, and the release will be built. All automagically.

That’s what I call “the perfect dev environment”.

Tips & Tricks

Paredit

You should use it. Since Clojure is Lisp, and consists of s-expressions, it has a very clear structure. Paredit allows to efficiently edit your code by manipulating s-expressions. This is when all these parentheses, which initially scare Lisp newcomers, will bring power. There is a plugin for Vim, a bit buggy, especially when you copy/paste large structures (especially when they are unbalanced), but it’s still worth it.

p and b

I found it very useful for my development process to create the function p and the macro b for debugging.

  • p just prints what the value it receives and returns it.
  • b prints what time did the execution of the nested s-expression take, and also returns the value.

They look like this:

1
2
3
4
(defn p "Prints given arguments, and then returns the last one"
  [& values]
  (.log js/console (apply pr-str values))
  (last values))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(defn benchmark
  "Prints the execution time for the given function. Accepts optional string, which will
   be used as a description"
  ([f] (benchmark nil f))
  ([msg f]
  (let [start (.now js/Date)
        result (f)]
    (p (str (when msg (str msg ": ")) (- (.now js/Date) start) "ms"))
    result)))

(defmacro b
  ([f]
  `(tixi.utils/benchmark (fn [] ~f)))

  ([msg f]
  `(tixi.utils/benchmark ~msg (fn [] ~f))))

And then we can use them, like that. Imagine you have a function:

1
2
3
(let [foo 2
      bar (+ 3 foo)]
  (+ foo bar))

And you are curious about what’s the output of (+ 3 foo). So you can do:

1
2
3
(let [foo 2
      bar (p (+ 3 foo))]
  (+ foo bar))

And it will put ‘5’ in the browser console. Curious how long it takes to execute it?

1
2
3
(let [foo 2
      bar (b (+ 3 foo))]
  (+ foo bar))

‘0ms’ (obviously :)) will appear in the browser console.

It starts to really shine especially with Paredit, when you can wrap and unwrap parentheses by one keystroke.

Video

This is a little screencast I recorded how I develop Textik, using the dev environment like that:

Conclusion

I really think ClojureScript so far is the most interesting and productive way to build complex front-end projects on the web. Especially if you use it with React (and some React wrapper, like Om, Reagent or Quiescent). If you didn’t try it yet, you should definitely try, and then hopefully you’ll find this article useful :)