Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cljs support for fressian. #1

Merged
merged 23 commits into from
Nov 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
(defproject io.replikativ/incognito "0.2.2"
(defproject io.replikativ/incognito "0.2.5-SNAPSHOT"
:description "Safe transport of unknown record types in distributed systems."
:url "https://github.com/replikativ/incognito"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.9.0-alpha14" :scope "provided"]
[org.clojure/clojurescript "1.9.946" :scope "provided"]
[org.clojure/data.fressian "0.2.1" :scope "provided"]
[com.cognitect/transit-clj "0.8.285" :scope "provided"]
[com.cognitect/transit-cljs "0.8.239" :scope "provided"]
[fress "0.3.1"]]

[com.cognitect/transit-cljs "0.8.239" :scope "provided"]]
:plugins [[lein-cljsbuild "1.1.7"]]

:profiles {:test {:dependencies [[clj-time "0.13.0"]]}})
:hooks [leiningen.cljsbuild]

:profiles {:dev {:dependencies [[com.cemerick/piggieback "0.2.1"]]
:figwheel {:nrepl-port 7888
:nrepl-middleware ["cider.nrepl/cider-middleware"
"cemerick.piggieback/wrap-cljs-repl"]}
:plugins [[lein-figwheel "0.5.8"]]}
:test {:dependencies [[clj-time "0.13.0"]]}}

:source-paths ["src"]
:test-paths ["test"]

:cljsbuild
{:test-commands {"unit-tests" ["node" "target/unit-tests.js"]}
:builds
{:tests
{:source-paths ["src" "test"]
:notify-command ["node" "target/unit-tests.js"]
:compiler {:output-to "target/unit-tests.js"
:optimizations :none
:target :nodejs
:main incognito.fressian-test}}}})
73 changes: 19 additions & 54 deletions src/incognito/base.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,28 @@

(defrecord IncognitoTaggedLiteral [tag value])

#_(defn pr-str->pure-read-string [r]
(let [r-str (pr-str r)]
#?(:cljs (binding [*tag-table* (atom (select-keys @cljs.reader/*tag-table*
["inst" "uuid" "queue" "js"]))
*default-data-reader-fn* (atom (fn [t v] [t v]))]
(read-string r-str))
:clj (read-string {:readers {} :default (fn [t v] [t v])} r-str))))
(defn rec-name-parser [handlers]
#?(:clj handlers
:cljs (into {} (map (fn [[k v]]
[(-> k
name
(clojure.string/replace-first #"(?s)(.*)(\.)" "$1/")
symbol)
v])
handlers))))

(defn incognito-reader [read-handlers m]
(if (read-handlers (:tag m))
((read-handlers (:tag m)) (:value m))
(map->IncognitoTaggedLiteral m)))
(if (read-handlers (:tag m))
((read-handlers (:tag m)) (:value m))
(map->IncognitoTaggedLiteral m)))

(defn incognito-writer [write-handlers r]
(let [s (-> r type pr-str symbol)
(let [write-handlers (rec-name-parser write-handlers)
s (-> r type pr-str symbol)
break-map-recursion (if (map? r) (into {} r) r)
[tag v] (if (write-handlers s)
[s ((write-handlers s) break-map-recursion)]
[s break-map-recursion]
#_(pr-str->pure-read-string r))]
{:tag tag
[tag v] (if (write-handlers s)
[s ((write-handlers s) break-map-recursion)]
[s break-map-recursion]
#_(pr-str->pure-read-string r))]
{:tag tag
:value v}))



(comment
(require '[clj-time.core :as t])
(require '[clj-time.format :as tf])

(incognito-writer {'org.joda.time.DateTime
(fn [r] (str r))}
(t/now))

(incognito-reader {'org.joda.time.DateTime
(fn [r] (t/date-time r))}
{:tag 'org.joda.time.DateTime, :value "2017-04-17T13:11:29.977Z"})





(type (t/now))


(t/now)

(defrecord Foos [a])

(into {} (map->Foos {:a 4}))

(cljs-type (map->Foos {:a 4}))



(incognito-writer {'incognito.base.Foos (fn [r] (assoc r :c "bananas"))}
(map->Foos {:a [1 2 3] :b {:c "Fooos"}}))

(incognito-writer {} (map->Foos {:a [1 2 3] :b {:c "Fooos"}}))

)
36 changes: 24 additions & 12 deletions src/incognito/fressian.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
(:require [clojure.edn :as edn]
[clojure.data.fressian :as fress]
[incognito.base :refer [incognito-reader incognito-writer]])
(:import [org.fressian.handlers WriteHandler ReadHandler]))

(:import [org.fressian StreamingWriter]
[org.fressian.handlers WriteHandler ReadHandler]))

(defn record-reader [read-handlers]
(reify ReadHandler
Expand Down Expand Up @@ -70,16 +70,16 @@

(defn incognito-read-handlers [read-handlers]
{"irecord" (record-reader read-handlers)
"plist" plist-reader
"pvec" pvec-reader
"set" set-reader})
"plist" plist-reader
"pvec" pvec-reader
"set" set-reader})

(defn incognito-write-handlers [write-handlers]
{clojure.lang.PersistentList {"plist" plist-writer}
{clojure.lang.PersistentList {"plist" plist-writer}
clojure.lang.PersistentList$EmptyList {"plist" plist-writer}
clojure.lang.IRecord {"irecord" (record-writer write-handlers)}
clojure.lang.LazySeq {"plist" plist-writer}
clojure.lang.PersistentVector {"pvec" pvec-writer}})
clojure.lang.IRecord {"irecord" (record-writer write-handlers)}
clojure.lang.LazySeq {"plist" plist-writer}
clojure.lang.PersistentVector {"pvec" pvec-writer}})


(comment
Expand All @@ -91,16 +91,28 @@

(with-open [baos (ByteArrayOutputStream.)]
(let [read-handlers (atom {'incognito.fressian.Foo map->Foo})
write-handlers (atom {'incognito.fressian.Foo (fn [foo] (println "foos") (assoc foo :c "donkey"))})
write-handlers (atom {'incognito.fressian.Foo (fn [foo]
(println "foos")
(assoc foo :c "donkey"))})
w (fress/create-writer baos :handlers (-> (merge fress/clojure-write-handlers
(incognito-write-handlers write-handlers))
fress/associative-lookup
fress/inheritance-lookup))] ;
(fress/write-object w #{1 2 3} #_(map->Foo {:a [1 2 3] :b {:c "Fooos"}}))
(fress/write-object w (map->Foo {:a [1 2 3] :b {:c "Fooos"}}))
(let [bais (ByteArrayInputStream. (.toByteArray baos))]
(type (fress/read bais
(prn (fress/read bais
:handlers (-> (merge fress/clojure-read-handlers
(incognito-read-handlers #_(atom {}) read-handlers))
fress/associative-lookup))))))











)
103 changes: 103 additions & 0 deletions src/incognito/fressian.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
(ns incognito.fressian
(:require [incognito.base :refer [incognito-reader incognito-writer IncognitoTaggedLiteral]]
[fress.api :refer [read-object write-tag write-object]]
[fress.reader :as r :refer [IFressianReader readInt]]
[fress.writer :as w :refer [IFressianWriter writeInt beginClosedList endList class-sym]]
[fress.impl.codes :as codes]
[cljs.reader :refer [read-string
*tag-table* *default-data-reader-fn*]]
[fress.impl.buffer :as buf]
[fress.impl.raw-input :as rawIn]))

;;add incognito record-writer-handlers
(defn record-writer
[write-handlers]
(fn [writer rec]
(let [{:keys [tag value] :as r} (if (isa? (type rec) incognito.base.IncognitoTaggedLiteral)
(into {} rec)
(incognito-writer @write-handlers rec))
tag (-> tag
str
(clojure.string/replace-first #"/" ".")
symbol)]
(write-tag writer "irecord" 2)
(writeInt writer (count value))
(write-object writer tag)
(doseq [e value]
(write-object writer e)))))

;;add incognito irecord-read-handler
(defn record-reader
[read-handlers]
(fn [reader tag component-count]
(let [len (readInt reader)
tag (read-object reader)
val (->> (range len)
(map (fn [_] (read-object reader)))
(map vec)
(into {}))]
(incognito-reader @read-handlers
{:tag tag :value val}))))

(defn- plist-reader [reader _ _]
(let [len (readInt reader)]
(->> (range len)
(map (fn [_] (read-object reader)))
reverse
(into '()))))

(defn- plist-writer [writer plist]
(write-tag writer "plist" 2)
(writeInt writer (count plist))
(doseq [e plist]
(write-object writer e)))

(defn- pvec-reader [reader _ _]
(let [len (readInt reader)]
(->> (range len)
(map (fn [_] (read-object reader)))
(into []))))

(defn- pvec-writer [writer pvec]
(write-tag writer "pvec" 2)
(writeInt writer (count pvec))
(doseq [e pvec]
(write-object writer e)))

(defn- set-reader [reader _ _]
(let [^List l (read-object reader)]
(into #{} l)))

(defn- write-tree-map [w m]
(write-tag w "map" 1)
(beginClosedList w)
(doseq [[field value] m]
(write-object w field true)
(write-object w value))
(endList w))

(defn incognito-read-handlers [read-handlers]
{"plist" plist-reader
"pvec" pvec-reader
"set" set-reader
"irecord" (record-reader read-handlers)})

(defn incognito-write-handlers [write-handlers]
{cljs.core/List plist-writer
cljs.core/EmptyList plist-writer
cljs.core/PersistentTreeMap write-tree-map
"record" (record-writer write-handlers)
cljs.core/LazySeq plist-writer
cljs.core/PersistentVector pvec-writer})

(comment

(do
(defrecord SomeRecord [f1 f2])
(def rec (SomeRecord. [1 2 2] {:c "213"}))
(def buf (fress.api/byte-stream))
(def writer (fress.api/create-writer buf :handlers (incognito-write-handlers (atom {'incognito.fressian.SomeRecord (fn [foo] (println "foos") (assoc foo :c "donkey"))}))))
(fress.api/write-object writer rec)
(fress.api/read buf :handlers (incognito-read-handlers (atom {}))))

)
41 changes: 41 additions & 0 deletions test/incognito/fressian_test.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
(ns incognito.fressian-test
(:require [cljs.test :refer-macros [deftest is testing run-tests async use-fixtures]]
[incognito.fressian :refer [incognito-read-handlers incognito-write-handlers]]))

(enable-console-print!)

(defrecord Bar [a b])

(deftest incognito-roundtrip-test
(testing "Test incognito transport."
(let [bar (map->Bar {:a [1 2 3] :b {:c "Fooos"}})
r (let [buffer (fress.api/byte-stream)

read-handlers (atom {'incognito.fressian-test.Bar map->Bar})
write-handlers (atom {'incognito.fressian-test.Bar
(fn [bar] (assoc bar :c "donkey"))})
w (fress.api/create-writer buffer :handlers (incognito-write-handlers write-handlers))]
(fress.api/write-object w bar)
(fress.api/read buffer :handlers (incognito-read-handlers (atom {}))))]
(is (= {:c "donkey", :b {:c "Fooos"}, :a [1 2 3]} (:value r)))
(is (= 'incognito.fressian-test.Bar (:tag r)))
(is (= incognito.base/IncognitoTaggedLiteral (type r))))))

(deftest double-roundtrip-test
(testing "Test two roundtrips, one incognito and deserialize at end."
(let [bar (map->Bar {:a [1 2 3] :b {:c "Fooos"}})
write-handlers (atom {})
read-handlers (atom {'incognito.fressian-test.Bar map->Bar})]
(is (= bar
(let [buffer (fress.api/byte-stream)
w (fress.api/create-writer buffer
:handlers
(incognito-write-handlers write-handlers))]
(fress.api/write-object w bar)
(fress.api/write-object w (fress.api/read buffer
:handlers
(incognito-read-handlers (atom {}))))
(fress.api/read buffer
:handlers (incognito-read-handlers read-handlers))))))))

(run-tests)