Last active
August 29, 2015 14:01
-
-
Save gtrak/f159d1e0c3339cad5259 to your computer and use it in GitHub Desktop.
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
(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