Add delay node

Add delay node

--- src/alder/node-type.cljs
+++ src/alder/node-type.cljs
@@ -273,6 +273,26 @@
              (fn [ctx] (.call (aget ctx "createChannelMerger") ctx 2))
              {:constructor "context.createChannelMerger(2)"}))

+(def delay-node-type
+  (NodeType. {:signal-in {:type :node
+                          :index 0
+                          :title "Signal in"}
+              :delay-time {:type :param
+                           :name "delayTime"
+                           :data-type :number
+                           :range [0 5]
+                           :title "Delay time"}}
+             {:signal-out {:type :node
+                           :index 0
+                           :title "Signal out"
+                           :data-type :signal}}
+             nil
+             false
+             "Delay"
+             [80 40]
+             (fn [ctx] (.call (aget ctx "createDelay") ctx 5))
+             {:constructor "context.createDelay(5)"}))
+
 (def midi-note-node-type
   (NodeType. {:device {:type :accessor
                        :name "device"
@@ -358,7 +378,8 @@
                      :scope scope-analyser-node-type
                      :stereo-panner stereo-panner-node-type
                      :stereo-splitter stereo-splitter-node-type
-                     :stereo-merger stereo-merger-node-type}
+                     :stereo-merger stereo-merger-node-type
+                     :delay delay-node-type}
         midi-nodes (if has-midi-support
                      {:midi-note midi-note-node-type
                       :midi-cc midi-cc-node-type}
@@ -372,7 +393,8 @@
                  [:gain gain-node-type]
                  [:stereo-panner stereo-panner-node-type]
                  [:stereo-splitter stereo-splitter-node-type]
-                 [:stereo-merger stereo-merger-node-type]]
+                 [:stereo-merger stereo-merger-node-type]
+                 [:delay delay-node-type]]
         envelopes [[:adsr adsr-node-type]]
         midi-nodes [[:midi-note midi-note-node-type]
                     [:midi-cc midi-cc-node-type]]


Story ID: develop Summary: - fixed tests around data fetch changes for ident-based queries

--- spec/untangled/client/data_fetch_spec.cljs
+++ spec/untangled/client/data_fetch_spec.cljs
@@ -112,8 +112,10 @@
                                  (let [cb (:callback params)]
                                    (is (fn? cb))
                                    (is (= "foo" (cb)))))
-                               (behavior "bases the query on the ident."
-                                 (is (= [{[:item/id 99] (om/get-query Item)}] (:query params)))))
+                               (behavior "includes the ident in the data state."
+                                 (is (= [:item/id 99] (:ident params))))
+                               (behavior "includes the query joined to the ident."
+                                 (is (= (om/get-query Item) (:query params)))))

       (df/load-singleton 'reconciler (om/get-query Item)
         :ident [:item/id 99]


Simples!

Simples!

--- src/aeolian/abc/core.clj
+++ src/aeolian/abc/core.clj
@@ -10,5 +10,5 @@
   (str "\nw: " lyric "\n"))

 (defn note [note instrument lyrics tempo]
-  (let [note-components (conj '() note instrument lyrics tempo)]
-    (apply str (interpose " " (filter #(not (nil? %)) note-components)))))
+  (let [note-components (list note instrument lyrics tempo)]
+    (apply str (interpose " " (remove nil? note-components)))))


Build framework to synch metronome snapshots.

Allow a metronome snapshot to be created to match the time of a
different metronome's snapshot, so that oscillated parameters running
off alternate metronomes can be properly synchronized with the frame,
issue #19

--- src/afterglow/rhythm.clj
+++ src/afterglow/rhythm.clj
@@ -53,10 +53,10 @@
     "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 offset]
+  (metro-snapshot [metro] [metro instant]
     "Take a snapshot of the current beat, bar, phrase, and phase state.
-  If an offset is supplied, calculates a snapshot based on adding that
-  many milliseconds to the current time.")
+  If an 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]
@@ -343,15 +343,14 @@
        (ref-set bpp new-bpp))))

   (metro-snapshot [metro]
-    (metro-snapshot metro 0))
-  (metro-snapshot [metro offset]
-    (dosync
-     (ensure start)
-     (ensure bpm)
-     (ensure bpb)
-     (ensure bpp)
-     (let [instant (+ (now) offset)
-           beat (marker-number instant @start (metro-tick metro))
+    (metro-snapshot metro (now)))
+  (metro-snapshot [metro instant]
+    (dosync
+     (ensure start)
+     (ensure bpm)
+     (ensure bpb)
+     (ensure bpp)
+     (let [beat (marker-number instant @start (metro-tick metro))
            bar (marker-number instant @start (metro-tock metro))
            phrase (marker-number instant @start (metro-ding metro))
            beat-phase (marker-phase instant @start (metro-tick metro))


update dev deps

update dev deps

--- profiles.clj
+++ profiles.clj
@@ -6,9 +6,9 @@
            :target-path    "target/%s"
            :clean-targets  ^{:protect false} [:target-path]

-           :plugins        [[org.apache.maven.wagon/wagon-ssh-external "3.4.2"]
-                            [org.apache.maven.wagon/wagon-http-lightweight "3.4.2"]]}
+           :plugins        [[org.apache.maven.wagon/wagon-ssh-external "3.4.3"]
+                            [org.apache.maven.wagon/wagon-http-lightweight "3.4.3"]]}

- :provided {:dependencies [[org.clojure/clojure "1.10.1"]]}
+ :provided {:dependencies [[org.clojure/clojure "1.10.3"]]}

  :jar     {:aot :all}}


Update :1.8 profile to clojure 1.8.0-RC4.

--- profiles.clj
+++ profiles.clj
@@ -47,7 +47,7 @@
                         "-Xms4g"]
    :main ^:skip-aot bench}]

- :1.8 {:dependencies [[org.clojure/clojure "1.8.0-RC3"]]}
+ :1.8 {:dependencies [[org.clojure/clojure "1.8.0-RC4"]]}

  ;; Examples
  :examples


refactoring instruments

refactoring instruments

--- src/borderless/sound.clj
+++ src/borderless/sound.clj
@@ -39,7 +39,7 @@
     (reset! person-sound (dissoc @person-sound pid))))

 ;;;;;;;;;;;;;;;;;;;;;
-;; Audio           ;;
+;; spec            ;;
 ;;;;;;;;;;;;;;;;;;;;;

 ;; VCA, VCO, and FX
@@ -66,16 +66,19 @@
    ::release 5.0
    ::gate 1})

-(defn tremelo-freq []
-  (fn [] (sin-osc:kr 0.5)))
-
 (s/fdef vowel-formant
         :args (s/cat :freq number? :eq-freq number? :q number?)
         :ret (s/fspec :args (s/cat :saw clojure.test/function? :freq number?)
                       :ret number?))

-(defn vowel-formant [freq eq-freq q]
-  (fn [] (resonz (saw freq) eq-freq q)))
+;;;;;;;;;;;;;;;;;;;;;
+;; Audio           ;;
+;;;;;;;;;;;;;;;;;;;;;
+
+(defn vowel-formant
+  "I EQ a narrow Q on a given frequency to shape a saw waveform of a certain pitch."
+  [pitch eq-freq q]
+  (fn [] (resonz (saw pitch) eq-freq q)))

 (defn synth-unit-layered [freq eq-freq q kr-mul]
   (let [[freq-a freq-b freq-c] eq-freq]
@@ -85,6 +88,7 @@
      ((vowel-formant (overtone.sc.ugen-collide/+ freq (sin-osc:kr (* 1.5 kr-mul))) freq-c q)))))

 (definst drone-eh
+  "a testing instrument, not used in runtime"
   [freq 100]
   (let [eq-freq      [530 1840 2480]
         q            0.1
@@ -93,63 +97,94 @@
         synth-unit-lpf (rlpf (* 0.5 synth-unit)
                                        (mouse-x 10 10000)
                                        (mouse-y 0.0001 0.9999))]
-
     synth-unit-lpf))

-(definst drone-aw-sus [freq 300
-                       amp (synth-defaults ::vca)
-                       verb (synth-defaults ::reverb)
-                       kr-mul (synth-defaults ::vco)
-                       attack (synth-defaults ::attack)
-                       sustain (synth-defaults ::sustain)
-                       release (synth-defaults ::release)
-                       gate (synth-defaults ::gate)]
-  (let [eq-freq      [570 840 2410]
-        q            0.1
-        synth-unit (synth-unit-layered freq eq-freq q 0.5)
-        synth-unit-env  (* (env-gen (asr attack sustain release) gate) synth-unit)
-        synth-unit-hpf  (hpf synth-unit-env 900)
-        synth-unit-lpf  (* amp (rlpf synth-unit-hpf 600 0.6))]
-
-    (out 0 (free-verb synth-unit-lpf verb verb verb))
-    (out 1 (free-verb synth-unit-lpf verb verb verb))))
-
-
-(definst drone-ae-sus [freq 100 verb 1 kr-mul 25 amp 0.75
-                       attack 35.0 sustain 0.4 release 15.0 gate 1]
-
-  (let [eq-freq      [270 2290 3010]
-        q            0.1
-        synth-unit (synth-unit-layered freq eq-freq q 0.5)
-        synth-unit-env  (* (env-gen (asr attack sustain release) gate) synth-unit)
-        synth-unit-hpf  (* amp (hpf synth-unit-env 600))
-        synth-unit-lpf  (rlpf synth-unit-hpf 8000 0.6)]
-
-    (out 0 (free-verb synth-unit-lpf verb verb verb))
-    (out 1 (free-verb synth-unit-lpf verb verb verb))))
-
-(definst drone-eh-sus [freq 80 verb 1 kr-mul 25 amp 1
-                       attack 5.0 sustain 0.4 release 15.0 gate 1]
-  (let [eq-freq      [530 1840 2480]
-        q            0.1
-        synth-unit (synth-unit-layered freq eq-freq q 0.5)
-        synth-unit-env  (* (env-gen (asr attack sustain release) gate) synth-unit)
-        synth-unit-lpf  (rlpf synth-unit-env 750 0.9)]
-
-    (out 0 (free-verb synth-unit-lpf verb verb verb))
-    (out 1 (free-verb synth-unit-lpf verb verb verb))))
-
-(definst drone-oo-sus [freq 120 verb 1 kr-mul 25 amp 1
-                       attack 5.0 sustain 0.4 release 15.0 gate 1]
-
-  (let [eq-freq      [300 870 2240]
-        q            0.1
-        synth-unit (synth-unit-layered freq eq-freq q 0.5)
-        synth-unit-env  (* (env-gen (asr attack sustain release) gate) synth-unit)
-        synth-unit-lpf  (rlpf synth-unit-env 600 0.6)]
-
-    (out 0 (free-verb synth-unit-lpf verb verb verb))
-    (out 1 (free-verb synth-unit-lpf verb verb verb))))
+(defn synth-filter-chain
+  "I shape a sound with high pass filters, resonante low pass filters, amplifiers, and reverb."
+  [synth-unit amp verb gate hpf-rlpf]
+  (let [attack (synth-defaults ::attack)
+        sustain (synth-defaults ::sustain)
+        release (synth-defaults ::release)
+        [hpf-freq rlpf-freq rlpf-q] hpf-rlpf]
+
+    (-> synth-unit
+        (overtone.sc.ugen-collide/* (env-gen (asr attack sustain release) gate))
+        (hpf hpf-freq)
+        (rlpf rlpf-freq rlpf-q)
+        (overtone.sc.ugen-collide/* amp)
+        (free-verb verb verb verb))))
+
+(definst drone-aw-sus
+  "I make the 'aw' vowel sound at a given frequency.
+   I start/stop with the gate set to 1 or 0."
+  [freq   300
+   gate   (synth-defaults ::gate)
+   amp    (synth-defaults ::vca)
+   verb   (synth-defaults ::reverb)
+   kr-mul (synth-defaults ::vco)]
+
+    (let [kr-mul     (:value kr-mul)
+          eq-freq    [570 840 2410]
+          hpf-rlpf   [900 600 0.6]
+          q          0.1
+          synth-unit (synth-unit-layered freq eq-freq q kr-mul)]
+
+        (synth-filter-chain synth-unit amp verb gate hpf-rlpf)))
+
+(definst drone-ae-sus
+  "I make the 'ae' vowel sound at a given frequency.
+   I start/stop with the gate set to 1 or 0."
+  [freq   100
+   gate   (synth-defaults ::gate)
+   amp    (synth-defaults ::vca)
+   verb   (synth-defaults ::reverb)
+   kr-mul (synth-defaults ::vco)]
+
+  (let [kr-mul     (:value kr-mul)
+        eq-freq    [270 2290 3010]
+        hpf-rlpf   [600 8000 0.6]
+        q          0.1
+        synth-unit (synth-unit-layered freq eq-freq q kr-mul)]
+
+    (synth-filter-chain synth-unit amp verb gate hpf-rlpf)))
+
+(definst drone-eh-sus
+  "I make the 'eh' vowel sound at a given frequency.
+   I start/stop with the gate set to 1 or 0."
+  [freq   80
+   gate   (synth-defaults ::gate)
+   amp    (synth-defaults ::vca)
+   verb   (synth-defaults ::reverb)
+   kr-mul (synth-defaults ::vco)]
+
+  (let [kr-mul     (:value kr-mul)
+        eq-freq    [530 1840 2480]
+        hpf-rlpf   [0 750 0.9]
+        q          0.1
+        synth-unit (synth-unit-layered freq eq-freq q kr-mul)]
+
+    (synth-filter-chain synth-unit amp verb gate hpf-rlpf)))
+
+(definst drone-oo-sus
+  "I make the 'oo' vowel sound at a given frequency.
+   I start/stop with the gate set to 1 or 0."
+  [freq   120
+   gate   (synth-defaults ::gate)
+   amp    (synth-defaults ::vca)
+   verb   (synth-defaults ::reverb)
+   kr-mul (synth-defaults ::vco)]
+
+  (let [kr-mul     (:value kr-mul)
+        eq-freq    [300 870 2240]
+        hpf-rlpf   [0 600 0.6]
+        q          0.1
+        synth-unit (synth-unit-layered freq eq-freq q kr-mul)]
+
+    (synth-filter-chain synth-unit amp verb gate hpf-rlpf)))
+
+;;;;;;;;;;;;;;;;;;;;;
+;; Control         ;;
+;;;;;;;;;;;;;;;;;;;;;

 (defn amplitude-mul
   "This is a quick 'n dirty function that lowers the amplitude over time. It was written to compensate the removal of reverb over time and the increase in the perceived loudness of a sound."
@@ -162,10 +197,6 @@
     (>= age 1000) 0.4
     :else 1))

-;;;;;;;;;;;;;;;;;;;;;
-;; Control         ;;
-;;;;;;;;;;;;;;;;;;;;;
-
 (defn end-sound! [pid]
   (if (contains? @person-sound pid)
     (do
@@ -196,9 +227,8 @@
     3 (add-person-sound! pid "drone-oo-sus")
     "atom full: four people are tracked")))

-
 (defn control-sound
-  "Here's a fn which will take a val between 0 and 1, map it linearly to a value between 50 and 1000 and send the mapped value as the new frequency of foo:"
+  "I take a val between 0 and 1000+, map the value, and send it to the instrument's controller."
   [pid val]
   (let [verb-val  (scale-range val 0 1000 1 0.3)
         kr-val    (scale-range val 0 1000 25 0)


Add a descending run for the end.

Add a descending run for the end.

--- src/overtunes/songs/canone_alla_quarta.clj
+++ src/overtunes/songs/canone_alla_quarta.clj
@@ -60,8 +60,8 @@

 (def bass
   (let [line
-          {:time (repeat 24 1)
-           :pitch (mapcat (partial repeat 3) (concat (run 0 -3) (run -5 -3) [0 0]))}
+          {:time (mapcat repeat [21 12] [1 1/4])
+           :pitch (concat (mapcat (partial repeat 3) (concat (run 0 -3) (run -5 -3))) (run 12 0))}
         lower-note #(- % 7)
         lower-melody (update :pitch #(connect lower-note %))]
    (-> line melody lower-melody)))
@@ -82,7 +82,7 @@

 (defn play# []
   (let [from-now #(translate % 0 (now))
-        beat (from-now (bpm 100))
+        beat (from-now (bpm 90))
         with-beat (update :time (partial connect beat))
         in-key (update :pitch (partial connect g-major))
         with-bass-accidentals (update :pitch (partial sharps [8]))


wip: fix introspection for type resolver

--- src/graphql_clj/introspection.clj
+++ src/graphql_clj/introspection.clj
@@ -41,77 +41,102 @@
     (default-root-query-node root-query-name)))

 (defn type-kind [type]
-  (assert (:tag type) (format "type tag is nil for type: %s" type))
-  (assert (:name type) (format "type name is nil for type: %s" type))
-  (cond
-    (contains? #{'Int 'Float 'String 'Boolean 'ID} (:name type)) :SCALAR
-    (:name type) :OBJECT
-    :else (throw (ex-info (format "unknow type kind for type: %s" type) {:type type}))))
+  (if (:kind type)
+    (:kind type)
+    (do
+      (assert (:tag type) (format "type tag is nil for type: %s" type))
+      (let [tag (:tag type)
+            name (:name type)]
+        (cond
+          (:required type) :NON_NULL
+          (and (= :basic-type tag)
+               (contains? #{'Int 'Float 'String 'Boolean 'ID} name)) :SCALAR
+          (= :basic-type tag) :OBJECT
+          (= :list-type tag) :LIST
+          (= :type-definition tag) :OBJECT ; root query kind
+          (= :union-definition tag) :UNION
+          (= :interface-definition tag) :INTERFACE
+          (= :enum-definition tag) :ENUM
+          :else (assert nil (format "unknown type kind for type: %s" type)))))))

-(defn type-resolver [type]
-  (println "type-resolver:" type)
-  (when (not (contains? #{:LIST :NON_NULL} (:kind type)))
-    (assert (or (:name type) (:type-name type)) (format "type name is null for type: %s." type))
-    (assert (:kind type) (format "kind is nil for type: %s" type)))
-  (if (:required type)
-    ;; Wrap required type ast NON_NULL
-    {;; A Non-Null type cannot modify another Non-Null type.
-     :kind :NON_NULL
-     ;; defer resolving ofType
-     :inner-type (dissoc type :required)}
-    {:kind (:kind type)
-     :name (:name type)
-     :description (:description type)
-
-     ;; OBJECT and INTERFACE only
-     :fields (when (contains? #{:OBJECT :INTERFACE} (:kind type))
-               (:fields type)) ; defer resolving of fields
-
-     ;; OBJECT only
-     :interfaces [] ; TODO
-
-     ;; INTERFACE and UNION only
-     :possibleTypes nil
-
-     ;; ENUM only
-     :enumValues (when (= :ENUM (:kind type))
-                   (assert (:constants type) (format "enum constants is empty for type: %s." type))
-                   (:fields type))
-
-     ;; INPUT_OBJECT only
-     :inputFields (when (= :INPUT_OBJECT (:kind type))
-                    (assert (:fields type) (format "input fields is empty for type: %s." type))
-                    (:fields type))
-
-     ;; NON_NULL and LIST only
-     ;; :ofType nil
-     :inner-type (:inner-type type) ; provide inner-type for ofType resolver
-     }))
-
-(defn schema-types [schema]
-  (assert (map? (:type-map schema)) (format "schema has no :type-map: %s" schema))
-  (map type-resolver (vals (:type-map schema))))
+(defn field-kind [field]
+  (assert (:tag field) (format "field tag is nil for type: %s" field))
+  (assert (:name field) (format "field name is nil for type: %s" field))
+  (let [tag (:tag field)
+        name (:name field)
+        type (:type field)]
+    (cond
+      (= :type-field tag) (type-kind type)
+      :else (throw (ex-info (format "unknown field kind for field: %s" field) {:field field})))))

 (defn field-resolver [field]
   (assert (:name field)
           (format "field name is null for field: %s" field))
-  (assert (:kind field)
-          (format "field kind is null for field: %s" field))
-  (let [type-name (or (:type-name field) (get-in field [:type :name]))]
-    (assert type-name (format "field type-name is nil for field: %s" field))
+  (assert (:tag field)
+          (format "field tag is null for field: %s" field))
+  (let [type-name (or (:type-name field) (get-in field [:type :name]))
+        kind (field-kind field)
+        inner-type (get-in field [:type :inner-type])]
+    (assert kind (format "field kind is nil for field: %s" field))
+    (assert (or type-name inner-type) (format "field type-name and inner-type are both nil for field: %s" field))
     {:name (:name field)
      :description (:description field)
      :args (:arguments field) ; defer resolving of arguments

      ;; :type nil ; defer resolving of type
      :type-name type-name
-     :kind (:kind field)
-     :inner-type (:inner-type field)
+     :kind kind
+     :inner-type inner-type
      :required (:required field)

      :isDeprecated false ; TODO
      :deprecationReason nil ; TODO
      }))
+
+(defn type-resolver [type]
+  (println "type-resolver:" type)
+  (let [kind (type-kind type)]
+    (when (not (contains? #{:LIST :NON_NULL} kind))
+      (assert (or (:name type) (:type-name type)) (format "type name is null for type: %s." type))
+      (assert kind (format "kind is nil for type: %s" type)))
+    (if (:required type)
+      ;; Wrap required type ast NON_NULL
+      {;; A Non-Null type cannot modify another Non-Null type.
+       :kind :NON_NULL
+       ;; defer resolving ofType
+       :inner-type (dissoc type :required)}
+      {:kind kind
+       :name (:name type)
+       :description (:description type)
+
+       ;; OBJECT and INTERFACE only
+       :fields (when (contains? #{:OBJECT :INTERFACE} kind)
+                 (:fields type)) ; defer resolving of fields
+
+       ;; OBJECT only
+       :interfaces [] ; TODO
+
+       ;; INTERFACE and UNION only
+       :possibleTypes nil
+
+       ;; ENUM only
+       :enumValues (when (= :ENUM kind)
+                     (assert (:constants type) (format "enum constants is empty for type: %s." type))
+                     (:fields type))
+
+       ;; INPUT_OBJECT only
+       :inputFields (when (= :INPUT_OBJECT kind)
+                      (assert (:fields type) (format "input fields is empty for type: %s." type))
+                      (:fields type))
+
+       ;; NON_NULL and LIST only
+       ;; :ofType nil
+       :inner-type (:inner-type type) ; provide inner-type for ofType resolver
+       })))
+
+(defn schema-types [schema]
+  (assert (map? (:type-map schema)) (format "schema has no :type-map: %s" schema))
+  (map type-resolver (vals (:type-map schema))))

 (defn input-field-resolver [field]
   (assert (:field-name field) (format "field name is null for input value: %s." field))


improve swagger docs on a few endpoints

improve swagger docs on a few endpoints

--- src/block_chain/http.clj
+++ src/block_chain/http.clj
@@ -126,7 +126,7 @@
    (sweet/POST "/pending_transactions" req
                :return {:message String :payload Transaction}
                :body [transaction Transaction]
-               :summary "Submit a new transaction to this node for inclusion in the next block."
+               :summary "Submit a new transaction to this node for inclusion in the next block. Txn must be fully valid and signed."
                (let [resp (h/handler {:message "submit_transaction" :payload transaction} {})]
                      (if (= "transaction-accepted" (:message resp))
                        (ok resp)
@@ -138,7 +138,7 @@
                              to_address :- s/Str
                              amount :- s/Int
                              fee :- s/Int]
-               :summary "Submit a new transaction to this node for inclusion in the next block."
+               :summary "Builds a payment transaction by sourcing appropriate inputs to fund the requested amount. Creates a structurally valid Txn without signatures on the inputs. A wallet-only client can use this endpoint to fetch a txn, then add the signatures and submit the txn for inclusion in the chain."
                (let [resp (h/handler {:message "generate_payment" :payload {:from-address from_address
                                                                             :to-address to_address
                                                                             :amount amount


Cleaned up example formatting

Cleaned up example formatting

--- src/clj/forma/schema.clj
+++ src/clj/forma/schema.clj
@@ -36,11 +36,10 @@
 (defn timeseries-value
   "Takes a period index > 0 `start-idx` and a time series `series`, outputs same plus `end-idx` calculated by counting the number of elements in `series`.

-   Example: (timeseries-value 0 [1 2 3])
-
-   {:start-idx 0
-     :end-idx 2
-     :series [1 2 3]}"
+    (timeseries-value 0 [1 2 3])
+    ;=> {:start-idx 0
+         :end-idx 2
+         :series [1 2 3]}"
   ([start-idx series]
      (let [elems (count series)]
        (timeseries-value start-idx
@@ -56,17 +55,15 @@
   "Takes in any number of timeseries objects, and returns a new
   sequence of appropriately truncated TimeSeries objects generated from the temporal overlap of the input timeseries objects.

-  Example:
-  (schema/adjust-timeseries
-     (schema/timeseries-value 0 [1 2 3])
-     (schema/timeseries-value 1 [1 2 3 4]))
-
- ({:start-idx 1,
-   :end-idx 2,
-   :series [2 3]}
-  {:start-idx 1,
-   :end-idx 2,
-   :series [1 2]})"
+   (schema/adjust-timeseries
+                             (schema/timeseries-value 0 [1 2 3])
+                             (schema/timeseries-value 1 [1 2 3 4]))
+   ;=> [{:start-idx 1,
+         :end-idx 2,
+         :series [2 3]}
+        {:start-idx 1,
+         :end-idx 2,
+         :series [1 2]}]"
   [& tseries]
   (let [[start & ts-seq] (->> tseries
                               (mapcat (juxt :start-idx :series))
@@ -89,8 +86,8 @@
     both temp and confidence
     total fires

-   Example: (schema/fire-value 1 0 3 4)
-    {:temp-330 1, :conf-50 0, :both-preds 3, :count 4}"
+    (schema/fire-value 1 0 3 4)
+    ;=> {:temp-330 1, :conf-50 0, :both-preds 3, :count 4}"
   [t-above-330 c-above-50 both-preds count]
   {:temp-330 t-above-330
    :conf-50 c-above-50
@@ -101,9 +98,8 @@
   "Returns a vector containing the value of the `temp-330`, `conf-50`,
   `both-preds` and `count` fields of the supplied fire tuple.

-   Example: (extract-fields {:temp-330 1, :conf-50 0, :both-preds 3, :count 4})
-    [1 0 3 4]"
-
+    (extract-fields {:temp-330 1, :conf-50 0, :both-preds 3, :count 4})
+    ;=> [1 0 3 4]"
   [tuple]
   (map tuple [:temp-330 :conf-50 :both-preds :count]))

@@ -111,12 +107,10 @@
   "Returns a new `fire-value` object generated by summing up the fields
   of each of the supplied `fire-value` objects.

-   Example:
     (schema/add-fires
-      {:temp-330 1, :conf-50 0, :both-preds 3, :count 4}
-      {:temp-330 1, :conf-50 0, :both-preds 3, :count 4})
-
-    {:count 8, :conf-50 0, :temp-330 2, :both-preds 6}"
+     {:temp-330 1, :conf-50 0, :both-preds 3, :count 4}
+     {:temp-330 1, :conf-50 0, :both-preds 3, :count 4})
+    ;=> {:count 8, :conf-50 0, :temp-330 2, :both-preds 6}"
   [& f-tuples]
   (apply merge-with + f-tuples))

@@ -124,17 +118,17 @@
   "Returns the section of fires data found appropriate based on the
   information in the estimation parameter map.

-  (let [est-map {:est-start \"2005-12-01\"
-                 :est-end \"2011-04-01\"
-                 :t-res \"32\"
-                 :neighbors 1
-                 :window-dims [600 600]
-                 :vcf-limit 25
-                 :long-block 15
-                 :window 5}
-        f-series []]
-    (adjust-fires est-map f-series)"
-  ;; TODO: add a sample f-series
+    (let [est-map {:est-start \"2005-12-01\"
+                   :est-end \"2011-04-01\"
+                   :t-res \"32\"
+                   :neighbors 1
+                   :window-dims [600 600]
+                   :vcf-limit 25
+                   :long-block 15
+                   :window 5}
+          f-series []]
+    ;=> (adjust-fires est-map f-series)"
+  ;; TODO: add a sample f-series, result
   [{:keys [est-start est-end t-res]} f-series]
   (let [[start end] (for [pd [est-start est-end]]
                       (date/datetime->period "32" pd))]
@@ -154,17 +148,15 @@
 (defn forma-value
   "Creates forma object containing various characteristics of pixel timeseries

-   Example:
-  (forma-value {:temp-330 1, :conf-50 0, :both-preds 3, :count 4}
-               -2.7
-               -1.2
-               1.725)
-
-  {:fire-value
-    {:count 4, :conf-50 0, :temp-330 1, :both-preds 3},
-   :short-drop -2.7,
-   :long-drop -1.2,
-   :t-stat 1.725}"
+    (forma-value {:temp-330 1, :conf-50 0, :both-preds 3, :count 4}
+                  -2.7
+                  -1.2
+                  1.725)
+    ;=> {:fire-value
+         {:count 4, :conf-50 0, :temp-330 1, :both-preds 3},
+         :short-drop -2.7,
+         :long-drop -1.2,
+         :t-stat 1.725}"
   [fire short long t-stat]
   {:fire-value (or fire (fire-value 0 0 0 0))
    :short-drop short
@@ -175,14 +167,13 @@
   "Returns a vector containing the fire value, short drop,
   long drop and t-stat fields of the supplied `FormaValue`.

-  Example:
-  (unpack-forma-val {:fire-value
+    (unpack-forma-val {:fire-value
                        {:count 4, :conf-50 0, :temp-330 1, :both-preds 3},
-                     :short-drop -2.7,
-                     :long-drop -1.2,
-                     :t-stat 1.725})
-
-  ({:count 4, :conf-50 0, :temp-330 1, :both-preds 3} -2.7 -1.2 1.725)"
+                       :short-drop -2.7,
+                       :long-drop -1.2,
+                       :t-stat 1.725})
+
+    ;=> [{:count 4, :conf-50 0, :temp-330 1, :both-preds 3} -2.7 -1.2 1.725]"
   [forma-val]
   (map forma-val [:fire-value :short-drop :long-drop :t-stat]))

@@ -260,6 +251,7 @@
 (defn textify
   "Converts the supplied coordinates, forma value and forma neighbor
   value into a line of text suitable for use in STATA."
+  ;; come up with an example
   [forma-val neighbor-val]
   (let [[fire-val s-drop l-drop t-drop] (unpack-forma-val forma-val)
         [fire-sum ct short-mean short-min
@@ -286,8 +278,8 @@
 (defn chunk-location
   "Creates a chunk-location object.

-   Example: (chunk-location \"1000\" 28 9 59 24000)
-    {:spatial-res \"1000\", :mod-h 28, :mod-v 9, :index 59, :size 24000}"
+    (chunk-location \"1000\" 28 9 59 24000)
+    ;=> {:spatial-res \"1000\", :mod-h 28, :mod-v 9, :index 59, :size 24000}"
   [spatial-res mod-h mod-v idx size]
   {:spatial-res spatial-res
    :mod-h       mod-h
@@ -305,10 +297,8 @@
 (defn pixel-location
   "Creates a pixel-location object.

-   Example:
     (pixel-location \"1000\" 28 9 1199 10)
-
-    {:spatial-res \"1000\", :mod-h 28, :mod-v 9, :sample 1199, :line 10}"
+    ;=> {:spatial-res \"1000\", :mod-h 28, :mod-v 9, :sample 1199, :line 10}"
   [spatial-res mod-h mod-v sample line]
   {:spatial-res spatial-res
    :mod-h       mod-h
@@ -319,27 +309,21 @@
 (defn unpack-pixel-location
   "Unpacks pixel-location object into a tuple.

-
-   Example:
     (unpack-pixel-location
      {:spatial-res \"1000\", :mod-h 28, :mod-v 9, :sample 1199, :line 10})
-
-    [\"1000\" 28 9 1199 10]"
+    ;=> [\"1000\" 28 9 1199 10]"
   [loc]
   (map loc [:spatial-res :mod-h :mod-v :sample :line]))

 (defn chunkloc->pixloc
   "Accepts a chunk location and a pixel index within that location and
    returns a pixel location.
-
-  Example:
-   (chunkloc->pixloc
-    {:spatial-res "1000", :mod-h 28, :mod-v 9, :index 59, :size 24000}
-     23999)
-
-   {:spatial-res "1000", :mod-h 28, :mod-v 9, :sample 1199, :line 1199}
-   "
-
+
+     (chunkloc->pixloc
+      {:spatial-res "1000", :mod-h 28, :mod-v 9, :index 59, :size 24000}
+      23999)
+
+    ;=> {:spatial-res "1000", :mod-h 28, :mod-v 9, :sample 1199, :line 1199}"
   [{:keys [spatial-res size index mod-h mod-v]} pix-idx]
   (apply pixel-location spatial-res mod-h mod-v
          (r/tile-position spatial-res size index pix-idx)))
@@ -355,11 +339,11 @@
           data (range size)]
      (chunk-value \"ndvi\" \"32\" \"2006-01-01\" location (take 10 data))

-    {:date \"2006-01-01\"
-     :temporal-res \"32\"
-     :location {:spatial-res \"1000\", :mod-h 8, :mod-v 6, :index 0, :size 24000}     :dataset \"ndvi\"
-     :value [0 1 2 3 4 5 6 7 8 9]}"
-    [dataset t-res date location data-value]
+  ;=> {:date \"2006-01-01\"
+       :temporal-res \"32\"
+       :location {:spatial-res \"1000\", :mod-h 8, :mod-v 6, :index 0, :size 24000}     :dataset \"ndvi\"
+       :value [0 1 2 3 4 5 6 7 8 9]}"
+  [dataset t-res date location data-value]
   (let [chunk {:temporal-res t-res
                :location     location
                :dataset      dataset
@@ -378,7 +362,7 @@
                             :location {:spatial-res \"1000\", :mod-h 8, :mod-v 6, :index 0, :size 24000}     :dataset \"ndvi\"
                             :value [0 1 2 3 4 5 6 7 8 9]})

-    [\"ndvi\" \"32\" \"2006-01-01\" {:mod-h 8, :size 24000, :mod-v 6, :spatial-res \"1000\", :index 0} [0 1 2 3 4 5 6 7 8 9]"
+  ;=> [\"ndvi\" \"32\" \"2006-01-01\" {:mod-h 8, :size 24000, :mod-v 6, :spatial-res \"1000\", :index 0} [0 1 2 3 4 5 6 7 8 9]"
   [chunk]
   (map chunk [:dataset :temporal-res :date :location :value]))


Grid now updating

Grid now updating

Cannot go off screen to left or right
Game can be won by landing on yellow block
Game lost when falling off of screen


Add multiarity to maybe/either constructors.

--- src/cljx/cats/types.cljx
+++ src/cljx/cats/types.cljx
@@ -51,13 +51,17 @@

 (defn left
   "Left constructor for Either type."
-  [^Object v]
-  (Either. v :left))
+  ([v]
+     (Either. v :left))
+  ([]
+     (Either. nil :left)))

 (defn right
   "Right constructor for Either type."
-  [^Object v]
-  (Either. v :right))
+  ([v]
+     (Either. v :right))
+  ([]
+     (Either. nil :right)))

 (defn left?
   [mv]
@@ -149,8 +153,10 @@
   (mplus [mv _] mv))

 (defn just
-  [v]
-  (Just. v))
+  ([v]
+     (Just. v))
+  ([]
+     (Just. nil)))

 (defn nothing
   []


Remove unused code

Remove unused code

--- src/buildviz/handler.clj
+++ src/buildviz/handler.clj
@@ -55,7 +55,6 @@
   (-> (app-routes build-results pipeline-name)
       (wrap-build-results-not-modified build-results)
       params/wrap-params
-      json/wrap-json-response
       (json/wrap-json-body {:keywords? true})
       (accept/wrap-accept {:mime ["application/json" :as :json,
                                   "application/xml" "text/xml" :as :xml


nicer desc

nicer desc

--- src/celestial/common.clj
+++ src/celestial/common.clj
@@ -31,7 +31,7 @@
     (throw+ {:type ::missing-conf :message (<< "No matching configuration keys ~{keys} found")})))

 (defn get*
-  "Quite version of get!"
+  "nil on missing version of get!"
   [& keys]
    (get-in config keys))


moved orering around

moved orering around

--- src/distributions/normal-inverse-gamma.clj
+++ src/distributions/normal-inverse-gamma.clj
@@ -21,6 +21,8 @@
                           (log-pdf (normal location (/ s2 lambda)) mu)))
   (log-pdf [d] (fn [x] (pdf d x))))

+(defn normal-inverse-gamma [location prec shape scale] (new NormalInverseGamma location prec shape scale))
+
 (defmethod posterior [distributions.core.Normal distributions.core.NormalInverseGamma]
   [y likelihood prior]
   (let [prior-mean (:location prior)
@@ -43,8 +45,6 @@
         ]
     (normal-inverse-gamma post-mean post-lambda post-shape post-scale)))

-(defn normal-inverse-gamma [location prec shape scale] (new NormalInverseGamma location prec shape scale))
-
 (defmethod marginal [distributions.core.Normal distributions.core.NormalInverseGamma]
   [likelihood prior]
   (let [{prior-shape :shape
@@ -56,7 +56,7 @@
                     (sqrt (/ (* prior-scale (+ 1 prior-lambda))
                              (* prior-lambda prior-shape))))))

-(comment
+(comment
 (sample (marginal (normal :mu :s2) (normal-inverse-gamma 2 3 1 2)))

 (def Y [1 1 1 2 2 3 1 2 5])


Adjusted example of exploration to new handler semantics

Signed-off-by: Daniel Borchmann <25857343a15bf1edafebc2912e82ecf775c585c1@mailbox.tu-dresden.de>

--- doc/examples/exploration.clj
+++ doc/examples/exploration.clj
@@ -52,11 +52,11 @@
 ;;; exploration. Just give, as second argument, a set of valid
 ;;; implications.

-(explore-attributes ctx #{(make-implication #{3} #{1})})
+(explore-attributes ctx :background-knowledge #{(make-implication #{3} #{1})})

 ;;; This "shortens" the exploration a bit:
 ;;;
-;;; user=> (explore-attributes ctx #{(make-implication #{3} #{1})})
+;;; user=> (explore-attributes ctx :background-knowledge #{(make-implication #{3} #{1})})
 ;;; {:implications #{}, :context   |1 2 3
 ;;;  --+------
 ;;;  a |. x .
@@ -69,24 +69,23 @@
 ;;; implications.

 ;;; Finally, you can control the way the exploration handles
-;;; interaction with a custom handler function as a third (!)
-;;; argument. This functions is called whenever an expert has to be
-;;; asked, getting as its arguments the current context and the
-;;; current implication. Standardly, the function «default-handler» is
-;;; used, which implements low level communication via the command
-;;; line.
+;;; interaction with a custom handler function. This functions is
+;;; called whenever an expert has to be asked, getting as its
+;;; arguments the current context and the current
+;;; implication. Standardly, the function «default-handler» is used,
+;;; which implements low level communication via the command line.

 ;;; Handlers have to return specific values to indicate approval or
 ;;; rejection of a given implication. The format of the return value
 ;;; is as follows:
 ;;;
 ;;;   On success: nil
-;;;   On failure: [«the new object» «a sequence of its attributes»]
+;;;   On failure: sequence of [«a new object» «a sequence of its attributes»]
 ;;;
 ;;; With that, you could immitate «stem-base» with explore-attributes
 ;;; via

-(= (:implications (explore-attributes ctx #{} (constantly nil)))
+(= (:implications (explore-attributes ctx :handler (constantly nil)))
    (stem-base ctx))

 ;;; and indeed this is one of the test cases for explore-attributes.


foreign libs may now require other libs

foreign libs may now require other libs

--- src/clj/cljs/closure.clj
+++ src/clj/cljs/closure.clj
@@ -393,7 +393,6 @@
   and :url"
   [lib-spec]
   (merge lib-spec {:foreign true
-                   :requires nil
                    :url (find-url (:file lib-spec))}))

 (def load-foreign-library (memoize load-foreign-library*))


gen overrides should be no-arg fns

gen overrides should be no-arg fns

--- src/clj/clojure/spec.clj
+++ src/clj/clojure/spec.clj
@@ -219,8 +219,9 @@
   [spec overrides path rmap form]
   ;;(prn {:spec spec :over overrides :path path :form form})
   (let [spec (specize spec)]
-    (if-let [g (c/or (get overrides (c/or (spec-name spec) spec))
-                     (get overrides path)
+    (if-let [g (c/or (when-let [gfn (c/or (get overrides (c/or (spec-name spec) spec))
+                                          (get overrides path))]
+                       (gfn))
                      (gen* spec overrides path rmap))]
       (gen/such-that #(valid? spec %) g 100)
       (let [abbr (abbrev form)]
@@ -230,8 +231,8 @@
 (defn gen
   "Given a spec, returns the generator for it, or throws if none can
   be constructed. Optionally an overrides map can be provided which
-  should map spec names or paths (vectors of keywords) to
-  generators. These will be used instead of the generators at those
+  should map spec names or paths (vectors of keywords) to no-arg
+  generator-creating fns. These will be used instead of the generators at those
   names/paths. Note that parent generator (in the spec or overrides
   map) will supersede those of any subtrees. A generator for a regex
   op must always return a sequential collection (i.e. a generator for


complete exploration algorithm

complete exploration algorithm

--- src/clj/signet/graph.clj
+++ src/clj/signet/graph.clj
@@ -75,6 +75,16 @@
            (into {})))))

+(defn find-merge-points [{:keys [causal-order branches] :as cg}]
+  (let [branch-heads (into {} (map (fn [[k v]] [v k]) branches))]
+    (assoc cg
+      :merge-points
+      (->> causal-order
+           (filter-map [key val] (> (count val) 1))
+           (into {})
+           (map (fn [[k v]] (map (fn [b] [b k]) (remove nil? (map branch-heads v)))))
+           (apply concat)
+           (into {})))))

 (comment
@@ -89,23 +99,25 @@
                     70 [60]
                     80 [30]
                     90 [80]
-                    100 [70]
+                    100 [70 140]
                     110 [100]
-                    120 [90]}
+                    120 [90]
+                    130 [30]
+                    140 [130]}
      :branches {"master" 110
                 "fix" 50
-                "dev" 120}})
+                "dev" 120
+                "fix-2" 140}})

-  (let [{:keys [causal-order branches]} (->> test-cg
-                                             commit-graph->nodes
-                                             distinct-nodes
-                                             nodes->order)
-        branch-heads (into {} (map (fn [[k v]] [v k]) branches))]
-    (map
-     (fn [[k v]]
-       [k ()])
-     (filter-map [key val] (> (count val) 1) causal-order)))
+  (->> test-cg
+       commit-graph->nodes
+       distinct-nodes
+       nodes->order
+       find-merge-points)
+
+
+


Adds all browse doc strings

Adds all browse doc strings

--- src/clj_spotify/core.clj
+++ src/clj_spotify/core.clj
@@ -141,8 +141,24 @@

     Example: (get-a-list-of-featured-playlists {:locale \"sv_SE\" :country \"SE\" :timestamp \"2015-10-23T09:00:00\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")")

-(spotify-api-call get-a-list-of-new-releases client/get (str spotify-api-url "browse/new-releases") "")
-(spotify-api-call get-a-list-of-categories client/get (str spotify-api-url "browse/categories") "")
+(spotify-api-call get-a-list-of-new-releases client/get (str spotify-api-url "browse/new-releases")
+" Takes two arguments, a map m with query parameters and an optional oauth-token t.
+    There are no compulsary keys in m, optional keys are :country, :limit and :offset.
+    :country is an ISO 3166-1 alpha-2 country code.
+    :limit is the maxium number of tracks to return, default is 20.
+    :offset is the index of the first track to return, default is 0.
+
+    Example: (get-a-list-of-new-releases {:country \"SE\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")")
+
+(spotify-api-call get-a-list-of-categories client/get (str spotify-api-url "browse/categories")
+" Takes two arguments, a map m with query parameters and an optional oauth-token t.
+    There are no compulsary keys in m, optional keys are :country, :limit and :offset.
+    :country is an ISO 3166-1 alpha-2 country code.
+    :limit is the maxium number of tracks to return, default is 20.
+    :offset is the index of the first track to return, default is 0.
+
+    Example: (get-a-list-of-categories {:country \"SE\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")")
+
 (spotify-api-call get-a-category client/get (str spotify-api-url "browse/categories/category_id")
 " Takes two arguments, a map m with query parameters and an optional oauth-token t.
     Compulsory key in m is :category_id, optional keys are :country and :locale.
@@ -152,7 +168,15 @@

     Example: (get-a-category {:category_id \"dinner\" :country \"SE\" :locale \"sv_SE\"} \"BQBw-JtC..._7GvA\")")

-(spotify-api-call get-a-categorys-playlists client/get (str spotify-api-url "browse/categories/category_id/playlists") "")
+(spotify-api-call get-a-categorys-playlists client/get (str spotify-api-url "browse/categories/category_id/playlists")
+" Takes two arguments, a map m with query parameters and an optional oauth-token t.
+    Compulsory key in m is :category_id, optional keys are :country, :limit and :offset.
+    :category_id has to be a spotify category id.
+    :country is an ISO 3166-1 alpha-2 country code.
+    :limit is the maxium number of tracks to return, default is 20.
+    :offset is the index of the first track to return, default is 0.
+
+    Example: (get-a-categorys-playlist {:category_id \"dinner\" :country \"SE\" :locale \"sv_SE\"} \"BQBw-JtC..._7GvA\") ")

 ;Follow
 (spotify-api-call get-users-followed-artists client/get (str spotify-api-url "me/following") "")


disable updater

disable updater

--- src/cljs/smgui/main.cljs
+++ src/cljs/smgui/main.cljs
@@ -61,7 +61,7 @@
 (defn init []
   (ws-repl/connect "ws://localhost:9001")
   (enable-console-print!)
-  (updater-main)
+  #_ (updater-main)
   (gui/listen-open #(do (put! flux-channel {:cmd :add-search :path %})
                         (put! flux-channel {:cmd :change-page :page :search})))
   (om/root main-view app-state {:target (.-body js/document)})


bugfix

bugfix

--- src/clj/de/sveri/friendui/routes/user.clj
+++ src/clj/de/sveri/friendui/routes/user.clj
@@ -7,7 +7,6 @@
             [de.sveri.friendui.views.admin-view :as admin]
             [noir.validation :as vali]
             [net.cgrand.enlive-html :as html]
-            [de.sveri.friendui.routes.util :as util]
             [de.sveri.friendui.service.user :as userservice]
             [de.sveri.friendui.globals :as globals]
             [noir.response :as resp]


dont show scrollbar when loading

dont show scrollbar when loading

--- src/cljs/swarmpit/component/progress.cljs
+++ src/cljs/swarmpit/component/progress.cljs
@@ -12,7 +12,7 @@
      (html
        [:div
         {:style (merge {:width          "100%"
-                        :height         "100vh"
+                        :height         "80vh"
                         :display        "flex"
                         :alignItems     "center"
                         :justifyContent "center"}


Adding failing test to test CI.

Adding failing test to test CI.

--- test/cmr/umm/test/echo10/collection.clj
+++ test/cmr/umm/test/echo10/collection.clj
@@ -10,6 +10,10 @@
             ;; my code
             [cmr.umm.echo10.collection :as c]
             [cmr.umm.collection :as umm-c]))
+
+(deftest will-always-fail
+  (println "Failing to test CI")
+  (is (= 5 4)))

 (defspec generate-collection-is-valid-xml-test 100
   (for-all [collection umm-gen/collections]


allow figwheel.core/build to handle changes files as input

--- figwheel-core/src/figwheel/core.cljc
+++ figwheel-core/src/figwheel/core.cljc
@@ -669,8 +669,11 @@
                    (not (:exception compile-data)))
               (binding [env/*compiler* compiler-env
                         cljs.repl/*repl-env* repl-env]
-                (let [modified-sources (sources-modified! @compiler-env last-modified)
-                      namespaces (sources->namespaces-to-reload modified-sources)]
+                (let [namespaces
+                      (if (contains? compile-data :changed-files)
+                        (paths->namespaces-to-reload (:changed-files compile-data))
+                        (->> (sources-modified! @compiler-env last-modified)
+                             (sources->namespaces-to-reload)))]
                   (when-let [warnings (:warnings compile-data)]
                     (handle-warnings warnings))
                   (reload-namespaces namespaces)))
@@ -699,12 +702,24 @@
 ;; building
 ;; -------------------------------------------------------------

+(defn notify-on-exception [compiler-env e extra-data]
+  (doto compiler-env
+    (swap! vary-meta assoc ::compile-data
+           {:started (System/currentTimeMillis)})
+    (swap! vary-meta update ::compile-data
+           (fn [x]
+             (merge (select-keys x [:started])
+                    extra-data
+                    {:exception e
+                     :finished (System/currentTimeMillis)})))))
+
 ;; TODO should handle case of already having changed files
 (let [cljs-build cljs.closure/build]
   (defn build
-    ([src opts] (with-redefs [cljs.closure/build build]
-                  (cljs-build src opts)))
-    ([src opts compiler-env]
+    ([src opts]
+     (with-redefs [cljs.closure/build build]
+       (cljs-build src opts)))
+    ([src opts compiler-env & [changed-files]]
      (let [local-data (volatile! {})]
        (binding [cljs.analyzer/*cljs-warning-handlers*
                  (conj cljs.analyzer/*cljs-warning-handlers*
@@ -724,7 +739,9 @@
                   (fn [x]
                     (merge (select-keys x [:started])
                            @local-data
-                           {:finished (System/currentTimeMillis)})))
+                           (cond-> {:finished (System/currentTimeMillis)}
+                             (some? changed-files) ;; accept empty list here
+                             (assoc :changed-files changed-files)))))
            (vreset! local-data {})
            (catch Throwable e
              (swap! compiler-env


figwheel.main: update help docs

figwheel.main: update help docs

--- figwheel-main/src/figwheel/main.cljc
+++ figwheel-main/src/figwheel/main.cljc
@@ -158,7 +158,7 @@

 (def help-template
-  "Usage: clj -m figwheel.main [init-opt*] [main-opt] [arg*]
+  "Usage: clojure -m figwheel.main [init-opt*] [main-opt] [arg*]

 Common usage:
   clj -m figwheel.main -b dev -r
@@ -200,7 +200,7 @@
 ^{:watch-dirs [\"dev\" \"cljs-src\"]}
 {:main example.core}

-Command Line Options
+Command Line Options:

 With no options or args, figwheel.main runs a ClojureScript REPL

@@ -222,14 +222,25 @@
 Paths may be absolute or relative in the filesystem or relative to
 classpath. Classpath-relative paths have prefix of @ or @/")

+(defn adjust-option-docs [commands]
+  (-> commands
+      (update-in [:groups :cljs.cli/main&compile :pseudos]
+                 dissoc ["-re" "--repl-env"])
+      (assoc-in [:init ["-d" "--output-dir"] :doc]
+                "Set the output directory to use")
+      (update-in [:init ["-w" "--watch"] :doc] str
+                ". This option can be supplied multiple times.")))
+
 (defn help-str [repl-env]
-  (format help-template
-    (#'cljs.cli/options-str (#'cljs.cli/merged-commands repl-env))))
+  (format
+   help-template
+   (#'cljs.cli/options-str
+    (adjust-option-docs
+     (#'cljs.cli/merged-commands repl-env)))))

 (defn help-opt
   [repl-env _ _]
   (println (help-str repl-env)))
-

 ;; safer option reading from files which prints out syntax errors

@@ -517,18 +528,29 @@
    :main {["-b" "--build"]
           {:fn build-main-opt
            :arg "string"
-           :doc (str "The name of a build config to build.")}
+           :doc (str "Run a compile. The supplied build name refers to a  "
+                     "compililation options edn file. IE. \"dev\" will indicate "
+                     "that a \"dev.cljs.edn\" will be read for "
+                     "compilation options. The --build option will make an "
+                     "extra attempt to "
+                     "initialize a figwheel live reloading workflow. "
+                     "If --repl follows, "
+                     "will launch a REPL after the compile completes. "
+                     "If --server follows, will start a web server according to "
+                     "current configuration after the compile "
+                     "completes.")}
           ["-bo" "--build-once"]
           {:fn build-once-main-opt
            :arg "string"
-           :doc "The name of a build config to build once."}
+           :doc (str "Compile for the build name one time. "
+                     "Looks for a build EDN file just like the --build command.")}
           ["-r" "--repl"]
           {:fn repl-main-opt
            :doc "Run a REPL"}
           ["-s" "--serve"]
           {:fn serve-main-opt
            :arg "host:port"
-           :doc "Run a server based on the figwheel-main configuration options"}
+           :doc "Run a server based on the figwheel-main configuration options."}
           ["-h" "--help" "-?"]
           {:fn help-opt
            :doc "Print this help message and exit"}


save server itself not just a fn to kill it

--- figwheel-repl/src/figwheel/repl.cljc
+++ figwheel-repl/src/figwheel/repl.cljc
@@ -937,7 +937,7 @@
   (when (and
          (or (not (bound? #'*server*))
              (nil? *server*))
-         (nil? @(:server-kill repl-env)))
+         (nil? @(:server repl-env)))
     (let [server (run-default-server
                   (merge (select-keys repl-env [:port
                                                 :host
@@ -950,7 +950,7 @@
                          (select-keys opts [:target
                                             :output-to]))
                   *connections*)]
-      (reset! (:server-kill repl-env) (fn [] (.stop server)))))
+      (reset! (:server repl-env) server)))
   ;; printing
   (when-not @(:printing-listener repl-env)
     (let [print-listener
@@ -985,10 +985,10 @@
     ;; load a file into all the appropriate envs
     (when-let [js-content (try (slurp url) (catch Throwable t))]
       (evaluate this js-content)))
-  (-tear-down [{:keys [server-kill printing-listener]}]
-    (when-let [kill-fn @server-kill]
-      (reset! server-kill nil)
-      (kill-fn))
+  (-tear-down [{:keys [server printing-listener]}]
+    (when-let [svr @server]
+      (reset! server nil)
+      (.stop svr))
     (when-let [listener @printing-listener]
       (remove-listener listener)))
   cljs.repl/IReplEnvOptions
@@ -1027,7 +1027,7 @@
                        port default-port} :as opts}]
   (merge (FigwheelReplEnv.)
          ;; TODO move to one atom
-         {:server-kill (atom nil)
+         {:server (atom nil)
           :printing-listener (atom nil)
           :bound-printer (atom nil)
           :open-url open-url


Temporary hack on groups of groups

Temporary hack on groups of groups

--- frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs
+++ frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs
@@ -202,11 +202,14 @@
         selected? (contains? selected (:id item))
         collapsed? (:collapsed item true)
         shapes-map (mx/react wb/shapes-by-id-ref)
-        classes (classnames
-                 :selected selected?
-                 :drag-top (= :top (:over @local))
-                 :drag-bottom (= :bottom (:over @local))
-                 :drag-inside (= :middle (:over @local)))
+        ;; TODO: Fix this temporary hack (Looks like a problem in defcs macro)
+        classes (if (nil? own)
+                 (classnames :selected selected?)
+                 (classnames
+                   :selected selected?
+                   :drag-top (= :top (:over @local))
+                   :drag-bottom (= :bottom (:over @local))
+                   :drag-inside (= :middle (:over @local))))
         select #(select-shape selected item %)
         toggle-visibility #(toggle-visibility selected item %)
         toggle-blocking #(toggle-blocking item %)]
@@ -280,7 +283,8 @@
           (for [shape (map #(get shapes-map %) (:items item))
                 :let [key (str (:id shape))]]
             (if (= (:type shape) :group)
-              (-> (layer-group shape selected)
+              ;; TODO: Fix this temporary hack (Looks like a problem in defcs macro)
+              (-> (layer-group nil shape selected)
                   (mx/with-key key))
               (-> (layer-simple shape selected)
                   (mx/with-key key))))])])))


Remove unused map index

Remove unused map index

--- src/zooniverse_live/world_map.cljs
+++ src/zooniverse_live/world_map.cljs
@@ -85,7 +85,7 @@
                    :cx (long->svg-x longitude)
                    :fill color}))))

-(defn classifier-key [c idx]
+(defn classifier-key [c]
   (str (:keynum c)  (:latitude c) (:longitude c) (:color c) (:created_at c)))

 (defn world-map
@@ -99,5 +99,5 @@
                 :viewBox (str "0 0" " " width " " height)}
                (om/build-all draw-land (:map-data data))
                (dom/g {:className "classifiers"}
-                      (map-indexed #(om/build classifiers %2 {:react-key (classifier-key %2 %1)})
+                      (map #(om/build classifiers % {:react-key (classifier-key %)})
                                    (take-last 100 (:classifications data))))))))


Remove the `do`

Remove the `do`

--- test/vignette/http/integration_test.clj
+++ test/vignette/http/integration_test.clj
@@ -19,7 +19,7 @@
 (def system-local (create-system lis))

 (with-state-changes [(before :facts (start system-local default-port))
-                     (after :facts (do (stop system-local)))]
+                     (after :facts (stop system-local))]

   (facts :requests :thumbnail-integration :scale-to-width
     (let [response (client/get (format "http://localhost:%d/bucket/a/ab/boat.jpg/revision/latest/scale-to-width/200" default-port) {:as :byte-array})]


another unnecessary explicit do in a while

--- src/overtone/sc/machinery/server/comms.clj
+++ src/overtone/sc/machinery/server/comms.clj
@@ -55,9 +55,8 @@
         key (uuid)]
     (on-event "/synced"
               (fn [msg] (when (= id (first (:args msg)))
-                         (do
-                           (handler-fn)
-                           :overtone/remove-handler)))
+                         (handler-fn)
+                         :overtone/remove-handler))
               key)

     (let [res (action-fn)]


Rearranged some things for presentation's sake.

--- src/overtunes/songs/canone_alla_quarta.clj
+++ src/overtunes/songs/canone_alla_quarta.clj
@@ -22,6 +22,9 @@

 ;(demo# (range 60 73))

+
+
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Scale                                        ;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -41,6 +44,13 @@
 ;(demo# (let [key (comp (partial + 67) major), rest -100]
 ;         (map key [0 1 2 0 0 1 2 0 2 3 4 rest 2 3 4 rest])))

+
+
+
+
+
+
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Melody                                       ;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -51,18 +61,16 @@
             (range start end)
             (reverse (range (inc end) (inc start)))))]
     (if tos
-      (concat
-        (up-or-down from (first tos))
-        (run tos))
+      (concat (up-or-down from (first tos)) (run tos))
       [from])))
+
+;(demo# (map g-major
+;            (run [0 3 1 3 -1 0])
+;            ))

 (defn accumulate [series] (cons 0 (reductions + series)))
 (def repeats (partial mapcat (partial apply repeat)))
 (def runs (partial mapcat run))
-
-;(demo# (map g-major
-;            (run [0 3 1 3 -1 0])
-;            ))

 (def melody
   (let [call
@@ -93,17 +101,25 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

 (defn bpm [beats] (fn [beat] (-> beat (/ beats) (* 60) (* 1000))))
-(defn transform [k f] (fn [points] (map #(update-in % [k] f) points)))
+(defn skew [k f] (fn [points] (map #(update-in % [k] f) points)))
 (defn shift [point] (fn [points] (map #(->> % (map + point) vec) points)))

 (defn canone-alla-quarta# []
   (let [[timing pitch] [0 1]
-        in-time (comp (shift [(now) 0]) (transform timing (bpm 90)))
-        in-key (transform pitch g-major)
-        play-now# (comp play# in-key in-time)]
+        [tempo start] [(bpm 90) (now)]
+        in-time (comp (shift [start 0]) (skew timing tempo))
+        in-key (skew pitch g-major)
+        play-now# #(=> % in-key in-time play#)]

     (=> bassline (shift [0 -7]) play-now#)
     (=> melody (shift [1/2 0]) play-now#)
-    (=> melody (transform pitch -) (shift [7/2 -3]) play-now#)))
+    (=> melody (skew pitch -) (shift [7/2 -3]) play-now#)))

-(canone-alla-quarta#)
+;(canone-alla-quarta#)
+
+
+
+
+
+
+


Re-enables tests after the issue was fixed.

--- test/active/clojure/match_test.clj
+++ test/active/clojure/match_test.clj
@@ -79,7 +79,7 @@

   (t/testing "optional clauses"

-    #_(t/is (= (p/make-optional-key-exists-with-binding-clause :k "Binding")
+    (t/is (= (p/make-optional-key-exists-with-binding-clause :k "Binding")
              (p/parse-clause (? :k :as Binding))))

     (t/is (= (p/make-optional-path-exists-with-binding-clause [:k "bar" "baz"] "Binding")
@@ -240,7 +240,7 @@
     (t/is (= ["d"] ((p/map-matcher [([:a b c] "d" :as z)] [z]) {:a { "b" {"c" "d"}}})))))

 (t/deftest map-matcher-optional-test
-  #_(t/testing "Optional values"
+  (t/testing "Optional values"
     (t/is (= ["a" "c" "C" 42]
              ((p/map-matcher [(:kind #"two")
                               (? :a :as a)
@@ -249,7 +249,7 @@
                               (? [:d Z] :as Z)]
                              [a c C Z])
               two-data))))
-  #_(t/testing "Fall back to given default value."
+  (t/testing "Fall back to given default value."
     (t/is (= ["a" "C" "C" 42]
              ((p/map-matcher [(:kind #"two")
                               (? :a :as a)
@@ -262,7 +262,7 @@
                    "W" {"foo" "bar"}}}))))
   (t/testing "More optionals"
     ;; key match with binding
-    #_(t/is (= [nil] ((p/map-matcher [(? :a :as a)] [a]) {})))
+    (t/is (= [nil] ((p/map-matcher [(? :a :as a)] [a]) {})))
     (t/is (= ["a"] ((p/map-matcher [(? :a :as a)] [a]) {:a "a"})))
     ;; key match with default value with binding
     (t/is (= ["A"] ((p/map-matcher [(? :a "A" :as a)] [a]) {})))


see if a lil sleep will let this future start

before next block comes in

this test fails sporadically, I THINK because sometimes
the miner doesn't have time to get started before the next
block arrives

--- test/block_chain/message_handlers_test.clj
+++ test/block_chain/message_handlers_test.clj
@@ -306,6 +306,8 @@
         mining-future (future
                         (miner/mine pending))]

+    (Thread/sleep 50)
+
     (is (= {:message "block-accepted" :payload alt-block}
            (handler {:message "submit_block" :payload alt-block}
                     sock-info)))


Add interpret test helper

Add interpret test helper

--- test/sablono/interpreter_test.cljs
+++ test/sablono/interpreter_test.cljs
@@ -2,31 +2,54 @@
   (:require-macros [cljs.test :refer [are is deftest testing]]
                    [sablono.test :refer [html-str]])
   (:require [cljs.test :as t]
+            [sablono.core :as c]
             [sablono.interpreter :as i]))
+
+(defn interpret [x]
+  (some->> (i/interpret x)
+           (c/render-static)
+           (hickory.core/parse-fragment)
+           (map hickory.core/as-hiccup)
+           (first)))

 (deftest test-attributes
   (are [attrs expected]
-    (is (= expected (js->clj (i/attributes attrs))))
+      (is (= expected (js->clj (i/attributes attrs))))
     {} {}
     {:className ""} {}
     {:className "aa"} {"className" "aa"}
     {:className "aa bb"} {"className" "aa bb"}
     {:className ["aa bb"]} {"className" "aa bb"}))

-(deftest test-interpret
-  (are [form expected]
-    (is (= expected (html-str (i/interpret form))))
-    [:div] "

"
-    [:div "x"] "
x
"
-    [:div "1"] "
1
"
-    [:div 1] "
1
"))
+(deftest test-interpret-shorthand-div-forms
+  (is (= (interpret [:#test.klass1])
+         [:div {:id "test" :class "klass1"}])))

-(deftest shorthand-div-forms
-  (are [form expected]
-    (is (= expected (html-str (i/interpret form))))
-    [:#test.klass1] "

"))
+(deftest test-interpret-static-children-as-arguments
+  (is (= (interpret
+          [:div
+           [:div {:class "1" :key 1}]
+           [:div {:class "2" :key 2}]])
+         [:div {}
+          [:div {:class "1"}]
+          [:div {:class "2"}]])))

-(deftest test-static-children-as-arguments
-  (testing "static children should interpret as direct React arguments"
-    (is (= (html-str (i/interpret [:div [:div {:key 1}] [:div {:key 2}]]))
-           "

"))))
+(deftest test-interpret-div
+  (is (= (interpret [:div])
+         [:div {}])))
+
+(deftest test-interpret-div-with-string
+  (is (= (interpret [:div "x"])
+         [:div {} "x"])))
+
+(deftest test-interpret-div-with-number
+  (is (= (interpret [:div 1])
+         [:div {} "1"])))
+
+(deftest test-interpret-div-with-nested-lazy-seq
+  (is (= (interpret [:div (map identity ["A" "B"])])
+         [:div {} "AB"])))
+
+(deftest test-interpret-div-with-nested-list
+  (is (= (interpret [:div (list "A" "B")])
+         [:div {} "AB"])))


#258: Initial set of doc strings for the public api.

--- src/datascript/core.cljc
+++ src/datascript/core.cljc
@@ -14,27 +14,103 @@

 ;; SUMMING UP

-(def ^{:arglists '([q & inputs])} q dq/q)
-(def ^{:arglists '([db eid])} entity de/entity)
+(def
+  ^{:arglists '([q & inputs])
+    :doc "Execute a datalog query.
+         (For full docs see Datomic reference: https://docs.datomic.com/query.html)
+
+         Usage:
+         (q '[:find ?value :where [_ :likes ?value]] db)
+         -> #{[:pizza]}"}
+  q dq/q)
+(def ^{:arglists '([db eid])
+       :doc "Retrieves a single entity by its id from the given database.
+            Retrieve using the internal db id:
+            (entity db 1)
+            Retrieve using a user defined id:
+            (entity db [:db/ident :a-unique-key])"}
+  entity de/entity)

 (defn entity-db [^Entity entity]
   {:pre [(de/entity? entity)]}
   (.-db entity))

-(def ^{:arglists '([e a v] [e a v tx] [e a v tx added])} datom db/datom)
-(def ^{:arglists '([db selector eid])} pull dp/pull)
-(def ^{:arglists '([db selector eids])} pull-many dp/pull-many)
-(def ^{:arglists '([e])} touch de/touch)
-(def ^{:arglists '([] [schema])} empty-db db/empty-db)
-(def ^{:arglists '([datoms] [datoms schema])} init-db db/init-db)
-(def ^{:arglists '([x])} datom? db/datom?)
-(def ^{:arglists '([x])} db? db/db?)
+(def ^{:arglists '([e a v] [e a v tx] [e a v tx added])
+       :doc "Creates a single datom that can be added to any database.
+             A datom consists of [entity-id attribute value] where:
+             - entity-id is the entity's unique id within the db
+             - attribute is the relation that the entity has to the value
+             - value is the value that the attribute applies to.
+             This can be a primitive value or refer to another entity.
+
+             For example:
+             To asserts a single fact:
+             (datom [1 :likes :pizza])
+             To assert with transaction data:
+             (datom [1 :likes :pizza 17])
+             Add true to assert the fact (default), false to retract it:
+             (datom [1 :likes :pizza 17 false])"}
+  datom db/datom)
+(def ^{:arglists '([db selector eid])
+       :doc "Pull a single entity from the database using Datomic pull syntax.
+            (For full docs see Datom reference: http://docs.datomic.com/pull.html)
+
+            Usage:
+            (pull db [:likes :dislikes] 1)
+            -> {:likes :pizza :dislikes :ice-cream}"}
+  pull dp/pull)
+(def ^{:arglists '([db selector eids])
+       :doc "Pull entities from the database using Datomic pull syntax.
+            (For full docs see Datomic reference: http://docs.datomic.com/pull.html)
+
+            Usage:
+            (pull-many db [:likes :dislikes] [1 2])
+            -> [{:likes :pizza :dislikes :ice-cream} {:likes :pie}]"}
+  pull-many dp/pull-many)
+(def ^{:arglists '([e])
+       :doc "Retrieves all attributes for a given entity as a single map.
+
+            Usage:
+            (touch (entity db 1)) ; -> {:dislikes :pie, :likes :pizza, :db/id 1}"}
+  touch de/touch)
+(def ^{:arglists '([] [schema])
+       :doc "Creates an empty database with an optional schema.
+
+            Usage:
+            (empty-db)
+            With schema:
+            (empty-db {:likes {:db/cardinality :db.cardinality/many}})"}
+  empty-db db/empty-db)
+(def ^{:arglists '([datoms] [datoms schema])
+       :doc "Creates a database from a given collection of datoms.
+            Takes an optional schema.
+
+            Usage:
+            (init-db
+              [(datom 1 :likes :pizza)])
+            With a schema:
+            (init-db
+              [(datom 1 :likes :pizza)]
+              {:likes {:db/cardinality :db.cardinality/many}})"}
+  init-db db/init-db)
+(def ^{:arglists '([x])
+       :doc "Returns true if the given value is a datom, false otherwise."}
+  datom? db/datom?)
+(def ^{:arglists '([x])
+       :doc "Return true if the given value is a database
+            (but not a database connection), false otherwise."}
+  db? db/db?)
 (def ^:const tx0 db/tx0)

-(defn is-filtered [x]
+(defn is-filtered
+  "Returns true if this is an instance of a filtered database, false otherwise."
+  [x]
   (instance? FilteredDB x))

-(defn filter [db pred]
+(defn filter
+  "Returns a database including only the datoms for which the (pred db datom)
+  holds true."
+  [db pred]
   {:pre [(db/db? db)]}
   (if (is-filtered db)
     (let [^FilteredDB fdb db
@@ -44,6 +120,11 @@
     (FilteredDB. db #(pred db %) (atom 0))))

 (defn with
+  "Apply the given transaction to the given database returning a
+  transaction report map with :db-before, :db-after, :tx-data, :tempids
+  and :tx-meta.
+  Note that this function has no side-effects and the given database
+  remains untouched."
   ([db tx-data] (with db tx-data nil))
   ([db tx-data tx-meta]
     {:pre [(db/db? db)]}
@@ -56,7 +137,9 @@
                                :tempids   {}
                                :tx-meta   tx-meta}) tx-data))))

-(defn db-with [db tx-data]
+(defn db-with
+  "Same as (with db tx-data) but will only return the new database (i.e. :db-after)."
+  [db tx-data]
   {:pre [(db/db? db)]}
   (:db-after (with db tx-data)))

@@ -90,19 +173,27 @@

 ;; Conn

-(defn conn? [conn]
+(defn conn?
+  "Returns true if this is a connection to a db, false otherwise."
+  [conn]
   (and #?(:clj  (instance? clojure.lang.IDeref conn)
           :cljs (satisfies? cljs.core/IDeref conn))
     (db/db? @conn)))

-(defn conn-from-db [db]
+(defn conn-from-db
+  "Given a database creates a connection to said database."
+  [db]
   (atom db :meta { :listeners (atom {}) }))

 (defn conn-from-datoms
+  "Creates a database for the given datoms and returns a connection
+  to said database. Takes an optional schema."
   ([datoms]        (conn-from-db (init-db datoms)))
   ([datoms schema] (conn-from-db (init-db datoms schema))))

 (defn create-conn
+  "Creates a connection to an empty database.
+  Takes an optional schema."
   ([]       (conn-from-db (empty-db)))
   ([schema] (conn-from-db (empty-db schema))))

@@ -116,6 +207,14 @@
     @report))

 (defn transact!
+  "Applies the given transaction data to the database connection.
+  The database under the connection now refers to the updated
+  database that has the transaction applied to it.
+  The result of the transaction is returned containing report map of
+  :db-before, :db-after, :tx-data, :tempids and :tx-meta.
+
+  Usage:
+  (transact! conn1 [{:db/id -1 :likes :pizza}])"
   ([conn tx-data] (transact! conn tx-data nil))
   ([conn tx-data tx-meta]
     {:pre [(conn? conn)]}
@@ -144,13 +243,19 @@
      :clj  (instance? clojure.lang.IAtom a)))

 (defn listen!
+  "Listen for changes on the given database connection.
+  Whenever a transaction is applied to the database the callback is called
+  with the transaction report.
+  Returns the key under which this listener is registered."
   ([conn callback] (listen! conn (rand) callback))
   ([conn key callback]
      {:pre [(conn? conn) (atom? (:listeners (meta conn)))]}
      (swap! (:listeners (meta conn)) assoc key callback)
      key))

-(defn unlisten! [conn key]
+(defn unlisten!
+  "Removes the listener under de given key from the database connection."
+  [conn key]
   {:pre [(conn? conn) (atom? (:listeners (meta conn)))]}
   (swap! (:listeners (meta conn)) dissoc key))

@@ -163,8 +268,10 @@
 ;;  (clojure.edn/read-string {:readers datascript/data-readers} "...")
 ;;

-(def data-readers {'datascript/Datom db/datom-from-reader
-                   'datascript/DB    db/db-from-reader})
+(def ^{:doc "data-readers available for parsing datascript values with clojure.edn/read-string.
+            Usage: (clojure.edn/read-string {:readers datascript/data-readers} \"...\")"}
+  data-readers {'datascript/Datom db/datom-from-reader
+                'datascript/DB    db/db-from-reader})

 #?(:cljs
    (doseq [[tag cb] data-readers] (cljs.reader/register-tag-parser! tag cb)))
@@ -187,7 +294,9 @@
 (defn resolve-tempid [_db tempids tempid]
   (get tempids tempid))

-(defn db [conn]
+(defn db
+  "Extracts the current database (as a value) from the connection."
+  [conn]
   {:pre [(conn? conn)]}
   @conn)


Minor refactoring of alt-printer-impl

Minor refactoring of alt-printer-impl

--- src/devtools/format.cljs
+++ src/devtools/format.cljs
@@ -112,11 +112,10 @@
     (-write writer tmpl)
     (let [inner-tmpl #js []
           inner-writer (TemplateWriter. inner-tmpl)
-          default-impl (:fallback-impl opts)]
-      ; we want to limit print-level, at second level use maximal abbreviation e.g. [...] or {...}
-      (if (= *print-level* 1)
-        (default-impl obj inner-writer (assoc opts :print-length 0))
-        (default-impl obj inner-writer opts))
+          default-impl (:fallback-impl opts)
+          ; we want to limit print-level, at second level use maximal abbreviation e.g. [...] or {...}
+          inner-opts (if (= *print-level* 1) (assoc opts :print-length 0) opts)]
+      (default-impl obj inner-writer inner-opts)
       (detect-else-case-and-patch-it inner-tmpl obj)        ; an ugly special case
       (.merge writer (wrap-group-in-reference-if-needed inner-tmpl obj) obj))))


Consolidate two nested `let` forms into one

--- src/expound/printer.cljc
+++ src/expound/printer.cljc
@@ -66,23 +66,23 @@
 (defn key->spec [keys problems]
   (doseq [p problems]
     (assert (some? (:expound/via p)) util/assert-message))
-  (let [vias (map :expound/via problems)]
-    (let [specs (if (every? qualified-keyword? keys)
-                  keys
-                  (if-let [specs (apply set/union (map specs-from-form vias))]
-                    specs
-                    keys))]
-      (reduce
-       (fn [m k]
-         (assoc m
+  (let [vias (map :expound/via problems)
+        specs (if (every? qualified-keyword? keys)
+                keys
+                (if-let [specs (apply set/union (map specs-from-form vias))]
+                  specs
+                  keys))]
+    (reduce
+     (fn [m k]
+       (assoc m
+              k
+              (if (qualified-keyword? k)
                 k
-                (if (qualified-keyword? k)
-                  k
-                  (->> specs
-                       (filter #(= (name k) (name %)))
-                       first))))
-       {}
-       keys))))
+                (->> specs
+                     (filter #(= (name k) (name %)))
+                     first))))
+     {}
+     keys)))

 (defn expand-spec [spec]
   (let [!seen-specs (atom #{})]


[#2634] Add tests

[#2634] Add tests

--- backend/test/akvo/lumen/lib/transformation_test.clj
+++ backend/test/akvo/lumen/lib/transformation_test.clj
@@ -70,6 +70,7 @@
 (hugsql/def-db-fns "akvo/lumen/lib/job-execution.sql")
 (hugsql/def-db-fns "akvo/lumen/lib/transformation_test.sql")
 (hugsql/def-db-fns "akvo/lumen/lib/transformation.sql")
+(hugsql/def-db-fns "akvo/lumen/lib/visualisation.sql")

 (defn async-tx-apply [{:keys [tenant-conn] :as deps} dataset-id command]
   (let [[tag {:keys [jobExecutionId datasetId]} :as res] (transformation/apply deps dataset-id command)
@@ -610,7 +611,21 @@
       (is (= ["c1" "c3" "c4" "c5"] (map #(get % "columnName") columns)))
       (let [{:strs [before after]} (get-in (last transformations) ["changedColumns" "c2"])]
         (is (= "c2" (get before "columnName")))
-        (is (nil? after))))))
+        (is (nil? after))))
+
+    (testing "Don't allow delete column transformation if there is a visualization using the column"
+      ;; https://github.com/akvo/akvo-lumen/issues/2634
+      (upsert-visualisation *tenant-conn* {:id   (str (squuid))
+                                           :name "Visualisation",
+                                           :type "bar",
+                                           :spec {:bucketColumn "c1", :filters [], :version 2}, :author {}, :dataset-id dataset-id})
+      (let [[_ _ _ job] (apply-transformation {:type :transformation
+                                               :transformation
+                                               (gen-transformation "core/delete-column"
+                                                                   {::db.dataset-version.column.s/columnName "c1"
+                                                                    :transformation.engine.s/onError         "fail"})})]
+        (is (= (:status job) "FAILED"))
+        (is (= (:error-message job) "Cannot delete column. It is used in the following visalisations: 'Visualisation'"))))))

 (deftest ^:functional multiple-column-test
   (let [multiple-column-type       "caddisfly"


Created my-games filter (instead of using a separate app db)

--- src/cljs/wombats_web_client/panels/games.cljs
+++ src/cljs/wombats_web_client/panels/games.cljs
@@ -31,12 +31,44 @@
      [:div.checkbox {:class (when current-state "selected")}]
      [:div.desc "SHOW MY GAMES"]]))

-(defn get-games [{:keys [show-open show-my-games open closed my-open my-closed]}]
+;; Utils (eventually move these into a better location)
+
+(defn is-open?
+  [game]
+  (let [status (:game/status game)]
+    (or (= status :pending-open)
+        (= status :pending-closed)
+        (= status :active)
+        (= status :active-intermission))))
+
+(defn is-closed?
+  [game]
+  (let [status (:game/status game)]
+    (= status :closed)))
+
+(defn is-mine?
+  [game github-username]
+  (> (count (filter #(= github-username
+                        (get-in % [:player/user :user/github-username]))
+                    (:game/players game)))
+     0))
+
+(defn get-my-open-games
+  [games current-user]
+  (reduce-kv (fn [coll _ [game]]
+               (if (and (is-open? game)
+                        (is-mine? game (:user/github-username current-user)))
+                 (conj coll game)
+                 coll)) [] games))
+
+;; End Utils
+
+(defn get-sorted-games [{:keys [show-open show-my-games open closed my-open my-closed games current-user]}]
   (cond
-   (and show-open show-my-games) (sort-by :game/start-time my-open)
-   (and (not show-open) show-my-games) (sort-by :game/end-time > my-closed)
-   (and show-open (not show-my-games)) (sort-by :game/start-time open)
-   (and (not show-open) (not show-my-games)) (sort-by :game/end-time > closed)))
+    (and show-open show-my-games) (sort-by :game/start-time (get-my-open-games games current-user))
+    (and (not show-open) show-my-games) (sort-by :game/end-time > my-closed)
+    (and show-open (not show-my-games)) (sort-by :game/start-time open)
+    (and (not show-open) (not show-my-games)) (sort-by :game/end-time > closed)))

 (defn get-empty-state [show-open show-my-games]
   (cond
@@ -58,23 +90,25 @@
         my-open (re-frame/subscribe [:my-open-games])
         my-closed (re-frame/subscribe [:my-closed-games])
         polling (open-game-polling)
-        current-user (re-frame/subscribe [:current-user])]
+        current-user (re-frame/subscribe [:current-user])
+        games (re-frame/subscribe [:games])]

     (get-all-games)

     (fn []
-      (let  [current-user @current-user
-             open @open-games
-             closed @closed-games
-             show-my-games (:show-my-games @cmpnt-state)
-             show-open (:show-open @cmpnt-state)
-             games (get-games {:show-open show-open
-                               :show-my-games show-my-games
-                               :open open
-                               :closed closed
-                               :my-open @my-open
-                               :my-closed @my-closed})]
-
+      (let [current-user @current-user
+            open @open-games
+            closed @closed-games
+            show-my-games (:show-my-games @cmpnt-state)
+            show-open (:show-open @cmpnt-state)
+            games-sorted (get-sorted-games {:show-open show-open
+                                            :show-my-games show-my-games
+                                            :open open
+                                            :closed closed
+                                            :games @games
+                                            :my-open @my-open
+                                            :my-closed @my-closed
+                                            :current-user current-user})]

         (swap! cmpnt-state assoc :polling polling)

@@ -83,9 +117,9 @@
           [tab-view-toggle cmpnt-state]
           [my-game-toggle cmpnt-state]]
          [:div.games
-          (if (pos? (count games))
+          (if (pos? (count games-sorted))
             [:ul.games-list
-             (for [game games]
+             (for [game games-sorted]
                (let [status (:game/status game)
                      players (:game/players game)
                      user-in-game (first (get-user-in-game players current-user))


Return stopped ConnectionManager

Return stopped ConnectionManager

--- src/com/frereth/server/connection_manager.clj
+++ src/com/frereth/server/connection_manager.clj
@@ -62,7 +62,8 @@
   (stop
       [this]
     (when-let [users (:users this)]
-      (reset! users {}))))
+      (reset! users {}))
+    this))

 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Public


use real filesystem as default if no type is specified

--- src/com/brainbot/vfs.clj
+++ src/com/brainbot/vfs.clj
@@ -12,6 +12,7 @@

 (def ^:private filesystem-registry
   (atom {"file" 'com.brainbot.real-fs
+         nil 'com.brainbot.real-fs
          "smbfs" 'com.brainbot.smb-fs}))

@@ -47,11 +48,10 @@
 (defn filesystem-from-inisection
   "create filesystem from ini config section"
   [section]
-  (if-let [fstype (section "type")]
+  (let [fstype (section "type")]
     (if-let [create-fs (get-create-fs-fn fstype)]
       (create-fs section)
-      (throw (Exception. (str "unknown filesystem type " fstype))))
-    (throw (Exception. (str "no type specified in section")))))
+      (throw (Exception. (str "unknown filesystem type " fstype))))))

 (defn cmd-listdir


Really basic beginning agent bank account example

--- clojure/src/app/core.clj
+++ clojure/src/app/core.clj
@@ -2,7 +2,25 @@
   (:refer-clojure :exclude [map reduce into partition partition-by take merge])
   (:require [clojure.core.async :refer :all :as async]))

-(let [c (chan 10)]
-  (>!! c "hello")
-  (println (<!! c))
-  (close! c))+; function to add some value 'c' to 'n'
+(defn increment [c n] (+ c n))
+
+(defn example []
+  (let [
+         ; start with a bank account of balance zero
+         bank-account (agent 0)]
+
+    (send bank-account increment 5)
+
+    (Thread/sleep 1000)
+
+    ; print output
+    (println @bank-account)))
+
+; onward!
+(example)
+(example)
+(example)
+(example)
+(example)
+(example)


cassandra: add consistency opts to UpsertOptsSchema

--- src/er_cassandra/model/alia/fn_schema.clj
+++ src/er_cassandra/model/alia/fn_schema.clj
@@ -51,6 +51,7 @@
   (conditional-upsert-schema
    (merge
     rs/PrepareOptSchema
+    UpsertConsistencySchema
     UpsertWhereSchema
     UpsertUsingSchema)))


run the millisecond loop 25 times in parallel

--- example/src/example/core.clj
+++ example/src/example/core.clj
@@ -13,7 +13,9 @@
   [interval msg-type msg]
   (go-loop [] (<! (timeout interval)) (inspect msg-type msg) (recur)))

-(interval-put-loop     1 :interval-put/every-millisecond {:msg "every millisecond"})
+(dotimes [n 25]
+  (interval-put-loop 1 :interval-put/every-millisecond {:msg "every millisecond" :n n}))
+
 (interval-put-loop  1000 :interval-put/every-second {:msg "every second"})
 (interval-put-loop  5000 :interval-put/every-five-seconds {:msg "every five seconds"})
 (interval-put-loop 10000 :interval-put/every-ten-seconds {:msg "every ten seconds"})


commend depenedancy

commend depenedancy

--- src/demo/web.clj
+++ src/demo/web.clj
@@ -4,7 +4,7 @@
             [immutant.web.middleware      :as mw]
             [demo.web.sse                 :as sse]
             [demo.web.http-kit-comparison :as hk]
-            [demo.brepl :refer (browser-repl conn start-figwheel)]
+        ;    [demo.brepl :refer (browser-repl conn start-figwheel)]
             [compojure.route              :as route]
             [compojure.core     :refer (ANY GET defroutes)]
             [ring.util.response :refer (response redirect content-type)]
@@ -78,5 +78,5 @@
       args)))

 (defn -main [& {:as args}]
-  (do (start-figwheel) #_(apply reload-main defaults)))
+  (do #_(start-figwheel) (apply reload-main defaults)))


add TODO

add TODO

--- src/main/clojure/mikera/spectral/core.clj
+++ src/main/clojure/mikera/spectral/core.clj
@@ -6,6 +6,9 @@
   (:require [mikera.image.spectrum :as spec])
   (:import [mikera.matrixx Matrix AMatrix])
   (:import [java.awt.image BufferedImage]))
+
+;; TODO
+;; This namespace contains exanmple code using overtone. Should really separate out overtone into demo code

 (defonce boot (boot-external-server))


Fix invalid number of arguments to core/collect-region-info

--- src/main/hbase_region_inspector/hbase.clj
+++ src/main/hbase_region_inspector/hbase.clj
@@ -28,7 +28,11 @@
                 (assoc (server-load->map load) :name name)]))
            (zipmap server-names server-loads)))))

-(def collect-region-info hbase-impl/collect-region-info)
+(defn collect-region-info
+  ([admin]
+   (hbase-impl/collect-region-info admin (.getClusterStatus admin)))
+  ([admin cluster-status]
+   (hbase-impl/collect-region-info admin cluster-status)))

 (defn collect-info
   "Collects server and region statistics"


ensure output-dir exists when writeing module-loader.{json,edn}

--- src/main/shadow/build/targets/browser.clj
+++ src/main/shadow/build/targets/browser.clj
@@ -339,6 +339,7 @@
         edn-file
         (data/output-file state "module-loader.edn")]

+    (io/make-parents json-file)

     (spit json-file (json module-data))
     (spit edn-file (pr-str module-data))


named function

named function

--- src/main/cljc/clojure_club/x121_universal_computation_engine/brockturner.clj
+++ src/main/cljc/clojure_club/x121_universal_computation_engine/brockturner.clj
@@ -1,6 +1,6 @@
 (ns clojure-club.x121-universal-computation-engine.brockturner)

-(fn [l]
+(defn engine [l]
   (letfn [(solve [m e]
             (if (sequential? e)
               (let [[op & r] e


change error message

change error message

--- src/leiningen/jshint.clj
+++ src/leiningen/jshint.clj
@@ -88,7 +88,7 @@
                           (apply invoke project sources)))
     (catch Throwable t
       (println
-       (joine (str "Can't execute jshint application: " (.getMessage t))
+       (joine (str "Can't execute JSHint: " (.getMessage t))
               "Something is wrong:"
               " - installation: npm install jshint -g"
               " - configuration: https://github.com/vbauer/lein-jshint"))


Fix merge conflict resolution issue.

Fix merge conflict resolution issue.

--- src/oc/email/content.clj
+++ src/oc/email/content.clj
@@ -418,7 +418,7 @@
         from-avatar? (not (s/blank? from-avatar))
         show-note? (and note? from-avatar?)
         secure-uuid (:secure-uuid entry)
-        entry-url (s/join "/" [origin-url org-slug "post" secure-uuid])]
+        entry-url (s/join "/" [config/web-url org-slug "post" secure-uuid])]
     [:td {:class "small-12 large-12 columns" :valign "middle" :align "center"}
       (spacer 40)
       (when logo? (org-logo entry))


optimize what gets rendered on the build page when output comes in over pusher

--- frontend/components/app.cljs
+++ frontend/components/app.cljs
@@ -30,6 +30,7 @@
             [frontend.components.common :as common]
             [frontend.state :as state]
             [frontend.utils :as utils :include-macros true]
+            [frontend.utils.seq :refer [dissoc-in]]
             [om.core :as om :include-macros true]
             [om.dom :as dom :include-macros true]
             [ankha.core :as ankha])
@@ -85,14 +86,16 @@
               restore-state! #(put! controls-ch [:state-restored])
               dom-com (dominant-component app)
               show-inspector? (get-in app state/show-inspector-path)
-              logged-in? (get-in app state/user-path)]
+              logged-in? (get-in app state/user-path)
+              ;; simple optimzation for real-time updates when the build is running
+              app-without-container-data (dissoc-in app state/container-data-path)]
           (reset! keymap {["ctrl+s"] persist-state!
                           ["ctrl+r"] restore-state!})
           (html
            (let [inner? (get-in app state/inner?-path)]

              [:div#app {:class (if inner? "inner" "outer")}
-              (om/build keyq/KeyboardHandler app
+              (om/build keyq/KeyboardHandler app-without-container-data
                         {:opts {:keymap keymap
                                 :error-ch (get-in app [:comms :errors])}})
               (when show-inspector?
@@ -100,9 +103,9 @@
                 ;;     expanding all datastructures.
                 (om/build inspector/inspector app))
               (when (and inner? logged-in?)
-                (om/build aside/aside app))
+                (om/build aside/aside app-without-container-data))
               [:main.app-main {:tab-index 1}
-               (om/build header/header app)
+               (om/build header/header app-without-container-data)
                [:div.main-body
                 (om/build dom-com app)]
                [:footer.main-foot


Adding some documentation and starting integration with internal audio server.

--- src/overtone/core/sc.clj
+++ src/overtone/core/sc.clj
@@ -51,6 +51,10 @@
 (defonce MAX-CONTROL-BUS 4096)
 (defonce MAX-OSC-SAMPLES 8192)

+; We use bit sets to store the allocation state of resources on the audio server.
+; These typically get allocated on usage by the client, and then freed either by
+; client request or automatically by receiving notifications from the server.
+; (e.g. When an envelope trigger fires to free a synth.)
 (defonce allocator-bits
   {:node (BitSet. MAX-NODES)
    :audio-buffer (BitSet. MAX-BUFFERS)
@@ -124,7 +128,9 @@
   [time-ms & body]
   `(in-osc-bundle @server* ~time-ms ~@body))

-(defn debug [& [on-off]]
+(defn debug
+  "Control debug output from both the Overtone and the audio server."
+  [& [on-off]]
   (if (or on-off (nil? on-off))
     (do
       (log/level :debug)
@@ -134,6 +140,8 @@
       (snd "/dumpOSC" 0))))

 (defn recv
+  "Block with a timeout to receive a message on a given osc path from
+  the server."
   [path & [timeout]]
   (osc-recv @server* path timeout))

@@ -170,33 +178,62 @@
 ;   int - trigger ID
 ;   float - trigger value

-(defn notify [notify?]
+(defn notify
+  "Turn on notification messages from the audio server.  This lets us free
+  synth IDs when they are automatically freed with envelope triggers.  It also lets
+  us receive custom messages from various trigger ugens."
+  [notify?]
   (snd "/notify" (if (false? notify?) 0 1)))

-(defn- node-destroyed [id]
+(defn- node-destroyed
+  "Frees up a synth node to keep in sync with the server."
+  [id]
   (log/debug (format "node-destroyed: %d" id))
   (free-id :node id))

-(defn- node-created [id]
+(defn- node-created
+  "Called when a node is created on the synth."
+  [id]
   (log/debug (format "node-created: %d" id)))

 (defn- register-notification-handlers
+  "Setup the feedback handlers with the audio server."
   []
   (osc-handle @server* "/n_end" #(node-destroyed (first (:args %))))
   (osc-handle @server* "/n_go" #(node-created (first (:args %)))))

+(def N-RETRIES 50)
+
+(defn- connect-thread
+  [host port]
+   (log/debug "Connecting to SuperCollider server: " host ":" port)
+   (dosync (ref-set server* (with-meta
+                              (osc-client host port)
+                              {:type ::external})))
+   (loop [cnt 0]
+    (when (and (< cnt N-RETRIES)
+               (= @status* :booting))
+      (snd "/status")
+      (when (recv "status.reply" REPLY-TIMEOUT)
+        (dosync (ref-set status* :booted))
+        (notify true) ; turn on notifications now that we can communicate
+        (run-boot-handlers))
+      (recur (inc cnt))))
+   (register-notification-handlers))
+
+; TODO: setup an error-handler in the case that we can't connect to the server
 (defn connect
+  "Connect to an external SC audio server on the specified host and port."
   ([] (connect SERVER-HOST SERVER-PORT))
-  ([host port]
-   (log/debug "Connecting to SuperCollider server: " host ":" port)
-   (dosync (ref-set server* (osc-client host port)))
-   (register-notification-handlers)))
+  ([host port] (.run (Thread. #(connect-thread host port)))))

 (defonce running?* (atom false))

 (def server-log* (ref []))

-(defn server-log []
+(defn server-log
+  "Print the server log."
+  []
   (doseq [msg @server-log*]
     (print msg)))

@@ -211,7 +248,9 @@
 ;	float - peak percent CPU usage for signal processing
 ;	double - nominal sample rate
 ;	double - actual sample rate
-(defn status []
+(defn status
+  "Check the status of the audio server."
+  []
   (if (= @status* :booted)
     (do
       (snd "/status")
@@ -228,34 +267,14 @@
            :actual-sample-rate actual})))
     @status*))

-; Wait until SuperCollider has completed all asynchronous commands currently in execution.
-(defn wait-sync [& [timeout]]
+(defn wait-sync
+  "Wait until the audio server has completed all asynchronous commands currently in execution."
+  [& [timeout]]
   (let [sync-id (rand-int 999999)
         _ (snd "/sync" sync-id)
         reply (recv "/synced" (if timeout timeout REPLY-TIMEOUT))
         reply-id (first (:args reply))]
     (= sync-id reply-id)))
-
-(defn sc-log [stream read-buf]
-  (while (pos? (.available stream))
-    (let [n (min (count read-buf) (.available stream))
-          _ (.read stream read-buf 0 n)
-          msg (String. read-buf 0 n)]
-      (dosync (alter server-log* conj msg))
-      (log/info (String. read-buf 0 n)))))
-
-(defn- boot-thread [cmd]
-  (reset! running?* true)
-  (log/debug "boot-thread: ")
-  (let [proc (.exec (Runtime/getRuntime) cmd)
-        in-stream (BufferedInputStream. (.getInputStream proc))
-        err-stream (BufferedInputStream. (.getErrorStream proc))
-        read-buf (make-array Byte/TYPE 256)]
-    (while @running?*
-      (sc-log in-stream read-buf)
-      (sc-log err-stream read-buf)
-      (Thread/sleep 250))
-    (.destroy proc)))

 (defn connect-jack-ports
   "Maybe this isn't necessary, since we can use the SC_JACK_DEFAULT_OUTPUTS
@@ -294,50 +313,62 @@
 (if (= :linux (@config* :os))
   (add-boot-handler connect-jack-ports))

-; TODO: setup an error-handler in the case that we can't connect to the server
-(defn- wait-for-boot
-  ([cnt]
-    (when (and (< cnt 100)
-               (= @status* :booting))
-      (snd "/status")
-      (when (recv "status.reply" REPLY-TIMEOUT)
-        (dosync (ref-set status* :booted))
-        (notify true) ; turn on notifications now that we can communicate
-        (run-boot-handlers))
-      (recur (inc cnt))))
-  ([host port]
-    (dosync (ref-set status* :booting))
-    (Thread/sleep 1000)
-    (connect host port)
-    (wait-for-boot 0)))
-
-(defn booti
-  ([] (booti nil))
+(defn internal-booter [port]
+  (reset! running?* true)
+  (log/debug "booting internal audio server...")
+  (let [opts (sc-jna-startoptions-byref)]
+    (set! (. opts udp-port-num) port)
+    (set! (. opts tcp-port-num) -1)
+    (set! (. opts verbosity) 1)
+    (set! (. opts lib-scsynth-path) (str (find-scsynth-lib-path)))
+    (set! (. opts plugin-path) (str (find-synthdefs-lib-path)))
+
+    (dosync (ref-set world* (ScJnaStart opts)))
+
+    (World_WaitForQuit @world*)
+    (ScJnaCleanup)))
+
+(defn boot-internal
+  ([] (boot-internal (+ (rand-int 50000) 2000)))
   ([port]
      (if (not @running?*)
-       (let [port (if (nil? port) (+ (rand-int 50000) 2000) port)
-             boot-thread (fn []
-                           (let [opts (sc-jna-startoptions-byref)]
-                             (set! (. opts udp-port-num) port)
-                             (set! (. opts tcp-port-num) -1)
-                             (set! (. opts verbosity) 1)
-                             (set! (. opts lib-scsynth-path) (str (find-scsynth-lib-path)))
-                             (set! (. opts plugin-path) (str (find-synthdefs-lib-path)))
-
-                             (dosync (ref-set world* (ScJnaStart opts)))
-
-                             (World_WaitForQuit @world*)
-                             (ScJnaCleanup)))
-             sc-thread (Thread. boot-thread)]
+       (let [sc-thread (Thread. #(internal-booter port))]
          (.setDaemon sc-thread true)
          (log/debug "Booting SuperCollider internal server (scsynth)...")
          (.start sc-thread)
          (dosync (ref-set server-thread* sc-thread))
-         (.run (Thread. #(wait-for-boot "127.0.0.1" port)))
+         (connect-internal)
          :booting))))

-(defn boot
-  ([] (boot SERVER-HOST SERVER-PORT))
+(defn- sc-log
+  "Pull audio server log data from a pipe and store for later printing."
+  [stream read-buf]
+  (while (pos? (.available stream))
+    (let [n (min (count read-buf) (.available stream))
+          _ (.read stream read-buf 0 n)
+          msg (String. read-buf 0 n)]
+      (dosync (alter server-log* conj msg))
+      (log/info (String. read-buf 0 n)))))
+
+(defn- external-booter
+  "Boot thread to start the external audio server process and hook up to
+  STDOUT for log messages."
+  [cmd]
+  (reset! running?* true)
+  (log/debug "booting external audio server...")
+  (let [proc (.exec (Runtime/getRuntime) cmd)
+        in-stream (BufferedInputStream. (.getInputStream proc))
+        err-stream (BufferedInputStream. (.getErrorStream proc))
+        read-buf (make-array Byte/TYPE 256)]
+    (while @running?*
+      (sc-log in-stream read-buf)
+      (sc-log err-stream read-buf)
+      (Thread/sleep 250))
+    (.destroy proc)))
+
+(defn boot-external
+  "Boot the audio server in an external process and tell it to listen on a
+  specific port."
   ([host port]
   (if (not @running?*)
     (let [port (if (nil? port) (+ (rand-int 50000) 2000) port)
@@ -350,6 +381,14 @@
       (.run (Thread. #(wait-for-boot host port)))
       :booting))))

+(defn boot
+  "Boot either the internal or external audio server."
+  ([] (boot (get @config :server :internal) SERVER-HOST SERVER-PORT))
+  ([which & [host port]]
+     (cond
+       (= :internal which) (boot-internal)
+       (= :external which) (boot-external host port))))
+
 (defn quit
   "Quit the SuperCollider synth process."
   []
@@ -372,7 +411,9 @@
    :replace-node 4})

 ;; Sending a synth-id of -1 lets the server choose an ID
-(defn node [synth-name & args]
+(defn node
+  "Instantiate a synth node on the audio server."
+  [synth-name & args]
   (let [id (alloc-id :node)
         argmap (apply hash-map args)
         position ((get argmap :position :tail) POSITION)
@@ -383,18 +424,18 @@
     id))

 (defn node-free
-  "Instantly remove a node from the graph."
+  "Remove a synth node"
   [& node-ids]
   (doseq [id node-ids] (free-id :node id))
   (apply snd "/n_free" node-ids))

 (defn node-run
-  "Start a stopped node."
+  "Start a stopped synth node."
   [node-id]
   (snd "/n_run" node-id 1))

 (defn node-stop
-  "Stop a running node."
+  "Stop a running synth node."
   [node-id]
   (snd "/n_run" node-id 0))

@@ -473,7 +514,8 @@

 (def *data* nil)

-(defn- parse-synth-tree [ctls?]
+(defn- parse-synth-tree
+  [ctls?]
   (let [sname (first *data*)]
     (if ctls?
       (let [n-ctls (second *data*)
@@ -515,12 +557,12 @@
       (parse-node-tree tree))))

 (defn prepend-node
-  "Add a node to the end of a group list."
+  "Add a synth node to the end of a group list."
   [g n]
   (snd "/g_head" g n))

 (defn append-node
-  "Add a node to the end of a group list."
+  "Add a synth node to the end of a group list."
   [g n]
   (snd "/g_tail" g n))

@@ -562,7 +604,10 @@
   (free-id :audio-buffer (:id buf))
   (snd "/b_free" (:id buf)))

-(defn buffer-read [buf start len]
+; TODO: Test me...
+(defn buffer-read
+  "Read a section of an audio buffer."
+  [buf start len]
   (assert (buffer? buf))
   (loop [reqd 0]
     (when (< reqd len)
@@ -583,7 +628,10 @@
               (recur (inc idx) (next samps))))
           (recur (+ recvd blen)))))))

-(defn buffer-write [buf start len data]
+;; TODO: test me...
+(defn buffer-write
+  "Write into a section of an audio buffer."
+  [buf start len data]
   (assert (buffer? buf))
   (snd "/b_setn" (:id buf) start len data))

@@ -608,12 +656,17 @@
                 :path path}
                {:type ::sample})))

-(defn sample? [s]
+(defn sample?
+  [s]
   (= ::sample (type s)))

+;; Samples are just audio files loaded into a buffer, so most buffer
+;; functions should work on samples.
 (derive ::sample ::buffer)

-(defn buffer-data [buf]
+(defn buffer-data
+  "Get the floating point data for a buffer on the internal server."
+  [buf]
   (let [buf-id (cond
                  (buffer? buf) (:id buf)
                  (sample? buf) (:id (:buf buf)))