On Jul 31, 2011, at 11:00 PM, Jack Moffitt wrote:

I'm having some trouble attempting to use interop with ClojureScript.
I'm trying to translate examples from the Closure book into
ClojureScript, and I keep getting stuck on various things.

1) When using goog.testing, it appears that I can't access
goog.testing.TestRunner and goog.testing.TestCase unless those
specific namespaces are imported as well.  For example:

(ns example.core
 (:require [goog.testing :as testing]))

(defn ^:export start-test []
 testing/TestCase))

If i call start_test() from the browser repl it returns null. If I add
[goog.testing.TestCase :as tc] to the requires, without changing
start-test, then the code works fine.


Yes, you'll have to :require all the components/files of a 'namespace' and 'class' in order to use them from ClojureScript with Clojure-like idioms. Since TestCase is in a different file, you need to require it as well. You have to look at each lib to see the approach it takes, e.g. some function oriented libs are self contained while 'class-like' ones have a file per 'class'.

2) Trying to launch the TestRunner fails if i do it in a function, but
works if I return the runner and call it from the browser.

(defn ^:export start-test []
 (let [runner (testing/TestRunner.)
       test (testing/TestCase. "foo")]
   (.add test (tc/Test. "test-html-escaping"
                        test-html-escaping
                        goog.global))
   (.initialize runner test)
   ((.execute runner))))

This will throw an exception that the TestRunner is not initialized.
However, if I change ((.execute runner)) to runner, then call
"start_test().execute()" it runs perfectly.  I can only guess that the
this object is not getting set correctly for some reason.

Note that changing the last line to (. runner (execute)) also works.


(.execute runner) currently returns the value in the 'execute' slot of runner, that value being a function object. Wrapping it in parens then calls that function with no arguments (and thus no target object, i.e. this) and cannot work.

(. runner (execute)) disambiguates the slot and forces it to be considered as a method, i.e. a call. On the JVM, methods are not first- class functions in fields, and we can use the type system to determine if (. target member) (with no arguments) is a method call or a field access. In JS, it is always potentially either, and there is no way with a single syntax to determine which interpretation is desired, thus the need to disambiguate.

(. target (method)) works, but is awkward, and, since it is so infrequently used in Clojure proper, is not idiomatic.

One alternative is to interpret (. obj member), and thus (.member obj) as a call always, but that begs the question as to field/slot access. Something new will be required, and it will have to be added to Clojure to allow for portable code. Changing Clojure was out of scope for the first version, but I'd like to improve this ASAP.

One option is (. target :slot), possibly with the not-so-great (.:slot target) as well.

Thoughts everyone?

3) Is there some way to export a symbol to be global? For example,
goog.testing.jsunit will automatically search the global namespace for
test functions, but I can't figure out (aside from js*) how to stick
something in there.  I basically created start-test above to get
around this.

There is no global namespace in ClojureScript, but you can use goog.global as a proxy (with some restrictions) for the JS global ns. I'm not familiar with the requirements of goog.testing.jsunit, but have you tried putting tests in goog.global. i.e.

(set! goog.global.mytest my-test)

Rich

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to