Skip to content

Commit 4f8dec0

Browse files
committed
[new] Update ref example to add dynamic packer
Handy for testing all packers under all modes, and for folks writing new packers.
1 parent f8219e0 commit 4f8dec0

File tree

3 files changed

+166
-94
lines changed

3 files changed

+166
-94
lines changed

example-project/src/example/client.cljs

Lines changed: 67 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
11
(ns example.client
22
"Official Sente reference example: client"
3-
{:author "Peter Taoussanis (@ptaoussanis)"}
4-
53
(:require
64
[clojure.string :as str]
7-
[cljs.core.async :as async :refer [<! >! put! chan]]
8-
[taoensso.encore :as encore :refer-macros [have have?]]
9-
[taoensso.timbre :as timbre :refer-macros []]
10-
[taoensso.sente :as sente :refer [cb-success?]]
11-
12-
;; Optional, for Transit encoding:
13-
[taoensso.sente.packers.transit :as sente-transit])
5+
[cljs.core.async :as async]
6+
[taoensso.encore :as encore]
7+
[taoensso.timbre :as timbre]
8+
[taoensso.sente :as sente]
9+
[example.dynamic-packer]))
1410

15-
(:require-macros
16-
[cljs.core.async.macros :as asyncm :refer [go go-loop]]))
11+
;;;; Logging
1712

18-
;;;; Logging config
19-
20-
(defonce min-log-level_ (atom nil))
21-
(defn- set-min-log-level! [level]
13+
(defonce min-log-level_ (atom nil))
14+
(defn- set-min-log-level! [level]
2215
(sente/set-min-log-level! level) ; Min log level for internal Sente namespaces
2316
(timbre/set-ns-min-level! level) ; Min log level for this namespace
2417
(reset! min-log-level_ level))
2518

26-
(when-let [el (.getElementById js/document "sente-min-log-level")]
27-
(let [level (if-let [attr (.getAttribute el "data-level")]
28-
(keyword attr)
29-
:warn)]
30-
(set-min-log-level! level)))
19+
;;;; Init config
20+
21+
(def init-config
22+
"{:keys [csrf-token min-log-level packer-mode]}"
23+
(when-let [el (.getElementById js/document "init-config")]
24+
(when-let [edn (.getAttribute el "data-edn")]
25+
(encore/read-edn edn))))
3126

32-
;;;; Util for logging output to on-screen console
27+
(set-min-log-level! (get init-config :min-log-level))
28+
(reset! example.dynamic-packer/mode_ (get init-config :packer-mode))
29+
30+
;;;; On-screen console
3331

3432
(let [output-el (.getElementById js/document "output")]
3533
(defn- ->output!! [x]
@@ -44,18 +42,15 @@
4442

4543
(->output! "ClojureScript has successfully loaded")
4644
(->output! "Sente version: %s" sente/sente-version)
47-
(->output! "Min log level: %s (use toggle button to change)" @min-log-level_)
45+
(->output! "Init config: %s" init-config)
4846
(->output!)
4947

5048
;;;; Define our Sente channel socket (chsk) client
5149

52-
(def ?csrf-token
53-
(when-let [el (.getElementById js/document "sente-csrf-token")]
54-
(.getAttribute el "data-token")))
55-
56-
(if ?csrf-token
57-
(->output! "CSRF token detected in HTML, great!")
58-
(->output! "**IMPORTANT** CSRF token NOT detected in HTML, default Sente config will reject requests!"))
50+
(def ?csrf-token (get init-config :csrf-token))
51+
(if ?csrf-token
52+
(->output! "CSRF token in init config, great!")
53+
(->output! "**IMPORTANT** no CSRF token in init config, default Sente config will reject requests!"))
5954

6055
(def chsk-type
6156
"We'll select a random protocol for this example"
@@ -64,17 +59,26 @@
6459
(->output! "Randomly selected chsk type: %s" chsk-type)
6560
(->output!)
6661

67-
(let [;; Serializtion format, must use same val for client + server:
68-
packer :edn ; Default packer, a good choice in most cases
69-
;; (sente-transit/get-transit-packer) ; Needs Transit dep
62+
(def packer
63+
"Sente uses \"packers\" to control how values are encoded during
64+
client<->server transit.
65+
66+
Default is to use edn, but this reference example uses a dynamic
67+
packer that can swap between edn/transit/binary for testing.
7068
71-
{:keys [chsk ch-recv send-fn state]}
72-
(sente/make-channel-socket-client!
73-
"/chsk" ; Must match server Ring routing URL
74-
?csrf-token
75-
{:type chsk-type
76-
:packer packer})]
69+
Client and server should use the same packer."
7770

71+
#_:edn ; Default
72+
(example.dynamic-packer/get-packer))
73+
74+
(def chsk-client
75+
(sente/make-channel-socket-client!
76+
"/chsk" ; Must match server Ring routing URL
77+
?csrf-token
78+
{:type chsk-type
79+
:packer packer}))
80+
81+
(let [{:keys [chsk ch-recv send-fn state]} chsk-client]
7882
(def chsk chsk)
7983
(def ch-chsk ch-recv) ; ChannelSocket's receive channel
8084
(def chsk-send! send-fn) ; ChannelSocket's send API fn
@@ -100,7 +104,7 @@
100104

101105
(defmethod -event-msg-handler :chsk/state
102106
[{:as ev-msg :keys [?data]}]
103-
(let [[old-state-map new-state-map] (have vector? ?data)]
107+
(let [[old-state-map new-state-map] (encore/have vector? ?data)]
104108
(cond
105109
;; Tip: look for {:keys [opened? closed? first-open?]} in `new-state-map` to
106110
;; easily identify these commonly useful state transitions
@@ -155,8 +159,8 @@
155159
(fn [ev]
156160
(chsk-send! [:example/toggle-broadcast-loop] 5000
157161
(fn [cb-reply]
158-
(when (cb-success? cb-reply)
159-
(let [enabled? cb-reply]
162+
(when (sente/cb-success? cb-reply)
163+
(let [enabled? cb-reply]
160164
(if enabled?
161165
(->output! "Server broadcast loop now ENABLED")
162166
(->output! "Server broadcast loop now DISABLED")))))))))
@@ -188,29 +192,37 @@
188192
(when-let [target-el (.getElementById js/document "btn-toggle-logging")]
189193
(.addEventListener target-el "click"
190194
(fn [ev]
191-
(chsk-send! [:example/toggle-min-log-level] 5000
195+
(chsk-send! [:example/toggle-log-level] 5000
196+
(fn [cb-reply]
197+
(when (sente/cb-success? cb-reply)
198+
(let [new-level cb-reply]
199+
(set-min-log-level! new-level)
200+
(->output! "New log level: %s" new-level))))))))
201+
202+
(when-let [target-el (.getElementById js/document "btn-toggle-packer")]
203+
(.addEventListener target-el "click"
204+
(fn [ev]
205+
(chsk-send! [:example/toggle-packer] 5000
192206
(fn [cb-reply]
193-
(if (cb-success? cb-reply)
194-
(let [level cb-reply]
195-
(set-min-log-level! level)
196-
(->output! "New minimum log level (client+server): %s" level))
197-
(->output! "Request failed: %s" cb-reply)))))))
207+
(when (sente/cb-success? cb-reply)
208+
(let [new-mode cb-reply]
209+
(reset! example.dynamic-packer/mode_ new-mode)
210+
(->output! "New packer mode: %s" new-mode))))))))
198211

199212
(when-let [target-el (.getElementById js/document "btn-toggle-bad-conn-rate")]
200213
(.addEventListener target-el "click"
201214
(fn [ev]
202215
(chsk-send! [:example/toggle-bad-conn-rate] 5000
203216
(fn [cb-reply]
204-
(if (cb-success? cb-reply)
205-
(->output! "New rate: %s" cb-reply)
206-
(->output! "Request failed: %s" cb-reply)))))))
217+
(when (sente/cb-success? cb-reply)
218+
(->output! "New rate: %s" cb-reply)))))))
207219

208220
(when-let [target-el (.getElementById js/document "btn-connected-uids")]
209221
(.addEventListener target-el "click"
210222
(fn [ev]
211223
(chsk-send! [:example/connected-uids] 5000
212224
(fn [cb-reply]
213-
(when (cb-success? cb-reply)
225+
(when (sente/cb-success? cb-reply)
214226
(->output! "Connected uids: %s" cb-reply)))))))
215227

216228
(when-let [target-el (.getElementById js/document "btn-login")]
@@ -249,17 +261,17 @@
249261
(->output!)
250262
(->output! "Will rapidly change user-id from \"1\" to \"10\"...")
251263
(let [c (async/chan)]
252-
(go-loop [uids (range 11)]
264+
(async/go-loop [uids (range 11)]
253265
(when-let [[next-uid] uids]
254266
(sente/ajax-call "/login"
255267
{:method :post
256268
:headers {:X-CSRF-Token (:csrf-token @chsk-state)}
257269
:params {:user-id (str next-uid)}}
258270
(fn [ajax-resp]
259271
(when (:success? ajax-resp) (sente/chsk-reconnect! chsk))
260-
(put! c :continue)))
261-
(<! c)
262-
(<! (async/timeout 100))
272+
(async/put! c :continue)))
273+
(async/<! c)
274+
(async/<! (async/timeout 100))
263275
(recur (next uids))))))))
264276

265277
;;;; Init stuff
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
(ns example.dynamic-packer
2+
"A dynamic Sente packer that can dynamically switch
3+
between edn/transit/binary modes.
4+
5+
Handy for testing, you wouldn't normally need/want
6+
something like this in production!"
7+
(:require
8+
[taoensso.encore :as encore]
9+
[taoensso.sente :as sente]
10+
[taoensso.sente.interfaces :as i]
11+
[taoensso.sente.packers.transit]))
12+
13+
(defonce mode_ (atom :edn))
14+
15+
(defn- str->bytes [s]
16+
#?(:clj (.getBytes ^String s java.nio.charset.StandardCharsets/UTF_8)
17+
:cljs (.-buffer (.encode (js/TextEncoder.) s))))
18+
19+
(defn- bytes->str [b]
20+
#?(:clj (String. ^bytes b java.nio.charset.StandardCharsets/UTF_8)
21+
:cljs (.decode (js/TextDecoder. "utf-8") (js/Uint8Array. b))))
22+
23+
(comment (-> "foo" str->bytes bytes->str))
24+
25+
(defn get-packer []
26+
(let [ep sente/edn-packer
27+
tp (taoensso.sente.packers.transit/get-packer)
28+
29+
bp ; Simple binary edn packer
30+
(reify
31+
i/IPacker2
32+
(pack [_ ws? clj cb-fn] (cb-fn {:value (str->bytes (encore/pr-edn clj))}))
33+
(unpack [_ ws? packed cb-fn] (cb-fn {:value (encore/read-edn (bytes->str packed))})))
34+
35+
get-packer
36+
(fn []
37+
(case @mode_
38+
:edn ep
39+
:transit tp
40+
:bin bp))]
41+
42+
(reify
43+
i/IPacker2
44+
(pack [_ ws? clj cb-fn] (i/pack (get-packer) ws? clj cb-fn))
45+
(unpack [_ ws? packed cb-fn] (i/unpack (get-packer) ws? packed cb-fn)))))

0 commit comments

Comments
 (0)