support exposing clojure functions to rhino

This commit is contained in:
Mariano Guerra 2013-01-09 16:26:14 +01:00
parent 3e8419f935
commit 2fc12781ef
3 changed files with 72 additions and 33 deletions

View file

@ -81,6 +81,23 @@ handy convertions::
user=> (js/from-js (js/eval sc "o = {name: 'spongebob', friends: ['patrick', 'sandy']};"))
{:friends ["patrick" "sandy"], :name "spongebob"}
exposing clojure functions to rhino::
user=> (require '[clj-rhino :as js])
nil
user=> (defn add [ctx scope this [a b]] (+ a b))
#'user/add
user=> (def scope (js/new-safe-scope))
#'user/scope
user=> (js/set! scope "add" (js/make-fn add))
nil
user=> (js/eval scope "add(1, 2)")
3.0
license?
--------

View file

@ -1,6 +1,7 @@
(ns clj-rhino
(:refer-clojure :exclude (eval get get-in set!))
(:import [org.mozilla.javascript Context UniqueTag NativeArray NativeObject]))
(:import [org.mozilla.javascript Context UniqueTag NativeArray NativeObject
BaseFunction]))
(defprotocol RhinoConvertible
(-to-rhino [object scope ctx] "convert a value to a rhino compatible type"))
@ -198,3 +199,13 @@
(.compileFunction ctx scope code
(or filename "<eval>")
(or line-number 1) sec-domain))))
(defn make-fn [fun]
"return an object that can be used as a function in rhino,
fun must receive the following arguments [ctx scope this args]
args will be passed through from-js before calling fun and the result
through to-js"
(proxy [BaseFunction] []
(call [ctx scope this args]
(to-js (fun ctx scope this (from-js args)) scope ctx))))

View file

@ -80,46 +80,49 @@
(is (= (js/get scope "b") false))))
(testing "to-js works"
(js/with-context (fn [ctx]
(let [scope (js/new-safe-scope)]
(js/with-context
(fn [ctx]
(let [scope (js/new-safe-scope)]
(is (= (js/to-js nil scope ctx) nil))
(is (= (js/to-js 1 scope ctx) 1))
(is (= (js/to-js 1/2 scope ctx) 0.5))
(is (= (js/to-js true scope ctx) true))
(is (= (js/to-js "foo" scope ctx) "foo"))
(is (= (js/to-js :foo scope ctx) "foo"))
(is (= (js/to-js nil scope ctx) nil))
(is (= (js/to-js 1 scope ctx) 1))
(is (= (js/to-js 1/2 scope ctx) 0.5))
(is (= (js/to-js true scope ctx) true))
(is (= (js/to-js "foo" scope ctx) "foo"))
(is (= (js/to-js :foo scope ctx) "foo"))
(check-array-to-js [] scope ctx)
(check-array-to-js [nil 1 1/2 true "foo" :foo] scope ctx)
(check-array-to-js '(nil 1 1/2 true "foo" :foo) scope ctx)
(check-array-to-js [] scope ctx)
(check-array-to-js [nil 1 1/2 true "foo" :foo] scope ctx)
(check-array-to-js '(nil 1 1/2 true "foo" :foo) scope ctx)
; check to-js-generic
(check-array-to-js (to-array [nil 1 1/2 true "foo" :foo]) scope ctx)
; check to-js-generic
(check-array-to-js (to-array [nil 1 1/2 true "foo" :foo]) scope ctx)
(is (= (get (js/to-js {:name "mariano"} scope ctx) "name") "mariano"))
(is (= (get (js/to-js {:name "mariano" :age 27} scope ctx) "age") 27))))))
(is (= (get (js/to-js {:name "mariano"} scope ctx) "name") "mariano"))
(is (= (get (js/to-js {:name "mariano" :age 27} scope ctx) "age") 27))))))
(testing "to-js works on complex nested structures"
(js/with-context (fn [ctx]
(let [scope (js/new-safe-scope)
obj {:name "bob"
:age 27
:tags ["yellow" :sponge ["a" :b 3.2]]
:friends {:sandy "squirrel"
:patrick "star"}}
js-obj (js/to-js obj scope ctx)]
(js/with-context
(fn [ctx]
(let [scope (js/new-safe-scope)
obj {:name "bob"
:age 27
:tags ["yellow" :sponge ["a" :b 3.2]]
:friends {:sandy "squirrel"
:patrick "star"}}
js-obj (js/to-js obj scope ctx)]
(is (= (get js-obj "name") "bob"))
(is (= (get js-obj "age") 27))
(is (= (.get (get js-obj "tags") 1) "sponge"))
(is (= (.get (.get (get js-obj "tags") 2) 1) "b"))
(is (= (get-in js-obj ["friends" "sandy"]) "squirrel"))))))
(is (= (get js-obj "name") "bob"))
(is (= (get js-obj "age") 27))
(is (= (.get (get js-obj "tags") 1) "sponge"))
(is (= (.get (.get (get js-obj "tags") 2) 1) "b"))
(is (= (get-in js-obj ["friends" "sandy"]) "squirrel"))))))
(testing "unknown types throw exception when converting to-js"
(js/with-context (fn [ctx]
(let [scope (js/new-safe-scope)]
(is (thrown? Exception (js/to-js (atom {}) scope ctx)))))))
(js/with-context
(fn [ctx]
(let [scope (js/new-safe-scope)]
(is (thrown? Exception (js/to-js (atom {}) scope ctx)))))))
(testing "from-js works"
(is-identity nil)
@ -154,5 +157,13 @@
(assert-simetric-convertion {:b {"c" {:d [{"e" 4}]}}} scope ctx
{:b {:c {:d [{:e 4}]}}})
))))
)
(testing "native clojure functions can be added"
(js/with-context
(fn [ctx]
(let [scope (js/new-safe-scope)]
(js/set! scope "add" (js/make-fn (fn [ctx scope this [a b]] (+ a b))))
(is (= (js/eval scope "add(1, 2)") 3.0))))))
)