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.