Skip to content

Instantly share code, notes, and snippets.

@gtrak
Last active August 29, 2015 14:01
Show Gist options
  • Save gtrak/f159d1e0c3339cad5259 to your computer and use it in GitHub Desktop.
Save gtrak/f159d1e0c3339cad5259 to your computer and use it in GitHub Desktop.
(ns cljs-app.data
(:require [clojure.string :as string]
[cljs-app.node-bits :as node]
[cljs-app.logging :as log]
[cljs.core.async :as async :refer [<! >!]])
(:require-macros [cljs.core :refer [specify!]]
[cljs-app.macros :refer [when-node]]
[cljs.core.async.macros :refer [go go-loop]]))
;; in a macros clj file:
(defmacro when-node
[& body]
`(when (= "node" (some-> ~'(this-as x x)
(aget "process")
(aget "title")))
~@body))
;; in a cljs file:
(defn as-map!
"Lets an arbitrary JS Object participate in CLJS's map lookup protocol"
[o]
(specify! o
ILookup
(-lookup
([o k] (aget o (name k)))
([o k nf] (if (.hasOwnProperty o (name k))
(aget o (name k)) nf)))))
(defn URL
[]
(when-node
(node/node-require "url")))
(defn HTTP
[]
(when-node
(node/node-require "http")))
(defn request
"Automatically retries a few times if timeout"
([url method data-callback error-callback]
(request url method data-callback error-callback 0))
([url method data-callback error-callback num-try]
(log/info (str "Outgoing URL " url))
(let [parsed (.parse (URL) url)
{:keys [hostname port path protocol] :as parsed} (as-map! parsed)
opts #js {:hostname hostname
:port port
:path path
:method (string/upper-case (name method))}
data (async/chan)
;; the cool part, rebuilds the string response via core.async channels,
;; how the heck do you do this in JS? setTimeout and closures? Promises? mutable data?
;; mutability and closures here: https://github.com/request/request/blob/v2.35.0/request.js#L943
response (go-loop [response ""]
(if-let [data (<! data)]
(recur (str response data))
response))]
(doto (.request (HTTP) opts
(fn [res]
(doto res
(.setEncoding "utf8")
(.on "data" (fn [chunk] (async/put! data chunk)))
(.on "end" (fn []
(async/close! data)
;; cool part #2, throws the full response onto the callback when it's ready
(go (data-callback (<! response)))))
(.on "error" error-callback))))
(.on "error" error-callback)
(.setTimeout
20000
(fn []
(async/close! data)
(if (> 5 num-try)
(request url method data-callback error-callback (inc num-try))
(do
(log/info "Failed request due to multiple timeouts" {:url url :method method})
(error-callback "Request Failed 5 times.")))))
(.end)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment