Skip to content

Commit

Permalink
Merge pull request #38 from jesims/JESI-2652
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreTheHunter committed May 13, 2019
2 parents b3eb5eb + 670e060 commit 886ccdc
Show file tree
Hide file tree
Showing 17 changed files with 421 additions and 49 deletions.
1 change: 1 addition & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.29
0.0.30
4 changes: 3 additions & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
:inherit [:plugins :managed-dependencies :deploy-repositories :dependencies [:profiles :dev] :test-refresh]}
:dependencies [[thheller/shadow-cljs]
[org.clojure/clojure]
[cheshire "5.8.1"]
[org.clojure/core.async]
[com.rpl/specter]
[fullcontact/full.async "1.0.0"]
[cheshire "5.8.1"]
[com.lucasbradstreet/cljs-uuid-utils "1.0.2"]
[medley "1.1.0"]]
; commented out until https://github.com/pjstadig/humane-test-output/issues/37 is fixed
Expand Down
2 changes: 2 additions & 0 deletions src/io/jesi/backpack.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
(import-vars
[io.jesi.backpack.collection
assoc-in
concat!
contains-any?
dissoc-all
dissoc-in
Expand All @@ -41,6 +42,7 @@
if-fn
map-if
noop
p=
partial-right
pass
pass-if]
Expand Down
115 changes: 115 additions & 0 deletions src/io/jesi/backpack/async.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
;TODO move to async utils library (to avoid core.async and full.async deps)
(ns io.jesi.backpack.async
#?(:cljs (:require-macros
[full.async]
[full.async.env :refer [if-cljs]]
[io.jesi.backpack.async :refer [when-open <? go-try]]))
(:require
[clojure.core.async :as async]
[clojure.core.async.impl.protocols :as proto]
[full.async :as fa]
#?(:clj [full.async.env :refer [if-cljs]])))

(defn closed?
"returns true if the channel is nil or closed"
[chan]
(or (nil? chan)
(proto/closed? chan)))

(def
^{:arglists '([chan])}
open?
"returns true if the channel is open. The complement of `closed?`"
(complement closed?))

(defmacro when-open [chan & body]
`(when-not (closed? ~chan)
~@body))

(def
^{:arglists '([] [buf-or-n] [buf-or-n xform] [buf-or-n xform ex-handler])}
chan
"Creates a channel with an optional buffer, an optional transducer
(like (map f), (filter p) etc or a composition thereof), and an
optional exception-handler. If buf-or-n is a number, will create
and use a fixed buffer of that size. If a transducer is supplied a
buffer must be specified. ex-handler must be a fn of one argument -
if an exception occurs during transformation it will be called with
the Throwable as an argument, and any non-nil return value will be
placed in the channel."
async/chan)

(defmacro >!
"puts a val into port. nil values are ignored. Must be called inside a (go ...) block. Will park if no buffer space is available.
Returns true unless port is already closed."
[port val]
`(when (some? ~val)
(if-cljs
(cljs.core.async/>! ~port ~val)
(async/>! ~port ~val))))

(defmacro go-try
"Asynchronously executes the body in a go block. Returns a channel which
will receive the result of the body when completed or an exception if one
is thrown."
[& body]
`(fa/go-try ~@body))

(defmacro go-retry
"Attempts to evaluate a go block and retries it if `should-retry-fn`
which is invoked with block's evaluation result evaluates to false.
`should-retry-fn` is optional and by default it will simply check if
result is of type Throwable (clj) / js/Error (cljs). If the evaluation
still fails after given retries, the last failed result will be
returned in channel.
Parameters:
* retries - how many times to retry (default 5 times)
* delay - how long to wait in seconds between retries (default 1s)
* should-retry-fn - function that is invoked with result of block's
evaluation and should indicate whether to retry
(if it returns true) or not (returns false)"
[config & body]
`(fa/go-retry ~config ~@body))

(defmacro <?
"Same as core.async <! but throws an exception if the channel returns a
throwable object. Also will not crash if channel is nil."
[ch]
`(fa/<? ~ch))

(defmacro go
"Asynchronously executes the body, returning immediately to the
calling thread. Additionally, any visible calls to <!, >! and alt!/alts!
channel operations within the body will block (if necessary) by
'parking' the calling thread rather than tying up an OS thread (or
the only JS thread when in ClojureScript). Upon completion of the
operation, the body will be resumed.
Returns a channel which will receive the result of the body when
completed"
[& body]
`(if-cljs
(cljs.core.async.macros/go ~@body)
(async/go ~@body)))

(defn close!
"Closes a channel. The channel will no longer accept any puts (they
will be ignored). Data in the channel remains available for taking, until
exhausted, after which takes will return nil. If there are any
pending takes, they will be dispatched with nil. Closing a closed
channel is a no-op. Returns the channel.
Logically closing happens after all puts have been delivered. Therefore, any
blocked or parked puts will remain blocked/parked until a taker releases them."
[chan]
(when chan
(async/close! chan)
chan))

(defn put!
"Asynchronously puts a val into a channel if it's open. `nil` values are
ignored. Returns the channel."
[chan val]
(when (and (open? chan) (some? val))
(async/put! chan val)
chan))
51 changes: 40 additions & 11 deletions src/io/jesi/backpack/collection.cljc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns io.jesi.backpack.collection
(:refer-clojure :exclude [assoc-in])
(:refer-clojure :exclude [assoc-in conj!])
(:require
[clojure.core :as clj]
[io.jesi.backpack.traverse :refer [postwalk]]))

(defn distinct-by [key entities]
Expand Down Expand Up @@ -136,13 +137,41 @@
key and f is a function that will take the old value and any supplied args
and return the new value, and returns a new structure.
If the key does not exist, nil is passed as the old value."
([tcol k f]
(assoc! tcol k (f (get tcol k))))
([tcol k f x]
(assoc! tcol k (f (get tcol k) x)))
([tcol k f x y]
(assoc! tcol k (f (get tcol k) x y)))
([tcol k f x y z]
(assoc! tcol k (f (get tcol k) x y z)))
([tcol k f x y z & more]
(assoc! tcol k (apply f (get tcol k) x y z more))))
([tcoll k f]
(assoc! tcoll k (f (get tcoll k))))
([tcoll k f x]
(assoc! tcoll k (f (get tcoll k) x)))
([tcoll k f x y]
(assoc! tcoll k (f (get tcoll k) x y)))
([tcoll k f x y z]
(assoc! tcoll k (f (get tcoll k) x y z)))
([tcoll k f x y z & more]
(assoc! tcoll k (apply f (get tcoll k) x y z more))))

;from cljs.core/conj!
(defn conj!
"Adds val to the transient collection, and return tcoll. The 'addition'
may happen at different 'places' depending on the concrete type."
([] (transient []))
([tcoll] tcoll)
([tcoll val]
(when tcoll
(clj/conj! tcoll val)))
([tcoll val & more]
(let [ntcoll (conj! tcoll val)]
(if more
(recur ntcoll (first more) (next more))
ntcoll))))

;TODO DRY up. create macro that creates the `& more` overload (could also be used in conj! and dissoc! and disj!)
(defn concat!
"Adds the values to the transient collection, returning tcoll. Concatenates of the elements in the supplied sequences"
([] (transient []))
([tcoll] tcoll)
([tcoll seq]
(apply conj! tcoll seq))
([tcoll seq & more]
(let [ntcoll (concat! tcoll seq)]
(if more
(recur ntcoll (first more) (next more))
ntcoll))))
6 changes: 6 additions & 0 deletions src/io/jesi/backpack/fn.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,9 @@
(if (pred v)
(then v)
(else v)))))

(def
^{:arglists '([& x])}
p=
"Partial ="
#(apply partial = %&))
36 changes: 27 additions & 9 deletions src/io/jesi/backpack/spy.cljc
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
;TODO move to test utils library
(ns io.jesi.backpack.spy
#?(:clj (:refer-clojure :exclude [prn])
:cljs (:refer-clojure :exclude [prn -name]))
#?(:clj (:refer-clojure :exclude [prn peek])
:cljs (:refer-clojure :exclude [prn -name peek]))
#?(:cljs (:require-macros [io.jesi.backpack.spy :refer [prn pprint]]))
(:require
[io.jesi.backpack :as bp]
[io.jesi.backpack.macros :refer [when-not=]]
[io.jesi.backpack.test.util :refer [pprint-str]])
#?(:cljs (:require-macros io.jesi.backpack.spy)))
[io.jesi.backpack.test.util :refer [pprint-str]]))

(def ^:dynamic *enabled* false)

Expand All @@ -32,12 +32,14 @@
file)
*ns*)
line (:line (meta form))]
(str f \: line)))
(if line
(str f \: line)
(str f))))

(defmacro prn [& more]
(defn- -prn [file form & more]
`(when-debug
(when *enabled*
(println ~@(let [line (line-number *file* &form)]
(println ~@(let [line (line-number file form)]
(bp/trans-reduce
(fn [col form]
(doto col
Expand All @@ -46,9 +48,25 @@
[line]
more))))))

(defmacro pprint [& more]
(defmacro prn [& more]
(apply -prn *file* &form more))

(defn- -pprint [file form & more]
`(when-debug
(when *enabled*
(do ~@(let [line (line-number *file* &form)]
(do ~@(let [line (line-number file form)]
(for [form more]
`(println (str ~(str line \space (-name form) \: \newline) (pprint-str ~form)))))))))

(defmacro pprint [& more]
(apply -pprint *file* &form more))

(defmacro peek [val]
`(do
~(-prn *file* &form val)
~val))

(defmacro ppeek [val]
`(do
~(-pprint *file* &form val)
~val))
26 changes: 26 additions & 0 deletions src/io/jesi/backpack/test/macros.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
(ns io.jesi.backpack.test.macros
#?(:cljs (:require-macros
[full.async.env :refer [if-cljs]]
[io.jesi.backpack.test.macros]))
(:require
[clojure.test :refer [is]]
[io.jesi.backpack.async :as async]
#?(:clj [full.async.env :refer [if-cljs]])
#?(:cljs [cljs.test])))

(defmacro async-go [& body]
`(if-cljs
(cljs.test/async ~'done
(async/go
(try
~@body
(finally
(~'done)))))
(clojure.core.async/<!! (async/go
~@body))))

(defmacro is-nil<? [body]
`(is (nil? (async/<? ~body))))

(defmacro is= [& body]
`(is (= ~@body)))
15 changes: 15 additions & 0 deletions src/io/jesi/backpack/test/reporter.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(ns io.jesi.backpack.test.reporter
(:require
[cljs-test-display.core :as ctd]
[cljs.test :as ct]
[io.jesi.backpack.async :as async]))

(defonce done-chan (async/chan))

(def ^:private ^:const dispatch-val [::ctd/default :summary])

(defonce ^:private report-summary (get-method ct/report dispatch-val))

(defmethod ct/report dispatch-val [m]
(async/put! done-chan true)
(report-summary m))
9 changes: 7 additions & 2 deletions src/io/jesi/backpack/test/runner.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
{:dev/always true}
(:require
[cljs-test-display.core :as ctd]
[io.jesi.backpack.async :as async]
[io.jesi.backpack.test.reporter :refer [done-chan]]
[shadow.dom :as dom]
[shadow.test :as st]
[shadow.test.env :as env]))
Expand All @@ -19,8 +21,11 @@
(st/run-all-tests (ctd/init! "test-root")))

(defn ^:dev/before-load-async stop [done]
;FIXME determine if async tests are still pending https://github.com/clojure/clojurescript-site/blob/master/content/tools/testing.adoc#detecting-test-completion--success
(done))
(async/go
(println "Waiting for tests to complete...")
;TODO cancel async tests
(async/<? done-chan)
(done)))

(defn ^:export init []
(dom/append [:div#test-root])
Expand Down

0 comments on commit 886ccdc

Please sign in to comment.