Improve spacing for readability.

Improve spacing for readability.

--- src/afterglow/rhythm.clj
+++ src/afterglow/rhythm.clj
@@ -12,61 +12,81 @@
   (do
 (defprotocol IMetronome
   "A time-keeping tool for music-related systems."
+
   (metro-start [metro] [metro start-beat]
   "Returns the start time of the metronome. Also restarts the
   metronome at `start-beat` if given.")
+
   (metro-bar-start [metro start-bar]
   "Restarts the metronome at `start-bar`, keeping the beat phase
   unchanged in case it is being synced to an external source.")
+
   (metro-phrase-start [metro start-phrase]
   "Restarts the metronome at `start-phrase`, keeping the beat phase
   unchanged in case it is being synced to an external source.")
+
   (metro-adjust [metro ms]
   "Adds a number of milliseconds to the start time of the metronome.")
+
   (metro-tick [metro]
   "Returns the duration of one beat in milleseconds.")
+
   (metro-tock [metro]
   "Returns the duration of one bar in milliseconds.")
+
   (metro-ding [metro]
   "Returns the duration of one phrase in milliseconds.")
+
   (metro-beat [metro] [metro beat]
   "Returns the next beat number or the timestamp (in milliseconds) of the
   given `beat`.")
+
   (metro-beat-phase [metro] [metro phase]
   "Returns the distance traveled into the current beat as a phase
   number ranging from [0.0, 1.0), or adjusts the phase to match the
   one supplied.")
+
   (metro-bar [metro] [metro bar]
   "Returns the next bar number or the timestamp (in milliseconds) of
   the given `bar`.")
+
   (metro-bar-phase [metro] [metro phase]
   "Returns the distance traveled into the current bar as a phase
   number ranging from [0.0, 1.0), or adjusts the phase to match the
   one supplied.")
+
   (metro-phrase [metro] [metro phrase]
   "Returns the next phrase number or the timestamp (in milliseconds)
   of the given `phrase`.")
+
   (metro-phrase-phase [metro] [metro phase]
   "Returns the distance traveled into the current phrase as a phase
   number ranging from [0.0, 1.0), or adjusts the phase to match the
   one supplied.")
+
   (metro-bpb [metro] [metro new-bpb]
   "Get the current beats per bar or change it to `new-bpb`")
+
   (metro-bpp [metro] [metro new-bpp]
   "Get the current bars per phrase, or change it to `new-bpp`")
+
   (metro-bpm [metro] [metro new-bpm]
   "Get the current bpm or change the bpm to `new-bpm`.")
+
   (metro-snapshot [metro] [metro instant]
   "Take a snapshot of the current beat, bar, phrase, and phase state.
   If `instant` is supplied, calculates a snapshot for the
   corresponding time rather than the current time.")
+
   (metro-marker [metro]
   "Returns the current time as `\"phrase.bar.beat\"`")
+
   (metro-add-bpm-watch [metro key f]
   "Register a function to be called whenever the metronome's BPM
   changes. The `key` and `function` arguments are the same as found in
   [clojure.core/add-watch](http://clojuredocs.org/clojure.core/add-watch),
   and in fact are passed on to it.")
+
   (metro-remove-bpm-watch [metro key]
   "Stop calling the function which was registered with the specified
   `key`."))
@@ -81,6 +101,7 @@
   fluke in timing as their evaluation occurs over time. Snapshots also
   extend the notions of beat phase to enable oscillators with
   frequencies that are fractions or multiples of a beat."
+
   (snapshot-beat-phase [snapshot] [snapshot beat-ratio]
   "Determine the metronome's phase at the time of the snapshot with
   respect to a multiple or fraction of beats. Calling this with a
@@ -89,6 +110,7 @@
   to [[metro-bpb]] is equivalent to [[metro-bar-phase]], 1/2
   oscillates twice as fast as 1, 3/4 oscillates 4 times every three
   beats... Phases range from [0-1).")
+
   (snapshot-bar-phase [snapshot] [snapshot bar-ratio]
   "Determine the metronome's phase at the time of the snapshot with
   respect to a multiple or fraction of bars. Calling this with a
@@ -97,18 +119,22 @@
   to [[metro-bpp]] is equivalent to [[metro-phrase-phase]], 1/2
   oscillates twice as fast as 1, 3/4 oscillates 4 times every three
   bars... Phases range from [0-1).")
+
   (snapshot-beat-within-bar [snapshot]
   "Returns the beat number within the snapshot relative to the start
   of the bar: The down beat is 1, and the range goes up to the value
   returned by [[metro-bpb]] for the metronome.")
+
   (snapshot-beat-within-phrase [snapshot]
   "Returns the beat number within the snapshot relative to the start
   of the phrase: The first beat is 1, and the range goes up to the
   values returned by
   [[metro-bpb]] times [[metro-bpp]] for the metronome.")
+
   (snapshot-down-beat? [snapshot]
   "True if the current beat at the time of the snapshot was the first
   beat in its bar.")
+
   (snapshot-phrase-phase [snapshot] [snapshot phrase-ratio]
   "Determine the metronome's phase at the time of the snapshot with
   respect to a multiple or fraction of phrases. Calling this with a
@@ -119,9 +145,11 @@
   "Returns the bar number within the snapshot relative to the start of
   the phrase: Ranges from 1 to the value returned by [[metro-bpp]] for
   the metronome.")
+
   (snapshot-phrase-start? [snapshot]
   "True if the current beat at the time of the snapshot wass the first
   beat in its phrase.")
+
   (snapshot-marker [snapshot]
   "Returns the time represented by the snapshot as
   `\"phrase.bar.beat`"))))
@@ -173,31 +201,42 @@

 (defrecord MetronomeSnapshot [start bpm bpb bpp instant beat bar phrase beat-phase bar-phase phrase-phase]
   ISnapshot
+
   (snapshot-beat-phase [snapshot]
     (snapshot-beat-phase snapshot 1))
+
   (snapshot-beat-phase [snapshot beat-ratio]
     (enhanced-phase beat beat-phase beat-ratio))
+
   (snapshot-bar-phase [snapshot]
     (snapshot-bar-phase snapshot 1))
+
   (snapshot-bar-phase [snapshot bar-ratio]
     (enhanced-phase bar bar-phase bar-ratio))
+
   (snapshot-beat-within-bar [snapshot]
     (let [beat-size (/ 1 bpb)]
       (inc (int (floor (/ (snapshot-bar-phase snapshot 1) beat-size))))))
+
   (snapshot-beat-within-phrase [snapshot]
     (let [beat-size (/ 1 bpb bpp)]
       (inc (int (floor (/ (snapshot-phrase-phase snapshot 1) beat-size))))))
+
   (snapshot-down-beat? [snapshot]
     (let [beat-size (/ 1 bpb)]
       (zero? (floor (/ (snapshot-bar-phase snapshot 1) beat-size)))))
+
   (snapshot-phrase-phase [snapshot phrase-ratio]
     (enhanced-phase phrase phrase-phase phrase-ratio))
+
   (snapshot-bar-within-phrase [snapshot]
     (let [phrase-size (/ 1 bpp)]
       (inc (int (floor (/ (snapshot-phrase-phase snapshot 1) phrase-size))))))
+
   (snapshot-phrase-start? [snapshot]
     (let [phrase-size (/ 1 bpp)]
       (zero? (floor (/ (snapshot-phrase-phase snapshot 1) phrase-size)))))
+
   (snapshot-marker [snapshot]
     (str (:phrase snapshot) "." (snapshot-bar-within-phrase snapshot) "." (snapshot-beat-within-bar snapshot))))

@@ -210,12 +249,15 @@

 (defrecord Metronome [start bpm bpb bpp]
   IMetronome
+
   (metro-start [metro] @start)
+
   (metro-start [metro start-beat]
     (dosync
      (ensure bpm)
      (let [new-start (round (- (now) (* (dec start-beat) (metro-tick metro))))]
        (ref-set start new-start))))
+
   (metro-bar-start [metro start-bar]
     (dosync
      (ensure bpm)
@@ -224,6 +266,7 @@
            shift (* (metro-tick metro) (if (> phase 0.5) (dec phase) phase))
            new-bar-start (round (- (now) shift (* (dec start-bar) (metro-tock metro))))]
        (ref-set start new-bar-start))))
+
   (metro-phrase-start [metro start-phrase]
     (dosync
      (ensure bpm)
@@ -233,15 +276,18 @@
            shift (* (metro-tick metro) (if (> phase 0.5) (dec phase) phase))
            new-phrase-start (round (- (now) shift (* (dec start-phrase) (metro-ding metro))))]
        (ref-set start new-phrase-start))))
+
   (metro-adjust [metro ms]
     (dosync
      (alter start + ms)))

   (metro-tick [metro] (beat-ms 1 @bpm))
+
   (metro-tock [metro] (dosync
                        (ensure bpm)
                        (ensure bpb)
                        (beat-ms @bpb @bpm)))
+
   (metro-ding [metro] (dosync
                        (ensure bpm)
                        (ensure bpb)
@@ -252,15 +298,18 @@
                        (ensure start)
                        (ensure bpm)
                        (marker-number (now) @start (metro-tick metro))))
+
   (metro-beat [metro b] (dosync
                          (ensure start)
                          (ensure bpm)
                          (+ (* b (metro-tick metro)) @start)))
+
   (metro-beat-phase [metro]
     (dosync
      (ensure start)
      (ensure bpm)
      (marker-phase (now) @start (metro-tick metro))))
+
   (metro-beat-phase [metro phase]
     (dosync
      (ensure bpm)
@@ -268,25 +317,28 @@
            shift (round (* (metro-tick metro) (if (> delta 0.5) (dec delta)
                                                   (if (< delta -0.5) (inc delta) delta))))]
        (alter start - shift))))
-
+
   (metro-bar [metro]
     (dosync
      (ensure start)
      (ensure bpm)
      (ensure bpb)
      (marker-number (now) @start (metro-tock metro))))
+
   (metro-bar [metro b]
     (dosync
      (ensure start)
      (ensure bpm)
      (ensure bpb)
      (+ (* b (metro-tock metro)) @start)))
+
   (metro-bar-phase [metro]
     (dosync
      (ensure start)
      (ensure bpm)
      (ensure bpb)
      (marker-phase (now) @start (metro-tock metro))))
+
   (metro-bar-phase [metro phase]
     (dosync
      (ensure bpm)
@@ -295,7 +347,7 @@
            shift (round (* (metro-tock metro) (if (> delta 0.5) (dec delta)
                                                   (if (< delta -0.5) (inc delta) delta))))]
        (alter start - shift))))
-
+
   (metro-phrase [metro]
     (dosync
      (ensure start)
@@ -303,6 +355,7 @@
      (ensure bpb)
      (ensure bpp)
      (marker-number (now) @start (metro-ding metro))))
+
   (metro-phrase [metro p]
     (dosync
      (ensure start)
@@ -310,6 +363,7 @@
      (ensure bpb)
      (ensure bpp)
      (+ (* p (metro-ding metro)) @start)))
+
   (metro-phrase-phase [metro]
     (dosync
      (ensure start)
@@ -317,6 +371,7 @@
      (ensure bpb)
      (ensure bpp)
      (marker-phase (now) @start (metro-ding metro))))
+
   (metro-phrase-phase [metro phase]
     (dosync
      (ensure bpm)
@@ -326,8 +381,9 @@
            shift (round (* (metro-ding metro) (if (> delta 0.5) (dec delta)
                                                   (if (< delta -0.5) (inc delta) delta))))]
        (alter start - shift))))
-
+
   (metro-bpm [metro] @bpm)
+
   (metro-bpm [metro new-bpm]
     (dosync
      (let [cur-beat (metro-beat metro)
@@ -338,6 +394,7 @@
     [:bpm new-bpm])

   (metro-bpb [metro] @bpb)
+
   (metro-bpb [metro new-bpb]
     (dosync
      (ensure bpm)
@@ -350,6 +407,7 @@
        (ref-set bpb new-bpb))))

   (metro-bpp [metro] @bpp)
+
   (metro-bpp [metro new-bpp]
     (dosync
      (ensure bpm)
@@ -364,6 +422,7 @@

   (metro-snapshot [metro]
     (metro-snapshot metro (now)))
+
   (metro-snapshot [metro instant]
     (dosync
      (ensure start)


Define pseudo-facts using Compute clauses

--- src/aurora/language.cljs
+++ src/aurora/language.cljs
@@ -249,6 +249,9 @@
 (defrecord FilterMap [nodes fun])
 (defrecord Index [nodes key-ixes])
 (defrecord Lookup [nodes index-node key-ixes val-ixes])
+
+(defn flow->nodes [flow]
+  (:nodes flow))

 ;; FLOW PLAN

@@ -284,7 +287,13 @@
     :remembered 2
     :forgotten 3))

-(def known&pretended [0 1])
+(defn memory->nodes [memory]
+  (case memory
+    :known [0]
+    :pretended [1]
+    :remembered [2]
+    :forgotten [3]
+    :known&pretended [0 1]))

 (def empty-flow-plan
   (FlowPlan. [(Union. #{}) (Union. #{}) (Union. #{}) (Union. #{})] {}))
@@ -303,6 +312,8 @@
 (comment
   (deffact edge "[x] has an edge to [y]")
   (deffact connected "[x] is connected to [y]")
+
+  (def known&pretended [0 1])

   (let [plan empty-flow-plan
         [plan edges] (add-flow plan (FilterMap. known&pretended (fn [x] (when (= edge (.-shape x)) x))))
@@ -419,20 +430,49 @@
 ;; CLAUSES

 (defrecord Recall [memory pattern]) ;; memory is one of :known :pretended :remembered :forgotten :known&pretended
-(defrecord Filter [expr])
-(defrecord Let [name expr])
-(defrecord Set [name vars clauses])
+(defrecord Compute [pattern])
 (defrecord Output [memory pattern]) ;; memory is one of :pretended :remembered :forgotten
-(defrecord OutputMany [memory expr]) ;; memory is one of :pretended :remembered :forgotten
-
-(defn clause->flow-plan [flow-plan shape clause]
-  (condp = (type clause)
-    ;; TODO !!! keep going here
-    ))
+(defrecord OutputMany [memory vars expr]) ;; memory is one of :pretended :remembered :forgotten
+
+;; horrible non-relational things
+(deffact Let "Let [name] be the result of [vars] [expr]")
+(deffact When "When [vars] [expr]")
+
+;; if clause can be calculated somehow then return a new [plan node shape] pair that calculates it
+;; TODO make this extensible
+(defn compute? [plan node shape {:keys pattern}]
+  (condp = (.-shape pattern)
+    Let (when (every? (set shape) (:vars pattern))
+          (let [let-fun (expr->fun shape (:expr pattern))
+                filter-map-fun (fn [fact]
+                                 (let [new-value (.apply let-fun (.-values fact))
+                                       new-values (aclone (.-values fact))]
+                                   (apush new-values new-value)
+                                   (Fact. nil new-values)))
+                [plan node] (add-flow plan (->FilterMap [node] filter-map-fun))]
+            [plan node (conj shape (:name pattern))]))
+    When (when (every? (set shape) (:vars pattern))
+           (let [when-fun (expr->fun shape (:expr pattern))
+                 filter-map-fun (fn [fact]
+                                  (when (.apply when-fun (.-values fact))
+                                    fact))
+                 [plan node] (add-flow plan (->FilterMap [node] filter-map-fun))]
+             [plan node shape]))))

 ;; RULES

 (defrecord Rule [clauses])
+
+;; turn all Recalls into nodes
+;; try to apply computes
+;; loop
+  ;; join two nodes
+  ;; try to apply computes
+;; filter down vars?
+
+;; Correctness: Each clause must appear at least once in the plan
+;; Heuristic: Each Recall clause is used at most once in the plan
+;; Heuristic: Each Filter/Let clause is used at most once per path in the pln

 (defn rule->flow-plan [flow-plan rule]
   (loop [flow-plan flow-plan
@@ -442,5 +482,3 @@
       (recur new-flow-plan new-shape (filter #(not= clause %) clauses))
       (do (assert (empty? clauses) (str "Cannot join " (pr-str shape) " with " (pr-str clauses)))
         flow-plan))))
-
-;; TODO !!! test


Clean up debug output

Clean up debug output

--- src/backloggery/core.clj
+++ src/backloggery/core.clj
@@ -54,13 +54,10 @@
 (def commands
   {"add"
    (fn []
-     (let [other-games (data/read-games (:from *opts*))
+     (let [in-games (data/read-games (:from *opts*))
            bl-games (if (:filter *opts*) (data/read-games "backloggery") [])
-           new-games (filter-games bl-games other-games)]
-       (println "BL " (count bl-games))
-       (println "NEW" (count new-games))
-       (println *in* *out*)
-       (println (:input *opts*) (:output *opts*))
+           new-games (filter-games bl-games in-games)]
+       (printf "Read %d games; of those, %d are new.\n" (count in-games) (count new-games))
        (data/write-games (:to *opts*) new-games)))
    "edit"
    (fn [] nil)


Shift ascii art to the left to stop wrapping on thin terminals.


Don't start the web handler in debug mode

--- src/catalog/web/main.clj
+++ src/catalog/web/main.clj
@@ -8,4 +8,4 @@

 (defn -main []
   ;; start web server
-  (web/run-dmc handler/site))
+  (web/run handler/site))


need to pass opts to get correct base module :output-to

need closure compiler to generate source for module

--- src/clj/cljs/closure.clj
+++ src/clj/cljs/closure.clj
@@ -695,7 +695,7 @@
               [sources'
                (conj ret
                  [name (assoc module-desc :closure-module js-module)])]))
-          [sources []] (sort-modules (add-cljs-base-module (:modules opts))))
+          [sources []] (sort-modules (add-cljs-base-module (:modules opts) opts)))
         cljs-base-closure-module (get-in (into {} modules) [:cljs-base :closure-module])]
     ;; add anything left to :cljs-base module
     (doseq [source sources']
@@ -804,7 +804,7 @@
       (vec
         (for [[name {:keys [closure-module] :as module}] modules]
           [name
-           (assoc module :source (.toSource ^JSModule closure-module))]))
+           (assoc module :source (.toSource closure-compiler ^JSModule closure-module))]))
       (report-failure result))))

 (defn optimize


Use anonymous function declaration style.

--- src/clj_ravendb/rest.clj
+++ src/clj_ravendb/rest.clj
@@ -109,10 +109,9 @@
                          request-builder req/query-index
                          response-parser res/query-index}}]
    {:pre [(:index query)]}
-   (let [get-result (fn[]
-                      (-> (request-builder client query)
+   (let [get-result #(-> (request-builder client query)
                           (wrap-retry-replicas get-req)
-                          (response-parser)))]
+                          (response-parser))]
      (loop [result (get-result) attempt 0]
        (if (or (not (:stale? result))
                (= attempt max-attempts))


Still need standalone-repl plugin for non-containerized network.

--- opt/profiles.clj
+++ opt/profiles.clj
@@ -2,7 +2,8 @@
                   ;; TODO: re-enable once clojars releases repo is up
                   ;; "clojars" "http://s3pository.herokuapp.com/clojars"
                   }
-        :aliases {"repl" ["trampoline" "repl"]}}
+        :aliases {"repl" "standalone-repl"}
+        :plugins [[lein-standalone-repl "0.1.4"]]}}
  :production {:mirrors {"central" "http://s3pository.herokuapp.com/maven-central"
                         ;; "clojars" "http://s3pository.herokuapp.com/clojars"
                         }


force app initialization when executing a dev workflow step that expects initialized app

--- src/bract/core/dev.clj
+++ src/bract/core/dev.clj
@@ -51,7 +51,17 @@
     (init))))

-(defonce ^:redef app-context nil)
+(defonce ^:redef app-context (format "Var %s/app-context not initialized" *ns*))
+
+
+(defn ensure-init
+  "Ensure that app-context is initialized."
+  []
+  (when (string? app-context)
+    (init))
+  (when (string? app-context)
+    (throw (ex-info "Failed to ensure initialization. Add `bract.core.dev/record-context!` to your inducer list."
+             {}))))

 (defn record-context!
@@ -64,6 +74,7 @@
 (defn deinit
   "De-initialize application. Throw error if app-context is not initialized."
   []
+  (ensure-init)
   (util/expected map? "app-context to be initialized as map using inducer bract.core.dev/record-context!" app-context)
   (let [f (config/ctx-deinit app-context)]
     (echo/with-latency-capture "De-initializing application"
@@ -73,6 +84,7 @@
 (defn start
   "Launch application. Throw error if app-context is not initialized."
   []
+  (ensure-init)
   (util/expected map? "app-context to be initialized as map using inducer bract.core.dev/record-context!" app-context)
   (echo/with-latency-capture "Launching application"
     (-> (config/ctx-config app-context)
@@ -85,6 +97,7 @@
 (defn stop
   "Stop the started application."
   []
+  (ensure-init)
   (let [stopper (config/ctx-stopper app-context)]
     (echo/with-latency-capture "Stopping the started application"
       (stopper))))


Better layout for t1 net nodes.

Better layout for t1 net nodes.

--- src/cbfg/world/t1.cljs
+++ src/cbfg/world/t1.cljs
@@ -293,7 +293,9 @@
            (reset! done true))
       (wait-done done)
       (prog-evt world :kv-server {:server-addr server-addr :server-port port}
-                #(update-in % [:servers server-addr] conj port)))))
+                #(update-in % [:servers server-addr] conj port))))
+  (addr-override-xy server-addr
+                    300 (+ 20 (* 120 (dec (count (:servers @prog-curr)))))))

 (defn kv-client [client-addr server-addr server-port]
   (let [world (:world @prog-base)
@@ -310,4 +312,6 @@
                          {:client-addr client-addr
                           :server-addr server-addr
                           :server-port server-port
-                          :req-ch req-ch}))))
+                          :req-ch req-ch})))
+  (addr-override-xy client-addr
+                    20 (+ 20 (* 120 (dec (count (:clients @prog-curr)))))))


More DRY with item-multi-op helper func.

--- src/cbfg/world/t2.cljs
+++ src/cbfg/world/t2.cljs
@@ -16,61 +16,50 @@
     nil
     (js/parseInt s)))

+(defn item-multi-op [actx coll-state m req-fn]
+  (let [res-ch (achan actx)]
+    (act item-op actx
+         (aput item-op (:kvs-mgr-ch coll-state)
+               (dissoc (req-fn res-ch) :res-ch))
+         (aput item-op (:res-ch m)
+               (dissoc (atake item-op res-ch) :more))
+         (while (atake item-op res-ch)))
+    [coll-state nil]))
+
 (defn coll-handler [actx coll-state m]
   (case (:op m)
     "coll-state"
     [coll-state (assoc m :status :ok :value coll-state)]

     "item-get"
-    (let [res-ch (achan actx)]
-      (act item-get actx
-           (aput item-get (:kvs-mgr-ch coll-state)
-                 (assoc (dissoc m :res-ch)
-                   :op :multi-get
-                   :kvs-ident (:kvs-ident coll-state)
-                   :key-reqs [{:res-ch res-ch
-                               :key (:key m)
-                               :sq (parse-sq (:sq m))}]))
-           (aput item-get (:res-ch m)
-                 (dissoc (atake item-get res-ch) :more))
-           (while (atake item-get res-ch)))
-      [coll-state nil])
+    (item-multi-op actx coll-state m
+                   #(assoc m :op :multi-get
+                           :kvs-ident (:kvs-ident coll-state)
+                           :key-reqs [{:res-ch %
+                                       :key (:key m)
+                                       :sq (parse-sq (:sq m))}]))

     "item-set"
-    (let [res-ch (achan actx)]
-      (act item-set actx
-           (aput item-set (:kvs-mgr-ch coll-state)
-                 (assoc (dissoc m :res-ch)
-                   :op :multi-change
-                   :kvs-ident (:kvs-ident coll-state)
-                   :change-reqs [{:res-ch res-ch
-                                  :change-fn
-                                  (cbfg.kvs/mutate-entry
-                                   {:key (:key m)
-                                    :val (:val m)
-                                    :sq (parse-sq (:sq m))})}]))
-           (aput item-set (:res-ch m)
-                 (dissoc (atake item-set res-ch) :more))
-           (while (atake item-set res-ch)))
-        [coll-state nil])
+    (item-multi-op actx coll-state m
+                   #(assoc m :op :multi-change
+                           :kvs-ident (:kvs-ident coll-state)
+                           :change-reqs [{:res-ch %
+                                          :change-fn
+                                          (cbfg.kvs/mutate-entry
+                                           {:key (:key m)
+                                            :val (:val m)
+                                            :sq (parse-sq (:sq m))})}]))

     "item-del"
-    (let [res-ch (achan actx)]
-      (act item-del actx
-           (aput item-del (:kvs-mgr-ch coll-state)
-                 (assoc (dissoc m :res-ch)
-                   :op :multi-change
-                   :kvs-ident (:kvs-ident coll-state)
-                   :change-reqs [{:res-ch res-ch
-                                  :change-fn
-                                  (cbfg.kvs/mutate-entry
-                                   {:deleted true
-                                    :key (:key m)
-                                    :sq (parse-sq (:sq m))})}]))
-           (aput item-del (:res-ch m)
-                 (dissoc (atake item-del res-ch) :more))
-           (while (atake item-del res-ch)))
-        [coll-state nil])
+    (item-multi-op actx coll-state m
+                   #(assoc m :op :multi-change
+                           :kvs-ident (:kvs-ident coll-state)
+                           :change-reqs [{:res-ch %
+                                          :change-fn
+                                          (cbfg.kvs/mutate-entry
+                                           {:deleted true
+                                            :key (:key m)
+                                            :sq (parse-sq (:sq m))})}]))

     [coll-state (assoc m :status :invalid :status-info :invalid-op)]))


Accept either 'target' or 'targets' in SQL query

--- src/clj/cglossa/db.clj
+++ src/clj/cglossa/db.clj
@@ -15,9 +15,9 @@
   OrientDB database, and returns the query result as a seq. The params argument
   is a hash map with the following optional keys:

-  * targets: A vertex ID, or a sequence of such IDs, to use as the target in the
-  SQL query (e.g. '#12:1' or ['#12:1' '#12:2']), replacing the placeholder #TARGET
-  or #TARGETS (e.g. 'select from #TARGETS').
+  * target or targets: A vertex ID, or a sequence of such IDs, to use as the target
+  in the SQL query (e.g. '#12:1' or ['#12:1' '#12:2']), replacing the placeholder
+  #TARGET or #TARGETS (e.g. 'select from #TARGETS').

   * strings: Map with strings that will be interpolated into the SQL query, replacing
   placeholders with the same name preceded by an ampersand. Restricted to
@@ -38,9 +38,8 @@
    (query sql {}))
   ([sql params]
    (let [graph      (get-graph)
-         targets    (if (:targets params)
-                      (-> (:targets params) vector flatten vec)
-                      [])
+         t          (or (:target params) (:targets params))
+         targets    (if t (-> t vector flatten vec) [])
          _          (doseq [target targets] (assert (re-matches #"#\d+:\d+" target)
                                                     (str "Invalid target: " target)))
          strings    (:strings params)


putting 3 arg parallelize back

putting 3 arg parallelize back

--- src/clj/flambo/api.clj
+++ src/clj/flambo/api.clj
@@ -47,9 +47,9 @@
 (defn text-file [spark-context filename]
   (.textFile spark-context filename))

-(defn parallelize  [spark-context lst] (.parallelize spark-context lst)
-
-  )
+(defn parallelize
+  ([spark-context lst] (.parallelize spark-context lst))
+  ([spark-context lst num-slices] (.parallelize spark-context lst num-slices)))

 ;;; Transformations


Return nil if the file being loaded doesn't exist

--- src/doctopus/storage/impls/permanent_fs.clj
+++ src/doctopus/storage/impls/permanent_fs.clj
@@ -26,7 +26,9 @@
   [key]
   (let [file-handle (binding [fs/*cwd* root] (fs/file key))
         file-name (str (.getPath file-handle) "/")]
-    [key {"/" (bidi-ring/->Files {:dir file-name})}]))
+    (if (fs/exists? file-handle)
+      [key {"/" (bidi-ring/->Files {:dir file-name})}]
+      nil)))

 (defn remove-fn
   [key]


whitespace

whitespace

--- build.boot
+++ build.boot
@@ -1,7 +1,6 @@
 (set-env!
   :source-paths #{"src"}
-  :dependencies '[
-                  ; dev
+  :dependencies '[; dev
                   [adzerk/bootlaces "0.1.13" :scope "test"]
                   [adzerk/boot-test "1.0.4"  :scope "test"]
                   [str-to-argv      "0.1.0"  :score "test"]


Setting up dev task that runs only Clojure tests

--- build.boot
+++ build.boot
@@ -122,7 +122,7 @@
                     :page "blog/index.html")))

 (deftask dev
-  "Run dev environment."
+  "Run full dev environment."
   [H host HOST str]
   (comp (serve)
         (repl)
@@ -140,6 +140,13 @@
                                  :preloads '[zetawar.dev]
                                  :parallel-build true})
         (target)))
+
+(deftask clj-dev
+  "Watch code and run Clojure tests without building ClojureScript or site."
+  [H host HOST str]
+  (comp (repl)
+        (watch)
+        (test)))

 (deftask ci
   "Run CI tests."


upgrade to latest versions of clojure and boot

--- build.boot
+++ build.boot
@@ -1,15 +1,15 @@
 (def +project+ 'migae/boot-gae)
-(def +version+ "0.2.0-SNAPSHOT")
+(def +version+ "0.2.1-SNAPSHOT")

 (set-env!
  :source-paths #{"src"}
  :resource-paths #{"src"}
  ;; :asset-paths #{"src"}
- :repositories #(conj % ["maven-central" {:url "http://mvnrepository.com"}]
-                      ["central" "http://repo1.maven.org/maven2/"])
- :dependencies   '[[org.clojure/clojure "1.8.0" :scope "provided"]
-                   [boot/core "2.7.1" :scope "provided"]
-                   [boot/pod "2.7.1" :scope "provided"]
+ :repositories #(conj % ["maven-central" {:url "https://mvnrepository.com"}]
+                      ["central" "https://repo1.maven.org/maven2/"])
+ :dependencies   '[[org.clojure/clojure "1.10.0" :scope "provided"]
+                   [boot/core "2.8.2" :scope "provided"]
+                   [boot/pod "2.8.2" :scope "provided"]
                    [me.raynes/fs "1.4.6"]
                    [stencil "0.5.0"]
                    [adzerk/boot-test "1.0.7" :scope "test"]


[Fix #359] Use new transit-cljs with MapEntry writing support (#380)

--- build.boot
+++ build.boot
@@ -8,7 +8,7 @@
  :dependencies (into [['org.clojure/clojure       "1.9.0"]
                       ['org.clojure/clojurescript +clojurescript-version+]]
                      '[[org.clojure/tools.reader    "1.2.2"]
-                       [com.cognitect/transit-cljs  "0.8.243"]
+                       [com.cognitect/transit-cljs  "0.8.248"]
                        [malabarba/lazy-map          "1.3"]
                        [fipp                        "0.6.12"]
                        [org.clojure/test.check      "0.10.0-alpha2" :scope "test"]


Correct test and resource path setting

Correct test and resource path setting

--- build.boot
+++ build.boot
@@ -19,8 +19,8 @@
                                 ["USERNAME"    "PASSWORD"]
                                 [get-cleartext get-password])]

-  (set-env! :source-paths #{"src/cljs" "test/clj" "test/cljs"}
-            :resource-paths #{"src/clj" "resources"}
+  (set-env! :source-paths #{"src/clj" "src/cljs"}
+            :resource-paths #{"resources"}
             :dependencies '[[org.clojure/clojure "1.8.0"]

                             ;; Core app dependencies
@@ -118,7 +118,7 @@
   identity)

 (deftask development []
-  (set-env! :source-paths #(conj % "dev-src")
+  (set-env! :source-paths #(conj % "dev-src/clj")
             :resource-paths #(conj %  "dev-resources"))
   (task-options! cljs {:optimizations :none}
                  repl {:init-ns 'comic-reader.dev}
@@ -132,7 +132,7 @@
         (run)))

 (deftask testing []
-  (set-env! :source-paths #(conj % "test"))
+  (set-env! :source-paths #(conj %  "test/clj" "test/cljs"))
   identity)

 ;;; This prevents a name collision WARNING between the test task and


Debug travis-config

Debug travis-config

--- build.boot
+++ build.boot
@@ -46,6 +46,7 @@

 (defn run-commands
   [& command-sets]
+  (util/info (apply format-commands command-sets))
   (->> command-sets
        (apply format-commands)
        (apply util/dosh)))


Add solver items to "system restart" list

--- build.boot
+++ build.boot
@@ -174,7 +174,7 @@
    (sass)
    (sift :move {#"herder/sass/(.*)" "resources/public/css/$1"})
    (target :no-clean true)
-   (system :sys #'dev-system :auto true :files ["lobos.clj" "handler.clj" "solver.clj" "systems.clj"])
+   (system :sys #'dev-system :auto true :files ["lobos.clj" "handler.clj" "solver.clj" "systems.clj" "run.clj" "rules.drl"])
    (make-solver)
    (testing)
    (test)


Update boot-reload

Update boot-reload

--- build.boot
+++ build.boot
@@ -15,7 +15,7 @@
  :dependencies
  '[[adzerk/boot-cljs "1.7.48-5" :scope "test"]
    [adzerk/boot-cljs-repl "0.3.0" :scope "test"]
-   [adzerk/boot-reload "0.4.0" :scope "test"]
+   [adzerk/boot-reload "0.4.9" :scope "test"]
    [weasel "0.7.0" :scope "test"] ;; Websocket Server
    [deraen/boot-sass "0.2.0" :scope "test"]
    [reloaded.repl "0.2.1" :scope "test"]


Add some new dependencies

Add some new dependencies

Switching to use data.avl for the faster sorted-set-by implementation
that it will give me, with logarithmic time splitting by key.

Also some dev dependencies for debugging re-frame

--- build.boot
+++ build.boot
@@ -61,6 +61,7 @@
                             [re-frame "0.9.2" :exclusions
                              [[org.clojure/clojurescript
                                :extension "jar"]]]
+                            [org.clojure/data.avl "0.0.17"]
                             [cljsjs/waypoints "4.0.0-0"]
                             [secretary "1.2.3"]
                             [cljs-ajax "0.5.8"]
@@ -70,8 +71,9 @@
                             [aysylu/loom "1.0.0" :scope "test"]

                             [ring/ring-mock "0.3.0" :scope "test"]
-                            [figwheel-sidecar "0.5.9" :scope "test"]
                             [devcards "0.2.1-7" :scope "test"]
+                            [day8/re-frame-tracer "0.1.1-SNAPSHOT" :scope "test"]
+                            [org.clojars.stumitchell/clairvoyant "0.2.0" :scope "test"]
                             [com.cemerick/piggieback "0.2.1" :scope "test"]
                             [weasel "0.7.0"     :scope "test"]
                             [org.clojure/tools.nrepl "0.2.12" :scope "test"]


Minor

Minor


Update paren-soup

Update paren-soup

--- build.boot
+++ build.boot
@@ -5,7 +5,7 @@
                   [adzerk/boot-cljs "1.7.228-2" :scope "test"]
                   ; cljs deps
                   [org.clojure/clojurescript "1.9.473" :scope "test"]
-                  [paren-soup "2.8.5" :scope "test"]
+                  [paren-soup "2.8.6" :scope "test"]
                   [cljsjs/codemirror "5.19.0-0" :scope "test"]
                   ; clj deps
                   [org.clojure/clojure "1.9.0-alpha14"]


bump version

bump version

--- build.boot
+++ build.boot
@@ -5,7 +5,7 @@
                   [tailrecursion/boot-useful "0.1.0-SNAPSHOT" :scope "test"]
                   [org.clojure/clojurescript "0.0-2371"       :scope "test"]])

-(def +VERSION+ "0.0-2371-2")
+(def +VERSION+ "0.0-2371-3")

 (require
   '[tailrecursion.boot-useful :refer [push-snapshot push-release]])


Clean u comments in build.boot

Clean u comments in build.boot

--- build.boot
+++ build.boot
@@ -7,7 +7,7 @@
           :dependencies '[[adzerk/bootlaces "0.1.13" :scope "test"]
                           [adzerk/boot-test "RELEASE" :scope "test"]
                           ;; Stick with whichever version of netty this inherits.
-                          ;; That library isn't shy about breaking backwards compatibility
+                          ;; netty isn't shy about breaking backwards compatibility
                           ;; between build versions.
                           [aleph "0.4.7-alpha3"]
                           [frereth/weald "0.0.3-SNAPSHOT"]


Use bootlaces for pushing to Clojars

Use bootlaces for pushing to Clojars

--- build.boot
+++ build.boot
@@ -2,9 +2,10 @@
 (def version "0.1.0-SNAPSHOT")

 (set-env! :resource-paths #{"resources" "src"}
-          :source-paths   #{"test"}
-          :dependencies   '[[org.clojure/clojure "RELEASE"]
-                            [adzerk/boot-test "RELEASE" :scope "test"]])
+          :source-paths #{"test"}
+          :dependencies '[[org.clojure/clojure "RELEASE"]
+                          [adzerk/boot-test "RELEASE" :scope "test"]
+                          [adzerk/bootlaces "0.1.13"]])

 (task-options!
  pom {:project     project
@@ -20,4 +21,5 @@
   []
   (comp (pom) (jar) (install)))

-(require '[adzerk.boot-test :refer [test]])
+(require '[adzerk.boot-test :refer [test]]
+         '[adzerk.bootlaces :refer :all])


Revamp and simplify block data ns.

Revamp and simplify block data ns.

--- src/blocks/data.clj
+++ src/blocks/data.clj
@@ -3,7 +3,9 @@

   Blocks have two primary attributes, `:id` and `:size`. The block identifier
   is a multihash with the digest identifying the content. The size is the
-  number of bytes in the block content.
+  number of bytes in the block content. Blocks also have a `:stored-at` value
+  giving the instant they were persisted, but this does not affect equality or
+  the block's hash code.

   Internally, blocks may reference their content in-memory as a byte array, or
   a _content reader_ which constructs new input streams for the block data on
@@ -11,7 +13,7 @@
   blocks with readers are _lazy blocks_."
   (:require
     [byte-streams :as bytes]
-    [multiformats.hash :as mhash])
+    [multiformats.hash :as multihash])
   (:import
     blocks.data.PersistentBytes
     (java.io
@@ -22,31 +24,25 @@
     org.apache.commons.io.input.BoundedInputStream))

-(defn persistent-bytes?
-  "True if the argument is a persistent byte array."
-  [x]
-  (instance? PersistentBytes x))
-
+;; ## Block Type

 (deftype Block
   [^Multihash id
    ^long size
+   ^Instant stored-at
    content
-   _attrs
-   _meta]
-
-  ;:load-ns true
+   _meta
+   ^:unsynchronized-mutable _hash]
+
+  :load-ns true

   java.lang.Object

   (toString
     [this]
-    (format "Block[%s %s %s]"
-            id size (cond
-                      (persistent-bytes? content) "*"
-                      content "~"
-                      :else "!")))
+    (format "Block[%s %s %s]" id size stored-at))
+

   (equals
     [this that]
@@ -54,115 +50,59 @@
       (or (identical? this that)
           (when (identical? (class this) (class that))
             (let [that ^Block that]
-              (and (= id     (.id     that))
-                   (= size   (.size   that))
-                   (= _attrs (._attrs that))))))))
+              (and (= id   (.id   that))
+                   (= size (.size that))))))))
+

   (hashCode
     [this]
-    (hash [(class this) id size _attrs]))
+    (let [hc _hash]
+      (if (zero? hc)
+        (let [hc (hash [(class this) id size])]
+          (set! _hash hc)
+          hc)
+        hc)))

   java.lang.Comparable

   (compareTo
     [this that]
-    (compare [id size _attrs]
-             [(:id that) (:size that)
-              (when (identical? (class this) (class that))
-                (._attrs ^Block that))]))
+    (if (= id (:id that))
+      (if (= size (:size that))
+        (if (= stored-at (:stored-at that))
+          0
+          (compare stored-at (:stored-at that)))
+        (compare size (:size that)))
+      (compare id (:id that))))

   clojure.lang.IObj

-  (meta [this] _meta)
+  (meta
+    [this]
+    _meta)
+

   (withMeta
     [this meta-map]
-    (Block. id size content _attrs meta-map))
-
-
-  ; TODO: IKeywordLookup?
+    (Block. id size content stored-at meta-map _hash))
+
+
   clojure.lang.ILookup

   (valAt
     [this k]
     (.valAt this k nil))
+

   (valAt
     [this k not-found]
     (case k
       :id id
       :size size
-      (get _attrs k not-found)))
-
-
-  clojure.lang.IPersistentMap
-
-  (count
-    [this]
-    (+ 2 (count _attrs)))
-
-  (empty
-    [this]
-    (Block. id size nil nil _meta))
-
-  (cons
-    [this element]
-    (cond
-      (instance? java.util.Map$Entry element)
-        (let [^java.util.Map$Entry entry element]
-          (.assoc this (.getKey entry) (.getValue entry)))
-      (vector? element)
-        (.assoc this (first element) (second element))
-      :else
-        (loop [result this
-               entries element]
-          (if (seq entries)
-            (let [^java.util.Map$Entry entry (first entries)]
-              (recur (.assoc result (.getKey entry) (.getValue entry))
-                     (rest entries)))
-            result))))
-
-  (equiv
-    [this that]
-    (.equals this that))
-
-  (containsKey
-    [this k]
-    (not (identical? this (.valAt this k this))))
-
-  (entryAt
-    [this k]
-    (let [v (.valAt this k this)]
-      (when-not (identical? this v)
-        (clojure.lang.MapEntry. k v))))
-
-  (seq
-    [this]
-    (seq (concat [(clojure.lang.MapEntry. :id id)
-                  (clojure.lang.MapEntry. :size size)]
-                 _attrs)))
-
-  (iterator
-    [this]
-    (clojure.lang.RT/iter (seq this)))
-
-  (assoc
-    [this k v]
-    (case k
-      (:id :size :content)
-        (throw (IllegalArgumentException.
-                 (str "Block " k " cannot be changed")))
-      (Block. id size content (assoc _attrs k v) _meta)))
-
-  (without
-    [this k]
-    (case k
-      (:id :size :content)
-        (throw (IllegalArgumentException.
-                 (str "Block " k " cannot be changed")))
-      (Block. id size content (not-empty (dissoc _attrs k)) _meta))))
+      :stored-at stored-at
+      not-found)))

 (defmethod print-method Block
@@ -184,7 +124,8 @@
   (read-range
     [reader start end]
     "Open an input stream that reads just bytes from `start` to `end`,
-    inclusive."))
+    inclusive. A `nil` for either value implies the beginning or end of the
+    stream, respectively."))

 (defn- bounded-input-stream
@@ -192,8 +133,11 @@
   range `start` to `end`."
   ^java.io.InputStream
   [^InputStream input start end]
-  (.skip input start)
-  (BoundedInputStream. input (- end start)))
+  (when (pos-int? start)
+    (.skip input start))
+  (if (pos-int? end)
+    (BoundedInputStream. input (- end (or start 0)))
+    input))

 (extend-protocol ContentReader
@@ -204,6 +148,7 @@
     [^PersistentBytes this]
     (.open this))

+
   (read-range
     [^PersistentBytes this start end]
     (bounded-input-stream (.open this) start end))
@@ -214,6 +159,7 @@
   (read-all
     [this]
     (this))
+

   (read-range
     [this start end]
@@ -227,14 +173,20 @@
   [^Block block start end]
   (let [content (.content block)]
     (when-not content
-      (throw (IOException. (str "Cannot open empty block " (:id block)))))
-    (if (and start end)
+      (throw (IOException. (str "Cannot open empty block " block))))
+    (if (or start end)
       (read-range content start end)
       (read-all content))))

-;; ## Utility Functions
+;; ## Content Functions
+
+(defn persistent-bytes?
+  "True if the argument is a persistent byte array."
+  [x]
+  (instance? PersistentBytes x))
+

 (defn byte-content?
   "True if the block has content loaded into memory as persistent bytes."
@@ -252,46 +204,6 @@
     (PersistentBytes/wrap (bytes/to-byte-array source))))

-(defn- resolve-hasher
-  "Resolves an algorithm designator to a hash function. Throws an exception on
-  invalid names or error."
-  [algorithm]
-  (cond
-    (nil? algorithm)
-      (throw (IllegalArgumentException.
-               "Cannot find hash function without algorithm name"))
-
-    (keyword? algorithm)
-      (if-let [hf (clojure.core/get digest/functions algorithm)]
-        hf
-        (throw (IllegalArgumentException.
-                 (str "Cannot map algorithm name " algorithm
-                      " to a supported hash function"))))
-
-    (ifn? algorithm)
-      algorithm
-
-    :else
-      (throw (IllegalArgumentException.
-               (str "Hash algorithm must be keyword name or direct function, got: "
-                    (pr-str algorithm))))))
-
-
-(defn checked-hasher
-  "Constructs a function for the given hash algorithm or function which checks
-  that the result is a `Multihash`."
-  [algorithm]
-  (let [hash-fn (resolve-hasher algorithm)]
-    (fn hasher
-      [source]
-      (let [id (hash-fn source)]
-        (when-not (instance? Multihash id)
-          (throw (RuntimeException.
-                   (str "Block identifier must be a Multihash, "
-                        "hashing algorithm returned: " (pr-str id)))))
-        id))))
-
-

 ;; ## Constructors

@@ -299,73 +211,92 @@
 (alter-meta! #'->Block assoc :private true)

-(defn lazy-block
+(defn- now
+  "Return the current instant in time.
+
+  This is mostly useful for rebinding during tests."
+  ^Instant
+  []
+  (Instant/now))
+
+
+(defn create-block
   "Creates a block from a content reader. The simplest version is a no-arg
   function which should return a new `InputStream` to read the full block
   content. The block is given the id and size directly, without being checked."
-  ^blocks.data.Block
-  [id size reader]
-  (->Block id size reader nil nil))
+  [id size stored-at content]
+  (when-not (instance? Multihash id)
+    (throw (ex-info "Block id must be a multihash"
+                    {:id id, :size size, :stored-at stored-at})))
+  (when-not (pos-int? size)
+    (throw (ex-info "Block size must be a positive integer"
+                    {:id id, :size size, :stored-at stored-at})))
+  (when-not stored-at
+    (throw (ex-info "Block must have a stored-at instant"
+                    {:id id, :size size, :stored-at stored-at})))
+  (when-not content
+    (throw (ex-info "Block must have a content reader"
+                    {:id id, :size size, :stored-at stored-at})))
+  (Block. id size stored-at content nil 0))

 (defn load-block
   "Creates a block by reading a source into memory. The block is given the id
   directly, without being checked."
-  ^blocks.data.Block
-  [id source]
-  (let [content (collect-bytes source)]
-    (when (pos? (count content))
-      (->Block id (count content) content nil nil))))
+  ([id source]
+   (load-block id (now) source))
+  ([id stored-at source]
+   (let [content (collect-bytes source)
+         size (count content)]
+     (when (pos? size)
+       (create-block id size stored-at content)))))

 (defn read-block
   "Creates a block by reading the source into memory and hashing it."
-  ^blocks.data.Block
   [algorithm source]
   ; OPTIMIZE: calculate the hash while reading the content in one pass.
-  (let [hash-fn (checked-hasher algorithm)
-        content (collect-bytes source)]
-    (when (pos? (count content))
-      (->Block (hash-fn (read-all content))
-               (count content)
-               content
-               nil
-               nil))))
-
-
-(defn wrap-block
+  (let [hash-fn (or (multihash/functions algorithm)
+                    (throw (IllegalArgumentException.
+                             (str "No digest function found for algorithm "
+                                  algorithm))))
+        content (collect-bytes source)
+        size (count content)]
+    (when (pos? size)
+      (create-block (hash-fn (read-all content)) size (now) content))))
+
+
+(defn merge-blocks
+  "Creates a new block by merging together two blocks representing the same
+  content. Block ids and sizes must match. The new block's content and
+  timestamp come from the second block, and any metadata is merged together."
+  [^Block left ^Block right]
+  (when (not= (.id left) (.id right))
+    (throw (ex-info
+             (str "Cannot merge blocks with differing ids " (.id left)
+                  " and " (.id right))
+             {:left left, :right right})))
+  (when (not= (.size left) (.size right))
+    (throw (ex-info
+             (str "Cannot merge blocks with differing sizes " (.size left)
+                  " and " (.size right))
+             {:left left, :right right})))
+  (Block. (.id right)
+          (.size right)
+          (.stored-at right)
+          (.content right)
+          (not-empty (merge (._meta left) (._meta right)))
+          0))
+
+
+(defn wrap-content
   "Wrap a block's content by calling `f` on it, returning a new block with the
   same id and size."
   ^blocks.data.Block
   [^Block block f]
-  (->Block (.id block)
-           (.size block)
-           (f (.content block))
-           (._attrs block)
-           (._meta block)))
-
-
-(defn clean-block
-  "Creates a version of the given block without extra attributes or metadata."
-  [^Block block]
-  (->Block (.id block)
-           (.size block)
-           (.content block)
-           nil
-           nil))
-
-
-(defn merge-blocks
-  "Creates a new block by merging together two blocks representing the same
-  content. Block ids and sizes must match. The new block's content comes from
-  the second block, and any extra attributes and metadata are merged together."
-  [^Block a ^Block b]
-  (when (not= (.id a) (.id b))
-    (throw (IllegalArgumentException.
-             (str "Cannot merge blocks with differing ids " (.id a)
-                  " and " (.id b)))))
-  (->Block (.id b)
-           (.size b)
-           (.content b)
-           (not-empty (merge (._attrs a) (._attrs b)))
-           (not-empty (merge (._meta  a) (._meta  b)))))
+  (Block. (.id block)
+          (.size block)
+          (.stored-at block)
+          (f (.content block))
+          (._meta block)
+          0))


Formatting

Formatting

--- src/clojure/clojurewerkz/cassaforte/client.clj
+++ src/clojure/clojurewerkz/cassaforte/client.clj
@@ -20,10 +20,10 @@
    * tuning load balancing, retries, reconnection strategies and consistency settings
    * preparing and executing queries constructed via DSL
    * working with executing results"
-  (:require [clojure.java.io :as io]
-            [clojurewerkz.cassaforte.policies :as cp]
+  (:require [clojure.java.io                    :as io]
+            [clojurewerkz.cassaforte.policies   :as cp]
             [clojurewerkz.cassaforte.conversion :as conv]
-            [qbits.hayt.cql :as hayt])
+            [qbits.hayt.cql                     :as hayt])
   (:import [com.datastax.driver.core Statement ResultSet ResultSetFuture Host Session Cluster
             Cluster$Builder SimpleStatement PreparedStatement HostDistance PoolingOptions
             SSLOptions ProtocolOptions$Compression]


make limit 24 hrs

make limit 24 hrs

git-svn-id: 03a8048b5ee8463be5048a3801110fb50f378627@8525 d0ab736e-dc22-4aeb-8dc9-08def0aa14fd

--- buildscripts/buildcheck/src/buildcheck/core.clj
+++ buildscripts/buildcheck/src/buildcheck/core.clj
@@ -30,7 +30,7 @@
        file-seq
        (filter #(= "BuildLog.htm" (.getName %)))
        (filter #(.contains (.getAbsolutePath %) "Release"))
-       (filter #(not (old-file? % 1)))))
+       (filter #(not (old-file? % 24)))))

 (defn vs-log-text [f]
   (->> (slurp f :encoding "utf-16")


fixed now?

fixed now?

--- src/mafia_votecount/scraper/team_liquid.clj
+++ src/mafia_votecount/scraper/team_liquid.clj
@@ -265,10 +265,10 @@
                      (drop-while #(< (first %) first-index))
                      (into (sorted-map)))
         cycle-posts (cycle-changes indexed hosts)
-        first-cycle-type (-> (first cycle-changes)
-                             (val)
-                             (:message)
-                             (is-cycle-change?))
+       ; first-cycle-type (-> (first cycle-changes)
+       ;                      (val)
+       ;                      (:message)
+       ;                      (is-cycle-change?))
         day-ranges (-> cycle-posts
                        (keys)
                        ; tähän if cycle-type :night vielä???


Refactor execution of tested fn.

Refactor execution of tested fn.

--- src/cli4clj/cli.clj
+++ src/cli4clj/cli.clj
@@ -181,15 +181,18 @@
   [cmds prompt-string]
   (create-repl-read-fn cmds))

+(defn exec-tested-fn
+  [tested-fn]
+  (binding [*read-factory* create-repl-read-test-fn]
+    (tested-fn)))
+
 (defn test-cli-stdout
   [tested-fn in-cmds]
-  (binding [*read-factory* create-repl-read-test-fn]
-    (.trim (with-out-str (with-in-str (cmd-vector-to-test-input-string in-cmds) (tested-fn))))))
+  (.trim (with-out-str (with-in-str (cmd-vector-to-test-input-string in-cmds) (exec-tested-fn tested-fn)))))

 (defn test-cli-stderr
   [tested-fn in-cmds]
-  (binding [*read-factory* create-repl-read-test-fn]
-    (.trim (with-err-str (with-out-str (with-in-str (cmd-vector-to-test-input-string in-cmds) (tested-fn)))))))
+  (.trim (with-err-str (with-out-str (with-in-str (cmd-vector-to-test-input-string in-cmds) (exec-tested-fn tested-fn))))))

 (defn expected-string
   ([expected-lines]


Wrappers pass all arguments to API functions

--- src/devtools/core.cljs
+++ src/devtools/core.cljs
@@ -12,13 +12,13 @@
 (deftype CLJSDevtoolsFormatter [header hasBody body])

 (defn- monitor-api-calls [name api-call]
-  (fn [value]
+  (fn [& args]
     (if-not *monitor-enabled*
-      (api-call value)                                      ; raw API call
+      (apply api-call args)                                 ; raw API call
       (do
-        (debug/log (debug/logger name) value)
+        (debug/log (debug/logger name) (json/serialize (clj->js args)))
         (debug/indent!)
-        (let [api-response (api-call value)                 ; wrapped API call
+        (let [api-response (apply api-call args)            ; wrapped API call
               api-response-filter (fn [key value] (if (= key "object") "##REF##" value))]
           (debug/log (debug/logger name) (str "=> " (js->clj (json/parse (json/serialize api-response api-response-filter)))))
           (debug/unindent!)
@@ -27,11 +27,11 @@
 (defn- sanitize
   "wraps our api-call in try-catch block to prevent leaking of exceptions if something goes wrong"
   [_ api-call]
-  (fn [value]
+  (fn [& args]
     (if-not *sanitizer-enabled*
-      (api-call value)                                      ; raw API call
+      (apply api-call args)                                 ; raw API call
       (try
-        (api-call value)                                    ; wrapped API call
+        (apply api-call args)                               ; wrapped API call
         (catch :default e
           (debug/log-exception e)
           nil)))))


Do not force existing viewers to authors if current org is not on premium. Let them be.

--- src/oc/storage/api/access.clj
+++ src/oc/storage/api/access.clj
@@ -69,8 +69,7 @@
                    member? :member
                    :else   :anonymous)
         base-access {:role role
-                     :premium? premium?}
-        premium-viewer-access (if premium? :viewer :author)]
+                     :premium? premium?}]
     (cond
       ;; an admin of this org's team
       ((set admin) (:team-id org))
@@ -82,7 +81,7 @@

       ;; a team member of this org
       ((set teams) (:team-id org))
-      (merge base-access {:access-level premium-viewer-access})
+      (merge base-access {:access-level :viewer})

       ;; public access to orgs w/ at least 1 public board AND that allow public boards
       (and
@@ -133,9 +132,7 @@
                                :author
                                :viewer)
         base-access {:role     role
-                     :premium? premium?}
-        ;; viewer role is allowed only on premium
-        premium-viewer-role (if premium? :viewer :author)]
+                     :premium? premium?}]
     (cond

       ;; a named author of this private board
@@ -157,11 +154,11 @@

       ;; a named viewer of this board
       (and (= board-access :private) (board-viewers user-id))
-      (merge base-access {:access-level premium-viewer-role})
+      (merge base-access {:access-level :viewer})

       ;; a team member on a non-private board
       (and (not= board-access :private) ((set teams) (:team-id org)))
-      (merge base-access {:access-level premium-viewer-role})
+      (merge base-access {:access-level :viewer})

       ;; anyone else on a public board IF the org allows public boards
       (and (= board-access :public) (not (-> org :content-visibility :disallow-public-board)))


Use the status present on the entries while creating a board, default to published.

--- src/oc/storage/api/boards.clj
+++ src/oc/storage/api/boards.clj
@@ -101,7 +101,7 @@
     (try
       (let [notifications (:private-notifications board-map)
             entry-data (map #(valid-entry-with-board?
-                               conn (assoc % :status "published") author) (:entries board-map))
+                               conn (assoc % :status (or (:status %) "published")) author) (:entries board-map))
             board-data (-> board-map
                         (dissoc :private-notifications :note)
                         (assoc :entries entry-data))]
@@ -178,7 +178,7 @@
       ;; Add any entries specified in the request
       (doseq [entry entries]
         (let [fixed-entry (-> entry
-                              (assoc :status "published")
+                              (assoc :status (or (:status entry) "published"))
                               (assoc :board-uuid board-uuid))
               entry-action (if (entry-res/get-entry conn (:uuid entry))
                              :update


Fix job creation.

Fix job creation.

--- src/onyx/coordinator/impl.clj
+++ src/onyx/coordinator/impl.clj
@@ -63,18 +63,18 @@

 (defmethod extensions/plan-job ZooKeeper
   [sync catalog workflow tasks]
-  (let [job-id (extensions/create sync :job)
-        workflow-path (extensions/create-at sync :workflow job-id)
-        catalog-path (extensions/create-at sync :catalog job-id)]
+  (let [job (extensions/create sync :job)
+        workflow-path (extensions/create-at sync :workflow (:uuid job))
+        catalog-path (extensions/create-at sync :catalog (:uuid job))]

     (extensions/write-place sync workflow-path workflow)
     (extensions/write-place sync catalog-path catalog)

     (doseq [task tasks]
-      (let [place (extensions/create-at sync :task job-id)]
+      (let [place (extensions/create-at sync :task (:uuid job))]
         (extensions/write-place sync place (serialize-task task))))

-    job-id))
+    (:uuid job)))

 (defmethod extensions/mark-offered ZooKeeper
   [sync task peer-data nodes]


issue-350: Differentiate feed source in judgements

The source key in judgements should reflect the feed source.

--- src/ctia/import/threatgrid/feed/judgements.clj
+++ src/ctia/import/threatgrid/feed/judgements.clj
@@ -6,9 +6,9 @@
             [clj-time.format :as f]
             [clj-http.client :as http]))

-(defn feed-judgements
+(defn transform
   "Extract Judgement objects from a list of TG feed entries"
-  [feed-entries observable-type observable-field & {:keys [confidence source
+  [feed-entries observable-type observable-field source & {:keys [confidence
                                                            priority
                                                            severity
                                                            days-valid
@@ -16,7 +16,6 @@
                                                     :or {confidence "High"
                                                          priority 90
                                                          severity 100
-                                                         source "threatgrid-feed"
                                                          days-valid 30
                                                          disposition 2}}]
   (map
@@ -40,21 +39,12 @@
         }))
    feed-entries))

-(defn entries->judgements
-  [entries observable-type observable-field & {:as options}]
-  (apply feed-judgements entries
-         observable-type observable-field options))
-
 (defn feed->judgements
-  [feed]
-  (let [{:keys [entries]
-         {observable-type :type
-          observable-field :field} :observable} feed]
-    (entries->judgements entries observable-type observable-field)))
-
-(defn feed-file->judgements
-  [file observable-type observable-field & {:as options}]
-  (let [entries (json/parse-string (slurp file) true)]
-    (entries->judgements entries observable-type observable-field options)))
-
-;; see feed.clj for usage example
+  [{:keys [entries title]
+    {observable-type :type
+     observable-field :field
+     :as observable} :observable :as feed}]
+  (transform entries
+             observable-type
+             observable-field
+             (str "Threat Grid " title " feed")))


squash! Move the predicate functions in to their own ns -- it seems likely that we'll wind up with a bunch of these. Also, remove the note about expecting a java.io.File from the docstrings -- our schmancy multimethod `is-type` obviates that.

--- src/doctopus/files.clj
+++ src/doctopus/files.clj
@@ -1,38 +1,18 @@
 (ns doctopus.files
-  (:require [me.raynes.fs :as fs]
-            [clojure.string :as str]))
+  "Tools for managing files.
+
+We have: tools for walking a directory and returning only files matching a predicate.
+
+We probably want, but don't yet have: tools for managing local temp (making
+temp dirs, moving around any output generated, cleaning up later)."
+  (:require [clojure.string :as str]
+            [doctopus.files.predicates :refer [markdown]]
+            [me.raynes.fs :as fs]))
+
+;; I think we've pretty much captured this by now? -- RMD Sun Mar 15 20:37:46 2015

 ;; THIS IS TOTALLY DATABASS
 ;; (pprint (fs/walk vector "/Users/rossdonaldson/Code/doctopus/src/"))
-
-(defmulti is-type?
-  "Returns true if the type of the file matches the type given."
-  (fn [fobj regex] (class fobj)))
-
-(defmethod is-type? java.lang.String
-  [file-string type-regex]
-  (if (re-find type-regex file-string)
-    true
-    false))
-
-(defmethod is-type? java.io.File
-  [file-handle type-regex]
-  (is-type? (.getPath file-handle) type-regex))
-
-
-(defn markdown?
-  "returns true only for type files. Expects a Java.Util.FileHandle"
-  [file-handle]
-  (is-type? file-handle #"(?i)md|markdown|mdown$"
-            ))
-
-
-(defn restructured-text?
-  "returns true only for type files. Expects a Java.Util.FileHandle"
-  [file-handle]
-  (is-type? #"(?i)rst|rest$" (.getPath file-handle)
-            ))
-

 (defn filter-the-docs
   "Vector Triple of (path, directories, files), and function that filters relevant doc-types"


Let both alloc-id and free-id take an optional action-fn which will be executed within the allocation/deallocation transaction. This allows fns with side-effects to have their execution tightly bound the the allocation mechanism such that they cannot be incorrectly interleaved in a concurrent context.

--- src/overtone/sc/allocator.clj
+++ src/overtone/sc/allocator.clj
@@ -1,4 +1,5 @@
 (ns overtone.sc.allocator
+  (:require [overtone.log :as log])
   (:use [overtone.sc defaults]))

 ;; ## Allocators
@@ -43,10 +44,31 @@
     (let [gap-found (if (not (get bs idx)) (inc gap-found) 0)]
       (find-gap bs size (inc idx) gap-found limit))))

+(def action-fn-executor* (agent nil))
+
+(defn- execute-action-fn
+  "Execute action-fn and catch all exceptions - outputting them to the error
+  log. All actions are executed in sequence."
+  [action-fn caller-name]
+  (send action-fn-executor* (fn [_]
+                              (try
+                                (action-fn)
+                                (catch Exception e
+                                  (log/error "Exception in " caller-name " action-fn: " e "\nstacktrace: " (.printStackTrace e)))
+                                (finally nil)))))
+
 (defn alloc-id
-  "Allocate a new ID for the type corresponding to key."
-  ([k] (alloc-id k 1))
-  ([k size]
+  "Allocate a new ID for the type corresponding to key. Takes an optional
+  action-fn which it will evaluate in transaction with the allocation of the id.
+  Therefore there is no possibility of interleaving concurrent allocation of ids
+  and the execution of associated action-fns. Execution of action-fn is also
+  synchronised with the execution of free-id action-fns. Action-fn takes one
+  param - the newly allocated id.
+
+  Returns newly allocated id."
+  ([k] (alloc-id k 1 nil))
+  ([k size] (alloc-id k 1 nil))
+  ([k size action-fn]
      (let [bits  (get allocator-bits k)
            limit (get allocator-limits k)]
        (when-not bits
@@ -56,20 +78,26 @@
        (dosync
         (let [id (find-gap @bits size 0 0 limit)]
           (alter bits fill-gaps id size true)
+          (when action-fn (execute-action-fn #(action-fn id) "alloc-id"))
           id)))))

 ; The root group is implicitly allocated
 (defonce _root-group_ (alloc-id :node))

 (defn free-id
-  "Free the id of type key."
-  ([k id] (free-id k id 1))
-  ([k id size]
-  (let [bits (get allocator-bits k)
-        limit (get allocator-limits k)]
-    (dosync
-     (alter bits fill-gaps id size false)))))
-
+  "Free the id of type key. Takes an optional action-fn which it will evaluate
+  in transaction with the freeing of the id. Therefore there is no possibility
+  of interleaving concurrent freeing of ids and execution of associated
+  action-fns. Execution of action-fn is also synchronised with the execution of
+  alloc-id action-fns."
+  ([k id] (free-id k id 1 nil))
+  ([k id size] (free-id k id size nil))
+  ([k id size action-fn]
+     (let [bits (get allocator-bits k)
+           limit (get allocator-limits k)]
+       (dosync
+        (alter bits fill-gaps id size false)
+        (when action-fn (execute-action-fn action-fn "free-id"))))))

 (defn clear-ids
   "Clear all ids allocated for key."


add primary key/unique constraints to 'moves' table

--- src/greybear/model.clj
+++ src/greybear/model.clj
@@ -26,7 +26,9 @@
   (jdbc/create-table :moves
                      [:move "varchar"]
                      [:ordinal "smallint"]
-                     [:game_id :serial "references games (id)"]))
+                     [:game_id :serial "references games (id)"]
+                     ["PRIMARY KEY" "(game_id, ordinal)"]
+                     ["UNIQUE" "(game_id, move)"]))

 (defn teardown
   "use with a jdbc connection"


Clarify note about cache

Clarify note about cache

--- src/io/aviso/twixt.clj
+++ src/io/aviso/twixt.clj
@@ -148,7 +148,7 @@
                   "text/less" less/less-compiler-factory
                   "text/jade" jade/jade-compiler-factory}
    :development-mode false
-   :cache-enabled false ;; cache is always enabled in development mode
+   :cache-enabled false ;; file-system cache is always enabled in development mode
    :cache-folder (System/getProperty "twixt.cache-dir" (System/getProperty "java.io.tmpdir"))})

 (defn new-twixt


Use clojure.core/read to read project.clj and close readers

Project.clj could contain non-edn forms so it needs to be read using
clojure.core/read.
Readers should be closed using with-open.

--- src/jarkeeper/core.clj
+++ src/jarkeeper/core.clj
@@ -37,19 +37,21 @@

 (defn safe-read [s]
   (binding [*read-eval* false]
-    (read-string s)))
+    (read s)))

 (defn read-project-clj [repo-owner repo-name]
   (try
     (let [url (str "https://raw.github.com/" repo-owner "/" repo-name "/master/project.clj")]
-      (edn/read (PushbackReader. (io/reader url))))
+      (with-open [rdr (PushbackReader. (io/reader url))]
+        (safe-read rdr)))
     (catch Exception e
       nil)))

 (defn read-build-boot [repo-owner repo-name]
   (try
     (let [url (str "https://raw.github.com/" repo-owner "/" repo-name "/master/build.boot")]
-      (safe-read (slurp url)))
+      (with-open [rdr (PushbackReader. (io/reader url))]
+        (safe-read rdr)))
     (catch Exception e
       nil)))


Tidy up the log msgs for build-deps

Tidy up the log msgs for build-deps

--- src/leiningen/voom.clj
+++ src/leiningen/voom.clj
@@ -40,6 +40,7 @@

 (defn sh
   [& cmdline]
+  ;; (prn :sh cmdline)
   (apply shell/sh (map #(if (= File (class %))
                           (.getPath ^File %)
                           %)
@@ -233,7 +234,6 @@

 (defn find-project
   [pgroup pname candidate]
-  (prn "find-project" pgroup pname candidate)
   (for [p (find-project-files candidate)
         :let [{:keys [group name] :as prj} (project/read p)]
         :when (and (= group pgroup)
@@ -243,7 +243,6 @@
 (defn find-matching-projects
   "[project {groupId name ctime sha}] [project coordinate-string]"
   [repos-dir pspec]
-  (prn "find-matching-projects" repos-dir pspec)
   ;; TODO: find correct sha and project.clj more efficiently and with
   ;; less ambiguity. (only consider projects changed at the given sha)
   (let [{:keys [sha artifactId groupId]} pspec
@@ -252,7 +251,6 @@
         sha-candidates (or sha-candidates
                            (do (fetch-all dirs)
                                (locate-sha dirs sha)))]
-    (prn "sha-candidates" sha-candidates)
     (mapcat (fn [c]
               (git {:gitdir c} "checkout" sha)
               (find-project groupId artifactId c))
@@ -296,6 +294,7 @@
   undefined. Throws an exception with detailed message if artifact
   cannot be resolved."
   [old-exception proj {:keys [groupId artifactId version] :as art}]
+  (println "Did not find, will build" (pr-str) art)

   (if-let [vmap (ver-parse version)]
     (let [dep-entry [(symbol groupId artifactId) version]
@@ -342,6 +341,7 @@
 (defn build-deps
   "Like 'lein deps', but also builds voom-versioned things as needed."
   [project & args]
+  (println "-- build-deps for" (:name project))
   (try
     (loop []
       (when-not (= :ok (try-once-resolve-voom-version project))


automode

automode

--- src/lights/my_show.clj
+++ src/lights/my_show.clj
@@ -284,7 +284,7 @@
 (afterglow.show/set-variable! :lightness-max-general-percent 0.5)
 (afterglow.show/set-variable! :lightness-max-front-percent 0.2)
 (afterglow.show/set-variable! :use-hue-chase false)
-(afterglow.show/set-variable! :osc-beat-ratio 4)
+(afterglow.show/set-variable! :osc-beat-ratio 8)

 (defn light-sawtooth-phase
   "Change light of fixtures with phase shift. WIP."
@@ -578,6 +578,29 @@
           :beyond :loop
           ))))))

+
+
+  (ct/set-cue! (:cue-grid *show*) 3 1
+    (cues/cue :color  (fn [_] (afterglow.effects/scene
+      "Sine all, change colors 16B"
+      (let [light-param (params/build-oscillated-param
+        (oscillators/sine :interval :beat :interval-ratio :osc-beat-ratio :down? true) :min :lightness-min-general :max :lightness-max-general)]
+        (fx/chase "Saw all, hue adjust" [(global-color-effect (params/build-color-param :h :main-hue :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 30 :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 60 :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 90 :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 120 :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 150 :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 180 :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 210 :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 240 :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 270 :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 300 :s 100 :l light-param))
+          (global-color-effect (params/build-color-param :h :main-hue :adjust-hue 330 :s 100 :l light-param))]
+          (params/build-step-param :interval :phrase :fade-fraction 0.3 :fade-curve :sine)
+          :beyond :loop
+          ))))))
+
   (ct/set-cue! (:cue-grid *show*) 0 0
     (cues/cue :set-main-hue (fn [_] (var-fx/variable-effect @var-binder :main-hue 0))
       :short-name "Red"


fix rendering of timeseries which were failing due to query results containing serialized iso8601 strings now instead of raw java.sql.Timestamp instances.

--- src/metabase/pulse.clj
+++ src/metabase/pulse.clj
@@ -7,7 +7,8 @@
                       [core :as t]
                       [format :as f])
             [hiccup.core :refer [html h]]
-            [metabase.util.urls :as urls])
+            [metabase.util.urls :as urls]
+            [metabase.util :as u])
   (:import (java.awt BasicStroke Color Dimension RenderingHints)
            java.awt.image.BufferedImage
            (java.io ByteArrayInputStream ByteArrayOutputStream)
@@ -314,11 +315,11 @@
     (.toByteArray os)))

 (defn- render:sparkline
-  [card {:keys [rows cols]}]
+  [_ {:keys [rows cols]}]
   (let [xs     (for [row  rows
                      :let [x (first row)]]
-                 (if (instance? Date x)
-                   (.getTime ^Date x)
+                 (if (string? x)
+                   (.getTime ^Date (u/->Timestamp x))
                    x))
         xmin   (apply min xs)
         xmax   (apply max xs)


[#244] Fix clj-kondo warning

[#244] Fix clj-kondo warning

--- api/src/clojure/org/akvo/flow_api/endpoint/stats.clj
+++ api/src/clojure/org/akvo/flow_api/endpoint/stats.clj
@@ -20,7 +20,7 @@
                                   ::spec/question-id]))

 (defn endpoint* [{:keys [remote-api]}]
-  (GET "/stats" {:keys [email instance-id alias params] :as req}
+  (GET "/stats" {:keys [email instance-id params]}
     (ds/with-remote-api remote-api instance-id
       (let [{:keys [survey-id
                     form-id


Return more info about adding books

Return more info about adding books

--- src/myshelf/worker.clj
+++ src/myshelf/worker.clj
@@ -75,6 +75,7 @@
                 (json/generate-string
                  {:user-handle user-handle
                   :sent-cmd "add-book"
+                  :sent-args [book-id shelf]
                   :results result}))))

 (defn rank-books-on-shelf


don't set socket option if so-rcvbuf isn't set

--- src/riemann/transport/udp.clj
+++ src/riemann/transport/udp.clj
@@ -92,8 +92,10 @@
                   (.option ChannelOption/SO_BROADCAST false)
                   (.option ChannelOption/MESSAGE_SIZE_ESTIMATOR
                            (DefaultMessageSizeEstimator. max-size))
-                  (.option ChannelOption/SO_RCVBUF so-rcvbuf)
                   (.handler handler))
+
+                ; Setup Channel options
+                (if (> so-rcvbuf 0) (.option bootstrap ChannelOption/SO_RCVBUF so-rcvbuf))

                 ; Start bootstrap
                 (->> (InetSocketAddress. host port)
@@ -102,14 +104,14 @@
                      (.channel)
                      (.add channel-group))

-                (info "UDP server" host port max-size "online")
+                (info "UDP server" host port max-size so-rcvbuf "online")

                 ; fn to close server
                 (reset! killer
                         (fn killer []
                           (-> channel-group .close .awaitUninterruptibly)
                           @(shutdown-event-executor-group worker-group)
-                          (info "UDP server" host port max-size "shut down")))))))
+                          (info "UDP server" host port max-size so-rcvbuf "shut down")))))))

   (stop! [this]
          (locking this
@@ -153,7 +155,7 @@
          host  (get opts :host "127.0.0.1")
          port  (get opts :port 5555)
          max-size (get opts :max-size 16384)
-         so-rcvbuf (get opts :so-rcvbuf 124928)
+         so-rcvbuf (get opts :so-rcvbuf -1)
          channel-group (get opts :channel-group
                             (channel-group
                               (str "udp-server" host ":" port "(" max-size ")")))


Switch `-value` to use newly generalised pattern-sequence-selector.

--- src/isla/parser.clj
+++ src/isla/parser.clj
@@ -45,13 +45,8 @@

 ;; values

-;;;;;;;; might need attention - doesn't get back left-tokens
 (defn -value [tokens]
-  (def alts (alternatives tokens [-string -integer -identifier]))
-  (if (> (count alts) 0)
-    (let [{node :node left-tokens :left-tokens} (first alts)]
-      {:node (nnode :value [node]) :left-tokens left-tokens})
-    nil))
+  (pattern-sequence-selector tokens [-string -integer -identifier] :value))

 (defn -assignee [tokens] (pattern tokens #"[A-Za-z]+" :assignee))


Generalise -expression as -pattern-sequence-selector and then use it instead.

--- src/isla/parser.clj
+++ src/isla/parser.clj
@@ -3,7 +3,7 @@
   (:use [isla.lexer])
   (:require [clojure.string :as str]))

-(declare parse alternatives is-type pattern-sequence pattern nnode
+(declare parse alternatives is-type pattern-sequence pattern nnode pattern-sequence-selector
          -root -block -expression -assignment -invocation
          -nl -integer -is -string -assignee -value -identifier)

@@ -14,19 +14,15 @@
   (nnode :root [(-block tokens [])]))

 (defn -block [tokens collected]
-  (let [{expr :expr left-tokens :left-tokens} (-expression tokens [])]
-    (if (nil? expr)
-      (nnode :block collected) ;; no more exprs, return block
-      (-block left-tokens (conj collected expr))))) ;; add expr, continue collecting more
+  (if-let [{expr :expr left-tokens :left-tokens}
+           (-expression tokens [])]
+    (-block left-tokens (conj collected expr)) ;; add expr, continue collecting more
+    (nnode :block collected))) ;; no more exprs, return block

 ;; expressions

 (defn -expression [tokens collected]
-  (def expressions (alternatives tokens [-assignment -invocation]))
-  (if (> (count expressions) 0)
-    (let [{expr :expr left-tokens :left-tokens} (first expressions)]
-      {:expr (nnode :expression [expr]) :left-tokens left-tokens}) ;; return expr
-    {:expr nil :left-tokens tokens})) ;; no expr match - return
+  (pattern-sequence-selector tokens [-assignment -invocation]))

 (defn -assignment [tokens]
   (if-let [{nodes :nodes left-tokens :left-tokens}
@@ -84,6 +80,13 @@
             (filter (fn [t] (not= nil (t input))) ;; get matching types
                     types))))

+(defn pattern-sequence-selector [tokens pattern-sequences]
+  (def alts (alternatives tokens pattern-sequences))
+  (if (> (count alts) 0)
+    (let [{expr :expr left-tokens :left-tokens} (first alts)]
+      {:expr (nnode :expression [expr]) :left-tokens left-tokens}) ;; return alternative
+    nil)) ;; no alternative match - return
+
 (defn pattern-sequence [tokens patterns collected]
   (let [pattern (first patterns)
         token (first tokens)]
@@ -93,7 +96,7 @@
         nil ;; pattern match failure - not enough tokens
         (if-let [node (pattern token)] ;; matched token
           (pattern-sequence (rest tokens) (rest patterns) (conj collected node)) ;; round again
-          nil))))) ;; pattern match failure - return
+          nil))))) ;; pattern match failure token match failure - return

 ;; only one token, for now
 (defn pattern [input matcher tag & args]


Add correct comment.

Add correct comment.

--- src/isla/parser.clj
+++ src/isla/parser.clj
@@ -39,7 +39,7 @@
   (let [{expr :expr left-tokens :left-tokens} (-expression tokens [])]
     (if (= expr nil)
       (nnode :block collected) ;; no more exprs, return block
-      (-block left-tokens (conj collected expr))))) ;; no more exprs, return block
+      (-block left-tokens (conj collected expr))))) ;; add expr, continue collecting more

 ;; expressions


Rewrite 'description' using 'it' for examples.

--- src/main/clojure/com/stuartsierra/lazytest.clj
+++ src/main/clojure/com/stuartsierra/lazytest.clj
@@ -2,26 +2,9 @@
   (:use [com.stuartsierra.lazytest.arguments
          :only (get-arg standard-metadata)]
         [com.stuartsierra.lazytest.groups
-         :only (description declare-subgroup-form!)]
-        [com.stuartsierra.lazytest.contexts
-         :only (context)]
+         :only (description group-examples)]
         [com.stuartsierra.lazytest.attach
          :only (add-group!)]))
-
-(declare describe)
-
-(defn- replace-nested-describe
-  "If form begins with 'describe', replace it with 'description'.
-  Throws an exception if first argument to the 'describe' form is a
-  symbol."
-  [form]
-  (if (and (seq? form)
-           (= #'describe (resolve (first form))))
-    (if (symbol? (second form))
-      (throw (IllegalArgumentException.
-              "Nested descriptions cannot be attached to Vars."))
-      `(description ~@(rest form)))
-    form))

 (defmacro describe
   "Creates an example group and attaches it to a target.  If first
@@ -31,12 +14,26 @@
   [& body]
   (let [[target body] (get-arg symbol? body)
         [docstring body] (get-arg string? body)
-        body (map replace-nested-describe body)
         target-ref (if target `(var ~target) `*ns*)]
-    `(add-group! ~target-ref
-                 (with-meta (description ~@body)
-                   (assoc ~(standard-metadata &form docstring)
-                     :target ~target-ref)))))
+    (if (some #(::in-describe (meta %)) (keys &env))
+      (if target
+        (throw (IllegalArgumentException.
+                "Nested 'describe' cannot have symbol target."))
+          `(with-meta (description ~@body)
+             ~(standard-metadata &form docstring))))
+    `(let [~(with-meta (gensym) {::in-describe true})]
+       (add-group! ~target-ref
+                   (with-meta (description ~@body)
+                     (assoc ~(standard-metadata &form docstring)
+                       :target ~target-ref))))))
+
+(defmacro it
+  "Creates a group of examples.  For use within 'describe', uses the
+  contexts and givens.  Body is a series of expressions, each of which
+  will be compiled to a single example.  A string in body will be
+  attached as a doc string on the following expression."
+  [& body]
+  `(group-examples ~@body))

 (defmacro thrown?
   "Returns true if body throws an instance of class c."


only enable the basic-authentication middleware if a username & password is set in the environment variables #4

--- src/bitfondue/handler.clj
+++ src/bitfondue/handler.clj
@@ -92,18 +92,19 @@
                                 :options {:alg :a256kw :enc :a128gcm}}))

 (defn basic-authenticated?
-  "Basic authentication for staging servers"
+  "Basic authentication for the staging server"
   [username password]
-  (let [env-username (env :basic-authentication-username)
-        env-password (env :basic-authentication-password)]
-    (if (or (nil? env-username)
-            (nil? env-password))
-      true
-      (and (= username env-username)
-           (= password env-password)))))
+  (and (= username (env :basic-authentication-username))
+       (= password (env :basic-authentication-password))))
+
+(defn wrap-if
+  "Only add a certain middleware if conditions are met."
+  [handler pred wrapper & args]
+  (if pred
+    (apply wrapper handler args)
+    handler))

 (def app (-> app-routes
-             (wrap-basic-authentication basic-authenticated?)
              (wrap-authorization auth-backend)
              (wrap-authentication auth-backend)
              (wrap-json-response {:pretty false})
@@ -112,7 +113,9 @@
              (wrap-params)
              (wrap-keyword-params)
              (wrap-json-params)
-             ))
+             (wrap-if (not (or (nil? (env :basic-authentication-username))
+                               (nil? (env :basic-authentication-password))))
+                      wrap-basic-authentication basic-authenticated?)))

 (defn -main [port]
   (run-jetty app {:port (read-string port) :join? false}))


refactoring

refactoring

--- src/block_chain/chain.clj
+++ src/block_chain/chain.clj
@@ -81,16 +81,14 @@
 (defn unspent-outputs [key blocks])

 (defn unspent-output-coords [key blocks]
-  (compact
-   (flatten
-    (for [txn (transactions blocks)]
-      (map (fn [output index]
-             (if (assigned-to-key? output key)
-               {:source-hash (:hash txn) :source-index index}
-               nil))
-           (:outputs txn)
-           (range (count (:outputs txn))))
-      ))))
+  (mapcat (fn [txn]
+            (mapcat (fn [output index]
+                      (if (assigned-to-key? output key)
+                        [{:source-hash (:hash txn) :source-index index}]
+                        nil))
+                    (:outputs txn)
+                    (range (count (:outputs txn)))))
+          (transactions blocks)))

 (defn payment [amount from-key to-key blocks])
 (defn broadcast-txn [txn])


namespace update for db

namespace update for db

--- src/listopia/db.clj
+++ src/listopia/db.clj
@@ -1,5 +1,5 @@
 (ns listopia.db
-  (:require [environ.core :refer [env]]))
+  (:require [environ.core :as environ]))

 (def database-url
-  (env :database-url))
+  (environ/env :database-url))


Update code to reflect Justin making :repositories not be a map. Make sure that a repository publishes its index before trying to download the possibly non-existent files. Temporarily hackafix the latest queries. Clucy needs a new parser.

--- src/cake/tasks/search.clj
+++ src/cake/tasks/search.clj
@@ -3,8 +3,9 @@
         cake.core
         [bake.core :only [log]]
         [clojure.string :only [join]]
-        [clojure.java.io :only [file copy reader]])
-  (:require [sherlock.core :as sherlock]))
+        [clojure.java.io :only [as-url file copy reader]])
+  (:require [sherlock.core :as sherlock])
+  (:import java.net.HttpURLConnection))

 (defn select-repos [repos]
   (let [repositories (:repositories *project*)]
@@ -13,7 +14,7 @@
       repositories)))

 (defn hash-url [url]
-  (str (sherlock/remote-index-url url) ".md5"))
+  (as-url (str (sherlock/remote-index-url url) ".md5")))

 (defn hash-location [url]
   (file (str (sherlock/index-location url) ".md5")))
@@ -21,22 +22,33 @@
 (defn new-hash [url]
   (copy (reader (hash-url url)) (hash-location url)))

-(defn download-index [id url]
+(defn published? [url]
+  (try
+    (= HttpURLConnection/HTTP_OK
+       (.getResponseCode
+        (doto (.openConnection (hash-url url))
+          (.setRequestMethod "HEAD"))))
+    (catch Exception _ false)))
+
+(defn download-index [url]
+  (sherlock/update-index url)
+  (new-hash url))
+
+(defn init-index [id url]
   (when (sherlock/download-needed? url)
     (log (str "Downloading index for " id ". This might take a bit."))
-    (sherlock/update-index url)
-    (new-hash url)))
+    (download-index url)))

-
-(defn needs-updating? [id url]
-  (when (and (not= (slurp (hash-location url))
-                   (slurp (hash-url url))))
-    (println (format "The index for %s is out of date. Pass --update to update it." id))))
+(defn update-message [id url]
+  (let [hash-location (hash-location url)]
+    (when (and (.exists hash-location)
+               (not= (slurp hash-location)
+                     (slurp (hash-url url))))
+      (println (format "The index for %s is out of date. Pass --update to update it." id)))))

 (defn update-repo [id url]
   (log (str "Updating index for " id ". This might take a bit."))
-  (sherlock/update-index url)
-  (new-hash url))
+  (download-index url))

 (defn identifier [{:keys [group-id artifact-id]}]
   (if group-id
@@ -51,6 +63,11 @@
       (println (str "[" (identifier artifact) " \"" version "\"]") description))
     (prn)))

+(defn batch-update [id url update]
+  (init-index id url)
+  (when update (update-repo id url))
+  (update-message id url))
+
 (deftask search
   "Search maven repos for artifacts."
   "Search takes a query that is treated as a lucene search query,
@@ -64,10 +81,8 @@
   {[page] :page repos :repos search :search update :update}
   (let [page (if page (Integer. page) 1)]
     (binding [sherlock/*page-size* 10]
-      (doseq [[id url] (select-repos repos)]
-        (download-index id url)
-        (when update (update-repo id url))
-        (needs-updating? id url)
+      (doseq [[id url] (select-repos repos) :when (published? url)]
+        (batch-update id url update)
         (print-results
          id page
          (sherlock/get-page page (sherlock/search url (join " " search) page)))))))
@@ -78,18 +93,16 @@
    and return the latest version. You can pass --repos which is expected to be a
    comma delimited list of repository names. If it is not passed, all repositories
    will be searched."
-  {[term] :latest-version, repos :repos update :update}
-  (let [repos (select-repos repos)
+  {[term] :latest, repos :repos update :update}
+  (let [repos (filter (comp published? second) (select-repos repos))
         [group-id artifact-id] (.split term "/")
-        query (str "g:" group-id " AND a:" (or artifact-id group-id))]
+        query (str "g:" group-id "* AND a:" (or artifact-id group-id))]
     (doseq [[id url] repos]
-      (download-index id url)
-      (when update (update-repo id url))
-      (needs-updating? id url))
+      (batch-update id url update))
     (let [result (first (sort-by :version (comp unchecked-negate compare)
                                  (filter (comp #{term} identifier)
                                          (mapcat #(sherlock/search % query 10 Integer/MAX_VALUE)
-                                                 (vals repos)))))]
+                                                 (map last repos)))))]
       (println
        (if result
          (str "[" (identifier result) " " (:version result) "]")


fix cljs compiler warning about *oscillators* not being marked dynamic

--- src/mantra/osc.cljs
+++ src/mantra/osc.cljs
@@ -59,13 +59,15 @@
      :context   context
      :clock     clock}))

-(def ^{:doc "A set of currently active oscillators. Each oscillator is
-             represented as a map containing:
+(def
+  ^{:doc
+    "A set of currently active oscillators. Each oscillator is represented as a
+     map containing:

-             :osc-node  -- the oscillator node
-             :gain-node -- the gain node it's connected to
-             :model-id  -- the ID of the model that was used as a
-                           blueprint to create the oscillator"}
+     :osc-node  -- the oscillator node :gain-node -- the gain node it's
+     connected to :model-id  -- the ID of the model that was used as a blueprint
+     to create the oscillator"
+    :dynamic true}
   *oscillators* (atom #{}))

 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


`sel :one` adds korma `(limit 1)` form to `select` call

--- src/metabase/db.clj
+++ src/metabase/db.clj
@@ -143,7 +143,7 @@
     (sel :many Table :db_id 1 (order :name :ASC)) -> (select User (where {:id 1}) (order :name ASC))"
   [one-or-many entity & forms]
   {:pre [(contains? #{:one :many} one-or-many)]}
-  `(->> (-sel-select ~entity ~@forms)
+  `(->> (-sel-select ~entity ~@forms ~@(when (= one-or-many :one) `((limit 1))))
         (map (partial post-select (entity->korma ~entity)))
         ~(case one-or-many
            :one 'first


display score with commas

display score with commas

--- src/client/game/core.cljs
+++ src/client/game/core.cljs
@@ -2,6 +2,7 @@
   (:require-macros
     [cljs.core.async.macros :refer [go alt!]])
   (:require
+    [client.util :as util]
     [cljs.reader :refer [read-string]]
     [client.game.board :refer [piece-fits?
                                rotate-piece
@@ -199,7 +200,7 @@

 (defn display-points!
   []
-  (.html ($ "#score") (str "Score: " (:score @state)))
+  (.html ($ "#score") (str "Score: " (util/format-number (:score @state))))
   (.html ($ "#level") (str "Level: " (:level @state)))
   (.html ($ "#lines") (str "Lines: " (:total-lines @state))))


* src/clj/cljs/compiler.clj: fix that shadowing bug that actually causes CLJ-140

--- src/clj/cljs/compiler.clj
+++ src/clj/cljs/compiler.clj
@@ -694,19 +694,21 @@
               ([_ sym init] {:sym sym :init init})
               ([_ sym doc init] {:sym sym :doc doc :init init}))
         args (apply pfn form)
-        sym (:sym args)
-        v (resolve-var (dissoc env :locals) sym)]
+        sym (:sym args)]
     (assert (not (namespace sym)) "Can't def ns-qualified name")
-    (when *cljs-warn-on-redef*
-      (binding [*out* *err*]
-        (let [ns-name (-> env :ns :name)]
-          (when (or (and (not= ns-name 'cljs.core)
-                         (core-name? env sym))
-                    (get-in @namespaces [ns-name :uses sym]))
-            (let [ev (resolve-existing-var (dissoc env :locals) sym)]
-              (println "WARNING:" sym "already refers to:" (symbol (str (:ns ev)) (str sym))
-                       "being replaced by:" (symbol (str ns-name) (str sym))))))))
-    (let [name (munge (:name v))
+    (let [env (let [ns-name (-> env :ns :name)]
+                (if (or (and (not= ns-name 'cljs.core)
+                             (core-name? env sym))
+                        (get-in @namespaces [ns-name :uses sym]))
+                  (let [ev (resolve-existing-var (dissoc env :locals) sym)]
+                    (when *cljs-warn-on-redef*
+                      (binding [*out* *err*]
+                        (println "WARNING:" sym "already refers to:" (symbol (str (:ns ev)) (str sym))
+                                 "being replaced by:" (symbol (str ns-name) (str sym)))))
+                    (swap! namespaces update-in [ns-name :excludes] conj sym)
+                    (update-in env [:ns :excludes] conj sym))
+                  env))
+          name (munge (:name (resolve-var (dissoc env :locals) sym)))
           init-expr (when (contains? args :init) (disallowing-recur
                                                   (analyze (assoc env :context :expr) (:init args) sym)))
           export-as (when-let [export-val (-> sym meta :export)]


Add system start, error catching

Add system start, error catching

--- build.boot
+++ build.boot
@@ -74,10 +74,14 @@

       (boot.repl/launch-nrepl {:init-ns 'user :port 5600 :server true
                                :middleware (:middleware pod/env)})
-      ;; Auto-start the system
-      (require 'dev) ;; Set the init-system (stateful at namespace level)
       (require 'reloaded.repl)
-      (reloaded.repl/go))
+      (try
+        ;; Auto-start the system
+        (require 'dev) ;; Set the init-system (stateful at namespace level)
+        (reloaded.repl/go)
+        (catch Exception e
+          (boot.util/fail "Exception while starting the system\n")
+          (boot.util/print-ex e))))
     identity)) ;; Return identity so that composition works

 (deftask frontend


* src/clj/cljs/compiler.clj: prep for passing along direct variadic info

--- src/clj/cljs/compiler.clj
+++ src/clj/cljs/compiler.clj
@@ -682,19 +682,20 @@
                  (not (-> f :info :dynamic))
                  (-> f :info :fn-var))
         js? (= (-> f :info :ns) 'js)
-        f (if fn?
-            (let [info (-> f :info)
-                  arity (count args)
-                  mps (:method-params info)]
-              (if (or (> arity (:max-fixed-arity info))
-                      (= (count mps) 1))
-                f
-                (let [arities (map count mps)]
-                  (if (some #{arity} arities)
-                    (update-in f [:info :name]
-                               (fn [name] (symbol (str name ".cljs$lang$arity$" arity))))
-                    f))))
-            f)]
+        [f direct-variadic?]
+        (if fn?
+          (let [info (-> f :info)
+                arity (count args)
+                mps (:method-params info)]
+            (if (or (> arity (:max-fixed-arity info))
+                    (= (count mps) 1))
+              [f false]
+              (let [arities (map count mps)]
+                (if (some #{arity} arities)
+                  [(update-in f [:info :name]
+                     (fn [name] (symbol (str name ".cljs$lang$arity$" arity)))) false]
+                  [f false]))))
+          [f false])]
     (emit-wrap env
       (emits f (when-not (or fn? js?) ".call") "("
                  (let [args (if (or fn? js?) args (cons "null" args))]


Bump version numbers, add test task

Bump version numbers, add test task

--- build.boot
+++ build.boot
@@ -6,13 +6,16 @@
                  [adzerk/boot-reload    "0.3.1"      :scope "test"]
                  [org.clojure/core.async "0.1.346.0-17112a-alpha"]
                  [reagent "0.5.0"]
+                 [cljs-tooling "0.1.7"]
+                 [org.clojure/tools.nrepl "0.2.10"]
                  [domina "1.0.3"]
                  [dragonmark/web "0.1.8"]
                  [cljsjs/jquery "1.9.1-0"]
                  [cljsjs/jquery-ui "1.11.3-1"]
+                 [org.clojure/clojurescript "1.7.48" ]
                  [boot-cljs-test/node-runner "0.1.0" :scope "test"]
-                 [org.clojure/clojurescript "0.0-3308" ]
-                 [pandeiro/boot-http    "0.6.3"      :scope "test"]])
+                 [pandeiro/boot-http    "0.6.3"      :scope "test"]
+                 [org.clojure/test.check "0.7.0" :scope "test"]])

 (require
  '[adzerk.boot-cljs      :refer [cljs]]
@@ -23,7 +26,7 @@

 (require 'boot.repl)
 (swap! boot.repl/*default-dependencies*
-       concat '[[cider/cider-nrepl "0.9.0"]])
+       concat '[[cider/cider-nrepl "0.10.0-SNAPSHOT"]])

 (swap! boot.repl/*default-middleware*
        conj 'cider.nrepl/cider-middleware)
@@ -37,20 +40,29 @@

 (deftask dev []
   (set-env!
-   :source-paths #{"src" "test"})
+   :source-paths #{"src"})
   (comp (serve :dir "target/")
         (watch)
         (speak)
         (reload :on-jsload 'app.core/main)
         (cljs-repl)
-#_        (cljs-test-node-runner :namespaces '[app.test])
+        (cljs :source-map true :optimizations :none)))

+(deftask test []
+  (set-env!
+   :source-paths #{"src" "test"})
+
+  (comp (serve :dir "target/")
+        (watch)
+        (speak)
+        (reload)
+        (cljs-repl)
+#_        (cljs-test-node-runner :namespaces '[app.misc-test])
         (cljs :source-map true :optimizations :none)
-
-  #_      (run-cljs-test)))
+ #_       (run-cljs-test)))

 (deftask build []
   (set-env! :source-paths #{"src"})
-  (comp (cljs :optimizations :advanced)))
+  (comp  (cljs :optimizations :advanced)))


Sanity check; we just aren't going to deal with the pathological case of no edges

--- src/cljs/fatlip/core.cljs
+++ src/cljs/fatlip/core.cljs
@@ -174,7 +174,9 @@
                                   :rs #{}})
          inp input]
     (if (empty? inp)
-      graph
+      (if (empty? (:succs graph))
+        (throw (js/Error. "At least one character must appear twice"))
+        graph)
       (let [i (first inp)
             dup-character (->> (mapcat #(:characters %) (:groups i))
                                frequencies


npm install -> yarn install

npm install -> yarn install

--- build.boot
+++ build.boot
@@ -29,9 +29,9 @@
     (let [nm (io/file "node_modules")]
       (if-not (and (.exists nm) (.isDirectory nm))
         (do
-          (util/info "Installing node dependencies with `npm install`\n")
-          (dosh "npm" "install"))
-        (util/info "Node dependencies already installed, skipping `npm install`\n")))))
+          (util/info "Installing node dependencies with `yarn install`\n")
+          (dosh "yarn" "install"))
+        (util/info "Node dependencies already installed, skipping `yarn install`\n")))))

 (deftask bundle-js
   [d dev     bool  "Development build"]