在 Clojure 中使用宏

发布于 2024-11-05 09:28:02 字数 1922 浏览 7 评论 0原文

我特别尝试使用 Clojure 中的 appengine-magic 生成 CRUD 函数的样板,以便与 Google App Engine 数据存储区一起使用。我很难弄清楚如何从下面复制的模型中生成值。

(def *model* {:users [{:name "Adam"
                       :email "[email protected]"
                       :registered-on "07-05-2011"}
                      {:name "Greg"
                       :email "[email protected]"
                       :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-entities,它将采用上面的输出并重复调用 ds/defentities 定义与我的模型一样多的实体需要。

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

此外,我完全不确定这是否是解决此问题的合理方法。我对宏还很陌生,可能会犯一些错误。任何帮助/澄清将不胜感激。

注意:

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

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

但是,它在编写宏方面更复杂,所以我将保持原样。

I'm specifically trying to generate the boilerplate for crud functions to work with the Google App Engine datastore using appengine-magic in Clojure. I'm having difficulty working out how to generate values from a model that I've reproduced below.

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

I'm fairly new to appengine-magic, but it provides a defentity which allows you to define entities that you can put into the datastore and save! which allows you to save predefined entities into the datastore.

These take the form of:

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

Now just to start with I've defined:

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

Calling it with:

(list-entities *model*)

Outputs:

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

Now I am having difficulty defining gen-entities which will take the output above and repeatedly call ds/defentities defining as many entities as my model requires.

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

Additionally I am in no way certain that this is a reasonable way to go about solving this problem. I'm still very new to macros and probably making several mistakes. Any help/clarity would be appreciated.

NOTE:

That model I've realised is badly designed, the one below is a lot better:

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

However it is more complex in terms of writing a macro so I will leave it as is.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

烟─花易冷 2024-11-12 09:28:02

我认为需要一个宏,因为 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>

I think a macro is required, because defentity seems to define a type.

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

You don't have to fiddle the keys and values from each other, just to put them together again with interleave. Mapping over a map will give you the key and the corresponding value in one go.

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

Note: this won't work with the model stored in a Var. You'll have to specify the model in the call to gen-entities.

user=> (macroexpand-1 '(gen-entities model))
(
#<IllegalArgumentException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文