I am creating a noir webapp, and I need to dynamically create new views and models. I've been following the noir examples, in which the view and the controller for a resource have separate namespaces, and I've found it to be a very clean approach.
In keeping with this, I need to be able to dynamically create new namespaces corresponding to views and models, and then intern the appropriate functions in them. My idea was to have macros specified in a separate namespace which, when called in the new namespace, would provide the appropriate routes/partials/whatever.
For example (forgive my first defmacro):
(ns project.views.proto
(:use noir.core
hiccup.core
hiccup.element
hiccup.form))
(defmacro def-all-page
[path]
`(defpage ~path []
(html
[:h1 "Ya'll here"])))
is called from...
(ns project.proto
(:use [clojure.contrib.with-ns :only [with-ns]])
(create-ns 'foo)
(intern 'foo 'path "path") ; In reality, the path is dynamic which is why I intern it
(with-ns 'foo
(clojure.core/refer-clojure)
(use 'noir.core
'hiccup.core
'hiccup.element
'[project.views.proto :only [def-all-page]])
(def-all-page path)
However, calling this from within my new namespace gives me a NullPointerException. I'd greatly appreciate any help, and whether or not there is a better approach. Like, just using refer for a namespace which contains all the necessary definitions?
First post, and I don't think it's a repeat of this. Thanks!
First of all, this question has become a bit outdated. Both Noir and Clojure have evolved over the last year. For clarity's sake i'll take Noir out of the equation and try to answer your question about dynamically creating functions using macros.
Follow along at the REPL:
$ lein repl
user=> (in-ns 'foo)
#<Namespace foo>
foo=> (clojure.core/refer-clojure)
nil
foo=> (defmacro say-hello-to
#_=> [name]
#_=> `(defn ~(symbol (str "hello-" name))
#_=> []
#_=> ~(str "hello: " name)))
#'foo/say-hello-to
Here we create a namespace 'foo' that contains a macro for defining 'hello-yourname' functions. Let's create another namespace:
foo=> (in-ns 'bar)
#<Namespace bar>
bar=> (clojure.core/refer-clojure)
nil
bar=> (refer 'foo :only '[say-hello-to])
nil
bar=> (say-hello-to "tom")
#'bar/hello-tom
bar=> (say-hello-to "jerry")
#'bar/hello-jerry
Let's see if these actually work:
bar=> (hello-tom)
"hello: tom"
bar=> (hello-jerry)
"hello: jerry"
I think this is pretty close to your original example.
Hope this helps!