Macros 在Clojure中使用宏

Macros 在Clojure中使用宏,macros,clojure,google-cloud-datastore,appengine-magic,Macros,Clojure,Google Cloud Datastore,Appengine Magic,我特别尝试使用Clojure中的appengine magic为crud函数生成样板,以便与Google应用程序引擎数据存储一起使用。我很难从下面复制的模型中生成值 (def *model* {:users [{:name "Adam" :email "adam@gmail.com" :registered-on "07-05-2011"} {:name "Gr

我特别尝试使用Clojure中的appengine magic为crud函数生成样板,以便与Google应用程序引擎数据存储一起使用。我很难从下面复制的模型中生成值

(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/Defensities来定义模型所需的任意多个实体

(defmacro gen-entities [entity fields]
  `(ds/defentity 'entity 'fields))
此外,我无法确定这是否是解决这个问题的合理方法。我对宏还是很陌生,可能会犯一些错误。如有任何帮助/澄清,将不胜感激

注:

我意识到的那个模型设计得很糟糕,下面的模型要好得多:

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

不过,编写宏要复杂得多,所以我将保持原样。

我认为需要一个宏,因为
防御性
似乎定义了一个类型

(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]))
注意:这不适用于存储在变量中的模型。您必须在调用
gen entities
时指定模型

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

#你为什么不想做一个函数,直接将输入转换为对ds/Defensity的调用?你需要宏的唯一原因是你的函数生成的输出。我可能在这里遗漏了什么,我试过了,但似乎不起作用。所以我想这是一个我必须使用mac的情况罗斯。我知道除非我们需要,否则我们不应该使用它们,所以我想我应该在这里问一下。不过,当我运行macroexpand-1时,正如您所示,我似乎遗漏了一些东西。但是如果我尝试评估调用(此处插入模型)与宏扩展相反,它不起作用,给我一个java.lang.ClassCastException:clojure.lang.Keyword不能转换为clojure.lang.IObj[抛出类java.lang.RuntimeException].知道我遗漏了什么吗?还有,你不能使用预定义的Var似乎很奇怪,有什么原因限制吗?@Toobarways修复了一个愚蠢的错误。它现在应该可以工作了。它不能与Var一起工作,因为宏看不到Var的实际值,但在这种情况下,符号-
模型
。用li调用eval怎么样ke(评估模型)?或类似的东西?或者这是一个很大的禁忌?对此我很抱歉,我只是想找出什么是好的做法。