6

我有一个这样开始的函数:

(defn data-one [suser]
    (def suser-first-name
       (select db/firstNames
            (fields :firstname)
            (where {:username suser})))
    (def suser-middle-name
        (select db/middleNames
            (fields :middlename)
            (where {:username suser})))
    (def suser-last-name
         (select db/middleNames
             (fields :lastname)
             (where {:username suser})))
    ;; And it just continues on and on...
        )

当然,我一点也不喜欢这个。我的代码库中的许多区域都有这种模式重复,我想概括一下。

所以,我想出了以下开始:

(def data-input {:one '[suser-first-name db/firstNames :firstname] 
                      '[suser-middle-name db/middleNames :middlename]
                      '[suser-last-name db/lastNames :lastname]})

(defpartial data-build [data-item suser]
    ;; data-item takes the arg :one in this case
     `(def (data-input data-item)
        (select (data-input data-item)
            (fields (data-input data-item))
            (where {:username suser}))))

这里真的有几个问题:

- 我如何解构数据输入,以便在 x 未知时创建 x 函数,即。:one 的值是未知的,并且 data-input 中键的数量是未知的。

-- 我在想现在是创建宏的时候,但我以前从未构建过宏,所以我对这个想法犹豫不决。

并且给出一点上下文,函数必须返回要解构的值,但我认为一旦我解决了这个问题,概括所有这些都是可行的:

(defpage "/page-one" []
    (let [suser (sesh/get :username)]       
    (data-one suser)
        [:p "Firat Name: " 
            [:i (let [[{fname :firstname}] suser-first-name]
                (format "%s" fname))]
        [:p "Middle Name: "  
            [:i (let [[{mname :emptype}] suser-middle-name]
                (format "%s" mname))]
        [:p "Last Name: " 
            [:i (let [[{lname :months}] suser-last-name]
                    (format "%s" lname))]]))
4

2 回答 2

5

一些建议:

  • def在函数内部真的很讨厌- 你正在改变全局环境,它可能会导致各种并发问题。我建议将结果存储在地图中。
  • 您在这里不需要宏- 所有数据提取都可以在函数内相对轻松地完成

因此,我建议如下:

(def data-input [[:suser-first-name db/firstNames :firstname] 
                 [:suser-middle-name db/middleNames :middlename]
                 [:suser-last-name db/lastNames :lastname]])

(def data-build [data-input suser]
  (loop [output {}
         items (seq data-input)]
    (if items
      (recur
        (let [[kw db fieldname] (first items)]
          (assoc output kw (select db (fields fieldname) (where {:username suser})))) 
        (next items))
      output)))

未经测试,因为我没有您的数据库设置 - 但希望这能让您了解如何在没有宏或可变全局变量的情况下执行此操作!

于 2012-09-30T00:25:06.253 回答
4

好问题。首先,这是您要求的宏:

(defmacro defquery [fname table fields ]
  (let [arg-name (symbol 'user-name)
        fname (symbol fname)]
    `(defn ~fname [~arg-name]
       (print ~arg-name (str ~@ fields)))))

你可以这样称呼它:

(defquery suser-first-name db/firstNames [:firstname])

或者,如果您希望将所有配置保存在地图中,那么它将接受字符串作为第一个参数而不是符号:

(defquery "suser-first-name" db/firstNames [:firstname])

现在,如果您不介意我推荐其他解决方案,我可能会选择使用围绕配置关闭的单个功能。像这样的东西:

(defn make-reader [query-configurations]
  (fn [query-type user-name]
    (let [{table :table field-names :fields} 
           (get query-configurations query-type)]
      (select table
             (apply fields field-names)
             (where {:username suser})))))

(def data-input {:firstname  {:table db/firstNames  :fields :firstname} 
                 :middlename {:table db/middleNames :fields :middlename}
                 :lastname   {:table db/lastNames   :fields :lastname}})

(def query-function (make-reader data-input))

;; Example of executing a query
(query-function :firstname "tom")

顺便说一句,还有另一种使用 Korma 的方法:

;; This creates a template select from the table
(def table-select (select* db/firstNames))

;; This creates new select query for a specific field
(def first-name-select (fields table-select :firstname))

;; Creating yet another query that filters results by :username
(defn mkselect-for-user [suser query] 
  (where query {:username suser}))

;; Running the query for username "tom"
;; I fully specified exec function name only to show where it comes from.
(korma.core/exec (mkselect-for-user "tom" first-name-select)) 

有关更多信息,我强烈建议您查看Korma 资源

于 2012-09-30T00:05:09.147 回答