Skip to content

Instantly share code, notes, and snippets.

@pandeiro
Created September 6, 2012 03:22
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 pandeiro/3650678 to your computer and use it in GitHub Desktop.
Save pandeiro/3650678 to your computer and use it in GitHub Desktop.
Experimental ClojureScript XHR API
(ns please.core
(:require [please.help :as help]
[cljs.reader :as reader])
(:refer-clojure :exclude [get])) ; please?
;;
;; Event handler delegation
;;
(def ^{:doc "If this is nil, no events will be sent. Use please.core/set-request-event-fn! to
change this value"}
request-event-fn nil)
(defn set-request-event-fn! [f]
(when (fn? f) (set! request-event-fn f)))
;;
;; Reserved keywords for HTTP verbs and flags
;;
(def http-methods
{:get "GET", :put! "PUT" :post! "POST" :head "HEAD" :delete! "DELETE"})
(def reserved-keywords
(apply conj #{:sync :async :json :html :basicauth} (keys http-methods)))
;;
;; Functions
;;
(defn request
"Request an HTTP resource as Clojure (or optionally JSON or HTML)"
[& args]
(let [xml-http-request #(js/XMLHttpRequest.)
async? (not (help/to-do-sync? args))
url (help/find-url-str args)
method (http-methods (or (some (set (keys http-methods)) args) :get))
data (filter help/looks-like-data? args)
events (filter keyword? (remove reserved-keywords args))
callbacks (filter fn? args)
content-type ({:json "application/json"
:html "text/html"} (some #{:json :html} args) "application/clojure")
response-type (cond (= content-type "application/json") "json"
(= content-type "text/html") "document"
:else "text")
as-clj? (= response-type "text")
creds (when (help/kw-in-args? :basicauth args)
(->> (partition-by #(= :basicauth %) args)
(last)
(filter string?)
(take 2)))]
(when url
(let [xhr (xml-http-request)
func (fn []
(doseq [callback callbacks]
(callback (if as-clj?
(reader/read-string (.-response xhr))
(.-response xhr)))
(when request-event-fn
(doseq [event events]
(request-event-fn event data)))))]
(do
(if creds
(.open xhr method url async? (first creds) (second creds))
(.open xhr method url async?))
(set! (.-onreadystatechange xhr) #(when (= (.-readyState xhr) 4) (func)))
(when async? (set! (.-responseType xhr) response-type))
(.setRequestHeader xhr "Content-Type" content-type)
(.send xhr (if (= 1 (count data)) (first data) data))
(or (and async? xhr)
(if as-clj? (reader/read-string (.-response xhr)) (.-response xhr))))))))
(def get (partial request :get))
(def post! (partial request :post!))
(def put! (partial request :put!))
(def head (partial request :head))
(def delete! (partial request :delete!))
(comment
;;
;; Reserved keyword list
;;
[:get :head :delete :put! :post! :sync :async :basicauth :html :json]
;;
;; API: example usages
;;
(request "some/res") ; minimum of 1 string arg
; if no fn arg and implicit :get, **synchronous** req
(request :html "some/res.html") ; resources assumed clj unless :json/:html present
(request :post! :json "some/api" {:a 1}) ; :json converts clj data to json ( "{\"a\": 1}" )
(request :post! "some/res" (or {:a 1 :b 2} "something" 3)) ; :put!/:post! async by default
(request :put! "some/res" :sync) ; explicit :sync for put and post
(request "some/res" :sync #(fire-some-event! %)) ; explicit sync for get w/ callback
(request "some/res" #(get-busy % :busy-event)) ;standard familiar callback patterns
(request :post! "some/res" {:a 1 :b 2} #(display-it %))
(request "file:///home/user/some/res"); local is fine
(request "some/res" :head) ; args can come in any order
(request #(fire-event :x %) "/api" :put! {:a 1})
(request "some/res" :some-event) ; non-reserved keywords are assumed to be events,
; which are fired if a default request event fn has
; been set. fn is called with event first
; plus any string, number, or clojure coll present
; in args
(set-request-event-fn! dispatch/fire) ; set a request event fn or keywords just get ignored
(request "some/res" "other things" "confused!?") ; only first str accepted and used as url
(request "some/res" #(do-stuff %) :basicauth "user" "pass") ; if :basicauth, first two strs
; after it in args are assumed to be user and pass
)
@mjrb
Copy link

mjrb commented Jun 8, 2018

lol :require [please.help]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment