11

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!

Community
  • 1
  • 1
jtmoulia
  • 660
  • 8
  • 19
  • Would you consider editing your post, and denoting your name space and some sample Clojure code that invokes the macro? I'd like to see in what context it is used. tnx – octopusgrabbus Apr 22 '12 at 22:02
  • 1
    I hope that's a bit more helpful. – jtmoulia Apr 22 '12 at 22:29
  • The error I'm getting is Unable to resolve symbol: with-ns in this context, compiling, but I don't know if that means you can use with-ns outside a function or macro. – octopusgrabbus Apr 22 '12 at 22:38
  • After reading the ns docs a little more, I'm not sure why you're using both create-ns and intern. intern will add to the name space you already declared, but I still don't know why with-ns shows up as an unresolvable symbol, unless you're meant to use it in a function. – octopusgrabbus Apr 22 '12 at 22:44
  • Ahhh sorry, I neutered my ns dec when I typed it in: I use clojure.contrib.with-ns. I'll edit my post to reflect this when I get to my computer. As for intern, the posted code doesn't reflect this, but the path variable is outside of the created namespace so it needs to be explicitly added. At least, that was the justification I gave myself... – jtmoulia Apr 22 '12 at 23:03
  • 1
    The code in the clojure-contrib is deprecated and unsupported: http://dev.clojure.org/display/design/Where+Did+Clojure.Contrib+Go I'd suggest not using clojure.contrib.with-ns in that case. – astine May 09 '12 at 19:00
  • That's good to know... and perhaps a different library will point me to a more idiomatic way to perform this task. Thanks – jtmoulia May 16 '12 at 13:30
  • Did you solve this issue? Its an interesting problem. – hawkeye Nov 23 '12 at 12:27
  • Nope, and that project is gaining dust now. That being said, I'm still very interested in the answer also... – jtmoulia Dec 14 '12 at 23:04
  • I'm actually curious about this issue because I can't seem to reproduce the problem in noir 1.3-beta1 and up. Do you have the code published somewhere? – Jared314 Sep 24 '13 at 05:02
  • @Jared314 I wish -- it and the laptop it was on are long gone. Are you working on something similar? As for the noir version, I just checked the github repo and found that a) it's deprecated, and b) the most recent tagged version is 1.2.2 -- So I'm guessing that I was using something prior to this 1.3-beta1. Also, I was probably using clojure 1.2. Sorry I don't have more info, and good luck! – jtmoulia Sep 25 '13 at 22:57

1 Answers1

1

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!

Dirk Geurs
  • 2,392
  • 19
  • 24