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.