Skip to content

Instantly share code, notes, and snippets.

@mccraigmccraig
Created June 9, 2015 09:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mccraigmccraig/f9dc2ced5f9fb696b772 to your computer and use it in GitHub Desktop.
Save mccraigmccraig/f9dc2ced5f9fb696b772 to your computer and use it in GitHub Desktop.
last-call wins for core.async API clients
;;;;;;;;;;;;;;;;;;;;; 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