1

I have a compojure app with a set of routes and handlers.

(defroutes app-routes
  (GET "/stuff/:id" [:as request] (stuff/get-stuff request))
  (POST "/stuff/" [:as request] (stuff/create-stuff request))

Each handler validates its input, like so

(defn create-stuff
  [request]
  (my-validation/validate-request
    request
    my-validation/create-stuff-validator
    stuff-ok-fn))

The validation code is based on Metis, and looks like this:

(metis/defvalidator :create-stuff-validator
  [:db :presence])

(defn validate-request
  [request request-validator ok-function]
  (let [validation-result (request-validator request)]
    (if (empty? validation-result)
      (ok-function request)
      (bad-request validation-result))))

My problem is that code in create-stuff is duplicated across each of the route handlers; i.e the get-stuff function looks like the create-stuff handler. The only thing that differs is their validator function and their the-validation-went-well-function.

How can I abstract this duplication in a idiomatic Clojure manner?

4

1 回答 1

1

由于这是一种函数式语言,我建议将区分处理程序的函数传递给通用处理程序函数。

;;; in core.clj
(defroutes app-routes
  (GET "/stuff/:id" [:as request]
       (handlers/handle
        my-validation/get-stuff-validator
        stuff/get-stuff-ok-fn
        request))
  (POST "/stuff/" [:as request]
        (handlers/handle
         my-validation/create-stuff-validator
         stuff/create-stuff-ok-fn
         request)))

;;; in handlers.clj

(defn handle
  [validator action request]
  (let [validation-result (validator request)]
    (if (empty? validation-result)
      (action request)
      (bad-request validation-result))))

从风格上讲,我建议如果避免使用 smurf 命名约定,代码会更容易阅读。命名空间告诉我们您是否正在验证,或者“stuff”是您正在操作的内容,您不需要将其包含在函数名称中。此外,您传递的参数应该是可调用的这一事实就足够了,您不需要将 fn 放在函数的名称中,它作为 ok 分支传递的事实告诉我们这是要做的事情当一切顺利时。

;;; in core.clj
(defroutes app-routes
  (GET "/stuff/:id" [:as request]
       (handlers/handle
        my-validation/get-stuff
        stuff/get
        request))
  (POST "/stuff/" [:as request]
        (handlers/handle
         my-validation/create-stuff
         stuff/create
         request)))

;;; in handlers.clj

(defn handle
  [validator ok request]
  (let [errors (validator request)]
    (if (empty? errors)
      (ok request)
      (bad-request errors))))

如果你可以减少冗长而不失去清晰度,你就会提高正确性,因为错误隐藏在冗长中。

于 2013-08-10T14:43:11.497 回答