The ring <https://github.com/ring-clojure/ring> middleware has and solves 
this problem by using functions like this:
(defn wrap-params [handler]
  (fn [req] (handler req)))

So, basically you just have a function that takes a request and returns a 
response. This function is called a handler here.
In order to compose and short-circuit them, you just use functions that 
take a handler and return a handler (such as wrap-params).

So if you want to check if params are ok, you can write

(defn wrap-params [handler]
  (fn [req] 
        (if (all-params-are-ok? req)
            (handler req)
            (create-params-are-not-ok-response))))


Of course, you can also put the processing after calling the handler inside 
your handler function.

You can compose your handlers via 
(def app (-> handler1 handler2 ... handlern)) 

where the handler1 is the inner-most handler and should return a native 
response (~HTTP status 200)

Hope that helps! 

Regards, Daniel


On Tuesday, December 8, 2015 at 9:18:18 PM UTC+1, Ditlev Tøjner wrote:
>
> In designing our (by now) two clojure-based REST services we are 
> struggling with managing flow-control.
>
> (Disclaimer: earlier backgrounds are with Python, C, Javascript and once 
> upon a time Java/C# etc)
>
> What we'd like is to accomplish a series of steps which we feel are 
> universal to REST-services.
> In the context of PUT/POST that is:
> ---
> 1. ensure that the content is of a given format (application/json)
>
> 2. receive & validate input
> 2a. if the input is invalid, return error output (in our case describing 
> what went wrong)
>
> 3. create a new entry / update an existing entry
> (Note if you know the entire URI, then using PUT for both 
> updating/creating is acceptable)
>
> 4. format a response (typically containing the created/updated object)
> ---
>
> Fig1 details how, after several passes, we're dealing with this. The 
> approach is heavily inspired
> by Ref1 and essentially executes each step outlined above with the 
> understanding that failure might
> occur. If a failure occurs, an error message is returned (of course).
>
> We chose an approach similar to Ref1 because we wanted to abort on errors, 
> occasionally recover
> (such as update becoming create if no record is found) and we wanted 
> detailed errors which
> precludes the use of (and ...).
>
> The astute observer may note that our model fails to capture the concept 
> of recoverable failures
> - update-record* may fail for more reasons than simply not having a row 
> (transient errors, actual
> logic-related errors etc) some of which are non-recoverable. 
>
> As it stands, this means the function needs refactoring, *again*. 
> What all this boils down to is this: given the unique interplay of 
> immutability with LISP-style
> (AST) syntax - which techniques are successfully employed to avoid heavily 
> indented code ? Is
> the only solution really to either:
>
> 1) decompose functions into smaller functions, regardless of
> these helper functions being of no use anywhere else.
>
> 2) define a bunch of helper functions in a top-level let-expression - 
> again to avoid having
>    functions which are too heavily indented?
>
>
> Does it ever get easier ? Are we missing something ? It seems extreme to 
> reach for monads for
> something of this nature.
>
> References
> 1. https://brehaut.net/blog/2011/error_monads (Ref1)
>
> Fig. 1. - a PUT endpoint
> ------------------------
> (defn update*
>   "create/update a pin."
>   [cid source rq]
>   (err/attempt-all
>    [_ (chk/mediatype-in? (get rq :content-type) ["application/json"])
>     rq (clojure.walk/keywordize-keys rq)
>     ;; data which can come from the user
>     input (merge {:method "app"
>                   :seen_at (-> (at/now-utc) (at/->dt-utc-str))}
>                  (get rq :body)
>                  {:source_type (get source :type) :source_id (get source :
> id)})
>     _ (av/input-valid? validate-pin-params input)
>
>
>     ;; additional data extracted from URI & token
>     record-data (merge input
>                        {:cid cid
>                         :uid (rq->uid* rq)})
>     record (err/any (update-record* record-data)
>                     (new-record* record-data))]
>    ;; Success
>    (rsp/created
>     (url cid source)
>     {:key-name "pinid"
>      :body {:request (model/transform record)}})
>    ;; Failure
>    err/handle-failure))
>
> ------------------------
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to