From 64d1e119665c26efacb2e49927297f945f568c87 Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Fri, 25 Sep 2015 11:48:50 +0200 Subject: [PATCH] Add obj and munging to interop .' and .! now behaves just like builtin interop, except that they work the same under advanced compilation. --- src/reagent/interop.clj | 37 +++++++++++++++++++++++++--- test/reagenttest/testinterop.cljs | 40 +++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/reagent/interop.clj b/src/reagent/interop.clj index 5b8ff44..64f67e9 100644 --- a/src/reagent/interop.clj +++ b/src/reagent/interop.clj @@ -20,16 +20,44 @@ names (-> (if (symbol? member) (string/replace n #"^-" "") n) - (string/split #"\."))] + (string/split #"\.")) + names (map munge names)] [field? names])) +(defmacro obj + "Create a javascript object in a Closure-safe way. The arguments + are expected to be key-value pairs, where the keys should be either + keywords or strings. + + (obj :foo 1) + is equivalent to + (let [o (js-obj)] + (set! (.-foo o) 1) + o) + except that it gives the same result under advanced compilation." + [& args] + (assert (= 0 (mod (count args) 2)) + "Even number of arguments expected") + (let [munged (map-indexed (fn [i x] + (if (odd? i) + x + (if (keyword? x) + (munge (name x)) + x))) + args)] + `(cljs.core/js-obj ~@munged))) + (defmacro .' "Access member in a javascript object, in a Closure-safe way. 'member' is assumed to be a field if it is a keyword or if the name starts with '-', otherwise the named function is called with the optional args. 'member' may contain '.', to allow access in nested objects. - If 'object' is a symbol it is not allowed contain '.'." + If 'object' is a symbol it is not allowed contain '.'. + + (.' o :foo) is equivalent to (.-foo o), except that it gives + the same result under advanced compilation. + (.' o foo arg1 arg2) is the same as (.foo o arg1 arg2)." [object member & args] (let [[field names] (dot-args object member)] (if field @@ -43,7 +71,10 @@ "Set field in a javascript object, in a Closure-safe way. 'field' should be a keyword or a symbol starting with '-'. 'field' may contain '.', to allow access in nested objects. - If 'object' is a symbol it is not allowed contain '.'." + If 'object' is a symbol it is not allowed contain '.'. + + (.! o :foo 1) is equivalent to (set! (.-foo o) 1), except that it + gives the same result under advanced compilation." [object field value] (let [[field names] (dot-args object field)] (assert field (str "Field name must start with - in " field)) diff --git a/test/reagenttest/testinterop.cljs b/test/reagenttest/testinterop.cljs index 441bf76..f4b327b 100644 --- a/test/reagenttest/testinterop.cljs +++ b/test/reagenttest/testinterop.cljs @@ -1,19 +1,28 @@ (ns reagenttest.testinterop (:require [cljs.test :as t :refer-macros [is deftest]] [reagent.debug :refer-macros [dbg]] - [reagent.interop :refer-macros [.' .!]])) + [reagent.interop :refer-macros [.' .! obj]])) +(def is-adv (let [o #js{}] + (set! (.-somethinglong o) true) + (not= (aget (.keys js/Object o) 0) "somethinglong"))) (deftest iterop-quote - (let [o #js{:foo "foo" - :foobar #js{:bar "bar"} - :bar-foo "barfoo"}] + (let [o (obj :foo "foo" + :foobar (obj :bar "bar" + :bar-foo "bar-foo") + :bar-foo "barfoo")] + (is (= "foo" (.' o :foo))) (is (= "bar" (.' o :foobar.bar))) (is (= "barfoo" (.' o :bar-foo))) + (when-not is-adv + (is (= "barfoo" (.-bar-foo o)))) (is (= "foo" (.' o -foo))) (is (= "bar" (.' o -foobar.bar))) + (is (= "bar-foo" (.' o -foobar.bar-foo))) + (is (= "bar-foo" (.' o :foobar.bar-foo))) (is (= "barfoo" (.' o -bar-foo))) (.! o :foo "foo1") @@ -48,3 +57,26 @@ (is (= "1bar2" (.' (.' o :foo) call o 1))))) + +(deftest interop-munge + (let [o (obj :foo-bar "foo-bar" + :foo? "foo?" + :foo$ "foo$" + :foo! "foo!" + :foo><*+% "foo><*+%")] + (is (= (.' o :foo-bar) "foo-bar")) + (is (= (.' o :foo?) "foo?")) + (is (= (.' o :foo$) "foo$")) + (is (= (.' o -foo!) "foo!")) + (is (= (.' o :foo><*+%) "foo><*+%")) + + (when-not is-adv + (is (= (.-foo-bar o) "foo-bar")) + (is (= (.-foo? o) "foo?")) + (is (= (.-foo$ o) "foo$")) + (is (= (.-foo><*+% o) "foo><*+%")) + (let [x (js-obj)] + (.! x -foo-bar "foo-bar") + (is (= (.-foo-bar x) "foo-bar")) + (set! (.-foo? x) "foo?") + (is (= (.' x :foo?) "foo?"))))))