Использование макросов в Clojure - PullRequest
6 голосов
/ 08 мая 2011

Я специально пытаюсь сгенерировать шаблон для грубых функций для работы с хранилищем данных Google App Engine, используя appengine-magic в Clojure.У меня возникают трудности при разработке способов генерации значений из модели, которую я воспроизвел ниже.

(def *model* {:users [{:name "Adam"
                       :email "adam@gmail.com"
                       :registered-on "07-05-2011"}
                      {:name "Greg"
                       :email "gregory@gmail.com"
                       :registered-on "11-05-2011"}]
              :post [{:title "A"
                      :authour "Adam"}
                     {:title "B"
                      :author "Greg"}]})

Я довольно новичок в appengine-magic, но он обеспечивает защиту, которая позволяет вам определятьсущности, которые вы можете положить в хранилище данных и сохранить!который позволяет вам сохранять предопределенные объекты в хранилище данных.

Они имеют вид:

(ds/defentity Post [title author])
(ds/save! (Post. title author))

Теперь просто для начала я определил:

(defn list-entities [model]
  "Takes a representation of the model and lists the entities in preparation for generating defentities"
  (interleave (vec (map first (partition 1 (map (comp symbol capitalize #(str % ".") name) (keys model)))))
    (map vec (map keys (map first (vals model))))))

Вызов с помощью:

(list-entities *model*)

Выходы:

(Users. [:name :email :registered-on] Post. [:title :author])

Теперь у меня возникают трудности с определением gen-сущностей, которые возьмут вывод выше и неоднократно вызывают ds / defentities, определяя столько сущностейкак моя модель требует.

(defmacro gen-entities [entity fields]
  `(ds/defentity 'entity 'fields))

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

ПРИМЕЧАНИЕ:

Эта модель, которую я понял, плохо спроектирована, приведенная ниже намного лучше:

(def *model* {:users [:name :email :registered-on]
              :post [:title :author]})

Однакоон более сложен с точки зрения написания макроса, поэтому я оставлю его как есть.

1 Ответ

1 голос
/ 08 мая 2011

Я думаю, что макрос необходим, потому что defentity, кажется, определяет тип.

(defmacro gen-entities
  [model]
  `(do
     ~@(for [[entity-kw values] model]
         (let [entity-sym (-> entity-kw name capitalize symbol)
               fields     (map (comp symbol name) (keys (first values)))]
           `(ds/defentity ~entity-sym [~@fields])))))

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

user=> (macroexpand-1 `(gen-entities ~model))
(do
  (ds/defentity Users [name registered-on email])
  (ds/defentity Post [title authour]))

Примечание: это не будет работать с моделью, хранящейся в Var. Вам нужно будет указать модель при звонке на gen-entities.

user=> (macroexpand-1 '(gen-entities model))
(
#<IllegalArgumentException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol>
...