defromeo

Hoplon, Storage and Stem Cell

Today we explore a little storage utility from Hoplon, and build some logic of our game.

Our data is simple vector of 14 numbers, and initial value is:

1
2
(def init-board-value
    [0, 6, 6, 6, 6, 6, 6, 0, 6, 6, 6, 6, 6, 6])

Data will be saved in cell “stem cell”

1
(def state (cell init-board-value))

In HTML our board is simple old fashion table, and text macro will help insert data into it:

1
2
<td class="player1"><text>~(get state 1)</text></td>
<td class="player1"><text>~(get state 2)</text></td>

this text will update when we change our state cell.

Lets write some transitions function which change state. First function reset state to initial and second store random values:

1
2
3
4
5
6
(defn random-board []
  (reset! state
        (vec (repeatedly 14 #(rand-int 10)))))

(defn init-board []
  (reset! state init-board-value))

Simple. And we can add two button for using them with special attribere “on-click” which will take function for call when event received:

1
2
<button on-click="{ {  init-board }}">init</button>
<button on-click="{ {  random-board }}">random</button>

So this things documented in Getting Started Hoplon.io, and lets detailed see how we can change our state for persistent state which synced with HTML5 Local Storage.

Guys created simple function local-storage from tailrecursion.hoplon.storage-atom namespace. Lets see how we can use it:

1
2
3
(def state (->
             (cell init-board-value)
             (local-storage ::store)))

Lets dive into standart library, local-storage take cell and key for which associate data.

local-storagelink
1
2
3
(defn local-storage
  [atom k]
  (html-storage atom js/localStorage k))

local-storage is simple proxy who redirect input to html-storage and add variable js/localStorage.

html-storage will create StorageBackend in js/localStorage in some key:

html-storagelink
1
2
3
(defn html-storage
  [atom storage k]
  (store atom (StorageBackend. storage k)))

Object StorageBackend contains 2 function for getting value and for setting and using function pr-str and read-string for serializing data:

StorageBackendlink
1
2
3
4
5
6
7
8
(deftype StorageBackend [store key]
  IStorageBackend
  (-get [this not-found]
    (if-let [existing (.getItem store (pr-str key))]
      (read-string existing)
      not-found))
  (-commit! [this value]
    (.setItem store (pr-str key) (pr-str value))))

And now we ready to see function store:

storelink
1
2
3
4
5
6
7
8
(defn store
  [atom backend]
  (let [existing (-get backend ::none)]
    (if (= ::none existing)
      (-commit! backend @atom)
      (reset! atom existing))
    (doto atom
      (add-watch ::storage-watch #(-commit! backend %4)))))

In lines 3-4 code check if value is in our local storage, and if not than we save it with -commit! function of storage, if data is present then we get it, and update our state with it.

After in limes 7-8 we add watcher to cell, so when data in cell changed than data will be saved to localStorage.

add-watch standart Clojure and ClojureScript function and it work for all standart ClojureScript referenced data. But how it could work with cell? Cell type implement cljs.core/IWatchable protocol by adding some functions:

Celllink
1
2
3
4
5
6
7
  cljs.core/IWatchable
  (-notify-watches [this oldval newval]
    (doseq [[key f] watches] (f key this oldval newval)))
  (-add-watch [this key f]
    (set! (.-watches this) (assoc watches key f)))
  (-remove-watch [this key]
    (set! (.-watches this) (dissoc watches key)))

And -add-watch simple add key and watch function to hash object property watches. And -notify-watches will execute by reset! or swap!.

Last step for today is make easy our table by using loop-tpl which is take some indexes and create <td>.

1
2
3
<loop-tpl bindings="">
  <td class="player2"><text>~(get state i)</text></td>
</loop-tpl>

loop-tpl is not really easy stuff, ‘cause to understand it work, we shoud understand many concepts of Hoplon. And we will do that in future.

I will hold next article awhile, I need some thing to work for Hoplon.

Final web-application you can try at here. Sourch code.

First steps with Hoplon

Hoplon is great framework. I hope it will change our standart way in building web.

In next a few blog posts I will create Kalah game, and I’ll post my adventure here. To be prepared you should read excellent Getting Started Hoplon.io.

So today our task is create main menu and some blocks which we will see when certain menu chosen. These menu items will be connected with hash. Result is here.

HTML

We start with simple template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script type="text/hoplon">
  (page "index.html"
    (:require
      [tailrecursion.hoplon.util :as hutil]))
</script>
<html>
<body>
  <ul>
    <li>
      <a href="#/">Home</a>
    </li>
    <li>
      <a href="#/about">About</a>
    </li>
  </ul>

  <div>Home</div>
  <div>About</div>
</body>
</html>

Notice, we require tailrecursion.hoplon.util and will use route-cell from it for creating cell updated with current hash:

route-celllink
1
2
3
4
(defn route-cell [msec default]
  (let [hash  #(.-hash (.-location js/window))]
    (with-let [ret (cell (hash))]
      (interval #(let [h (hash)] (reset! ret (if (empty? h) default h))) msec))))

In our case with-let macro first create cell with current hash (cell (hash)), then run setInterval with function which will update our cell, after hash cell will be returned.

with-letlink
1
2
3
4
5
(defmacro with-let
  "Binds resource to binding and evaluates body.  Then, returns
  resource.  It's a cross between doto and with-open."
  [[binding resource] & body]
  `(let [~binding ~resource] ~@body ~binding))
intervallink
1
(defn interval  [f msec]  (.setInterval js/window f msec))

So with this library we only need in our index.html.hl:

1
(def route (hutil/route-cell 100 "#/"))

Links highlighting

I’m using bootstrap, and we need set active class for current menu item. So we should use do-class atribute which takes cell with hash-map like {:active true}do-class='{ { (cell {:active true}) }}'.

To depend on current hash we change it to formula cell:

1
2
3
<li do-class='{ {  (cell= {:active (= "#/" route)})  }}'>
  <a href="#/">Home</a>
</li>

We can make a little easy by creating boolean help-cell (there also example of macro defc=):

1
2
(def is-home? (cell= (= "#/" route)))
(defc= is-about? (= "#/about" route))

So now we can write links like this:

1
2
3
<li do-class='{ {  (cell= {:active is-about?}) }}'>
  <a href="#/about">About</a>
</li>

Hidden Content

Now we need only hide with hidden class our div which is not connected to current hash path.

And this is exactly like in above code with menu items (with and without our helper):

1
2
3
4
5
6
7
<div do-class='{ {  (cell= {:hidden (not= "#/" route)}) }}'>
Home
</div>

<div do-class='{ {  (cell= {:hidden (not is-about?)}) }}'>
About
</div>

That is all for now.

All sources

Hello En World

(say “hi”)

(def name “Romeo”)

So, I see you already found what programming language I like.

I’m engineer (not really like word developer). And I’m sure you seen already that I was born in non-english country.

I had a blog maybe for five years, but afterwards I understood that google know too much about me. It was personal and profession blog. So now I don’t wanna do that mistake, and this will be only profession blog, without my second name and this will save you from my life story.