Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a clean way to add functions to a dynamically created namespace?

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!

like image 366
jtmoulia Avatar asked Apr 22 '12 20:04

jtmoulia


1 Answers

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!

like image 69
Dirk Geurs Avatar answered Oct 07 '22 04:10

Dirk Geurs