Created
June 9, 2015 09:34
-
-
Save mccraigmccraig/f9dc2ced5f9fb696b772 to your computer and use it in GitHub Desktop.
last-call wins for core.async API clients
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;;;;;;;;;;;;;;;;;;;; macro | |
(ns clustermap.lastcall-method | |
(:require [clojure.tools.macro :refer [name-with-attributes]])) | |
(defn ^:private lastcall-method* | |
"defines an API method with last-call-wins semantics : returns a | |
channel of a single-value, the response | |
only responses corresponding to the the last | |
call made are returned, so out-of-order responses are discarded. | |
channels corresponding to discarded responses will be closed | |
empty | |
the body must return a core.async channel of a single-value, | |
being the response of a single api call | |
an additional no-args version of the function is defined which | |
discards any pending responses and ensures any outstanding takes | |
return nil" | |
[def-sym name params-body] | |
(let [[name [params & body]] (name-with-attributes name params-body)] | |
`(let [in-flight-atom# (atom nil)] | |
(~def-sym ~name | |
([] | |
(let [emptych# (cljs.core.async/chan)] | |
(cljs.core.async/close! emptych#) | |
(clustermap.lastcall-method/lastcall-method-impl in-flight-atom# emptych#))) | |
(~params | |
(let [valch# ~@body] | |
(clustermap.lastcall-method/lastcall-method-impl in-flight-atom# valch#))))))) | |
(defmacro def-lastcall-method | |
"defn a lastcall method" | |
[name & params-body] | |
(lastcall-method* 'defn name params-body)) | |
(defmacro def-lastcall-method-factory | |
"defn a zero-args factory for a lastcall method, which returns an instance | |
of the method when called" | |
[name & params-body] | |
(let [[name [params & body]] (name-with-attributes name params-body) | |
method-name (gensym name) | |
method-fn (lastcall-method* 'fn method-name params-body)] | |
`(defn ~name | |
[] | |
~method-fn))) | |
(defmacro lastcall-method | |
"return an anonymous instance of a lastcall method" | |
[name & params-body] | |
(lastcall-method* 'fn name params-body)) | |
;;;;;;;;;;;;;;;;;;;;;;; implementation | |
(ns clustermap.lastcall-method | |
(:require-macros | |
[cljs.core.async.macros :refer [go]]) | |
(:require | |
[cljs.core.async :as async :refer [<! chan close! put! sliding-buffer]])) | |
(defn lastcall-method-impl | |
"implements last-call-wins aync api-call semantics, discarding results from | |
any earlier api calls | |
- in-flight-atom : an atom used to match received results to calls | |
- valch : a single-value channel eventually containing one api-call result" | |
[in-flight-atom valch] | |
(let [rx (chan)] | |
(reset! in-flight-atom valch) | |
(go | |
(let [val (<! valch)] | |
(when (and val | |
(= @in-flight-atom valch)) | |
(put! rx val)) | |
(close! valch) | |
(close! rx))) | |
rx)) | |
;;;;; examples | |
(def-lastcall-method-factory geotags-of-type-factory | |
[tag-type] | |
(GET (str "/api/" api-prefix "/geotags/" tag-type))) | |
(def-lastcall-method company-search | |
[query] | |
(GET (str "/api/" api-prefix "/companies/v2/name-id-search?q=" query))) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment