使用 schema.core 时避免重复

avoiding repetition while using schema.core

我定义了以下架构:

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

有没有一种优雅的方法来避免键名的重复?类似于构造函数方法,我可以在其中传递值? (也许以某种特定顺序)

这样的方法可行:您可以定义一个同时定义架构和构造函数的宏。

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

(这个没测试过,不过应该可以,不然早上更新)