使用schema.core时避免重复

时间:2017-01-12 20:53:19

标签: clojure compojure compojure-api

我定义了以下架构:

(s/defschema Card
 {:cardNumber s/Str
  :cvv s/Str
  :creditCardMonthValidity s/Str
  :creditCardYearValidity s/Str
  :cpf s/Str
  :name s/Str
  :phoneNumber s/Str})

然后在路由中我在JSON响应中使用相同的键:

(GET "/card" []
        :summary "fetches card info given the access token & checkout id"
        :query-params [accessToken :- String checkoutId :- String]
        :return Card
        (let [checkout  (CheckoutApi/show checkoutId accessToken)
              card      (.getCard checkout)
              contact   (.getContact checkout)
              (ok {:cardNumber (.getAccountNumber card)
                   :cvv "000"
                   :creditCardMonthValidity (.getExpiryMonth card)
                   :creditCardYearValidity (.getExpiryYear card)
                   :cpf (.getNationalID contact)
                   :name (.getFirstName contact)
                   :phoneNumber (.getPhoneNumber contact)})]))

有一种优雅的方法可以避免重复键名吗?像构造函数方法,我可以传递值? (可能按某种特定顺序)

1 个答案:

答案 0 :(得分:1)

这样的东西可以工作:你可以定义一个宏,它一次定义模式和构造函数。

user> (defmacro defresponse [schema constructor-name constructor-params data]
        `(do
           (s/defschema ~schema ~(into {} (map (fn [[k [t _]]] [k t])
                                               data)))
           (defn ~constructor-name ~constructor-params
             ~(into {} (map (fn [[k [_ init]]] [k init])
                            data)))))
#'user/defresponse
user> (defresponse Card card-response [card contact]
        {:cardNumber [s/Str (.getAccountNumber card)]
         :cvv [s/Str "000"]
         :creditCardMonthValidity [s/Str (.getExpiryMonth card)]
         :creditCardYearValidity [s/Str (.getExpiryYear card)]
         :cpf [s/Str (.getNationalID contact)]
         :name [s/Str (.getFirstName contact)]
         :phoneNumber [s/Str (.getPhoneNumber contact)]})

此defresponse将扩展为以下内容:

(do
  (s/defschema
    Card
    {:cardNumber s/Str,
     :cvv s/Str,
     :creditCardMonthValidity s/Str,
     :creditCardYearValidity s/Str,
     :cpf s/Str,
     :name s/Str,
     :phoneNumber s/Str})
  (defn card-response [card contact]
    {:cardNumber (.getAccountNumber card),
     :cvv "000",
     :creditCardMonthValidity (.getExpiryMonth card),
     :creditCardYearValidity (.getExpiryYear card),
     :cpf (.getNationalID contact),
     :name (.getFirstName contact),
     :phoneNumber (.getPhoneNumber contact)}))

然后您可以像往常一样使用您的架构,card-response就像这样:

(let [checkout  (CheckoutApi/show checkoutId accessToken)
      card      (.getCard checkout)
      contact   (.getContact checkout)]
  (ok (card-response card contact)))

(没有测试过这个,但它应该有用。否则会在早上更新它)