Есть ли способ добавить функции в динамически созданное пространство имен?

Я создаю noir webapp, и мне нужно динамически создавать новые представления и модели. Я слежу за примерами noir, в которых представление и контроллер для ресурса имеют отдельные пространства имен, и я нашел, что это очень чистый подход.

В соответствии с этим мне нужно иметь возможность динамически создавать новые пространства имен, соответствующие представлениям и моделям, а затем ставить в них соответствующие функции. Моя идея заключалась в том, чтобы макросы указывались в отдельном пространстве имен, которое при вызове в новом пространстве имен предоставляло соответствующие маршруты /partials/whatever.

Например (пропустите мой первый 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"])))

вызывается из...

(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)

Однако вызов этого из моего нового пространства имен дает мне исключение NullPointerException. Я бы очень признателен за любую помощь, и есть ли лучший подход. Например, просто используя ссылку для пространства имен, которое содержит все необходимые определения?

Первое сообщение, и я не думаю, что это повторение этого. Спасибо!

Ответ 1

Прежде всего, этот вопрос немного устарел. Оба Нойра и Clojure развились за последний год. Для ясности я возьму Noir из уравнения и постараюсь ответить на ваш вопрос о динамическом создании функций с помощью макросов.

Следуйте за 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

Здесь мы создаем пространство имен "foo", которое содержит макрос для определения функций "hello-yourname". Позвольте создать другое пространство имен:

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

Посмотрим, действительно ли они работают:

bar=> (hello-tom)
"hello: tom"
bar=> (hello-jerry)
"hello: jerry"

Я думаю, что это очень близко к вашему оригинальному примеру.

Надеюсь, это поможет!