Skip to content
This repository has been archived by the owner on Jan 24, 2018. It is now read-only.

Quick Start

题叶 edited this page Jun 19, 2016 · 1 revision

Cumulo is designed for tiny chat web apps. Maybe you want to explore by yourself:

What is Cumulo?

If you want to build realtime web apps, you should probably try one of them:

Cumulo is an experimental project for build really small web apps. It's slow and immature. But you can try it in your toy app if you like.

Before writing code

I will suppose you have experiences on:

My packages maintains WebSockets internally but make sure you can handle them. On the browser-side you get a store, it's learnt from React.

Dataflow in Cumulo

Read the docs below and compare it to React:

db_0 = {states: {}, users: {}, messages: {}}
# get `action` from network
db_n+1 = updater(db_n, action)

scene = renderScene(db)
store = renderStore(scene, user_id)

changes = diff(store_n, store_n+1)
# send `changes` over network
clientStore_n+1 = patch(clientStore_n, changes)

updater is a pure function, like reducer in Redux, or updater in Elm. renderScene is merely part of renderStore but abstracted out for performance purpose. You should be familiar with diff patch already by learning React.

Start a server

You need a package [cumulo/server "0.1.0"], here's a demo:

(ns cumulo-server.main
  (:require [cumulo-server.schema :as schema]
            [cumulo-server.core :refer [setup-server! reload-renderer!]]
            [cumulo-server.updater.core :refer [updater]]
            [cumulo-server.view :refer [render-view render-scene]]))

(defonce db-ref (atom schema/database))

(defn -main []
  (setup-server! db-ref updater render-scene render-view {:port 4010}))

(set! *main-cli-fn* -main)

(defn on-jsload []
  (reload-renderer! @db-ref updater render-scene render-view))

reload-renderer! is to handle code swapping, might looks strange though.

You may be wondering about updater, try reading this example:

(ns cumulo-server.updater.core
  (:require [cumulo-server.updater.state :as state]
            [cumulo-server.updater.task :as task]))

(defn updater [db op op-data state-id op-id op-time]
  (case op
    :state/connect    (state/connect    db op-data state-id op-id op-time)
    :state/disconnect (state/disconnect db op-data state-id op-id op-time)
    :task/add         (task/add         db op-data state-id op-id op-time)
    :task/rm          (task/rm          db op-data state-id op-id op-time)
    db))

I have to say states in db is special since Cumulo code wants to read it to decide whom to send messages. Make sure state/connect and state/disconnect are handled like below. My code does not give warning if your script is wrong, my bad.

(ns cumulo-server.updater.state
  (:require [cumulo-server.schema :as schema]))

(defn connect [db op-data state-id op-id op-time]
  (assoc-in db [:states state-id] (merge schema/state {:id state-id})))

(defn disconnect [db op-data state-id op-id op-time]
  (update db :states (fn [state] (dissoc state state-id))))

render-view returns the store you want in a browser:

(ns cumulo-server.view)

(defn render-scene [db] db-scene)

(defn render-view [state-id db-scene]
  {:states (get-in db-scene [:states state-id]), :tasks (:tasks db)})

To create a server, adding updaters and render-view should be enough.

Start a client

You will need this package on client [cumulo/client "0.1.0"] https://github.com/Cumulo/cumulo-client/

(ns cumulo-client.main
  (:require [respo-spa.core :refer [render]]
            [cumulo-client.component.container :refer [comp-container]]
            [cumulo-client.core :refer [send! setup-socket!]]))

(defonce store-ref (atom {}))
(defonce states-ref (atom {}))

(defn dispatch [op op-data] (send! op op-data))

(defn render-app []
  (let [target (.querySelector js/document "#app")]
    (render (comp-container @store-ref) target dispatch states-ref)))

(defn -main []
  (render-app)
  (setup-socket! store-ref {:url "ws://localhost:4010"})
  (add-watch store-ref :changes render-app)
  (add-watch states-ref :changes render-app))

(set! js/window.onload -main)

(defn on-jsload []
  (render-app))

Now you get a store that's synced with the server. No need to maintain state by yourself. In this demo the store is rendered with Respo, which is one of my libraries. I would suggest you using Reagent instead since Respo is another piece of code to learn.

More

I got ideas on Cumulo less than 2 years ago. But I have to say it's still in early stage. You may witness bugs. I just believe it's simple and straight forward enough a library for you to figure out.

Contact me on Twitter if you got problems in Cumulo.

Clone this wiki locally