├── .gitignore ├── LICENSE ├── README.md ├── examples ├── css │ └── styles.css ├── index.html └── src │ ├── examples.cljs │ └── examples │ ├── github_search.cljs │ └── replicated_search.cljs ├── project.clj └── src └── cljs_promises ├── async.clj ├── async.cljs └── core.cljs /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | /lib/ 4 | /classes/ 5 | /out/ 6 | /target/ 7 | .lein-deps-sum 8 | .lein-repl-history 9 | .lein-plugins/ 10 | 11 | .nrepl-port 12 | 13 | /examples/out/ 14 | /examples/js/ 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 James MacAulay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cljs-promises 2 | 3 | A ClojureScript library for working with JavaScript promises. 4 | 5 | ### Overview 6 | 7 | This library leverages the power of [core.async](https://github.com/clojure/core.async) to let you write promise code like this: 8 | 9 | ```clojure 10 | (cljs-promises.async/extend-promises-as-pair-channels!) 11 | 12 | (go 13 | (let [user-promise (get-user "jamesmacaulay")] 14 | (try 15 | (println (:blog ( 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

replicated_search.cljs

10 | 11 |
12 |
13 | 14 |
15 |

github_search.cljs

16 | 17 | 18 | 19 |
20 |

21 |
22 |
23 |
24 |
25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/src/examples.cljs: -------------------------------------------------------------------------------- 1 | (ns examples 2 | (:require [examples.github-search] 3 | [examples.replicated-search])) 4 | 5 | (enable-console-print!) 6 | 7 | (cljs-promises.async/extend-promises-as-pair-channels!) 8 | -------------------------------------------------------------------------------- /examples/src/examples/github_search.cljs: -------------------------------------------------------------------------------- 1 | (ns examples.github-search 2 | (:require-macros [cljs.core.async.macros :refer [go]]) 3 | (:require [cljs-promises.core :as p] 4 | [cljs-promises.async :refer-macros [ (jsonp url) 27 | (p/then (fn [response] 28 | (let [response (js->clj response :keywordize-keys true)] 29 | (if (-> response :meta :status (= 200)) 30 | (:data response) 31 | (throw (ex-info (-> response :data :message) 32 | response))))))))) 33 | 34 | (defn build-view-context 35 | "Returns a map of promises for various GitHub resources associated with the 36 | given user." 37 | [username] 38 | {:user (github-get (str "/users/" username)) 39 | :gists (github-get (str "/users/" username "/gists")) 40 | :repos (github-get (str "/users/" username "/repos?sort=created")) 41 | :events (github-get (str "/users/" username "/events"))}) 42 | 43 | (defn link-to [text url] 44 | (str "" text "")) 45 | 46 | (defn avatar-view 47 | "Takes a user map from the GitHub API and returns some HTML for the user's 48 | avatar." 49 | [user] 50 | (-> (str "") 51 | (link-to (:html_url user)))) 52 | 53 | (defn summary-view 54 | "Takes a user map, a collection of gists, and a collection of repos, and 55 | returns a little summary of things." 56 | [user gists repos] 57 | (let [how-many #(let [n (count %)] 58 | (if (>= n 30) 59 | "lots of" 60 | n))] 61 | (str (:login user) " has " (how-many gists) " gists and " (how-many repos) " repos."))) 62 | 63 | (defn latest-view 64 | "Returns an HTML list describing the latest gist, repo, and event from the 65 | arguments." 66 | [gists repos events] 67 | (let [[gist repo event] (map first [gists repos events]) 68 | gist-link (-> (or (:description gist) 69 | (:html_url gist)) 70 | (link-to (:html_url gist))) 71 | repo-link (-> (:name repo) 72 | (link-to (:html_url repo))) 73 | event-link (if-let [name (-> event :repo :name)] 74 | (link-to name (str "https://github.com/" name))) 75 | event-desc (str (:type event) (when event-link 76 | (str " on " event-link)))] 77 | (str ""))) 82 | 83 | ;; A map of DOM IDs to view description vectors. The first item of each vector 84 | ;; is a function which should return HTML or plain text. The second item is a 85 | ;; vector of functions (here we're using keywords) which can be applied to a 86 | ;; view context map to provide the arguments to the view function. 87 | (def views 88 | {:github-username-header [:login [:user]] 89 | :github-avatar [avatar-view [:user]] 90 | :github-summary [summary-view [:user :gists :repos]] 91 | :github-latest [latest-view [:gists :repos :events]]}) 92 | 93 | (defn render! 94 | "Takes a view map of DOM IDs to view description vectors, and a context 95 | map whose values may be promises. For each view, its argument-supplying 96 | functions are applied to the context map. Any promises returned are waited 97 | on, after which their resolved values are passed to the view function and 98 | the returned text is set as the HTML of the DOM node identified by the key." 99 | [view-map context] 100 | (doseq [[view-id [view-fn args-fns]] view-map] 101 | (let [node (dom/getElement (name view-id)) 102 | args-promise (p/all ((apply juxt args-fns) context))] 103 | (go (->> args-promise 104 | > (get-username-from-input) 125 | build-view-context 126 | (render! views))))) 127 | -------------------------------------------------------------------------------- /examples/src/examples/replicated_search.cljs: -------------------------------------------------------------------------------- 1 | (ns examples.replicated-search 2 | (:require-macros [cljs.core.async.macros :refer [go]]) 3 | (:require [cljs-promises.core :as p] 4 | [cljs-promises.async :refer-macros [ (p/all [web-search image-search video-search]) 34 | (p/then (partial filter identity))))) 35 | 36 | (defn listen [el type] 37 | (let [c (chan)] 38 | (events/listen el type #(put! c %)) 39 | c)) 40 | 41 | (let [el (dom/getElement "replicated-output") 42 | c (listen (dom/getElement "replicated-search") "click")] 43 | (go (while true 44 | (