a nice wrapper to handle rhino from clojure
marianoguerra
the java api for rhino is not really nice
go to clojars page for instructions:
https://clojars.org/org.marianoguerra/clj-rhino
you can see the tests for some usage, here are some REPL examples:
user=> (require '[clj-rhino :as js]) nil user=> (def sc (js/new-safe-scope)) #'user/sc user=> (js/eval sc "1 + 1") 2 user=> (js/eval sc "a = 1 + 1") 2 user=> (js/get sc "a") 2 user=> (js/get sc "b") #<UniqueTag org.mozilla.javascript.UniqueTag@172897f: NOT_FOUND> user=> (js/undefined? (js/get sc "b")) true user=> (js/get sc "b" :w00t?) :w00t? user=> (js/defined? (js/get sc "b")) false user=> (js/set! sc "b" 42) nil user=> (js/defined? (js/get sc "b")) true user=> (js/get sc "b" :w00t?) 42 user=> (js/eval sc "a = {name: 'spongebob'}") #<NativeObject [object Object]> user=> (js/get-in sc [:a :name]) "spongebob" user=> (js/get-in sc [:a :age]) #<UniqueTag org.mozilla.javascript.UniqueTag@172897f: NOT_FOUND> user=> (js/get-in sc [:a :age] :dont-know) :dont-know user=> (def compiled-fun (js/compile-function sc "function (a, b) { return a + b; }" :filename "foo.js")) #'user/compiled-fun user=> (js/set! sc "add" compiled-fun) nil user=> (js/eval sc "add(1, 3)") 4.0
handy convertions:
user=> (def get-field (js/compile-function sc "function getField(obj, field) { return obj[field]; }")) #'user/get-field user=> (js/set! sc "getField" get-field) nil user=> (js/set! sc "obj" (js/with-context #(js/to-js {:name "spongebob"} sc %))) nil user=> (js/eval sc "getField(obj, 'name')") "spongebob" user=> (js/eval sc "o = {name: 'spongebob', friends: ['patrick', 'sandy']};") #<NativeObject [object Object]> 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
exposing objects with functions:
user=> (require '[clj-rhino :as js]) nil user=> (defn add [ctx scope this [a b]] (+ a b)) #'user/add user=> (defn mul [ctx scope this [a b]] (* a b)) #'user/mul user=> (def scope (js/new-safe-scope)) #'user/scope user=> (def api {:add add :multiply mul}) #'user/api user=> (js/with-context (fn [ctx] #_=> (js/set! scope "api" (js/to-js api scope ctx)) #_=> (js/eval scope "api.add(1, 2) + api.multiply(2, 3)"))) 9.0
or if you don't need the internal parameters:
(js/with-context (fn [ctx] (let [scope (js/new-safe-scope) add (fn [a b] (+ a b))] (js/set! scope "add" (js/wrap-plain-fn add)) (js/eval scope "add(1, 2)") 3.0)))
execute code with timeout:
(def scope (js/new-safe-scope)) (js/eval-timeout scope "while (true);" 1000) ; will throw a Error exception
call function with timeout:
user=> (def f (js/compile-function scope "function () { while(true); }")) #'user/f user=> (js/call-timeout scope f 1000) TimeOutError org.marianoguerra.rhino.TimedContextFactory.observeInstructionCount (TimedContextFactory.java:47) ; function that doesn't timeout user=> (def f1 (js/compile-function scope "function (a, b) { return a + b; }")) #'user/f1 user=> (js/call-timeout scope f1 1000 1 3) 4.0
it seems the clojure people under this circumstances say something like:
Copyright © 2013 marianoguerra
Distributed under the Eclipse Public License, the same as Clojure.