This commit is contained in:
Gered 2015-01-04 15:14:02 -05:00
parent bd038047db
commit a5010e423e

View file

@ -12,6 +12,12 @@
(~fail-response ~request)))) (~fail-response ~request))))
(defmacro checked-routes (defmacro checked-routes
"Wraps any number of routes with one or more filters which are run before any of the
wrapped routes are executed. If any of the filters return nil, fail-response is
returned by the server instead of the running a route handler. Filter functions
should accept a Ring request as the first argument and return a Ring request map
if the filter logic passed. The same Ring request map is threaded through all of
the filters before eventually being passed on to the route handler."
[checks fail-response & routes] [checks fail-response & routes]
`(fn [request#] `(fn [request#]
(let [result# (threaded-checks request# ~checks ~fail-response)] (let [result# (threaded-checks request# ~checks ~fail-response)]
@ -24,8 +30,17 @@
(response/status 500))) (response/status 500)))
(defmacro checked-route (defmacro checked-route
"Used in place of a normal Compojure route handler's body. Applies a series of
filters to the route/request parameters before finally running the handler
body itself. All of the filters used should accept a Ring request as the first
argument and return a filtered Ring request, or nil if the filter failed. Use
routefn at the end to wrap your actual route handler logic. The arguments it
receives are destructured the same as with a normal Compojure route, but the
request's :safe-params map is used instead of :params to find the values to
bind to the arguments listed. If any of the filters fail, the :on-fail response
is returned (or a default HTTP 500 response if not specified)."
{:arglists '([& body] {:arglists '([& body]
[:on-fail fail-response & body])} [:on-fail fail-response & body])}
[& body] [& body]
(let [has-fail-response? (= :on-fail (first body)) (let [has-fail-response? (= :on-fail (first body))
fail-response (if has-fail-response? (second body) default-check-error-response) fail-response (if has-fail-response? (second body) default-check-error-response)
@ -34,27 +49,46 @@
(threaded-checks request# ~body ~fail-response)))) (threaded-checks request# ~body ~fail-response))))
(defmacro checked (defmacro checked
"Convenience method of defining a 'checked' Compojure route. Place this at the
start of the Compojure route definition (before GET, PUT, etc) and omit the
params vector after the route path. The route handler body is automatically
wrapped in a call to checked-route, so you just define the contents of
it as you would if you were using checked-route manually."
{:arglists '([method-fn path & body] {:arglists '([method-fn path & body]
[method-fn path :on-fail fail-response & body])} [method-fn path :on-fail fail-response & body])}
[method-fn path & body] [method-fn path & body]
`(~method-fn ~path [] `(~method-fn ~path []
(checked-route ~@body))) (checked-route ~@body)))
(defmacro routefn [request args & body] (defmacro routefn
"The final form present in a checked-route call. The forms in body are executed
only if none of the previous filters failed. The arguments vector is destructured
the same was as normal Compojure route parameter destructuring is done, except
that parameters come from the request's :safe-params map instead."
[request args & body]
(if (vector? args) (if (vector? args)
`(let [~@(destructure-route-bindings args :safe-params request)] ~@body) `(let [~@(destructure-route-bindings args :safe-params request)] ~@body)
`(let [~args ~request] ~@body))) `(let [~args ~request] ~@body)))
(defn safe (defn safe
"Marks one or more parameters as safe (copying them from the request's :params
map to :safe-params)."
[request & params] [request & params]
(let [safe-params (select-keys (:params request) params)] (let [safe-params (select-keys (:params request) params)]
(update-in request [:safe-params] merge safe-params))) (update-in request [:safe-params] merge safe-params)))
(defn validate (defn validate
"Validates the specified parameter using function f which gets passed the value
of the parameter and any additional arguments given. If f returns a 'truthy' value
the parameter is marked safe."
[request param f & args] [request param f & args]
(if (apply f (get-in request [:params param]) args) (if (apply f (get-in request [:params param]) args)
(safe request param))) (safe request param)))
(defn transform (defn transform
[request param-k f & args] "Transforms the specified parameter using function f which gets passed the value
(apply update-in request [:params param-k] f args)) of the parameter and any additional arguments given. Note that this does not
mark a parameter safe after transformation. This is intended to be used to
perform any pre-validation transformations if necessary."
[request param f & args]
(apply update-in request [:params param] f args))