• Stars
    star
    438
  • Rank 99,453 (Top 2 %)
  • Language
    Clojure
  • Created over 11 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A library designed to bridge the gap between the triad of CLJ/CLJS, web-sockets and core.async.

Chord

A lightweight Clojure/ClojureScript library designed to bridge the gap between the triad of CLJ/CLJS, web-sockets and core.async.

Usage

Include the following in your project.clj:

[jarohen/chord "0.8.1"]

Chord now supports EDN, JSON, Transit and Fressian out of the box - please remove dependencies to ‘chord-fressian’ and ‘chord-transit’ if you have them. Thanks to Luke Snape, Jeff Rose and Thomas Getgood for their help supporting these formats!

Example project

There is a simple example server/client project under the example-project directory. The client sends websocket messages to the server, that get echoed back to the client and written on the page.

You can run it with lein dev - an alias that starts up an http-kit server using Frodo and automatically re-compiles the CLJS.

Once it is running - navigate to http://localhost:3000/ and you should see Send a message to the server:

ClojureScript

Chord only has one function, chord.client/ws-ch, which takes a web-socket URL and returns a map, containing either :ws-channel or :error. When the connection opens successfully, this channel then returns a two-way channel that you can use to communicate with the web-socket server:

(:require [chord.client :refer [ws-ch]]
          [cljs.core.async :refer [<! >! put! close!]])
(:require-macros [cljs.core.async.macros :refer [go]])

(go
  (let [{:keys [ws-channel error]} (<! (ws-ch "ws://localhost:3000/ws"))]
    (if-not error
      (>! ws-channel "Hello server from client!")
      (js/console.log "Error:" (pr-str error)))))

Messages that come from the server are received as a map with a :message key:

(go
  (let [{:keys [ws-channel]} (<! (ws-ch "ws://localhost:3000/ws"))
        {:keys [message]} (<! ws-channel)]
    (js/console.log "Got message from server:" (pr-str message))))

Errors in the web-socket channel (i.e. if the server goes away) are returned as a map with an :error key:

(go
  (let [{:keys [ws-channel]} (<! (ws-ch "ws://localhost:3000/ws"))
        {:keys [message error]} (<! ws-channel)]
    (if error
      (js/console.log "Uh oh:" error)
      (js/console.log "Hooray! Message:" (pr-str message)))))

As of 0.3.0, you can pass a :format option, to pass messages over the channel as EDN (default), as raw strings, or JSON (0.3.1). Valid formats are #{:edn :json :json-kw :str :fressian :transit-json}, defaulting to :edn.

(If you do use fressian, you’ll need to require chord.format.fressian, in addition to the usual Chord namespaces)

(:require [cljs.core.async :as a])
(ws-ch "ws://localhost:3000/ws"
       {:format :json-kw})

As of 0.2.1, you can configure the buffering of the channel by (optionally) passing custom read/write channels, as follows:

(:require [cljs.core.async :as a])
(ws-ch "ws://localhost:3000/ws"
       {:read-ch (a/chan (a/sliding-buffer 10))
        :write-ch (a/chan 5)})

By default, Chord uses unbuffered channels, like core.async itself.

Clojure

Chord wraps the websocket support provided by http-kit, a fast Clojure web server compatible with Ring.

N.B. Currently, Ring’s standard Jetty adapter ~does not~ support Websockets. http-kit is a Ring-compatible alternative.

Again, there’s only one entry point to remember here: a wrapper around http-kit’s with-channel macro. The only difference is that, rather than using http-kit’s functions to interface with the channel, you can use core.async’s primitives.

Chord’s with-channel is used as follows:

(:require [chord.http-kit :refer [with-channel]]
          [org.httpkit.server :refer [run-server]]
          [clojure.core.async :refer [<! >! put! close! go]])

(defn your-handler [req]
  (with-channel req ws-ch
    (go
      (let [{:keys [message]} (<! ws-ch)]
        (prn "Message received:" message)
        (>! ws-ch "Hello client from server!")
        (close! ws-ch)))))

This can take a :format option, and custom buffered read/write channels as well:

(require '[clojure.core.async :as a])

(defn your-handler [req]
  (with-channel req ws-ch
    {:read-ch (a/chan (a/dropping-buffer 10))
         :format :str} ; again, :edn is default
    (go
      (let [{:keys [message]} (<! ws-ch)]
        (prn "Message received:" message)
        (>! ws-ch "Hello client from server!")
        (close! ws-ch)))))

You can also use the wrap-websocket-handler middleware, which will put a :ws-channel key in the request map:

(require '[chord.http-kit :refer [wrap-websocket-handler]]
         '[org.httpkit.server :refer [run-server]]
         '[clojure.core.async :as a])

(defn your-handler [{:keys [ws-channel] :as req}]
  (go
    (let [{:keys [message]} (<! ws-channel)]
      (println "Message received:" message)
      (>! ws-channel "Hello client from server!")
      (close! ws-channel))))

(run-server (-> #'your-handler wrap-websocket-handler) {:port 3000})

You can pass custom channels to wrap-websocket-handler as a second (optional) parameter:

(run-server (-> #'your-handler
              (wrap-websocket-handler {:read-ch ...}))
            {:port 3000})

Bug reports/pull requests/comments/suggestions etc?

Yes please! Please submit these in the traditional GitHub manner.

Contributors

Chord’s contributors are listed in the ChangeLog - thank you all for your help!

License

Copyright © 2013-2015 James Henderson

Distributed under the Eclipse Public License, the same as Clojure.

More Repositories

1

chime

A really lightweight Clojure scheduler
Clojure
537
star
2

nomad

A configuration library designed to allow Clojure applications to travel painlessly between different environments.
Clojure
213
star
3

yoyo

Yo-yo is a protocol-less, function composition-based alternative to Component
Clojure
87
star
4

phoenix

A plugin for configuring, co-ordinating and reloading Components
Clojure
69
star
5

simple-brepl

A really simple plugin to start CLJS browser REPLs, built atop 'Weasel'
Clojure
44
star
6

flow

Lightweight library to help you write dynamic CLJS webapps
Clojure
38
star
7

frodo

A lein plugin to start a Ring server via configuration in Nomad
Clojure
38
star
8

clidget

Clidget is a lightweight CLJS state utility that allows you to build UIs through small, composable ‘widgets’.
Clojure
24
star
9

graph-zip

A zipper library for Clojure that navigates graph structures
Clojure
19
star
10

bounce

Bounce is a protocol-less, function composition-based alternative to Component
Clojure
17
star
11

oak

A ClojureScript library to structure single-page apps - taking inspiration from the Elm Architecture
Clojure
13
star
12

with-open

A one-macro library to extend the behaviour of Clojure's 'with-open' without having to implement Closeable.
Clojure
12
star
13

splat

A Leiningen template to create ClojureScript single page web applications.
Clojure
12
star
14

selectable

An example CLJS app replicating David Nolan's 'CSP is Responsive Design' blog, with a slightly different approach.
Clojure
11
star
15

cljs-tetris

A Tetris clone written in ClojureScript + core.async
Clojure
8
star
16

embed-nrepl

A micro-library to start up an nREPL server with my opinionated defaults
Clojure
6
star
17

advent-of-code

Clojure
6
star
18

clojurex2013

My slides and references from 'Putting the Blocks Together' at ClojureX 2013
CSS
3
star
19

neo-zip

A Clojure library to query Neo4J databases using Graph-Zip syntax
Clojure
3
star
20

wiring

A Clojure library to configure and wire-up component-based applications
Clojure
2
star
21

multi-chat

A very basic multi-player chat webapp written using ClojureScript, WebSockets and Chord
Clojure
2
star
22

dot-emacs

My Emacs dotfiles
Emacs Lisp
1
star
23

eclj

A Clojure library to execute Clojure forms embedded inside text files - similar to ERB.
Clojure
1
star
24

ghost-middleware

Some Ring middleware to handle Google's AJAX shebang spec
Clojure
1
star
25

adventures

Clojure
1
star
26

datomic-arch-package

An attempt to package up Datomic on Arch Linux
Shell
1
star