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.
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..
Related
I am new to Closure and trying out Ring and Compojure. I want to make an HTTP request when a user hits a route (to a third party API) and then use that response data in my HTML template
. I know this is likely a very easy thing to pull off - but being new to language and the syntax I am a bit lost.
(defroutes app
(GET "/" request
; try to GET "https://third-party-api" and do something with the response
)
)
What is the best practice and format for this - It's possible I am missing some key concepts in routing / response expectations here. Thanks very much!
I recommend the library clj-http for making http requests. You can find many examples on the linked page for how to use it.
Your usage of clj-http may look something like this:
(ns my-app.core
(:require [clj-http.client :as client]))
...
(defn get-api-data []
(:body (client/get "https://third-party-api" {:as :json})))
Note that clj-http.client/get returns a map that includes things like the response status code and headers.
If you use the {:as :json} option to coerce the response into json, make sure to include cheshire in your project.clj (assuming you're using leiningen)
:dependencies [...
[clj-http "3.9.0"]
[cheshire "5.8.0"]]
Documentation on ring requests and responses can be found here.
A large portion of the power in ring is its concept of middlewares. Most of the "nice" features that you'd want in an http server can be found as middlewares in ring itself or other libraries. For example, if you want all of your responses to be serialized as json by default, you might use ring-json
If you're trying to get something "that just works", up and running quickly with a few examples present, Luminus may be useful. It's a curated collection of libraries that prove useful for most webservers. (Disclaimer: I've only minimally experimented with Luminus, opting to understand more explicitly my dependencies).
I personally use compojure sweet at the start of most of my webservice projects, it includes some nicer routing features (including path params) and a swagger UI for testing your endpoints. Unfortunately, it uses its own form of destructuring and includes a bit more magic and "just needing to know" than I'd like, but I'm yet to find something that works better for me.
This may actually be a bit of an XY-problem, so I'll try to explain what the goal is first.
I'm building a ClojureScript application which is composed of a set of Reagent components. It provides a user interface where you can dynamically add or remove UI elements. These UI elements (components) have a certain type. For example a Markdown component is-a Text component. Whenever the user is presented with the option to add Text we list all the components that match the type+descendants (in this case Markdown, there could be others).
The way I've coded it up is as follows.
Each component is in its own namespace, this namespace contains a builder function that returns the new component. At the root of the namespace it also calls (derive ::type ::parent)
now in some different namespace we require and enumerate all of these components in a map like:
(ns app.components
(:require
[app.gui.markdown :as markdown]
[app.gui.study-list :as study-list]))
(def all
{markdown/t markdown/builder
study-list/t study-list/builder})
Where the /t refers to the namespace-qualified keyword which was used to define the hierarchy. We use the all map to provide the data for the menu's that face the user (which components can be added, filtered by type).
Now, as you can imagine, this isn't pretty. Since it must now maintain such a (potentially) long list of all the types in the hierarchy manually.
Rather I would do something like (def all (components-of (descendants ::root))) but I'm unsure how to tackle this, since I think it would require finding vars by name (not supported in ClojureScript).
So my question is: how do you maintain a map or list of namespaces + vars (dynamically) in ClojureScript?
You cannot do this dynamically, but as far as I can tell for your problem there isn't much need. ClojureScript macros can reflect back into the compiler - you could easily use the cljs.analyzer.api namespace to figure out which vars you need (through var metadata or whatever) and automatically emit the runtime info map you want. This is in fact very similar to how cljs.test/run-tests works. It uses the compiler to filter out all vars in all namespaces with :test metadata attached and it generates the code to test each one. It's worth examining cljs.test in detail to see how this can be done.
Coming from Haskell, my usual workflow would be to :l <file name.hs> on ghci and use the functions and ADT that I have there.
Right now I am using lein repl on a typical lein new app project context. I have created a testing.clj file next to my core.clj. There I defined a couple of functions, a protocol and a record implementing the protocol. I was able to use the function by (use 'testing.testing :reload) the problem is that I am not able to use the actual record:
(def c (Something. 0))
I get:
CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: Something
So, what would be the a "better" workflow in this case? Where I don't want to set the functions, protocols, records directly on the REPL, but also I don't want to rely on my core.cls file? I just want a file where I can dump a bunch of stuff and play with it.
PS: My env is Mac OSX Terminal + Sublime
Edit: After a couple of minutes I was able to load the record by:
(load-file <file name>)
(import 'testing.testing.Something)
I mean, for sure there is a better way than this... :/ I just want to load everything. On the other hand I am able to use the protocol methods the record implements.
Have you tried using the convenience function that is automatically defined for creating records? In this example it would be (->Something 0).
(Something. 0) is using the Java constructor, which requires importing the Java class separately. The Java class is created automatically when you define a record to allow Java interop with things you've defined in Clojure.
Using the (->Something 0) syntax is the correct way to go and should be possible after you (use 'testing.testing :reload).
Edit Given the above didn't seem to help, here's some step-by-step instructions to get a minimal working example
You have an app directory testing created with lein new app testing
In testing/src/testing you create testing.clj containing the following two lines
(ns testing.testing)
(defrecord Something [n])
Run lein repl from within your project directory
Use the namespace with (use 'testing.testing :reload)
(:n (->Something 42)) will create an instance of Something and retrieve the value of its n member - in this case 42.
I have created a record like this:
(defrecord User [user-id email])
:but when I try to access it from another name-space I get the error:
(User. "name" "email")
java.lang.IllegalArgumentException: Unable to resolve classname: User
It works when I do:
(oe.model.modelcore.User. "name" "email")
: I know I will need to import the Java class, but is there any way for clojure to do this automatically when I do:
(use 'oe.model.modelcore :reload)
Technically you must require it (don't necessarily need to use it) so that the file containing the record definition is compiled and the class is created. Then you must import it so that it is available to construct as a Java class. If you create a constructor function in the first ns like
(defn new-user [id email]
(User. id email))
then you will not need to import it in the calling ns.
I wrote this up a while back here:
http://tech.puredanger.com/2010/06/30/using-records-from-a-different-namespace-in-clojure/
It could be really tricky if you have - (dash) in your name space.
As it turns out there were two errors:
– Importing defrecord from another namespace is not just :use. I had to first :require the namespace, then import the defrecord. This was a trivial problem to solve and I figured it out quickly. Only this did not work in my case
– Dashes “-” and Underscores “_” are a nuisance since we are mixing Lisp with Java. While the file system uses underscores Clojure converts things to dashes. Brilliant.
So to fix the second error I use the follow in the ns block
(ns adder.core
(:require building-blocks.activity)
(:import [building_blocks.activity Activity]))
https://cyrax.wordpress.com/2013/07/22/clojure-importrequireuse-defrecord-from-another-namespace/
In your question you are creating a record, then invoking the constructor for the class generated as a side effect. To do so you need to import that class as mentioned in another answer.
However, imho the preferred path (since Clojure 1.4) is to use the constructor functions generated by defrecord (here they'll be named ->User and map->User). These functions allow you to avoid the interop forms and just refer in the constructor functions like any other function. By avoiding interop, this is a more portable less-hosty solution:
(ns some-ns
(:require [oe.model.modelcore :refer (->User)]))
(def user (->User "name" "email"))
You have to import the record like this:
(ns some-ns
(:import [oe.model.modelcore User]))
Given a java package x.y.z, can I alias x.y.z to a shorter name, so that i can then refer to java classes inside the package as my-alias.MyJavaClass.
If that isn't possible, I could just import all classes into my namespace, but I don't want to specify the names of each class manually, and the clojure API docs doesn't seem clear on whether it's possible to import all classes in a package automatically.
There isn't any functionality for this (yet). Java packages are in a different namespace than Clojure's, so the usual alias tricks won't work.
What you can do is import each class, which lets you avoid the full package+class name:
(import [java.io File Writer Reader])
(new File "/")
; #<File />
Rich provides his reasons for not supporting (import [java.io.*]) here.