Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the namespace gotchas for clojurescript when coming from clojure?

I'm trying to understand the namespacing model in clojurescript. I understand that javascript doesn't come built in with namespace support, so its been an add on via the google closure library. However, I don't understand how clojurescript deals with these differences. Can someone please explain?

Lets say for example I want to use the google charts api and whenever possible would like to use advanced compilation. What do I use in the closure/build call, how would the ns form look like and what's the best way to refer to the api from the code? I've looked at https://github.com/sritchie/contour but that example doesn't use advanced compilation, and I don't see the options referring to the google api in the project.clj file.

like image 425
bmillare Avatar asked Mar 15 '12 15:03

bmillare


1 Answers

The namespace declaration is the same regardless of whether you are using simple or advanced mode compilation. The difference is that libraries that don't participate in google dependency management via goog.require('') and goog.provide('') need to be explictly referenced via an externs.js file. This file, which you pass to the cljs compiler, provides stubs of all the vars and associated methods. A typical externs.js would look something like:

// contents of externs.js
var externalLibrary = {}
var externalLibrary.method = function() {}

So to reference a google closure aware library your namespace declaration looks like this:

(ns my.namespace
  (:require  [goog.events.KeyCodes :as key-codes])

And that emits the following javascript:

goog.provide("my.namespace");
goog.require("goog.events.keycodes");

Note that if you want to call your closurescript from another javascript file then you need to tag the method as exported so that the optimizing compiler knows to preserve the name. (e.g. you might have a script tag on a page from where you want to call a javascript function that has been generated from clojurescript)

That looks like this:

(defn ^:export externallyCalled [])

The other thing to be aware of is that you have to treat macros differently in clojurescript, this is because the ability to process them 'on the fly' in the reader is not present in the same way as it is in clojure.

you do that as follows (note that you are obliged to bind the macros with :as in this case)

(ns my.namespace
   (:require-macros [my.macros :as my]))

If you are calling code that didn't participate in google's dependency management you access it via the js namespace and the javascript interop...

(. js/nonGoogle (method args)) ; a method access

(. js/nonGoogle -p)  ; a property access 

all the usual . variants work too..

like image 61
sw1nn Avatar answered Oct 16 '22 09:10

sw1nn