<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Ragnar Dahlén</title>
 <link href="http://ragnard.github.io/atom.xml" rel="self"/>
 <link href="http://ragnard.github.io/"/>
 <updated>2016-06-23T14:06:08+00:00</updated>
 <id>http://ragnard.github.io/</id>
 <author>
   <name>Ragnar Dahlén</name>
   <email>r.dahlen@gmail.com</email>
 </author>

 
   
     <entry>
       <title>Clojure Cup 2013, Pong and core.async</title>
       <link href="http://ragnard.github.io/2013/10/01/clojurecup-pong-async.html"/>
       <updated>2013-10-01T00:00:00+00:00</updated>
       <id>http://ragnard.github.io/2013/10/01/clojurecup-pong-async</id>
       <content type="html">&lt;p&gt;Last weekend, &lt;a href=&quot;http://twitter.com/tgk&quot;&gt;Thomas&lt;/a&gt;,
&lt;a href=&quot;http://twitter.com/Jon_Neale&quot;&gt;Jon&lt;/a&gt;,
&lt;a href=&quot;http://twitter.com/quantisan&quot;&gt;Paul&lt;/a&gt; and I teamed up to participate in
the very first &lt;a href=&quot;http://www.clojurecup.com&quot;&gt;Clojure Cup&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It was great fun and resulted in &lt;strong&gt;Torus Pong&lt;/strong&gt;, a multi-player take
on the classic game of Pong (with a twist). You can check it out
at &lt;a href=&quot;http://pong.clojurecup.com&quot;&gt;pong.clojurecup.com&lt;/a&gt;.&lt;/p&gt;

&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;!-- &lt;a href=&quot;http://pong.clojurecup.com&quot;&gt; --&gt;
    &lt;img alt=&quot;Screenshot&quot; src=&quot;/assets/clojurecup/screenshot.png&quot; style=&quot;text-align: center&quot; /&gt;
  &lt;!-- &lt;/a&gt; --&gt;
&lt;/div&gt;

&lt;p&gt;If you feel so inclined, please give it a vote
&lt;a href=&quot;http://clojurecup.com/app.html?app=pong&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The source code is available under the uSwitch GitHub organization at:
&lt;a href=&quot;https://github.com/uswitch/torus-pong&quot;&gt;https://github.com/uswitch/torus-pong&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thomas has written a
&lt;a href=&quot;http://tgk.github.io/2013/10/torus-pong.html&quot;&gt;great post&lt;/a&gt; about the
process leading up to the “finished” game, the current status, some
lessons learned and things we didn’t have time for (lots!).&lt;/p&gt;

&lt;p&gt;In this post, I thought I’d share some more techincal information
around the &lt;em&gt;implementation&lt;/em&gt;, in particular how we leveraged channels
and processes provided by
&lt;a href=&quot;https://github.com/clojure/core.async&quot;&gt;core.async&lt;/a&gt; to help us
decouple different components and handle asynchrony and communication.
Hopefully, it can provide an interesting example of an application
using core.async. Saying that, all code was written in 48hrs so treat
it accordingly.&lt;/p&gt;

&lt;p&gt;Most code examples shown have been simplified to illustrate a point,
and may not be exactly as they are in the game source code.&lt;/p&gt;

&lt;h1 id=&quot;the-game&quot;&gt;The Game&lt;/h1&gt;

&lt;p&gt;In essence, our idea was to allow multiple players to play a game of
Pong together. Each player would gets his own paddle and an even share
of the whole playing field field, but with a different opponent on
each side.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;last&lt;/em&gt; playing field would be connected to the &lt;em&gt;first&lt;/em&gt;, forming a
circular list of playing fields, or, with a bit of imagination, a
torus.&lt;/p&gt;

&lt;!-- I would say this was as about as concrete our _idea_ of the game was, --&gt;
&lt;!-- before starting the competition. --&gt;

&lt;h1 id=&quot;the-problems&quot;&gt;The Problem(s)&lt;/h1&gt;

&lt;p&gt;As a team, we are all quite green when it comes to game development.
This means I’m probably missing a lot of terminology and well-known
solutions to typical game programming problems, so bare with me.&lt;/p&gt;

&lt;p&gt;During our design discussions in between work on thursday and friday,
a couple of distinct problems emerged. I would say the three most
important ones were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;game core&lt;/strong&gt;: how does the game actually work and how do we
implement its rules?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;communication&lt;/strong&gt;: how do handle communication between players and
server?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;rendering&lt;/strong&gt;: what should the game look like to the user?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think one reason we were able to complete anything at all was
finding these parts, understanding their interactions and then pulling
them apart as much as possible, enabling parallel design,
implementation and change.&lt;/p&gt;

&lt;h2 id=&quot;game-core&quot;&gt;Game Core&lt;/h2&gt;

&lt;p&gt;I won’t spend any time on the game rules and their implementation, but
we’ll look brifefly at the interface to the core game logic.&lt;/p&gt;

&lt;p&gt;This interface is a single &lt;em&gt;pure&lt;/em&gt; function that given a &lt;code class=&quot;highlighter-rouge&quot;&gt;game-state&lt;/code&gt;
and some &lt;code class=&quot;highlighter-rouge&quot;&gt;commands&lt;/code&gt; from players, produces a new &lt;code class=&quot;highlighter-rouge&quot;&gt;game-state&lt;/code&gt;. This
function is called each time the game should advance and is
responsible for enforcing the rules of the game, physics, scoring etc.&lt;/p&gt;

&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img alt=&quot;Screenshot&quot; src=&quot;/assets/clojurecup/advance.png&quot; style=&quot;text-align: center&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;The implementation ended up looking something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;advance&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Given a game-state and some commands, advance the game-state one iteration&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;apply-scoring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;handle-commands&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;check-for-winner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;advance-fields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;play-sounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update-ball-fields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;engine&quot;&gt;Engine&lt;/h2&gt;

&lt;p&gt;Given the core game function, we needed something that was responsible
for feeding it with &lt;code class=&quot;highlighter-rouge&quot;&gt;commands&lt;/code&gt;, and applying it to the current
&lt;code class=&quot;highlighter-rouge&quot;&gt;game-state&lt;/code&gt; when appropriate. We called this part the &lt;strong&gt;engine&lt;/strong&gt;, and
this is where core.async enters the picture.&lt;/p&gt;

&lt;p&gt;We modeled the engine as a process that:
- takes and accumulates commands from a &lt;em&gt;command&lt;/em&gt; channel
- advances the game at some interval, determined by a timeout expiring
- puts each new &lt;code class=&quot;highlighter-rouge&quot;&gt;game-state&lt;/code&gt; to a &lt;em&gt;game-state&lt;/em&gt; channel&lt;/p&gt;

&lt;p&gt;Visually, this looks like this (blue boxes are channels):&lt;/p&gt;

&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img alt=&quot;Screenshot&quot; src=&quot;/assets/clojurecup/engine.png&quot; style=&quot;text-align: center&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;The implementation uses &lt;code class=&quot;highlighter-rouge&quot;&gt;alts!&lt;/code&gt; to wait for either a command to appear
or the timer to elapse, taking the appropriate action in each case.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;engine-process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command-chan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state-chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-core/initial-game-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commands&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params/tick-ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;alts!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command-chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:priority&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;condp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command-chan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commands&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-game-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;game-core/advance&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state-chan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updated-game-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-game-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params/tick-ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;talking-to-clients&quot;&gt;Talking to Clients&lt;/h2&gt;

&lt;p&gt;Being a browser based game, we chose WebSockets for client/server
communication. &lt;a href=&quot;http://keminglabs.com&quot;&gt;Kevin Lynagh&lt;/a&gt; has released an
excellent library called
&lt;a href=&quot;https://github.com/lynaghk/jetty7-websockets-async&quot;&gt;jetty7-websockets-async&lt;/a&gt;
which bridges the gap between the jetty7 WebSocket implementation and
core.async in a very elegant way.&lt;/p&gt;

&lt;p&gt;The library works as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;you hand it a channel on which you want new WebSocket connections to
be put as clients connect&lt;/li&gt;
  &lt;li&gt;you can then take new WebSocket connections from this channel, where
each connection is represented by a channel pair:
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;in&lt;/code&gt;: on which you can &lt;em&gt;put&lt;/em&gt; messages that should be sent to client&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt;: from which you can &lt;em&gt;take&lt;/em&gt; messages received from the client&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-connection-process&quot;&gt;The Connection Process&lt;/h3&gt;

&lt;p&gt;We have a singleton process that accepts new connections from a
connection channel, and then spawns a separate process to handle
further communication with the client. A slightly simplified version
of the code that does this is as follows:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection-process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn-chan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command-chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn-chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;next-id!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;client-process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command-chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn-chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We read from the connection channel. When we get a new connection we
pull out the &lt;code class=&quot;highlighter-rouge&quot;&gt;in&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; channels, generate a new unique id for the
client and spawn a client process, passing it the id, the &lt;code class=&quot;highlighter-rouge&quot;&gt;in&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; channels as well as the shared &lt;code class=&quot;highlighter-rouge&quot;&gt;command-chan&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-client-process&quot;&gt;The Client Process&lt;/h3&gt;

&lt;p&gt;The main responsibility of the client process is to forward commands
from the &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; channel of the client to the command channel consumed
by the engine, decorating them with a player id:&lt;/p&gt;

&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img alt=&quot;Screenshot&quot; src=&quot;/assets/clojurecup/clients.png&quot; style=&quot;text-align: center&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;The implementation:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command-chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command-chan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:player/join&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clojure.edn/read-string&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command-chan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command-chan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:player/leave&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The code works as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;notify engine that a new player has joined by sending a
&lt;code class=&quot;highlighter-rouge&quot;&gt;[:player/join id]&lt;/code&gt; command to &lt;code class=&quot;highlighter-rouge&quot;&gt;command-chan&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;repeatedly read messages from WebSocket &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt; channel, parse them as
EDN, tack on the client id and forward to engine&lt;/li&gt;
  &lt;li&gt;when &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt; is read from &lt;code class=&quot;highlighter-rouge&quot;&gt;out&lt;/code&gt;, meaning the connection has been
closed, notify game engine that the player has left by sending a
&lt;code class=&quot;highlighter-rouge&quot;&gt;[:player/leave id&lt;/code&gt; command, and exit the process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;propagating-change&quot;&gt;Propagating Change&lt;/h2&gt;

&lt;p&gt;So far we can accept new players, feed their commands to the game
engine and advance the game.&lt;/p&gt;

&lt;p&gt;The final part is propagating information about new game states to
clients. This is handeled by a process we called the &lt;em&gt;game-state
emitter&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This process consumes new &lt;code class=&quot;highlighter-rouge&quot;&gt;game-state&lt;/code&gt; data put on the game-state
channel by the engine process. It then broadcasts this data in some
form to connected clients.&lt;/p&gt;

&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img alt=&quot;Screenshot&quot; src=&quot;/assets/clojurecup/emitter.png&quot; style=&quot;text-align: center&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;This process needs to know which channel it should use to talk to a
specific client. This information is conveyed by an &lt;code class=&quot;highlighter-rouge&quot;&gt;atom&lt;/code&gt; with
mappings from player ids to &lt;code class=&quot;highlighter-rouge&quot;&gt;in&lt;/code&gt; channels. This mapping is
maintained by the connection and client processes described above, but
I omitted them in this post to simplify matters.&lt;/p&gt;

&lt;p&gt;Our implementation ended up always sending the entire game state to
all players in the game.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state-emitter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state-chan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clients-atom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state-chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;doseq&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player-id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clients-atom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-chan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pr-str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;game-state-chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;all-together-now&quot;&gt;All Together Now&lt;/h1&gt;

&lt;p&gt;Putting channels, processes and shared state together, we get
something like this:&lt;/p&gt;

&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img alt=&quot;Screenshot&quot; src=&quot;/assets/clojurecup/whole.png&quot; style=&quot;text-align: center&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;The angry pink box is of course the &lt;em&gt;shared state&lt;/em&gt;, ie. the &lt;code class=&quot;highlighter-rouge&quot;&gt;atom&lt;/code&gt;
described above. We could have eliminated this by introducing a bit
more communication, but we didn’t have time to.&lt;/p&gt;

&lt;p&gt;Once you get past the core.async concepts, the architecture is really
very simple, and worked out quite well for us.&lt;/p&gt;

&lt;h1 id=&quot;a-word-on-buffering&quot;&gt;A Word on Buffering&lt;/h1&gt;

&lt;p&gt;One thing I really like about core.async is the way it forces you to
think about contention in a system of communicating processes. By
default, all channels are unbuffered and blocking&lt;sup id=&quot;fnref:blocking&quot;&gt;&lt;a href=&quot;#fn:blocking&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, meaning
that if you try to put something on a channel, and nobody is there to
take it, you will block until someone arrives on the other end (and
vice versa).&lt;/p&gt;

&lt;p&gt;This is a great default, but it’s also simple to introduce different
buffering policies when needed. We didn’t have a lot of time to tune
this, but there are definitely places we should improve.&lt;/p&gt;

&lt;p&gt;One example is the game-state channel:&lt;/p&gt;

&lt;p&gt;When the engine has produced a new &lt;code class=&quot;highlighter-rouge&quot;&gt;game-state&lt;/code&gt;, it wants to continue
to accumulate new commands and prepare for the next iteration. If the
emitter is busy transmitting a previous game to all clients, it won’t
pickup the new &lt;code class=&quot;highlighter-rouge&quot;&gt;game-state&lt;/code&gt; until its ready. This would block the
progress of the engine, which is bad.&lt;/p&gt;

&lt;p&gt;We could improve this by using a &lt;code class=&quot;highlighter-rouge&quot;&gt;sliding-buffer&lt;/code&gt; of size &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; for the
game-state channel. In that case, each new &lt;code class=&quot;highlighter-rouge&quot;&gt;game-state&lt;/code&gt; put to the
channel would just replace the previous, older one. The engine could
continue immediately, and the emitter, when ready, would pick up the
latest &lt;code class=&quot;highlighter-rouge&quot;&gt;game-state&lt;/code&gt; available. It doesn’t care about older
&lt;code class=&quot;highlighter-rouge&quot;&gt;game-state&lt;/code&gt;s it didn’t have time to distribute.&lt;/p&gt;

&lt;p&gt;As the code currently stand, the impact of this issue is reduced by
the fact that we’re wrapping each &lt;em&gt;client&lt;/em&gt; channel in a
&lt;code class=&quot;highlighter-rouge&quot;&gt;sliding-buffer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It think there are lots of interesting things to explore in this area.&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;I’ve tried to explain the way we used core.async in our architecture.&lt;/p&gt;

&lt;p&gt;There are lots of things I didn’t mention, like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;how we used it on the client side&lt;/li&gt;
  &lt;li&gt;how we halfway through the competition changed the entire structure
of the game, but only had to introduce one more simple process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are, of course, also &lt;em&gt;lots&lt;/em&gt; of things we didn’t have time to do.&lt;/p&gt;

&lt;p&gt;One thing in particular, that I’d really like to do at some point is
implement &lt;em&gt;interpolation&lt;/em&gt;, &lt;em&gt;input prediction&lt;/em&gt; and &lt;em&gt;lag compensation&lt;/em&gt;
as described in
&lt;a href=&quot;https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking&quot;&gt;Source Multiplayer Networking&lt;/a&gt;.
The game play currently requires very low latencies, and I’m almost
amazed it works as well as it does, considering the way input,
communication and rendering is currently coupled.&lt;/p&gt;

&lt;p&gt;If you read this far, &lt;a href=&quot;http://pong.clojurecup.com&quot;&gt;played&lt;/a&gt; the game
and maybe even liked it, please don’t forget to
&lt;a href=&quot;http://clojurecup.com/app.html?app=pong&quot;&gt;vote&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;blocking an actual JVM thread. A great discussion on this, and
core.async in general can be found on
&lt;a href=&quot;http://thinkrelevance.com/blog/2013/07/10/rich-hickey-and-core-async-podcast-episode-035&quot;&gt;episode 35&lt;/a&gt;
of the Think Relevance podcast.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:blocking&quot;&gt;
      &lt;p&gt;&lt;em&gt;blocking&lt;/em&gt; in core.async is not necessarily the same as &lt;a href=&quot;#fnref:blocking&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
     </entry>
   
 
   
     <entry>
       <title>Datomic in the Browser</title>
       <link href="http://ragnard.github.io/2013/08/12/datomic-in-the-browser.html"/>
       <updated>2013-08-12T00:00:00+00:00</updated>
       <id>http://ragnard.github.io/2013/08/12/datomic-in-the-browser</id>
       <content type="html">&lt;p&gt;Lately, I’ve been having a lot of fun using
&lt;a href=&quot;http://www.datomic.com&quot;&gt;Datomic&lt;/a&gt; on a couple of projects. One of them
is something we’re calling &lt;strong&gt;torch&lt;/strong&gt;, a source code analysis and
visualisation tool built on top of
&lt;a href=&quot;http://github.com/datomic/codeq&quot;&gt;codeq&lt;/a&gt;. It’s mostly an experiment
which will hopefully help us understand and visualise some aspects of
our entire Clojure code-base at
&lt;a href=&quot;http://www.uswitch.com/careers&quot;&gt;uSwitch&lt;/a&gt;. If you happen to be
attending &lt;a href=&quot;http://fpdays.net/fpdays2013/&quot;&gt;FP Days&lt;/a&gt; in Cambridge, you
might get to &lt;a href=&quot;http://lanyrd.com/2013/fpdays/sckzmk/&quot;&gt;see&lt;/a&gt; more of
torch.&lt;/p&gt;

&lt;p&gt;torch is currently a single-page browser application built in
ClojureScript, and in this post, I’ll show how we leverage the power
of Datomic from the browser using
&lt;a href=&quot;http://github.com/clojure/core.async&quot;&gt;core.async&lt;/a&gt; and the Datomic
REST API.&lt;/p&gt;

&lt;h1 id=&quot;going-hypertext&quot;&gt;Going Hypertext&lt;/h1&gt;

&lt;p&gt;A big part of torch is just querying Datomic for facts, possibly
deriving some new information, in order to then visualise it.&lt;/p&gt;

&lt;p&gt;Our initial prototype was a Java application but for a couple of
reasons which are not important for this post, we have since moved to
a browser based ClojureScript app.&lt;/p&gt;

&lt;p&gt;One thing that was obviously really easy in the Java application was
talking to Datomic, we could just use the standard Clojure API.
Querying Datomic is most commonly done using its dialect of Datalog,
and the process of connecting and performing a query looks something
like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; create a connection to the codeq database on the Datomic
;; system running on localhost:4334
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datomic.api/connect&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;datomic:free://localhost:4334/codeq&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; capture the most recent value of the database known by this peer 
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datomic.api/db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; perform a query
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datomic.api/q&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:find&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;?e&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;?v&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:where&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;?e&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/doc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;?v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The query finds all entities with a &lt;code class=&quot;highlighter-rouge&quot;&gt;:db/doc&lt;/code&gt; attribute, and returns
the id of each entity and the documentation string.&lt;/p&gt;

&lt;p&gt;It does this by calling the &lt;code class=&quot;highlighter-rouge&quot;&gt;q&lt;/code&gt; function in the &lt;code class=&quot;highlighter-rouge&quot;&gt;datomic.api&lt;/code&gt;
namespace, passing in a query expressed using a data structure, and
the database value that should be used as the basis for the
query&lt;sup id=&quot;fnref:awesome&quot;&gt;&lt;a href=&quot;#fn:awesome&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;So, moving to a browser based app, how do we query Datomic?&lt;/p&gt;

&lt;h1 id=&quot;datomic-rest-api&quot;&gt;Datomic REST API&lt;/h1&gt;

&lt;p&gt;Fortunately, Datomic provides a REST API out of the box. If you have a
Datomic free system running on &lt;code class=&quot;highlighter-rouge&quot;&gt;localhost:4334&lt;/code&gt;, you can start a HTTP
server exposing a REST interface to that system on port 9000 like
this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt; &lt;/span&gt;bin/rest -o &lt;span class=&quot;s2&quot;&gt;&quot;*&quot;&lt;/span&gt; -p 9000 free datomic:free://localhost:4334/&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;-o&lt;/code&gt; specifies allowed origins, we’re being overly liberal in this
example. With this process running, we can now fire off queries over
good old HTTP.&lt;/p&gt;

&lt;p&gt;The query resource, exposed at &lt;code class=&quot;highlighter-rouge&quot;&gt;/api/query&lt;/code&gt; requires two parameters:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;q&lt;/code&gt; — the query&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;args&lt;/code&gt; — a vector of arguments to the query&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both arguments should be the URL encoded EDN representation of its
data.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;args&lt;/code&gt; is a little bit special in that it always needs to include at
least one element, &lt;code class=&quot;highlighter-rouge&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:db/alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;your-storage/your-db&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;/code&gt;, identifying
which database to query. In our example, it would be &lt;code class=&quot;highlighter-rouge&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:db/alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;free/codeq&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Compared to 99.999% of all HTTP APIs on the web, Datomic also makes
the very unorthodox choice of using a sane&lt;sup id=&quot;fnref:json-quiz&quot;&gt;&lt;a href=&quot;#fn:json-quiz&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; format for data
interchange, &lt;a href=&quot;http://www.edn-format.org&quot;&gt;EDN&lt;/a&gt;. This means we need to
indicate that we accept content of type &lt;code class=&quot;highlighter-rouge&quot;&gt;application/edn&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So how does the same query look using this approach?&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;curl -H &lt;span class=&quot;s2&quot;&gt;&quot;Accept: application/edn&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:9000/api/query?q=%5B%3Afind+%3Fe+%3Fv+%3Ain+%24+%3Awhere+%5B%3Fe+%3Adb%2Fdoc+%3Fv%5D%5D&amp;amp;args=%5B%7B%3Adb%2Falias+&quot;&lt;/span&gt;free%2Fenergy-pal-perf&lt;span class=&quot;s2&quot;&gt;&quot;%7D%5D&amp;amp;offset=&amp;amp;limit=&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;A slight step back from the elegance of the Java/Clojure API, but the
ability is there. So how can we use this in ClojureScript?&lt;/p&gt;

&lt;h1 id=&quot;a-diversion-on-http&quot;&gt;A Diversion on HTTP&lt;/h1&gt;

&lt;p&gt;The
&lt;a href=&quot;https://developers.google.com/closure/library/&quot;&gt;Google Closure Library&lt;/a&gt;
provides a few ways to do HTTP requests, the most basic one being the
static method
&lt;a href=&quot;http://docs.closure-library.googlecode.com/git/class_goog_net_XhrIo.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;goog.net.XhrIo/send&lt;/code&gt;&lt;/a&gt;
which provides an interface very similar to most AJAX-capable
JavaScript libraries. We can use this method for writing a very simple
function that can GET some EDN from a given URL:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get-edn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;goog.net.XhrIo/send&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                       &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;edn-response&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GET&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                       &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                       &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clj-&amp;gt;js&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Accept&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;application/edn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This should seem fairly familiar; we pass in a URL, a callback, the
HTTP verb, maybe a body (&lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt; in this case) and some headers.&lt;/p&gt;

&lt;p&gt;Before calling the user-supplied callback, we apply &lt;code class=&quot;highlighter-rouge&quot;&gt;edn-response&lt;/code&gt; to
the result of the operation to save the caller from having to mess
with the &lt;code class=&quot;highlighter-rouge&quot;&gt;XhrIo&lt;/code&gt; result object. So, &lt;code class=&quot;highlighter-rouge&quot;&gt;edn-response&lt;/code&gt; is a function that
receives the result of an &lt;code class=&quot;highlighter-rouge&quot;&gt;XhrIo/send&lt;/code&gt;, checks if it was successful and
returns either a vector of &lt;code class=&quot;highlighter-rouge&quot;&gt;:ok&lt;/code&gt; and the EDN data in the body, or
&lt;code class=&quot;highlighter-rouge&quot;&gt;:fail&lt;/code&gt; and the status code.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edn-response&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.-target&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.isSuccess&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cljs.reader/read-string&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.getResponseText&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.getStatus&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As a consumer of &lt;code class=&quot;highlighter-rouge&quot;&gt;get-edn&lt;/code&gt;, you would use it like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get-edn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://localhost:9000/api/query?q=...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;js/console&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Got status &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; and value &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nothing revolutionary here. However, there is a fundamental difference
from the in-process Java version. The fact that we have to supply a
callback that will be invoked when the operation has completed means
that we give up some of our control over the flow of the program. This
is unfortunate, but the asynchronous callback based model is simply
something we have to live with when doing I/O in JavaScript.&lt;/p&gt;

&lt;p&gt;Or is it?&lt;/p&gt;

&lt;h1 id=&quot;turning-the-world-inside-out-with-coreasync&quot;&gt;Turning the World Inside Out With core.async&lt;/h1&gt;

&lt;p&gt;The core.async library is another piece of very inspiring work which
we in this particular case can use to invert the control of
asynchronous HTTP calls.&lt;/p&gt;

&lt;p&gt;We can modify our &lt;code class=&quot;highlighter-rouge&quot;&gt;get-edn&lt;/code&gt; function to not take a callback, but
instead return a channel on which the result of the operation will be
put when the the operation completes. For this example, you can think
of a channel as a queue with room for exactly one element. This could
look something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get-edn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;goog.net.XhrIo/send&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;put!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;edn-response&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GET&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clj-&amp;gt;js&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Accept&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;application/edn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This time, we create a channel using &lt;code class=&quot;highlighter-rouge&quot;&gt;chan&lt;/code&gt;, fire off the request and
return the channel immediately. When the request has completed, the
callback function will use &lt;code class=&quot;highlighter-rouge&quot;&gt;edn-response&lt;/code&gt; as before and then simply
&lt;code class=&quot;highlighter-rouge&quot;&gt;put!&lt;/code&gt; the resulting value on the channel.&lt;/p&gt;

&lt;p&gt;We can use our channel based &lt;code class=&quot;highlighter-rouge&quot;&gt;get-edn&lt;/code&gt; like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get-edn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://localhost:9000/api/query?q=...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;js/console&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Got status&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; and value &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This short snippet contains a &lt;strong&gt;lot&lt;/strong&gt; of core.async concepts but one
way of understanding it is as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We call &lt;code class=&quot;highlighter-rouge&quot;&gt;get-edn&lt;/code&gt; which will return a channel on which the result
of the request will eventually be &lt;code class=&quot;highlighter-rouge&quot;&gt;put!&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;We use &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;!&lt;/code&gt; to read a value from the channel. If no value is
available yet, execution will be parked at this point, and resumed
when a value becomes available.&lt;/li&gt;
  &lt;li&gt;This &lt;em&gt;parking&lt;/em&gt; is made possible by the &lt;code class=&quot;highlighter-rouge&quot;&gt;go&lt;/code&gt; macro, which wraps our code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is really only scratching the surface of what’s possible with
&lt;code class=&quot;highlighter-rouge&quot;&gt;core.async&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The important bit is that we’ve regained control of the program flow,
we’re no longer forced to hand over a callback that will be used to
resume our execution in some other place.&lt;/p&gt;

&lt;h1 id=&quot;tying-the-knot&quot;&gt;Tying the Knot&lt;/h1&gt;

&lt;p&gt;We now have a nice way of doing HTTP requests for getting EDN, which
we can use to talk to the Datomic REST API.&lt;/p&gt;

&lt;p&gt;As we’ve seen, the REST API requires two parameters, &lt;code class=&quot;highlighter-rouge&quot;&gt;q&lt;/code&gt; the query,
and &lt;code class=&quot;highlighter-rouge&quot;&gt;args&lt;/code&gt;, any args to the query. Both are expressed using Clojure
data structures, but when transmitted they should be the URL encoded
EDN representations of this data. Unsurprisingly, the Closure Library
knows how to URL encode strings, and ClojureScript how to generate EDN
from Clojure data. We combine this knowledge in the &lt;code class=&quot;highlighter-rouge&quot;&gt;encode&lt;/code&gt; function:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;goog.string/urlEncode&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pr-str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To perform queries, we need to know where the URL of the REST API,
which storage it uses and the database we wish to query. We introduce
a &lt;code class=&quot;highlighter-rouge&quot;&gt;connect&lt;/code&gt; function that given those parameters returns a map with
all information we need to perform multiple queries.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest-api-url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest-api-url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Calling it &lt;code class=&quot;highlighter-rouge&quot;&gt;connect&lt;/code&gt; is a bit of a misnomer given what it currently
does. I’ll discuss this in the next section.&lt;/p&gt;

&lt;p&gt;We can now implement a &lt;code class=&quot;highlighter-rouge&quot;&gt;q&lt;/code&gt; function with an interface that is
&lt;em&gt;superficially&lt;/em&gt; quite similar to what we have in the Clojure/Java
API:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;select-keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get-edn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/api/query&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;?q=&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;args=&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Finally, we can perform the same query as above from ClojureScript:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://localhost:9000&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;free&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;codeq&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; perform a query
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:find&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;?e&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;?v&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                            &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:where&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;?e&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/doc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;?v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;the-database-as-a-value-&quot;&gt;The Database as a Value (?)&lt;/h1&gt;

&lt;p&gt;It should be noted that the interface of the &lt;code class=&quot;highlighter-rouge&quot;&gt;q&lt;/code&gt; function is actually
quite different from the native API. We perform the query directly on
a connection, rather than a database value. This means we lose the
stable basis, which is a big deal. Every query will execute on
whatever the latest value of database is when the query is received.&lt;/p&gt;

&lt;p&gt;However, the Datomic REST API supports specifying the value of the
database you wish the query to be performed against, and it is also
possible to query for the current version. This means that it is
possible to implement the notion of a database value even in the
browser. In the interest of keeping this post to a reasonable length,
I’ll stop here.&lt;/p&gt;

&lt;!-- I have hopefully shown a way of communicating with practical --&gt;
&lt;!-- application of using core.async for regaining control of your program --&gt;
&lt;!-- flow, and how --&gt;

&lt;p&gt;are completely awesome about Datomic, and you should give it a try and
find out what they are.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://skillsmatter.com/podcast/scala/the-language-of-the-system&quot;&gt;The Language of the System&lt;/a&gt;
talk for a very good motivation for this.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:awesome&quot;&gt;
      &lt;p&gt;This sentence quickly sweeps over at least two things that &lt;a href=&quot;#fnref:awesome&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:json-quiz&quot;&gt;
      &lt;p&gt;Hop forward to ~38:10 of Rich Hickeys &lt;a href=&quot;#fnref:json-quiz&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
     </entry>
   
 
   
     <entry>
       <title>Exploring enlive</title>
       <link href="http://ragnard.github.io/2013/04/19/exploring-enlive.html"/>
       <updated>2013-04-19T00:00:00+00:00</updated>
       <id>http://ragnard.github.io/2013/04/19/exploring-enlive</id>
       <content type="html">&lt;p&gt;Templating is perhaps not the most interesting topic in software
engineering, but nevertheless a useful technique in practice. Whether
you are generating HTML for a web application or creating a PDF from
the latest TPS&lt;sup id=&quot;fnref:tps-report&quot;&gt;&lt;a href=&quot;#fn:tps-report&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; report, some kind of templating might just
make your life better.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/cgrand/enlive&quot;&gt;enlive&lt;/a&gt; is a clojure templating
library created by &lt;a href=&quot;http:///www.cgrand.net&quot;&gt;Christophe Grand&lt;/a&gt;. It is
not a general purpose templating solution, rather it is specifically
targeted at generating HTML&lt;sup id=&quot;fnref:xml-too&quot;&gt;&lt;a href=&quot;#fn:xml-too&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. While this may sound very
limiting, it is simply a tradeoff that also enables other things,
giving enlive some of its power.&lt;/p&gt;

&lt;p&gt;We’ve given enlive a go in one of our internal clojure applications at
&lt;a href=&quot;http://www.uswitch.com/careers/&quot;&gt;work&lt;/a&gt; for a couple of months and I think it
has been an interesting experience.&lt;/p&gt;

&lt;p&gt;Besides being a very usable templating library for clojure
applications, I think enlive provides a model that is quite
interesting in itself, worth exploring simply for educational
purposes.&lt;/p&gt;

&lt;p&gt;With this series of posts, I hope to collect and share some of my
thoughts and creations around enlive and its usage.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2013/04/19/exploring-enlive-peek-behind-curtain.html&quot;&gt;A Peek Behind the Curtain&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2013/04/19/exploring-enlive-haml.html&quot;&gt;enlive, meet haml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;lack of namespace support.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:tps-report&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;http://www.youtube.com/watch?v=Fy3rjQGc6lA&quot;&gt;http://www.youtube.com/watch?v=Fy3rjQGc6lA&lt;/a&gt; &lt;a href=&quot;#fnref:tps-report&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:xml-too&quot;&gt;
      &lt;p&gt;XML is doable too, although it is somewhat limited due to the &lt;a href=&quot;#fnref:xml-too&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
     </entry>
   
 
   
     <entry>
       <title>Exploring enlive: A Peek Behind the Curtain </title>
       <link href="http://ragnard.github.io/2013/04/19/exploring-enlive-peek-behind-curtain.html"/>
       <updated>2013-04-19T00:00:00+00:00</updated>
       <id>http://ragnard.github.io/2013/04/19/exploring-enlive-peek-behind-curtain</id>
       <content type="html">&lt;p&gt;This post is part of a small series of posts on enlive. See
&lt;a href=&quot;/2013/04/19/exploring-enlive.html&quot;&gt;Exploring Enlive&lt;/a&gt; for the
background.&lt;/p&gt;

&lt;h1 id=&quot;understanding-enlive&quot;&gt;Understanding enlive&lt;/h1&gt;

&lt;p&gt;The fundamental model implemented by enlive is slightly different from
many other templating solutions&lt;sup id=&quot;fnref:xslt&quot;&gt;&lt;a href=&quot;#fn:xslt&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, and, while it is not
necessarily more complicated than other models, I think that
understanding it well is key to a good enlive experience. With this
post, I hope to share some realisations that helped me understand
enlive better.&lt;/p&gt;

&lt;p&gt;I will begin by simply stating the following minimal enlive example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initech.reports&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;net.cgrand.enlive-html&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enlive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enlive/deftemplate&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tps-report&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tps-report.html&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report-data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:head&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enlive/content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TPS Report for&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:date&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report-data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Depending on your background, some of this may make sense, some of it
may be more cryptic. The interesting part of the above snippet is the
contents of the &lt;code class=&quot;highlighter-rouge&quot;&gt;deftemplate&lt;/code&gt; form, which can be read as:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;give me a function called &lt;code class=&quot;highlighter-rouge&quot;&gt;tps-report&lt;/code&gt; that uses &lt;code class=&quot;highlighter-rouge&quot;&gt;tps-report.html&lt;/code&gt;
as its template, and when given some &lt;code class=&quot;highlighter-rouge&quot;&gt;report-data&lt;/code&gt; finds all &lt;code class=&quot;highlighter-rouge&quot;&gt;title&lt;/code&gt;
elements that are descendants of &lt;code class=&quot;highlighter-rouge&quot;&gt;head&lt;/code&gt; elements (hopefully only
one), and replaces their content with a nice string with the date
from the &lt;code class=&quot;highlighter-rouge&quot;&gt;report-data&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s see how all this is actually accomplished.&lt;/p&gt;

&lt;h1 id=&quot;the-enlive-way&quot;&gt;The Enlive Way&lt;/h1&gt;

&lt;p&gt;The process of constructing a document using enlive can be divided
into the following three steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Start with one or more source documents&lt;/li&gt;
  &lt;li&gt;Pick the parts you want from the source document(s)&lt;/li&gt;
  &lt;li&gt;Modify and/or put the parts together, forming a new document&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;enlive-data&quot;&gt;Enlive Data&lt;/h2&gt;

&lt;p&gt;The first step is parsing the source documents into what I’ll call
&lt;em&gt;enlive data&lt;/em&gt;. This is the fundamental data structure used by enlive
in the other two steps. While knowing about this data structure is not
strictly required, I think it is very helpful for understanding the
last step, and it also gives you more power as a user of the library.&lt;/p&gt;

&lt;p&gt;A source document is simply a plain old HTML file, no special
funny-looking markup or tags. It only serves as a base (or template,
if you will) from which you can pick as much, or as little as you want
for your output document.&lt;/p&gt;

&lt;p&gt;The data structure for &lt;em&gt;enlive data&lt;/em&gt; is a very simple tree,
represented using nested maps. Each individual map is referred to as a
&lt;em&gt;node&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;example&quot;&gt;Example&lt;/h3&gt;

&lt;p&gt;This HTML snippet:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Warning!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wat&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Invalid use of robot&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Will be translated into the following enlive data:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:div&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:attrs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:h1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:attrs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Warning&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:attrs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Invalid use of robot&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As illustrated, a &lt;em&gt;node&lt;/em&gt; map has the keys &lt;code class=&quot;highlighter-rouge&quot;&gt;tag&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;attrs&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;content&lt;/code&gt;. There are a few other node types, for example used to
represent a doctype, but you will rarely need to know about those.&lt;/p&gt;

&lt;h2 id=&quot;selection&quot;&gt;Selection&lt;/h2&gt;

&lt;p&gt;The second step is picking the parts of the template that you wish to
use or affect. So, given some &lt;em&gt;enlive data&lt;/em&gt;, we want to select a
subset of the &lt;em&gt;nodes&lt;/em&gt; for some further processing.&lt;/p&gt;

&lt;p&gt;Expressed more succinctly, selection is a function of &lt;em&gt;enlive data&lt;/em&gt; to
a sequence of &lt;em&gt;nodes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This function could have been expressed using plain clojure, but since
enlive targets HTML, it can leverage a more domain specific language
for this purpose:
&lt;a href=&quot;http://www.w3.org/TR/CSS2/selector.html&quot;&gt;CSS selectors&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;CSS selectors where initially conceived for CSS stylesheets, where
they are used to determine which elements certain style attributes
should apply to. However, they are more general than that, it is
really a pattern language for matching DOM elements. For example,
&lt;a href=&quot;http://www.jquery.org&quot;&gt;jQuery&lt;/a&gt; uses CSS selectors to select a subset
of DOM elements for some function application.&lt;/p&gt;

&lt;p&gt;The enlive implementation of selectors is not strictly equivalent to
CSS selectors, but most patterns translate quite easily. In enlive, a
selector is represented using clojure data rather than strings.
Christophe has a good explanation on the syntax in the
&lt;a href=&quot;https://github.com/cgrand/enlive#selectors-101&quot;&gt;readme&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;example-1&quot;&gt;Example&lt;/h3&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; match all p elements that are descendants of the div element with id &#39;message&#39;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:div#message&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; match all p elements that have a &#39;wat&#39; class
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:p.wat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; match the second tr of all tables
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:table&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nth-of-type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;transformation&quot;&gt;Transformation&lt;/h2&gt;

&lt;p&gt;The final step is where the action happens.&lt;/p&gt;

&lt;p&gt;Each &lt;em&gt;selector&lt;/em&gt; has a corresponding &lt;em&gt;transformation&lt;/em&gt;, they are
specified in pairs. Since selection is a function that selects
(returns) a sequence of &lt;em&gt;nodes&lt;/em&gt;, it may not be surprising to learn
that &lt;em&gt;transformation&lt;/em&gt; is a function that is applied to each of the
selected nodes. This will yield a new document where each &lt;em&gt;node&lt;/em&gt; has
been “replaced” by the result of the transformation function. Thus,
&lt;em&gt;transformation&lt;/em&gt; is a function from a single node to a single node, or
to a sequence of nodes.&lt;/p&gt;

&lt;p&gt;Let’s take a look at a specific part of the initial example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:head&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enlive/content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TPS Report for&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:date&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report-data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The body of a &lt;code class=&quot;highlighter-rouge&quot;&gt;deftemplate&lt;/code&gt; form specifies pairs of selectors (a
vector) and transformations. In this case we have one selector,
&lt;code class=&quot;highlighter-rouge&quot;&gt;[:head :title]&lt;/code&gt; and its corresponding transformation. So what does
the &lt;code class=&quot;highlighter-rouge&quot;&gt;enlive/content&lt;/code&gt; function actually do?&lt;/p&gt;

&lt;p&gt;Looking at the enlive source code, you would find something
almost&lt;sup id=&quot;fnref:content-source&quot;&gt;&lt;a href=&quot;#fn:content-source&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So, &lt;code class=&quot;highlighter-rouge&quot;&gt;enlive/content&lt;/code&gt; returns a new function that when given a node,
&lt;code class=&quot;highlighter-rouge&quot;&gt;assoc&lt;/code&gt;s a new value for the &lt;code class=&quot;highlighter-rouge&quot;&gt;:content&lt;/code&gt; of that node. Functions
returning functions can be hard to grasp at first glance, but we could
just as easily declare the same transformation inline in our template:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:head&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                   &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TPS Report for&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:date&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report-data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Understanding selectors and transformations at this level made a great
difference for me.&lt;/p&gt;

&lt;p&gt;Most
&lt;a href=&quot;https://github.com/cgrand/enlive#transformations&quot;&gt;transformations&lt;/a&gt;
provided by enlive are really utility functions that help you
&lt;em&gt;construct&lt;/em&gt; appropriate transformation functions. These functions will
take you far without having to write your own transformation, but
knowing what really goes on is always powerful.&lt;/p&gt;

&lt;h1 id=&quot;moving-on&quot;&gt;Moving On&lt;/h1&gt;

&lt;p&gt;Hopefully, this post has given some insight into the fundamental
workings of enlive. The next, and perhaps most difficult step, is to
understand how to best use the constructs enlive provides to actually
get what you want; a pretty HTML document.&lt;/p&gt;

&lt;p&gt;This may require a change of thinking. Some things which you know are
trivial in your favourite templating language might seem verbose when
you try to implement them using enlive. While enlive is definitely
more suited for certain scenarios, and less for others, I’ve often
found that this perceived difficulty is more a result of how you’re
used to think about templating, than anything else.&lt;/p&gt;

&lt;p&gt;You simply need to approach the problem in a different way, much like
the way you change your thinking when moving from object-oriented
programming to functional programming.&lt;/p&gt;

&lt;p&gt;This might be the topic of a future post.&lt;/p&gt;

&lt;p&gt;that scare you.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:xslt&quot;&gt;
      &lt;p&gt;Funnily enough it is quite similair to XSLT, but don’t let &lt;a href=&quot;#fnref:xslt&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:content-source&quot;&gt;
      &lt;p&gt;But not quite: &lt;a href=&quot;https://github.com/cgrand/enlive/blob/69075cdb1983a5d6f4487fcdfaa21314c44080b5/src/net/cgrand/enlive_html.clj#L626&quot;&gt;actual implementation&lt;/a&gt; &lt;a href=&quot;#fnref:content-source&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
     </entry>
   
 
   
     <entry>
       <title>Exploring enlive: enlive, meet haml</title>
       <link href="http://ragnard.github.io/2013/04/19/exploring-enlive-haml.html"/>
       <updated>2013-04-19T00:00:00+00:00</updated>
       <id>http://ragnard.github.io/2013/04/19/exploring-enlive-haml</id>
       <content type="html">&lt;p&gt;This post is part of a small series of posts on enlive. See
&lt;a href=&quot;/2013/04/19/exploring-enlive.html&quot;&gt;Exploring Enlive&lt;/a&gt; for the
background.&lt;/p&gt;

&lt;h1 id=&quot;in-search-of-lost-joy&quot;&gt;In Search of Lost Joy&lt;/h1&gt;

&lt;p&gt;Out of the box, enlive supports only HTML documents as the source for
templates. When we started using enlive, one thing we quickly
re-discovered was how incredibly dull and cumbersome it is to actually
work with HTML.&lt;/p&gt;

&lt;p&gt;One popular templating language that aims to remedy this is
&lt;a href=&quot;http://haml.info&quot;&gt;haml&lt;/a&gt;, marketed as a “Beautiful, DRY,
well-indented, clear markup” for creating HTML-like documents. A quick
taste for the uninitiated:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-haml&quot; data-lang=&quot;haml&quot;&gt;&lt;span class=&quot;nf&quot;&gt;#message&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;%h1&lt;/span&gt; Warning!
  &lt;span class=&quot;nt&quot;&gt;%p&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.wat&lt;/span&gt; Invalid use of robot&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;div class=&quot;center&quot;&gt;↓&lt;/div&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Warning!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wat&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Invalid use of robot&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;However, in all its beauty, haml has slightly a less exciting model
for actually generating dynamic content. Like many other templating
solutions, it allows you to embed and execute arbitrary code in your
template.&lt;/p&gt;

&lt;p&gt;Nothing particularly wrong with that, but it would be interesting to
see if one could leverage the terse way of describing a HTML-like
document provided by haml with the enlive model for transformation and
composition.&lt;/p&gt;

&lt;h1 id=&quot;a-seed-is-planted&quot;&gt;A Seed is Planted&lt;/h1&gt;

&lt;p&gt;Unsurprisingly, we were not the first ones to experience the pain of
HTML when using enlive, and other people have explored ways of
marrying it with haml.&lt;/p&gt;

&lt;p&gt;Two noteworthy approaches:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://twitter.com/nonrecursive&quot;&gt;Daniel Higginbotham&lt;/a&gt; explains how
to use &lt;a href=&quot;http://middlemanapp.com&quot;&gt;Middleman&lt;/a&gt; to comfortably generate
HTML from haml templates out of band, and then use the generated HTML
in an enlive template: &lt;a href=&quot;http://www.flyingmachinestudios.com/programming/dissecting-gratefulplace/&quot;&gt;blog post&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://d.hatena.ne.jp/yokolet/&quot;&gt;Yoko Harada&lt;/a&gt; uses jRuby and the
haml gem to generate HTML:
&lt;a href=&quot;http://yokolet.blogspot.co.uk/2011/09/haml-on-clojure-web-app.html&quot;&gt;blog post&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think both approaches are interesting and have their strengths and
weaknesses, but my main concern with both of them was the added
ceremony and slightly awkward integration.&lt;/p&gt;

&lt;p&gt;My starting point when exploring this idea further was this: Given an
enlive template definition, such as:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deftemplate&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tps-report&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tps-report.html&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report-data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Would it be possibly to somehow just specify a haml document as source
instead, and have it all work out nicely:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deftemplate&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tps-report&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tps-report.haml&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; &amp;lt;- OMG, different extension!
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report-data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After a quick look in the enlive source the idea seemed quite
possible. All that was needed was, given a source, provide enlive with
some &lt;a href=&quot;/2013/04/19/exploring-enlive-peek-behind-curtain.html#enlive_data&quot;&gt;&lt;em&gt;enlive data&lt;/em&gt;&lt;/a&gt;, and leave it to do its magic.&lt;/p&gt;

&lt;p&gt;Also, since the goal is to use enlive for the transformation and
composition, we don’t care about the parts of haml that concern
embedded code.&lt;/p&gt;

&lt;p&gt;Given this limitation, how hard can it be to write something that
reads a haml document, skips any code related content, and translates
it into enlive data?&lt;/p&gt;

&lt;h1 id=&quot;enter-hamelito&quot;&gt;Enter hamelito&lt;/h1&gt;

&lt;p&gt;Not entirely trivial, it turned out, but quite fun and a good hacking
project for my daily commute:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;http://github.com/ragnard/hamelito&quot;&gt;hamelito&lt;/a&gt; is a clojure library
that allows you to use a subset of haml as the source for enlive
templates&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the
&lt;a href=&quot;https://github.com/ragnard/hamelito/blob/master/src/com/github/ragnard/hamelito/parser.clj&quot;&gt;parsing&lt;/a&gt;,
I use excellent parser combinator library
&lt;a href=&quot;http://github.com/blancas/kern&quot;&gt;kern&lt;/a&gt; by Armando Blancas. A pleasure
to learn and very well documented.&lt;/p&gt;

&lt;p&gt;With exceptionally good timing to my little effort, Christophe also
&lt;a href=&quot;http://github.com/cgrand/enlive/commit/e69925f62a2c8c240b352c76ef5be5040c38f350&quot;&gt;added&lt;/a&gt;
support for &lt;em&gt;pluggable parsers&lt;/em&gt; to enlive, defining an interface for
libraries like &lt;em&gt;hamelito&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blahonga&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;net.cgrand.enlive-html&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enlive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;com.github.ragnard.hamelito.enlive&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;haml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; To use haml for all templates in the current namespace:
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enlive/set-ns-parser!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;haml/parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deftemplate&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tps-report&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tps-report.haml&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; &amp;lt;- mission accomplished!
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report-data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; To use haml for a specific template/snippet:
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enlive/deftemplate&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;haml-template&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:parser&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;haml/parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;template.haml&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;exit-html&quot;&gt;Exit HTML?&lt;/h1&gt;

&lt;p&gt;Once hamelito was capable enough, we converted our application to use
hamelito and haml templates. The actual conversion took exactly four
key-strokes and a mouse-click per template&lt;sup id=&quot;fnref:conversion&quot;&gt;&lt;a href=&quot;#fn:conversion&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, thanks to
&lt;a href=&quot;http://html2haml.heroku.com&quot;&gt;html2haml.heroku.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;hamelito is still young and I’m sure that throwing more haml documents
at it will expose bugs. If you’re curious, please go ahead and try it,
I’d be happy for any comments or bug reports.&lt;/p&gt;

&lt;p&gt;but was good when experimenting.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:conversion&quot;&gt;
      &lt;p&gt;copy, paste, click convert, copy, paste &lt;a href=&quot;#fnref:conversion&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
     </entry>
   
 
 
</feed>
