pre-tree traversal

pre-tree traversal

--- src/atom_finder/core.clj
+++ src/atom_finder/core.clj
@@ -47,26 +47,45 @@
     ;;(.startVisit visitor node)
     ))

-(defn print-tree [node index]
-  (let [children (.getChildren node)
-        print-contents? (and (not (instance? CPPASTTranslationUnit node))
-                             (< (-> node .getFileLocation .getNodeLength) 30))
-        offset (format " (offset: %s, %s)"
-                       (-> node .getFileLocation .getNodeOffset)
-                       (-> node .getFileLocation .getNodeLength))]
+(defn arg-count [f]
+  (let [m (first (.getDeclaredMethods (class f)))
+        p (.getParameterTypes m)]
+        (alength p)))

-    (printf "%s -%s %s -> %s\n"
-            (apply str (repeat index "  "))
-            (-> node .getClass .getSimpleName)
-            offset
-            (if print-contents?
-              (-> node .getRawSignature (.replaceAll "\n" " \\ "))
-              (-> node .getRawSignature (.subSequence 0 5))))
+(defn pre-tree
+  ([f node] (pre-tree f node 1))
+  ([f node index]
+
+   (let [children (.getChildren node)
+         ret (case (arg-count f)
+                   1 (f node)
+                   2 (f node index))]
+
+     (conj
+           (for [iast-node (.getChildren node)]
+             (pre-tree f iast-node (inc index)))
+           ret))))
+
+(defn print-tree [node]
+  (letfn
+      [(f [node index]
+         (let [print-contents? (and (not (instance? CPPASTTranslationUnit node))
+                                    (< (-> node .getFileLocation .getNodeLength) 30))
+               offset (format " (offset: %s, %s)"
+                              (-> node .getFileLocation .getNodeOffset)
+                              (-> node .getFileLocation .getNodeLength))]
+
+           (printf "%s -%s %s -> %s\n"
+                   (apply str (repeat index "  "))
+                   (-> node .getClass .getSimpleName)
+                   offset
+                   (if print-contents?
+                     (-> node .getRawSignature (.replaceAll "\n" " \\ "))
+                     (-> node .getRawSignature (.subSequence 0 5))))))]

-    (for [iast-node children]
-      (print-tree iast-node (inc index)))))
+     (pre-tree f node)))

-(def filename "/home/dgopstein/nyu/confusion/atoms/simplifications/2000-natori/nonconfusing.c")
+(def filename "/Users/dgopstein/nyu/confusion/atoms/simplifications/2000-natori/nonconfusing.c")

 (defn -main
   [& args]
@@ -75,5 +94,6 @@

   (visit-ast filename)
   (ast-visitor-proxy)
-  (print-tree (translation-unit filename) 1)
+  (print-tree (translation-unit filename))
+  (pre-tree #(-> % .getClass .getSimpleName) (translation-unit filename))
   )


cleanup: remove italian-specific stuff

cleanup: remove italian-specific stuff

--- src/babel/lexiconfn.cljc
+++ src/babel/lexiconfn.cljc
@@ -381,86 +381,6 @@
          ;; rather than equality-check to see if merged has changed.
          (sem-impl merged) ;; we've added some new information: more implications possible from that.
          merged))))) ;; no more implications: return
-
-(def essere-common
-  (let [infl (atom :top)
-        agr (atom :top)]
-    {:synsem {:essere true
-              :subcat {:1 {:agr agr}}
-              :agr agr
-              :infl infl}
-     :italiano {:agr agr
-               :futuro-stem "sar"
-               :essere true
-               :infinitive "essere"
-               :infl infl
-               :irregular {:present {:1sing "sono"
-                                     :2sing "sei"
-                                     :3sing "è"
-                                     :1plur "siamo"
-                                     :2plur "siete"
-                                     :3plur "sono"}
-                           :passato "stato"
-                           :imperfetto {:1sing "ero"
-                                        :2sing "eri"
-                                        :3sing "era"
-                                        :1plur "eravamo"
-                                        :2plur "eravate"
-                                        :3plur "erano"}
-                           :futuro {:1sing "sarò"
-                                    :2sing "sarai"
-                                    :3sing "sarà"
-                                    :1plur "saremo"
-                                    :2plur "sarete"
-                                    :3plur "saranno"}}}
-     :english {:agr agr
-               :infinitive "to be"
-               :infl infl
-               :irregular {:present {:1sing "am"
-                                     :2sing "are"
-                                     :3sing "is"
-                                     :1plur "are"
-                                     :2plur "are"
-                                     :3plur "are"}
-                           :past {:participle "been"
-                                  :1sing "was"
-                                  :2sing "were"
-                                  :3sing "was"
-                                  :1plur "were"
-                                  :2plur "were"
-                                  :3plur "were"}}}}))
-
-(def fare-common
-  ;; factor out common stuff from all senses of "fare".
-  {:synsem {:essere false}
-   :italiano {:infinitive "fare"
-             :futuro-stem "far"
-             :irregular {:passato "fatto"
-                         :present {:1sing "faccio"
-                                   :2sing "fai"
-                                   :3sing "fa"
-                                   :1plur "facciamo"
-                                   :2plur "fate"
-                                   :3plur "fanno"}
-                         :imperfetto {:1sing "facevo"
-                                      :2sing "facevi"
-                                      :3sing "faceva"
-                                      :1plur "facevamo"
-                                      :2plur "facevate"
-                                      :3plur "facevano"}}}})
-
-(def venire-common
-  {:italiano {:infinitive "venire"
-             :futuro-stem "verr"
-             :irregular {:passato "venuto"
-                         :present {:1sing "vengo"
-                                   :2sing "vieni"
-                                   :3sing "viene"
-                                   :1plur "veniamo"
-                                   :2plur "venete"
-                                   :3plur "vengono"}}}
-   :english {:infinitive "to come"
-             :irregular {:past "came"}}})

 (defn listify [m]
   (into {}


envelopes and gates working

envelopes and gates working

--- src/borderless/sound.clj
+++ src/borderless/sound.clj
@@ -35,6 +35,16 @@

 (def noise-1 (sample "~/work/code/borderless/resources/audio/Test21-1.aif"))

+;; (demo 10
+;;       (rlpf (* 0.5 (saw [338 440]))
+;;             (mouse-x 10 10000)
+;;             (mouse-y 0.0001 0.9999))) ; cutoff frequency
+
+(definst saw-wave3 [freq 440 attack 5.0 sustain 0.4 release 5.0 vol 0.4 gate 1]
+                      (* (env-gen (asr attack sustain release) gate)
+                              (saw freq)
+                              vol))
+
 ;; SYNTHS

 (defn tremelo-freq []
@@ -49,13 +59,16 @@
 (definst drone-aw [freq 100 verb 0]
   "Inst calls the synth macro which takes a synthesizer definition form. The saw function represents a unit-generator, or ugen. These are the basic building blocks for creating synthesizers, and they can generate or process both audio and control signals (odoc saw)"
   (let [tremolo-freq (+ freq ((tremelo-freq)))
-        synth-unit (* (env-gen (lin :sustain 2) 1 1 0 1 FREE)
+        synth-unit (* (env-gen (lin :attack 10 :sustain 5 :release 5) 1 1 0 1 FREE)
                       (+
                        ((vowel-formant tremolo-freq 570 0.1))
                        ((vowel-formant tremolo-freq 840 0.1))
-                       ((vowel-formant tremolo-freq 2410 0.1))))]
-    (out 0 (free-verb synth-unit verb verb verb))
-    (out 1 (free-verb synth-unit verb verb verb))))
+                       ((vowel-formant tremolo-freq 2410 0.1))))
+        synth-unit-lpf (rlpf (* 0.5 synth-unit)
+                                       (mouse-x 10 10000)
+                                       (mouse-y 0.0001 0.9999))]
+    (out 0 (free-verb synth-unit-lpf verb verb verb))
+    (out 1 (free-verb synth-unit-lpf verb verb verb))))

 (definst drone-eh [freq 100]
@@ -68,37 +81,62 @@
     synth-unit))

-(definst drone-aw-sus [freq 100 verb 1 kr-mul 25]
-  (let [synth-unit  (+
-                     ((vowel-formant (+ freq (sin-osc:kr (* 2.5 kr-mul))) 570 0.1))
-                     ((vowel-formant (+ freq (sin-osc:kr (* 0.5 kr-mul))) 840 0.1))
-                     ((vowel-formant (+ freq (sin-osc:kr (* 1.5 kr-mul))) 2410 0.1)))]
-    (out 0 (free-verb synth-unit verb verb verb))
-    (out 1 (free-verb synth-unit verb verb verb))))
+(definst saw-wave3 [freq 440 attack 5.0 sustain 0.4 release 5.0 vol 0.4 gate 1]
+                      (* (env-gen (asr attack sustain release) gate)
+                              (saw freq)
+                              vol))

-(definst drone-eh-sus [freq 80 verb 1 kr-mul 25]
+
+(definst drone-aw-sus [freq 100 verb 1 kr-mul 25
+                       attack 5.0 sustain 0.4 release 5.0 gate 1]
+
+  (let [synth-unit      (+
+                          ((vowel-formant (+ freq (sin-osc:kr (* 2.5 kr-mul))) 570 0.1))
+                          ((vowel-formant (+ freq (sin-osc:kr (* 0.5 kr-mul))) 840 0.1))
+                          ((vowel-formant (+ freq (sin-osc:kr (* 1.5 kr-mul))) 2410 0.1)))
+        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))))
+
+(definst drone-eh-sus [freq 80 verb 1 kr-mul 25
+                       attack 5.0 sustain 0.4 release 5.0 gate 1]
   (let [synth-unit  (+
                      ((vowel-formant (+ freq (sin-osc:kr (* 2.5 kr-mul))) 530 0.1))
                      ((vowel-formant (+ freq (sin-osc:kr (* 0.5 kr-mul))) 1840 0.1))
-                     ((vowel-formant (+ freq (sin-osc:kr (* 1.5 kr-mul))) 2480 0.1)))]
-    (out 0 (free-verb synth-unit verb verb verb))
-    (out 1 (free-verb synth-unit verb verb verb))))
+                     ((vowel-formant (+ freq (sin-osc:kr (* 1.5 kr-mul))) 2480 0.1)))
+        synth-unit-env  (* (env-gen (asr attack sustain release) gate) synth-unit)
+        synth-unit-lpf  (rlpf synth-unit-env 750 0.9)]

-(definst drone-oo-sus [freq 120 verb 1 kr-mul 25]
+    (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
+                       attack 5.0 sustain 0.4 release 5.0 gate 1]
+
   (let [synth-unit  (+
                      ((vowel-formant (+ freq (sin-osc:kr (* 2.5 kr-mul))) 300 0.1))
                      ((vowel-formant (+ freq (sin-osc:kr (* 0.5 kr-mul))) 870 0.1))
-                     ((vowel-formant (+ freq (sin-osc:kr (* 1.5 kr-mul))) 2240 0.1)))]
-    (out 0 (free-verb synth-unit verb verb verb))
-    (out 1 (free-verb synth-unit verb verb verb))))
+                     ((vowel-formant (+ freq (sin-osc:kr (* 1.5 kr-mul))) 2240 0.1)))
+        synth-unit-env  (* (env-gen (asr attack sustain release) gate) synth-unit)
+        synth-unit-lpf  (rlpf synth-unit-env 600 0.6)]

-(definst drone-ae-sus [freq 85 verb 1 kr-mul 25]
+    (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 85 verb 1 kr-mul 25
+                       attack 5.0 sustain 0.4 release 5.0 gate 1]
+
   (let [synth-unit  (+
                      ((vowel-formant (+ freq (sin-osc:kr (* 2.5 kr-mul))) 660 0.1))
                      ((vowel-formant (+ freq (sin-osc:kr (* 0.5 kr-mul))) 1720 0.1))
-                     ((vowel-formant (+ freq (sin-osc:kr (* 1.5 kr-mul))) 2410 0.1)))]
-    (out 0 (free-verb synth-unit verb verb verb))
-    (out 1 (free-verb synth-unit verb verb verb))))
+                     ((vowel-formant (+ freq (sin-osc:kr (* 1.5 kr-mul))) 2410 0.1)))
+        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))))

 ;;;;;;;;;;;;;;;;;;;;;
 ;; Control         ;;
@@ -116,7 +154,7 @@
 (defn end-sound! [pid]
   (if (contains? @person-sound pid)
     (do
-      (kill (get-sound pid))
+      (ctl (get-sound pid) :gate 0)
       (remove-person-sound! pid))))

 (defn control-sound


Add new anonymous group load hook to make bots show up when viewing

--- src/braid/bots/core.cljc
+++ src/braid/bots/core.cljc
@@ -45,4 +45,7 @@
        (doseq [route routes/bot-routes]
          (core/register-raw-http-handler! route))
        (core/register-initial-user-data!
-         (fn [user-id] {::bots (db/bots-for-user-groups user-id)})))))
+         (fn [user-id] {::bots (db/bots-for-user-groups user-id)}))
+       (core/register-anonymous-group-load!
+         (fn [group-id info]
+           (assoc-in info [:group :bots] (db/bots-in-group group-id)))))))


Register `:aws/credentials-provider` config var

--- src/braid/chat/core.cljc
+++ src/braid/chat/core.cljc
@@ -37,6 +37,7 @@
                   :aws-bucket
                   :aws-region
                   :aws-secret-key
+                  :aws/credentials-provider
                   :db-url
                   :embedly-key
                   :environment


Fix bad symbol: s/createClass/createReactClass/

--- src/breeze/quiescent.clj
+++ src/breeze/quiescent.clj
@@ -48,7 +48,7 @@
           `(cljs.core/specify! ~o ~@proto+specs)))))

 (defmacro createClass [& specs]
-  `(js/createClass (createMixin ~@specs)))
+  `(js/createReactClass (createMixin ~@specs)))

 (defmacro defclass [name docstr & specs]
   (assert (symbol? name) "Must supply a symbol for name (as with def).")


Remove (fn []) wrapper around datepickers

--- src/re_demo/datepicker.cljs
+++ src/re_demo/datepicker.cljs
@@ -99,46 +99,44 @@
                   [h-box
                    :gap      "20px"
                    :align    :start
-                   :children [[(fn []
-                                 [v-box
-                                  :gap      "5px"
-                                  :children [[label :style label-style :label ":minimum or :maximum not specified"]
-                                             [datepicker
-                                              :model         model1
-                                              :disabled?     disabled?
-                                              :show-today?   @show-today?
-                                              :show-weeks?   @show-weeks?
-                                              :selectable-fn selectable-pred
-                                              :on-change     #(reset! model1 %)]
-                                             [label :style label-style :label (str "selected: " (date->string @model1))]
-                                             [h-box
-                                              :gap      "6px"
-                                              :margin   "10px 0px 0px 0px"
-                                              :children [[label :style label-style :label "Change model:"]
-                                                         [md-icon-button
-                                                          :md-icon-name "zmdi-arrow-left"
-                                                          :size         :smaller
-                                                          :on-click     #(reset! model1 (minus @model1 (days 1)))]
-                                                         [md-icon-button
-                                                          :md-icon-name "zmdi-arrow-right"
-                                                          :size         :smaller
-                                                          :on-click     #(reset! model1 (plus @model1 (days 1)))]]]]])]
+                   :children [[v-box
+                               :gap      "5px"
+                               :children [[label :style label-style :label ":minimum or :maximum not specified"]
+                                          [datepicker
+                                           :model         model1
+                                           :disabled?     disabled?
+                                           :show-today?   @show-today?
+                                           :show-weeks?   @show-weeks?
+                                           :selectable-fn selectable-pred
+                                           :on-change     #(reset! model1 %)]
+                                          [label :style label-style :label (str "selected: " (date->string @model1))]
+                                          [h-box
+                                           :gap      "6px"
+                                           :margin   "10px 0px 0px 0px"
+                                           :children [[label :style label-style :label "Change model:"]
+                                                      [md-icon-button
+                                                       :md-icon-name "zmdi-arrow-left"
+                                                       :size         :smaller
+                                                       :on-click     #(reset! model1 (minus @model1 (days 1)))]
+                                                      [md-icon-button
+                                                       :md-icon-name "zmdi-arrow-right"
+                                                       :size         :smaller
+                                                       :on-click     #(reset! model1 (plus @model1 (days 1)))]]]]]
                               ;; restricted to both minimum & maximum date
-                              [(fn []
-                                 [v-box
-                                  :gap      "5px"
-                                  :children [[label :style label-style :label ":minimum \"20140831\" :maximum \"20141019\", :start-of-week Monday"]
-                                             [datepicker
-                                              :start-of-week 0
-                                              :model         model2
-                                              :minimum       (iso8601->date "20140831")
-                                              :maximum       (iso8601->date "20141019")
-                                              :show-today?   @show-today?
-                                              :show-weeks?   @show-weeks?
-                                              :selectable-fn selectable-pred
-                                              :disabled?     disabled?
-                                              :on-change     #(reset! model2 %)]
-                                             [label :style label-style :label (str "selected: " (date->string @model2))]]])]]]
+                              [v-box
+                               :gap      "5px"
+                               :children [[label :style label-style :label ":minimum \"20140831\" :maximum \"20141019\", :start-of-week Monday"]
+                                          [datepicker
+                                           :start-of-week 0
+                                           :model         model2
+                                           :minimum       (iso8601->date "20140831")
+                                           :maximum       (iso8601->date "20141019")
+                                           :show-today?   @show-today?
+                                           :show-weeks?   @show-weeks?
+                                           :selectable-fn selectable-pred
+                                           :disabled?     disabled?
+                                           :on-change     #(reset! model2 %)]
+                                          [label :style label-style :label (str "selected: " (date->string @model2))]]]]]
                   enabled-days
                   disabled?
                   show-today?
@@ -150,15 +148,14 @@
                      :size     "auto"
                      :align    :start
                      :children [[gap :size "120px"]
-                                [(fn []
-                                   [datepicker-dropdown
-                                    :model         model1
-                                    :show-today?   @show-today?
-                                    :show-weeks?   @show-weeks?
-                                    :selectable-fn selectable-pred
-                                    :format        "dd MMM, yyyy"
-                                    :disabled?     disabled?
-                                    :on-change     #(reset! model1 %)])]]]
+                                [datepicker-dropdown
+                                 :model         model1
+                                 :show-today?   @show-today?
+                                 :show-weeks?   @show-weeks?
+                                 :selectable-fn selectable-pred
+                                 :format        "dd MMM, yyyy"
+                                 :disabled?     disabled?
+                                 :on-change     #(reset! model1 %)]]]
                     enabled-days
                     disabled?
                     show-today?


Switch back to case in parse-str

Switch back to case in parse-str

--- src/transit/read.clj
+++ src/transit/read.clj
@@ -64,19 +64,14 @@
                      (> (.length ^String s) 1)
                      (= w/ESC (subs ^String s 0 1)))
               (let [c (subs s 1 2)]
-                (cond
-                 (or (= w/ESC c)
-                     (= w/SUB c)
-                     (= w/RESERVED c))
-                 (subs s 1)
-
-                 (= w/TAG c)
-                 s
-
-                 :else
-                 (if-let [decode-fn (decode-fn (subs s 1 2))]
-                   (decode-fn (subs s 2))
-                   s)))
+                (case c
+                  "~" (subs s 1)
+                  "^" (subs s 1)
+                  "`" (subs s 1)
+                  "#" s
+                  (if-let [decode-fn (decode-fn (subs s 1 2))]
+                    (decode-fn (subs s 2))
+                    s)))
               s)]
     ;;(prn "parse-str after" res)
     res))


fix seed

fix seed

--- src/chat/server/seed.clj
+++ src/chat/server/seed.clj
@@ -13,6 +13,7 @@

         msg (db/create-message! {:id (db/uuid)
                                  :user-id (user-1 :id)
+                                 :thread-id (db/uuid)
                                  :created-at (java.util.Date.)
                                  :content "Hello?"})
         _ (db/create-message! {:id (db/uuid)


do not emit the closure source map by default, add :closure-source-map knob

--- src/clj/cljs/closure.clj
+++ src/clj/cljs/closure.clj
@@ -870,13 +870,15 @@
       ;; compiler.getSourceMap().reset()
       (let [source (.toSource closure-compiler)]
         (when-let [name (:source-map opts)]
-          (let [name' (str name ".closure")]
-            (with-open [out (io/writer name')]
-              (.appendTo (.getSourceMap closure-compiler) out name'))
+          (let [name' (str name ".closure")
+                sw (StringWriter.)
+                sm-json-str (do
+                              (.appendTo (.getSourceMap closure-compiler) sw name')
+                              (.toString sw))]
+            (when (true? (:closure-source-map opts))
+              (spit (io/file name') sm-json-str))
             (emit-optimized-source-map
-              (-> (io/file name')
-                slurp
-                (json/read-str :key-fn keyword))
+              (json/read-str sm-json-str :key-fn keyword)
               sources name
               (assoc opts
                 :preamble-line-count
@@ -1038,7 +1040,8 @@
       (when (:source-map opts)
         (let [sm-json-str (:source-map-json module-desc)
               sm-json     (json/read-str sm-json-str :key-fn keyword)]
-          (spit (io/file (:source-map-name module-desc)) sm-json-str)
+          (when (true? (:closure-source-map opts))
+            (spit (io/file (:source-map-name module-desc)) sm-json-str))
           (emit-optimized-source-map sm-json js-sources sm-name
             (merge opts
               {:source-map sm-name


Removes the def-spotify-api-call macro and replaces it with the function spotify-api-call. The reasons are readability, users will be able to redefine new endpoints with the functions provided and the spotify-api-call is just a normal function and can therefore be used as one.

--- src/clj_spotify/core.clj
+++ src/clj_spotify/core.clj
@@ -18,11 +18,11 @@
   "Parse body of http respose to json"
   [response]
   (with-meta
-  (try
-    (json-string-to-map (:body response))
-    (catch java.lang.NullPointerException e {:error {:status "NullPointerException"  :message (.getMessage e)}})
-    (catch Exception e {:error {:status "Exception"  :message (.getMessage e)}})
-    ) {:status (:status response)}))
+    (try
+      (json-string-to-map (:body response))
+      (catch java.lang.NullPointerException e {:error {:status "NullPointerException"  :message (.getMessage e)}})
+      (catch Exception e {:error {:status "Exception"  :message (.getMessage e)}})
+      ) {:status (:status response)}))

 (defn- build-new-url
   "Do the building nescessary in replace-url-values here. url-parts is the split up url which values works as keys in param-map."
@@ -39,11 +39,11 @@
   "Build a new url by switching parts in url with values in param-map. Keys are substrings of the full url. If a key is not present in template-keys it won't be exchanged."
   [param-map url]
   (if (string/blank? url)
-   ""
-  (let [act-tmplt-keys  (select-keys param-map template-keys)
-        split-url (string/split (string/replace url "https://" "") #"/")
-        ]
-    (build-new-url "" split-url act-tmplt-keys))))
+    ""
+    (let [act-tmplt-keys  (select-keys param-map template-keys)
+          split-url (string/split (string/replace url "https://" "") #"/")
+          ]
+      (build-new-url "" split-url act-tmplt-keys))))

 (defn comma-separate
   "If v is a sequence, convert to a comma separated string."
@@ -73,46 +73,46 @@
 (defn set-params-type
   "Return either :query-params or :form-params key depending on value of verb"
   [verb]
- (let [verb-type (type verb)]
-  (if
-    (or (= verb-type clj_http.client$put) (= verb-type clj_http.client$post))
-    :form-params
-    :query-params)))
+  (let [verb-type (type verb)]
+    (if
+      (or (= verb-type clj_http.client$put) (= verb-type clj_http.client$post))
+      :form-params
+      :query-params)))

 ;TODO - better doc string
-(defmacro def-spotify-api-call
+(defn spotify-api-call
   "Creates a function f with doc-string d that calls the http-verb verb for url url."
-  [f verb url doc-string]
-  `(defn ~f
-     ~doc-string
-     ([m#] (~f m# nil))
-     ([m# t#]
-      (let [query-params# {(set-params-type ~verb) (modify-form-params m#) :oauth-token t# :content-type :json}]
-        (->
-          (try
-            (~verb (replace-url-values m# ~url) query-params#)
-            (catch Exception e# (ex-data e#))
-            )
-          (response-to-map))))))
+  [verb url]
+  (fn f
+    ([m] (f m nil))
+    ([m t]
+     (let [query-params {(set-params-type verb) (modify-form-params m) :oauth-token t :content-type :json}]
+       (->
+         (try
+           (verb (replace-url-values m url) query-params)
+           (catch Exception e (ex-data e)))
+         (response-to-map))))))

 ;Albums
-(def-spotify-api-call get-an-album client/get (str spotify-api-url "albums/id")
+(def get-an-album
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :id, optional key is :market.
   :id has to have the value of an existing album's id.
   :market is an ISO 3166-1 alpha-2 country code.

-  Example: (get-an-album {:id \"0MnG7y5F1n4Wns63RjEItx\" :market \"SE\"} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call get-several-albums client/get (str spotify-api-url "albums")
+  Example: (get-an-album {:id \"0MnG7y5F1n4Wns63RjEItx\" :market \"SE\"} \"BQBw-JtC..._7GvA\")"
+  (spotify-api-call client/get (str spotify-api-url "albums/id")))
+
+(def get-several-albums
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :ids, optional key is :market.
   :ids has to be a comma separated string of spotify album ids.
   :market is an ISO 3166-1 alpha-2 country code.

-  Example: (get-several-albums {:ids \"4hTil0JVdMyxK2vH11ZbiX,4RjjFbiqFaO8mKTDM2Kzna\" :market \"SE\"} \"BQBw-JtC..._7GvA\") ")
-
-(def-spotify-api-call get-an-albums-tracks client/get (str spotify-api-url "albums/id/tracks")
+  Example: (get-several-albums {:ids \"4hTil0JVdMyxK2vH11ZbiX,4RjjFbiqFaO8mKTDM2Kzna\" :market \"SE\"} \"BQBw-JtC..._7GvA\") "
+  (spotify-api-call client/get (str spotify-api-url "albums")))
+
+(def get-an-albums-tracks
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :id, optional keys are :limit, :offset and :market.
   :id has to have the value of an existing album's id.
@@ -120,24 +120,24 @@
   :offset is the index of the first track to return, default is 0.
   :market is an ISO 3166-1 alpha-2 country code.

-  Example: (get-tracks-of-album {:id \"3XCGFOBqESirxxICswSity\" :market \"SE\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")")
+  Example: (get-tracks-of-album {:id \"3XCGFOBqESirxxICswSity\" :market \"SE\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "albums/id/tracks")))

 ;Artists
-(def-spotify-api-call get-an-artist client/get (str spotify-api-url "artists/id")
+(def get-an-artist
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :id.
   :id has to have the value of an existing artist's id.

-  Example: (get-an-artist {:id \"5CE2IfdYZEQGIDsfiRm8SI\"} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call get-several-artists client/get (str spotify-api-url "artists")
+  Example: (get-an-artist {:id \"5CE2IfdYZEQGIDsfiRm8SI\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "artists/id")))
+
+(def get-several-artists
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :ids.
   :ids has to be a comma separated string of spotify artist ids.

-  Example: (get-several-artists {:ids \"1kK2LYgoP3raZNngbb1qMT,2RitCPbwEYyYNw5LkbXTGv\"} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call get-an-artists-albums client/get (str spotify-api-url "artists/id/albums")
+  Example: (get-several-artists {:ids \"1kK2LYgoP3raZNngbb1qMT,2RitCPbwEYyYNw5LkbXTGv\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "artists")))
+
+(def get-an-artists-albums
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :id, optional keys are :album_type, :market, :limit and :offset.
   :id has to have the value of an existing artist's id.
@@ -147,25 +147,25 @@
   :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-an-artists-albums {:id \"7lOJ7WXyopaxri0dbOiZkd\" :album_type \"album, single\" :market \"SE\" :limit 50 :offset 50}} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call get-an-artists-top-tracks client/get (str spotify-api-url "artists/id/top-tracks")
+  Example: (get-an-artists-albums {:id \"7lOJ7WXyopaxri0dbOiZkd\" :album_type \"album, single\" :market \"SE\" :limit 50 :offset 50}} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "artists/id/albums")))
+
+(def get-an-artists-top-tracks
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory keys in m are :id and :country.
   :id has to be a spotify artist id.
   :country is an ISO 3166-1 alpha-2 country code.

-  Example: (get-an-artists-top-tracks {:id \"7hCsRnXtcbez8msLPfjbkz\" :country \"SE\"} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call get-an-artists-related-artists client/get (str spotify-api-url "artists/id/related-artists")
+  Example: (get-an-artists-top-tracks {:id \"7hCsRnXtcbez8msLPfjbkz\" :country \"SE\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "artists/id/top-tracks")))
+
+(def get-an-artists-related-artists
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :id.
   :id has to be a spotify artist id.

-  Example: (get-an-artists-related-artists {:id \"4EF5vIcCYKMM61oYOG2Tqa\"} \"BQBw-JtC..._7GvA\")")
+  Example: (get-an-artists-related-artists {:id \"4EF5vIcCYKMM61oYOG2Tqa\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "artists/id/related-artists")))

 ;Browse
-(def-spotify-api-call get-a-list-of-featured-playlists client/get (str spotify-api-url "browse/featured-playlists")
+(def get-a-list-of-featured-playlists
   " 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 :locale, :country, :timestamp, :limit and :offset.
   :locale is an ISO 639 language code and a ISO 3166-1 alpha-2 country code joined with an underscore.
@@ -174,36 +174,37 @@
   :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-featured-playlists {:locale \"sv_SE\" :country \"SE\" :timestamp \"2015-10-23T09:00:00\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call get-a-list-of-new-releases client/get (str spotify-api-url "browse/new-releases")
+  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 client/get (str spotify-api-url "browse/featured-playlists")))
+
+(def get-a-list-of-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\")")
-
-(def-spotify-api-call get-a-list-of-categories client/get (str spotify-api-url "browse/categories")
+  Example: (get-a-list-of-new-releases {:country \"SE\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")"
+  (spotify-api-call client/get (str spotify-api-url "browse/new-releases")))
+
+(def get-a-list-of-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\")")
-
-(def-spotify-api-call get-a-category client/get (str spotify-api-url "browse/categories/category_id")
+  Example: (get-a-list-of-categories {:country \"SE\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "browse/categories")))
+
+(def get-a-category
   " 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.
   :category_id has to be a spotify category id.
   :country is an ISO 3166-1 alpha-2 country code.
   :locale is an ISO 639 language code and a ISO 3166-1 alpha-2 country code joined with an underscore.

-  Example: (get-a-category {:category_id \"dinner\" :country \"SE\" :locale \"sv_SE\"} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call get-a-categorys-playlists client/get (str spotify-api-url "browse/categories/category_id/playlists")
+  Example: (get-a-category {:category_id \"dinner\" :country \"SE\" :locale \"sv_SE\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "browse/categories/category_id")))
+
+(def get-a-categorys-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.
@@ -211,110 +212,110 @@
   :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\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\") ")
+  Example: (get-a-categorys-playlist {:category_id \"dinner\" :country \"SE\" :locale \"sv_SE\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "browse/categories/category_id/playlists")))

 ;Follow
-(def-spotify-api-call get-users-followed-artists client/get (str spotify-api-url "me/following")
+(def get-users-followed-artists
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :type, optional keys are :limit and :after.
   :type has to be set to \"artist\".
   :limit is the maxium number of items to return, default is 20.
   :after the last artist id retrieved from a previous request. Use this to get next set of artists.

-  Example: (get-users-followed-artists {:type \"artist\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\") ")
-
-(def-spotify-api-call follow-artists-or-users client/put (str spotify-api-url "me/following")
+  Example: (get-users-followed-artists {:type \"artist\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\") " (spotify-api-call client/get (str spotify-api-url "me/following")))
+
+(def follow-artists-or-users
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :type and :ids.
   :type has to be set to \"artist\" or \"user\".
   :ids has to be a comma separated list of users or artists.

-  Example: (follow-artists-or-users {:type \"artist\" :ids \"2933wDUojoQmvqSdTAE5NB,1AUCkAIfT6Ig8lOegDGK3Z\" } \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call unfollow-artists-or-users client/delete (str spotify-api-url "me/following")
+  Example: (follow-artists-or-users {:type \"artist\" :ids \"2933wDUojoQmvqSdTAE5NB,1AUCkAIfT6Ig8lOegDGK3Z\" } \"BQBw-JtC..._7GvA\")" (spotify-api-call client/put (str spotify-api-url "me/following")))
+
+(def unfollow-artists-or-users
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :type and :ids.
   :type has to be set to \"artist\" or \"user\".
   :ids has to be a comma separated list of users or artists.

-  Example: (unfollow-artists-or-users {:type \"artist\" :ids \"0uCCBpmg6MrPb1KY2msceF,1WsMRWV5KEC2AxpYkeb2Cf\" } \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call user-following-artists-or-users? client/get (str spotify-api-url "me/following/contains")
+  Example: (unfollow-artists-or-users {:type \"artist\" :ids \"0uCCBpmg6MrPb1KY2msceF,1WsMRWV5KEC2AxpYkeb2Cf\" } \"BQBw-JtC..._7GvA\")" (spotify-api-call client/delete (str spotify-api-url "me/following")))
+
+(def user-following-artists-or-users?
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :type and :ids.
   :type has to be set to \"artist\" or \"user\".
   :ids has to be a comma separated list of users or artists.

-  Example: (user-following-artists-or-users? {:type \"artist\" :ids \"3H6Js0rhywEhHda3UwuhGW,4r8bVMyYyGyARs9OfBvyj4\" } \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call follow-a-playlist client/put (str spotify-api-url "users/owner_id/playlists/playlist_id/followers")
+  Example: (user-following-artists-or-users? {:type \"artist\" :ids \"3H6Js0rhywEhHda3UwuhGW,4r8bVMyYyGyARs9OfBvyj4\" } \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "me/following/contains")))
+
+(def follow-a-playlist
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory keys in m are :owner_id and :playlist_id.
   :owner_id has to be the spotify user id of the user owning the playlist.
   :playlist_id the spotify playlist id.

-  Example: (follow-a-playlist {:owner_id \"sondre_lerche\" :playlist_id \"7q8QuE0YbAgbvzR0NJVwV8\" } \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call unfollow-a-playlist client/delete (str spotify-api-url "users/owner_id/playlists/playlist_id/followers")
+  Example: (follow-a-playlist {:owner_id \"sondre_lerche\" :playlist_id \"7q8QuE0YbAgbvzR0NJVwV8\" } \"BQBw-JtC..._7GvA\")" (spotify-api-call client/put (str spotify-api-url "users/owner_id/playlists/playlist_id/followers")))
+
+(def unfollow-a-playlist
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory keys in m are :owner_id and :playlist_id.
   :owner_id has to be the spotify user id of the user owning the playlist.
   :playlist_id the spotify playlist id.

-  Example: (unfollow-a-playlist {:owner_id \"ulyssestone\" :playlist_id \"3WG4abOpUpocexRakPioAg\" } \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call user-following-playlist? client/get (str spotify-api-url "users/owner_id/playlists/playlist_id/followers/contains")
+  Example: (unfollow-a-playlist {:owner_id \"ulyssestone\" :playlist_id \"3WG4abOpUpocexRakPioAg\" } \"BQBw-JtC..._7GvA\")" (spotify-api-call client/delete (str spotify-api-url "users/owner_id/playlists/playlist_id/followers")))
+
+(def user-following-playlist?
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory keys in m are :owner_id and :playlist_id.
   :owner_id has to be the spotify user id of the user owning the playlist.
   :playlist_id the spotify playlist id.

-  Example: (user-following-playlist? {:owner_id \"lecowboy\" :playlist_id \"2PftXV7dgcxFjMWe75GuSG\" } \"BQBw-JtC..._7GvA\")")
+  Example: (user-following-playlist? {:owner_id \"lecowboy\" :playlist_id \"2PftXV7dgcxFjMWe75GuSG\" } \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "users/owner_id/playlists/playlist_id/followers/contains")))

 ;Library
-(def-spotify-api-call save-tracks-for-user client/put (str spotify-api-url "me/tracks")
+(def save-tracks-for-user
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :ids.
   :ids has to be a comma separated list of spotify track ids.

-  Example: (save-tracks-for-user {:ids \"3BSpuy2yHhoEazfXVU7WGq,0BcaWLANkstu2v9kghrCAj\"} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call get-users-saved-tracks client/get (str spotify-api-url "me/tracks")
+  Example: (save-tracks-for-user {:ids \"3BSpuy2yHhoEazfXVU7WGq,0BcaWLANkstu2v9kghrCAj\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/put (str spotify-api-url "me/tracks")))
+
+(def get-users-saved-tracks
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   There are no compulsory keys in m, optional keys are :market, :limit and :offset.
   :market 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-users-saved-tracks {:market \"SE\" :limit 50 :offset 50}} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call remove-users-saved-tracks client/delete (str spotify-api-url "me/tracks")
+  Example: (get-users-saved-tracks {:market \"SE\" :limit 50 :offset 50}} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "me/tracks")))
+
+(def remove-users-saved-tracks
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :ids.
   :ids has to be a comma separated list of spotify track ids.

-  Example: (remove-users-saved-tracks {:ids \"1RyZgkIghj1eL2U1zlYOoE,6NwbeybX6TDtXlpXvnUOZC\"} \"BQBw-JtC..._7GvA\")")
+  Example: (remove-users-saved-tracks {:ids \"1RyZgkIghj1eL2U1zlYOoE,6NwbeybX6TDtXlpXvnUOZC\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/delete (str spotify-api-url "me/tracks")))

 ;TODO - better name for this function
-(def-spotify-api-call check-users-saved-tracks client/get (str spotify-api-url "me/tracks/contains")
+(def check-users-saved-tracks
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :ids.
   :ids has to be a comma separated list of spotify track ids.

-  Example: (check-users-saved-tracks {:ids \"28nlyt9KhVZQ0lGQsg8Yht,09ZGF6mwJVzw5jxqbtT53E\"} \"BQBw-JtC..._7GvA\")")
+  Example: (check-users-saved-tracks {:ids \"28nlyt9KhVZQ0lGQsg8Yht,09ZGF6mwJVzw5jxqbtT53E\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "me/tracks/contains")))

 ;Playlists
-(def-spotify-api-call get-a-list-of-a-users-playlists client/get (str spotify-api-url "users/user_id/playlists")
+(def get-a-list-of-a-users-playlists
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in  m is :user_id, optional keys are :limit and :offset.
   :user_id is the users spotify id.
   :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-a-users-playlists {:user_id \"elkalel\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")")
+  Example: (get-a-list-of-a-users-playlists {:user_id \"elkalel\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "users/user_id/playlists")))

 ;TODO - Change this fields string to be a map?
-(def-spotify-api-call get-a-playlist client/get (str spotify-api-url "users/owner_id/playlists/playlist_id")
+(def get-a-playlist
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory keys in  m are :user_id and :playlist_id, optional keys are :fields, :market, :limit and :offset.
   :user_id is the users spotify id.
@@ -325,9 +326,9 @@
   :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-playlist {:owner_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :fields \"href,name,owner\" :market \"SE\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call get-a-playlists-tracks client/get (str spotify-api-url "users/owner_id/playlists/playlist_id/tracks")
+  Example: (get-a-playlist {:owner_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :fields \"href,name,owner\" :market \"SE\" :limit 50 :offset 50} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "users/owner_id/playlists/playlist_id")))
+
+(def get-a-playlists-tracks
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory keys in  m are :user_id and :playlist_id, optional keys are :fields, :limit and :offset and :market.
   :owner_id is the users spotify id.
@@ -338,18 +339,18 @@
   :offset is the index of the first track to return, default is 0.
   :market is an ISO 3166-1 alpha-2 country code.

-  Example: (get-a-playlists-tracks {:owner_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :fields \"href,name,owner\":limit 50 :offset 50 :market \"SE\"} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call create-a-playlist client/post (str spotify-api-url "users/user_id/playlists")
+  Example: (get-a-playlists-tracks {:owner_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :fields \"href,name,owner\":limit 50 :offset 50 :market \"SE\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "users/owner_id/playlists/playlist_id/tracks")))
+
+(def create-a-playlist
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory keys in m are :user_id and :name, optional key is :public.
   :user_id is the users spotify id.
   :name is the name for the new playlist.
   :public is set to true if a playlist should be public and false if not.

-  Example: (create-a-playlist {:user_id \"elkalel\" :name \"The songs you didn't know you liked.\" :public true} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call add-tracks-to-a-playlist client/post (str spotify-api-url "users/user_id/playlists/playlist_id/tracks")
+  Example: (create-a-playlist {:user_id \"elkalel\" :name \"The songs you didn't know you liked.\" :public true} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/post (str spotify-api-url "users/user_id/playlists")))
+
+(def add-tracks-to-a-playlist
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m are :user_id and :playlist_id, optional keys are :uris and :position.
   :user_id is the users spotify id.
@@ -358,11 +359,11 @@
   :position the position to insert the tracks.

   Example: (add-tracks-to-playlist {:user_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :uris \"spotify:track:4iV5W9uYEdYUVa79Axb7Rh,
-  spotify:track:1301WleyT98MSxVHPZCA6M\" :position 2} \"BQBw-JtC..._7GvA\")")
+  spotify:track:1301WleyT98MSxVHPZCA6M\" :position 2} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/post (str spotify-api-url "users/user_id/playlists/playlist_id/tracks")))

 ;TODO - Deal with correct formatting of :tracks.
 ;TODO - Additional doc string with optional and required values in :tracks.
-(def-spotify-api-call remove-tracks-from-a-playlist client/delete (str spotify-api-url "users/user_id/playlists/playlist_id/tracks")
+(def remove-tracks-from-a-playlist
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m are :user_id, :playlist_id and :tracks, optional keys is :snapshot_id.
   :user_id is the users spotify id.
@@ -371,9 +372,9 @@
   :snapshot_id a playlist snapshot ID.
   See the spotify developer API for the logic behind deleting tracks.

-  Example: (remove-tracks-from-playlist {:user_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :tracks [{:uri \"spotify:track:4iV5W9uYEdYUVa79Axb7Rh\", :positions [2]} {:uri \"spotify:track:1301WleyT98MSxVHPZCA6M\", :positions [7]}]}  \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call reorder-a-playlists-tracks client/put (str spotify-api-url "users/user_id/playlists/playlist_id/tracks")
+  Example: (remove-tracks-from-playlist {:user_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :tracks [{:uri \"spotify:track:4iV5W9uYEdYUVa79Axb7Rh\", :positions [2]} {:uri \"spotify:track:1301WleyT98MSxVHPZCA6M\", :positions [7]}]}  \"BQBw-JtC..._7GvA\")" (spotify-api-call client/delete (str spotify-api-url "users/user_id/playlists/playlist_id/tracks")))
+
+(def reorder-a-playlists-tracks
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m are :user_id, :playlist_id, :range_start and :insert_before, optional keys are :range_length and :snapshot_id.
   :user_id is the users spotify id.
@@ -383,9 +384,9 @@
   :range_length the amount of tracks to be reordered.
   :snapshot_id a playlist snapshot ID.

-  Example: (reorder-a-playlists-tracks {:user_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :range_start 1 :range_length 2 :insert_before 3} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call replace-a-playlists-tracks client/put (str spotify-api-url "users/user_id/playlists/playlist_id/tracks")
+  Example: (reorder-a-playlists-tracks {:user_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :range_start 1 :range_length 2 :insert_before 3} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/put (str spotify-api-url "users/user_id/playlists/playlist_id/tracks")))
+
+(def replace-a-playlists-tracks
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m are :user_id and :playlist_id, optional keys are :uris and :position.
   :user_id is the users spotify id.
@@ -393,9 +394,9 @@
   :uris a list of spotify track uris.

   Example: (replace-a-playlists-tracks {:user_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :uris \"spotify:track:4iV5W9uYEdYUVa79Axb7Rh,
-  spotify:track:1301WleyT98MSxVHPZCA6M\"} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call change-a-playlists-details client/put (str spotify-api-url "users/user_id/playlists/playlist_id")
+  spotify:track:1301WleyT98MSxVHPZCA6M\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/put (str spotify-api-url "users/user_id/playlists/playlist_id/tracks")))
+
+(def change-a-playlists-details
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m are :user_id and :playlist_id, optional keys are :name and :public.
   :user_id is the users spotify id.
@@ -403,24 +404,24 @@
   :name is the name for the new playlist.
   :public is set to true if a playlist should be public and false if not.

-  Example: (change-a-playlists-details {:user_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :name \"Fantastic playlist\" :public true} \"BQBw-JtC..._7GvA\")")
+  Example: (change-a-playlists-details {:user_id \"elkalel\" :playlist_id \"6IIjEBw2BrRXbrSLerA7A6\" :name \"Fantastic playlist\" :public true} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/put (str spotify-api-url "users/user_id/playlists/playlist_id")))

 ;Profiles
-(def-spotify-api-call get-a-users-profile client/get (str spotify-api-url "users/user_id")
+(def get-a-users-profile
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :user_id.
   :user_id has to be a spotify user id.

-  Example: (get-a-users-profile {:user_id \"elkalel\"} \"BQBw-JtC..._7GvA\")")
+  Example: (get-a-users-profile {:user_id \"elkalel\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "users/user_id")))

 ;TODO - what happens when map is not present or nil?
-(def-spotify-api-call get-current-users-profile client/get (str spotify-api-url "me")
+(def get-current-users-profile
   " Takes an oauth-token t.

-  Example: (get-current-users-profile {} \"BQBw-JtC..._7GvA\")")
+  Example: (get-current-users-profile {} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "me")))

 ;Search
-(def-spotify-api-call search client/get (str spotify-api-url "search")
+(def search
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory keys in m are :q and :type, optional keys are :market, :limit and :offset.
   :q is the search string.
@@ -429,21 +430,21 @@
   :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: (search {:q \"Gerry Rafferty\" :type \"artist\"  :market \"SE\" :limit 50 :offset 50}} \"BQBw-JtC..._7GvA\")")
+  Example: (search {:q \"Gerry Rafferty\" :type \"artist\"  :market \"SE\" :limit 50 :offset 50}} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "search")))

 ;Tracks
-(def-spotify-api-call get-a-track client/get (str spotify-api-url "tracks/id")
+(def get-a-track
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :id, optional key is :market.
   :id has to have the value of an existing track's id.
   :market is an ISO 3166-1 alpha-2 country code.

-  Example: (get-a-track {:id \"3BEmmHuv5jm0tbCUVI6ceD\" :market \"SE\"} \"BQBw-JtC..._7GvA\")")
-
-(def-spotify-api-call get-several-tracks client/get (str spotify-api-url "tracks")
+  Example: (get-a-track {:id \"3BEmmHuv5jm0tbCUVI6ceD\" :market \"SE\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "tracks/id")))
+
+(def get-several-tracks
   " Takes two arguments, a map m with query parameters and an optional oauth-token t.
   Compulsory key in m is :ids, optional key is :market.
   :ids has to be a comma separated string of spotify track ids.
   :market is an ISO 3166-1 alpha-2 country code.

-  Example: (get-several-tracks {:ids \"2Dlo9QsN9ohaz9kASYkKmv,6XYvLpqJFebcjnOTolwkGw\" :market \"SE\"} \"BQBw-JtC..._7GvA\")")
+  Example: (get-several-tracks {:ids \"2Dlo9QsN9ohaz9kASYkKmv,6XYvLpqJFebcjnOTolwkGw\" :market \"SE\"} \"BQBw-JtC..._7GvA\")" (spotify-api-call client/get (str spotify-api-url "tracks")))


Keep text when using tab in either direction

--- src/cljs/notes/core.cljs
+++ src/cljs/notes/core.cljs
@@ -14,7 +14,7 @@
 (defn now [] (new js/Date))
 (def custom-formatter (time-format/formatter "dd/MM/yyyy"))
 (def app-title (str "Notes " (time-format/unparse custom-formatter (time-core/date-time (.getFullYear (now)) (+ 1 (.getMonth (now))) (.getDate (now))))))
-(def app-state (atom {:notes [] :tab 0 }))
+(def app-state (atom {:entered [] :current { :tab 0 :title "" }}))

 (defn note-view  [note owner]
   (reify
@@ -38,6 +38,7 @@
    39 "right"
    40 "down"
    46 "del"
+   32 "space"
    186 ";"})

 (defn event-modifiers
@@ -72,8 +73,20 @@
     (if (and key (not (empty? key)) (not (some #{key} mod-keys)))
       (string/join "+" (conj mods key)))))

+(defn alphanumeric [s]
+  (re-find #"^[a-z0-9]+$" s))
+
 (defn handle-new-note-keydown [e app owner]
-  (let [code (event->key e)]
+  (let [code (event->key e)
+        which (.-which e)
+        key (.toLowerCase (js/String.fromCharCode which))]
+  (if (or (alphanumeric key) (== code "space"))
+  (do
+    (om/transact! app [:current :title]
+      #(str % key)
+      [:title nil])
+  )
+  (do
   (case code
     "enter"
     (do
@@ -82,24 +95,27 @@
         (let [new-note {:id (guid)
                         :title (.-value new-field)
                         :status "pending"
-                        :tab (:tab @app) }]
-          (om/transact! app :notes
+                        :tab (get-in @app [:current :tab])}]
+          (om/transact! app :entered
             #(conj % new-note)
-            [:create new-note]))
-        (set! (.-value new-field) "")))
+            [:create new-note])
+          (om/transact! app [:current :title]
+            (fn [_] "")
+            [:clean-current]))
+        ))
         false)
     "tab" (do
-      (om/transact! app :tab
+      (om/transact! app [:current :tab]
         #(+ % 1)
         [:tab nil])
       false)
     "shift+tab" (do
-      (om/transact! app :tab
+      (om/transact! app [:current :tab]
         #(- % 1)
         [:tab nil])
       false)
     nil
-    )))
+    )))))

 (defn focus-on-input [] (-> js/document (.querySelector "input") (.focus)))

@@ -121,7 +137,7 @@
       (let [delete (om/get-state owner :delete)]
         (go (loop []
               (let [note (<! delete)]
-                (om/transact! app :notes
+                (om/transact! app :entered
                   (fn [xs] (vec (remove #(= note %) xs))))
                 (recur))))))
     om/IRenderState
@@ -129,14 +145,15 @@
       (dom/div nil
         (dom/h1 nil app-title)
         (apply dom/ul nil
-          (om/build-all note-view (:notes app)
+          (om/build-all note-view (:entered app)
             {:init-state state}))
         (dom/ul nil
           (indent-element
             (dom/li nil (dom/input #js {:type "text" :ref "newNote"
               :placeholder "Enter note here..."
               :onKeyDown #(handle-new-note-keydown % app owner)
-            })) (:tab app)
+              :value (get-in app [:current :title])
+            })) (get-in app [:current :tab])
           )
           ;(dom/button #js {:onClick #(add-note app owner)} "Add note")
         )))))
@@ -148,7 +165,7 @@
   (let [new-note (-> (om/get-node owner "newNote")
                         .-value)]
     (when new-note
-      (om/transact! app :notes #(conj % new-note)))))
+      (om/transact! app :entered #(conj % new-note)))))

 (om/root
   (fn [app owner]


Completely rewrite logging namespace

Completely rewrite logging namespace

--- src/clojure/neko/log.clj
+++ src/clojure/neko/log.clj
@@ -10,64 +10,56 @@
 ; this software.

 (ns neko.log
-  "Utility for logging in Android.  To use this utility simply use the deflog
-  macro as follows:
+  "Utility for logging in Android. There are five logging macros: `i`,
+  `d`, `e`, `v`, `w`; for different purposes. Each of them takes
+  variable number of arguments and optional keyword arguments at the
+  end: `:exception` and `:tag`. If `:tag` is not provided, current
+  namespace is used instead. Examples:

-    (deflog \"MyTag\")
+    (require '[neko.log :as log])

-  This will intern a number of functions in your namespace, namely: log-d,
-  log-v, log-i, log-w, log-e, and log-wtf.  These functions will perform the
-  equivalent of calling the corresponding method on the android.util.Log
-  class with the tag given in the deflog macro call.
-
-  For example, given the above deflog call,
-
-    (log-d \"Some log string\")
-
-  is equivalent to:
-
-    (android.util.Log/d \"MyTag\" \"Some log string\")
-
-  Calls to log-wtf on platforms that do not support Log.wtf() will be
-  downgraded to Log.e() calls."
-  {:author "Daniel Solano Gómez"}
-  (:require neko.context)
+    (neko.log/d \"Some log string\" {:foo 1, :bar 2})
+    (neko.log/i \"Logging to custom tag\" [1 2 3] :tag \"custom\")
+    (neko.log/e \"Something went wrong\" [1 2 3] :exception ex)"
+  {:author "Adam Clements"}
   (:import android.util.Log))

-(defmacro deflogfn
-  "Macro for generating log functions."
-  {:private true}
-  [fn-name doc-string method-name]
-  `(defn ~fn-name
-     ~doc-string
-     ([^String tag#, ^String message#]
-      (. Log (~method-name tag# message#)))
-     ([^String tag#, ^String, message#, ^Throwable throwable#]
-      (. Log (~method-name tag# message# throwable#)))))
+(defn- logger [logfn args]
+  (let [[strings kwargs] (split-with (complement #{:exception :tag}) args)
+        {:keys [exception tag]} (if (odd? (count kwargs))
+                                  (butlast kwargs)
+                                  kwargs)
+        my-ns (str *ns*)]
+    (if exception
+      `(. Log ~logfn ~(or tag my-ns) (pr-str ~@strings) ~exception)
+      `(. Log ~logfn ~(or tag my-ns) (pr-str ~@strings)))))

-(deflogfn d "Sends a DEBUG log message." d)
-(deflogfn e "Sends a ERROR log message." e)
-(deflogfn i "Sends a INFO log message." i)
-(deflogfn v "Sends a VERBOSE log message." v)
-(deflogfn w "Sends a WARN log message." w)
+(defmacro e
+  "Log an ERROR message, applying pr-str to all the arguments and taking
+   an optional keyword :exception or :tag at the end which will print the
+   exception stacktrace or override the TAG respectively"
+  [& args] (logger 'e args))

-(defn log-exception
-  "Takes a Throwable instance and logs its stacktrace with error priority."
-  [throwable]
-  (e (.getPackageName neko.context/context)
-     (android.util.Log/getStackTraceString throwable)))
+(defmacro d
+  "Log a DEBUG message, applying pr-str to all the arguments and taking
+   an optional keyword :exception or :tag at the end which will print the
+   exception stacktrace or override the TAG respectively"
+  [& args] (logger 'd args))

-(defmacro deflog
-  "Creates a number of logging functions for the given tag."
-  [tag]
-  (let [intern-logger
-        (fn [log-name log-fn]
-          `(intern *ns* (symbol ~log-name)
-                   (-> (partial ~log-fn ~tag)
-                       (with-meta  {:private true}))))]
-    `(do
-       ~(intern-logger "log-d" `d)
-       ~(intern-logger "log-e" `e)
-       ~(intern-logger "log-i" `i)
-       ~(intern-logger "log-v" `v)
-       ~(intern-logger "log-w" `w))))
+(defmacro i
+  "Log an INFO message, applying pr-str to all the arguments and taking
+   an optional keyword :exception or :tag at the end which will print the
+   exception stacktrace or override the TAG respectively"
+  [& args] (logger 'i args))
+
+(defmacro v
+  "Log a VERBOSE message, applying pr-str to all the arguments and taking
+   an optional keyword :exception or :tag at the end which will print the
+   exception stacktrace or override the TAG respectively"
+  [& args] (logger 'v args))
+
+(defmacro w
+  "Log a WARN message, applying pr-str to all the arguments and taking
+   an optional keyword :exception or :tag at the end which will print the
+   exception stacktrace or override the TAG respectively"
+  [& args] (logger 'w args))


That's not how you spell stdout

That's not how you spell stdout

--- src/desdemona/launcher/launch_prod_peers.clj
+++ src/desdemona/launcher/launch_prod_peers.clj
@@ -11,7 +11,7 @@
             [desdemona.lifecycles.sample-lifecycle])
   (:gen-class))

-(defn standard-out-logger
+(defn stdout-logger
   "Logger to output on std-out, for use with docker-compose"
   [data]
   (let [{:keys [output-fn]} data]
@@ -24,11 +24,11 @@
                      (:peer-config config)
                      :onyx.log/config
                      {:appenders
-                      {:standard-out
+                      {:stdout
                        {:enabled? true,
                         :async? false,
                         :output-fn t/default-output-fn,
-                        :fn standard-out-logger}}})
+                        :fn stdout-logger}}})
         peer-group (onyx.api/start-peer-group peer-config)
         env (onyx.api/start-env (:env-config config))
         peers (onyx.api/start-peers n-peers peer-group)]


removed unused fn

removed unused fn

--- src/alibi/domain/entry_app_svc.clj
+++ src/alibi/domain/entry_app_svc.clj
@@ -27,11 +27,6 @@
         entry (entry/new-entry cmd :for-task task :for-project project)]
     (entry-repo/add-entry! entry)))

-(defn valid-billable-value-for-task?
-  [task-id billable?-value]
-  (let [task (task-repo/get task-id)]
-    (entry/valid-billable? billable?-value task)))
-
 (defn update-entry!
   [as-identity cmd]
   (let [task-id (:task-id cmd)


Make custom-emoji display in same dimensions as others

--- src/braid/emoji/client/styles.cljs
+++ src/braid/emoji/client/styles.cljs
@@ -1,17 +1,28 @@
 (ns braid.emoji.client.styles)

 (def autocomplete
-  [:.app>.main>.page>.threads>.thread>.card
-   [:>.message.new
-    [:>.autocomplete-wrapper
-     [:>.autocomplete
-      [:>.result
-       [:>.emoji.match
-        [:>img
-         {:display "block"
-          :width "2em"
-          :height "2em"
-          :float "left"
-          :margin "0.25em 0.5em 0.25em 0"}]]]]]]])
+  [:.app
+   [:.message>.content
+    [:.emoji.custom-emoji
+     {:width "3ex"
+      :height "3.1ex"
+      :min-height "20px"
+      :display "inline-block"
+      :margin [["-0.2ex" "0.15em" "0.2ex"]]
+      :line-height "normal"
+      :vertical-align "middle"
+      :min-width "20px"}]]
+   [:>.main>.page>.threads>.thread>.card
+    [:>.message.new
+     [:>.autocomplete-wrapper
+      [:>.autocomplete
+       [:>.result
+        [:>.emoji.match
+         [:>img
+          {:display "block"
+           :width "2em"
+           :height "2em"
+           :float "left"
+           :margin "0.25em 0.5em 0.25em 0"}]]]]]]]])


Fix link-finding exploding

Fix link-finding exploding

Perhaps as a result of the middleware refactor, the query-string
arguments aren't URL-decoded automatically anymore

--- src/braid/website_embeds/core.cljc
+++ src/braid/website_embeds/core.cljc
@@ -1,13 +1,16 @@
 (ns braid.website-embeds.core
   "If a message contains a link, displays a generic website embed"
   (:require
-    [braid.base.api :as base]
-    [braid.embeds.api :as embeds]
-    #?@(:cljs
-         [[braid.website-embeds.styles :as styles]
-          [braid.website-embeds.views :as views]]
-         :clj
-         [[braid.website-embeds.link-extract :as link-extract]])))
+   [braid.base.api :as base]
+   [braid.embeds.api :as embeds]
+   #?@(:cljs
+       [[braid.website-embeds.styles :as styles]
+        [braid.website-embeds.views :as views]]
+       :clj
+       [[braid.website-embeds.link-extract :as link-extract]]))
+  #?(:clj
+     (:import
+      (java.net URLDecoder))))

 (defn init! []
   #?(:cljs
@@ -21,5 +24,5 @@
        (base/register-public-http-route!
          [:get "/extract"
           (fn [request]
-            (let [url (get-in request [:params :url])]
+            (let [url (URLDecoder/decode (get-in request [:params :url]))]
               {:body (link-extract/extract url)}))]))))


Use arrays, magic sparkles for blake2b

Use arrays, magic sparkles for blake2b

--- src/caesium/crypto/generichash.clj
+++ src/caesium/crypto/generichash.clj
@@ -52,19 +52,15 @@
   want [[blake2b]]."
   ([buf msg]
    (blake2b-to-buf! buf msg {}))
-  ([buf msg {:keys [key salt personal]
-             :or {key (byte-array 0)}}]
+  ([buf msg {:keys [key salt personal] :or {key (bb/alloc 0)}}]
    (if (or salt personal)
      ;; You can't set the defaults in the argspec's destructuring form,
      ;; because you want to be able to differentiate between a salt that
      ;; wasn't passed and an empty salt, to call a different fn.
-     (let [salt (or salt (byte-array blake2b-saltbytes))
-           personal (or personal (byte-array blake2b-personalbytes))]
-       (.crypto_generichash_blake2b_salt_personal
-        b/sodium buf (bb/buflen buf) msg (bb/buflen msg) key (bb/buflen key)
-        salt personal))
-     (.crypto_generichash_blake2b
-      b/sodium buf (bb/buflen buf) msg (bb/buflen msg) key (bb/buflen key)))
+     (let [salt (or salt (bb/alloc blake2b-saltbytes))
+           personal (or personal (bb/alloc blake2b-personalbytes))]
+       (b/✨ blake2b-salt-personal buf msg key salt personal))
+     (b/✨ blake2b buf msg key))
    buf))

 (defn blake2b


Fix some cases where old git versions can cause unnecessary (re-)cloning

--- src/cider_ci/ex/git/repository.clj
+++ src/cider_ci/ex/git/repository.clj
@@ -68,14 +68,21 @@
   (git-fetch path (or proxy-url repository-url)))

 (defn- repository-includes-commit? [path commit-id]
-  (and (system/exec-with-success?  ["git" "cat-file" "-t" commit-id] {:dir path})
-       (system/exec-with-success?  ["git" "ls-tree" commit-id] {:dir path})))
+  (and (system/exec-with-success? ["git" "cat-file" "-t" commit-id]
+                                  {:dir path})
+       (system/exec-with-success? ["git" "ls-tree" commit-id]
+                                  {:dir path})))
+
+(defn- valid-git-repository? [repository-path]
+  (and (fs/directory? repository-path)
+       (system/exec-with-success?
+         ["git" "rev-parse" "--resolve-git-dir" "."]
+         {:dir repository-path})))

 (defn- initialize-or-update-if-required [repository proxy-url commit-id]
   (let [repository-path (:repository-path repository)
         repository-url (:repository-url repository)]
-    (when-not (system/exec-with-success?
-                ["git" "rev-parse" "--resolve-git-dir" repository-path])
+    (when-not (valid-git-repository? repository-path)
       (initialize-repo repository-url proxy-url repository-path))
     (loop [update-count 1]
       (when-not (repository-includes-commit? repository-path commit-id)
@@ -94,7 +101,6 @@
   inside is guaranteed to contain the commit-id."
   [repository-url proxy-url commit-id]
   (let [repository (get-or-create-repository repository-url)]
-    (logging/debug 'repository repository)
     (locking (:lock repository)
       (initialize-or-update-if-required repository proxy-url commit-id))
     repository))


Minor changes

Minor changes

--- src/clj/algorithms_clj/huffman.clj
+++ src/clj/algorithms_clj/huffman.clj
@@ -67,9 +67,8 @@
 (defn encode
   "Encode a stream, using the stream frequences to build the huffman tree"
   [values]
-  (-> (frequencies values)
-      (make-tree)
-      (encode-with values)))
+  (let [t (-> values frequencies make-tree)]
+    [t (encode-with t values)])) ;; TODO - Encode the tree as well

 ;; -----------------------------------------------------------


only render capacity if it's non-null

only render capacity if it's non-null

--- src/clj/backtype/storm/ui/core.clj
+++ src/clj/backtype/storm/ui/core.clj
@@ -379,7 +379,7 @@
   (link-to (url-format "/topology/%s/component/%s" storm-id id) id))

 (defn render-capacity [capacity]
-  [:span (if (> capacity 0.9)
+  [:span (if (and capacity (> capacity 0.9))
                {:class "red"}
                {})
          (float-str capacity)])


Refactored event-loop handler to top-level

--- src/clojure/frereth_cp/message.clj
+++ src/clojure/frereth_cp/message.clj
@@ -541,6 +541,146 @@
                           alt))
     actual-next))

+(declare schedule-next-timeout!)
+(defn action-trigger
+  [{:keys [::actual-next
+           ::delta_f
+           ::scheduling-time]
+    :as timing-details}
+   {:keys [::specs/message-loop-name]
+    :as io-handle}
+   state
+   success]
+  (let [prelog (utils/pre-log message-loop-name)  ; might be on a different thread
+        fmt (str "Interrupting event loop waiting for ~:d ms "
+                 "after ~:d at ~:d\n"
+                 "at ~:d because: ~a")]
+    (log/debug prelog
+               (cl-format nil
+                          fmt
+                          delta_f
+                          scheduling-time
+                          actual-next
+                          (System/nanoTime)
+                          success))
+    (let [tag (try (first success)
+                   (catch IllegalArgumentException ex
+                     (log/error ex
+                                prelog
+                                "Should have been a variant")
+                     ::no-op))
+          updater
+          (case tag
+            ::child-> (partial trigger-from-child io-handle (second success))
+            ::drained (do (log/warn prelog
+                                    ;; Actually, this seems like a strong argument for
+                                    ;; having a pair of streams. Child could still have
+                                    ;; bytes to send to the parent after the latter's
+                                    ;; stopped sending, or vice versa.
+                                    ;; I'm pretty sure the complexity I haven't finished
+                                    ;; translating stems from that case.
+                                    ;; TODO: Another piece to revisit once the basics
+                                    ;; work.
+                                    "Stream closed. Surely there's more to do")
+                          (constantly nil))
+            ::parent-> (partial trigger-from-parent
+                                io-handle
+                                (second success))
+            ::no-op identity
+            ::query-state (fn [state]
+                            (if-let [dst (second success)]
+                              (deliver dst state)
+                              (log/warn prelog "state-query request missing required deferred"))
+                            state)
+            ::timed-out (do
+                          (log/debug prelog
+                                     (cl-format nil
+                                                "Timer for ~:d ms after ~:d timed out. Re-triggering Output"
+                                                delta_f
+                                                scheduling-time))
+                          (partial trigger-from-timer io-handle)))]
+      (when (not= tag ::drained)
+        (log/debug prelog "Processing event")
+        ;; At the end of the main ioloop in the refernce
+        ;; implementation, there's a block that closes the pipe
+        ;; to the child if we're done.
+        ;; I think the point is that we'll quit polling on
+        ;; that and start short-circuiting out of the blocks
+        ;; that might do the send, once we've hit EOF
+        ;; Q: What can I do here to
+        ;; produce the same effect?
+        ;; TODO: Worry about that once the basic idea works.
+
+        ;; If the child sent a big batch of data to go out
+        ;; all at once, don't waste time setting up a timeout
+        ;; scheduler. The poll in the original would have
+        ;; returned immediately anyway.
+        ;; Except that n-sec-per-block puts a hard limit on how
+        ;; fast we can send.
+        (let [start (System/nanoTime)
+              ;; I'd prefer to do these next two
+              ;; pieces in a single step.
+              ;; But the fn passed to swap! must
+              ;; be functionally pure, which definitely
+              ;; is not the case with what's going on here.
+              ;; TODO: Break these pieces into something
+              ;; like the interceptor-chain idea. They should
+              ;; return a value that includes a key for a
+              ;; seq of functions to run to perform the
+              ;; side-effects.
+              ;; I'd still have to call updater, get
+              ;; that updating seq, update the state,
+              ;; and then modify the atom (well, modifying
+              ;; the atom first seems safer to avoid
+              ;; race conditions)
+              ;; Q: How long before I get bitten by that?
+              ;; Better Q: Is there a way to avoid it
+              ;; by scrapping the atom?
+              ;; A2: Yes. Add a ::get-state tag to
+              ;; the available tags above. The 'success'
+              ;; parameter is a deferred. (fulfill) that
+              ;; with the current state and go straight
+              ;; to scheduling the next timeout.
+              ;; Then the state atom turns into a normal
+              ;; value.
+              ;; TODO: That needs to happen fairly quickly.
+              ;; TODO: Read up on Executors. I could wind up
+              ;; with really nasty interactions now that I
+              ;; don't have an agent to keep this single-
+              ;; threaded.
+              ;; Actually, it should be safe as written.
+              ;; Just be sure to keep everything synchronized
+              ;; around takes from the i/o handle. (Not
+              ;; needing to do that manually is
+              ;; a great reason to not introduce a second
+              ;; one for bytes travelling the other direction)
+              state' (try (updater state)
+                          (catch RuntimeException ex
+                            (log/error ex "Running updater failed")
+                            ;; The eternal question in this scenario:
+                            ;; Fail fast, or hope we can keep limping
+                            ;; along?
+                            ;; TODO: Add prod vs. dev environment options
+                            ;; to give the caller control over what
+                            ;; should happen here.
+                            ;; (Note that, either way, it really should
+                            ;; include a callback to some
+                            ;; currently-undefined status updater
+                            (comment state)))
+              mid (System/nanoTime)
+              ;; This is taking a ludicrous amount of time.
+              ;; Q: How much should I blame on logging?
+              _ (schedule-next-timeout! io-handle state')
+              end (System/nanoTime)]
+          (log/debug prelog
+                     (cl-format nil
+                                (str
+                                 "Event handling took ~:d nanoseconds\n"
+                                 "Scheduling next timeout took ~:d  nanoseconds")
+                                (- mid start)
+                                (- end mid)))
+          nil)))))
+
 ;;; I really want to move schedule-next-timeout! to flow-control.
 ;;; But it has a circular dependency with trigger-from-timer.
 ;;; Which...honestly also belongs in there.
@@ -617,137 +757,12 @@
                                 recent
                                 now))
           (dfrd/on-realized next-action
-                            ;; TODO: Refactor this to top-level
-                            (fn [success]
-                              (let [prelog (utils/pre-log message-loop-name)  ; might be on a different thread
-                                    fmt (str "Interrupting event loop waiting for ~:d ms "
-                                             "after ~:d at ~:d\n"
-                                             "at ~:d because: ~a")]
-                                (log/debug prelog
-                                           (cl-format nil
-                                                      fmt
-                                                      delta_f
-                                                      now
-                                                      actual-next
-                                                      (System/nanoTime)
-                                                      success))
-                                (let [tag (try (first success)
-                                               (catch IllegalArgumentException ex
-                                                 (log/error ex
-                                                            prelog
-                                                            "Should have been a variant")
-                                                 ::no-op))
-                                      updater
-                                      (case tag
-                                        ::child-> (partial trigger-from-child io-handle (second success))
-                                        ::drained (do (log/warn prelog
-                                                                ;; Actually, this seems like a strong argument for
-                                                                ;; having a pair of streams. Child could still have
-                                                                ;; bytes to send to the parent after the latter's
-                                                                ;; stopped sending, or vice versa.
-                                                                ;; I'm pretty sure the complexity I haven't finished
-                                                                ;; translating stems from that case.
-                                                                ;; TODO: Another piece to revisit once the basics
-                                                                ;; work.
-                                                                "Stream closed. Surely there's more to do")
-                                                      (constantly nil))
-                                        ::parent-> (partial trigger-from-parent
-                                                            io-handle
-                                                            (second success))
-                                        ::no-op identity
-                                        ::query-state (fn [state]
-                                                        (if-let [dst (second success)]
-                                                          (deliver dst state)
-                                                          (log/warn prelog "state-query request missing required deferred"))
-                                                        state)
-                                        ::timed-out (do
-                                                      (log/debug prelog
-                                                                 (cl-format nil
-                                                                            "Timer for ~:d ms after ~:d timed out. Re-triggering Output"
-                                                                            delta_f
-                                                                            now))
-                                                      (partial trigger-from-timer io-handle)))]
-                                  (when (not= tag ::drained)
-                                    (log/debug prelog "Processing event")
-                                    ;; At the end of the main ioloop in the refernce
-                                    ;; implementation, there's a block that closes the pipe
-                                    ;; to the child if we're done.
-                                    ;; I think the point is that we'll quit polling on
-                                    ;; that and start short-circuiting out of the blocks
-                                    ;; that might do the send, once we've hit EOF
-                                    ;; Q: What can I do here to
-                                    ;; produce the same effect?
-                                    ;; TODO: Worry about that once the basic idea works.
-
-                                    ;; If the child sent a big batch of data to go out
-                                    ;; all at once, don't waste time setting up a timeout
-                                    ;; scheduler. The poll in the original would have
-                                    ;; returned immediately anyway.
-                                    ;; Except that n-sec-per-block puts a hard limit on how
-                                    ;; fast we can send.
-                                    (let [start (System/nanoTime)
-                                          ;; I'd prefer to do these next two
-                                          ;; pieces in a single step.
-                                          ;; But the fn passed to swap! must
-                                          ;; be functionally pure, which definitely
-                                          ;; is not the case with what's going on here.
-                                          ;; TODO: Break these pieces into something
-                                          ;; like the interceptor-chain idea. They should
-                                          ;; return a value that includes a key for a
-                                          ;; seq of functions to run to perform the
-                                          ;; side-effects.
-                                          ;; I'd still have to call updater, get
-                                          ;; that updating seq, update the state,
-                                          ;; and then modify the atom (well, modifying
-                                          ;; the atom first seems safer to avoid
-                                          ;; race conditions)
-                                          ;; Q: How long before I get bitten by that?
-                                          ;; Better Q: Is there a way to avoid it
-                                          ;; by scrapping the atom?
-                                          ;; A2: Yes. Add a ::get-state tag to
-                                          ;; the available tags above. The 'success'
-                                          ;; parameter is a deferred. (fulfill) that
-                                          ;; with the current state and go straight
-                                          ;; to scheduling the next timeout.
-                                          ;; Then the state atom turns into a normal
-                                          ;; value.
-                                          ;; TODO: That needs to happen fairly quickly.
-                                          ;; TODO: Read up on Executors. I could wind up
-                                          ;; with really nasty interactions now that I
-                                          ;; don't have an agent to keep this single-
-                                          ;; threaded.
-                                          ;; Actually, it should be safe as written.
-                                          ;; Just be sure to keep everything synchronized
-                                          ;; around takes from the i/o handle. (Not
-                                          ;; needing to do that manually is
-                                          ;; a great reason to not introduce a second
-                                          ;; one for bytes travelling the other direction)
-                                          state' (try (updater state)
-                                                      (catch RuntimeException ex
-                                                        (log/error ex "Running updater failed")
-                                                        ;; The eternal question in this scenario:
-                                                        ;; Fail fast, or hope we can keep limping
-                                                        ;; along?
-                                                        ;; TODO: Add prod vs. dev environment options
-                                                        ;; to give the caller control over what
-                                                        ;; should happen here.
-                                                        ;; (Note that, either way, it really should
-                                                        ;; include a callback to some
-                                                        ;; currently-undefined status updater
-                                                        (comment state)))
-                                          mid (System/nanoTime)
-                                          ;; This is taking a ludicrous amount of time.
-                                          ;; Q: How much should I blame on logging?
-                                          _ (schedule-next-timeout! io-handle state')
-                                          end (System/nanoTime)]
-                                      (log/debug prelog
-                                                 (cl-format nil
-                                                            (str
-                                                             "Event handling took ~:d nanoseconds\n"
-                                                             "Scheduling next timeout took ~:d  nanoseconds")
-                                                            (- mid start)
-                                                            (- end mid)))
-                                      nil)))))
+                            (partial action-trigger
+                                     {::actual-next actual-next
+                                      ::delta_f delta_f
+                                      ::scheduling-time now}
+                                     io-handle
+                                     state)
                             (fn [failure]
                               (log/error failure
                                          prelog


use ISO 8601 strings for dates rather than time since epoch.

--- src/coming_soon/models/contact.clj
+++ src/coming_soon/models/contact.clj
@@ -1,6 +1,8 @@
 (ns coming-soon.models.contact
   (require [taoensso.carmine :as car]
-           [coming-soon.config :as config]))
+           [coming-soon.config :as config])
+  (import java.text.SimpleDateFormat
+          java.util.Calendar))

 ;; A Redis connection with Carmine
 (def redis-pool (car/make-conn-pool))
@@ -54,6 +56,10 @@
   "Return a vector of all the emails that have been collected so far."
   [])

+;; ISO 8601 timestamp
+(defn current-timestamp []
+  (.format (SimpleDateFormat. "yyyy-MM-dd'T'HH:mm:ssZ") (.getTime (Calendar/getInstance))))
+
 ;; Store the contact by email and by id
 (defn store [id email referrer]
   (with-car
@@ -66,7 +72,7 @@
       {:id id
       :email email
       :referrer referrer
-      :updated-at (System/currentTimeMillis)}))
+      :updated-at (current-timestamp)}))
     ;; commit the transaction
     (car/exec)))


Start fleshing out channel function support.

--- src/afterglow/channels.clj
+++ src/afterglow/channels.clj
@@ -58,20 +58,27 @@

 ;; TODO: is this a good range data structure for finding which one a value falls into?
 (defn full-range
-  "Returns a range spefication that encompasses all possible DMX values as a single variable setting."
-  [range-type label]
+  "Returns a function spefication that encompasses all
+  possible DMX values as a single range."
+  [range-type function-type label]
   {:start 0
    :end 255
-   :type range-type
+   :range range-type
+   :type function-type
    :label label})

 (defn fine-channel
-  "Defines a channel for which sometimes multi-byte values are desired, via a separate
-channel which specifies the fractional value to be added to the main channel."
-  [chan-type offset & {:keys [fine-offset range-label]}]
-  (let [base (assoc (channel offset)
+  "Defines a channel for which sometimes multi-byte values are
+  desired, via a separate channel which specifies the fractional value
+  to be added to the main channel."
+  [chan-type offset & {:keys [fine-offset function-type function-name] :or {function-type chan-type}}]
+  {:pre (some? chan-type) (integer? offset) (<= 1 offset 512)}
+  (let [chan-type (keyword chan-type)
+        function-type (keyword function-type)
+        function-name (or function-name (clojure.string/capitalize (name function-type)))
+        base (assoc (channel offset)
                     :type chan-type
-                    :ranges [(full-range :variable (or range-label (clojure.string/capitalize (name chan-type))))])]
+                    :functions [(full-range :variable function-type function-name)])]
     (if fine-offset
       (assoc base :fine-offset fine-offset)
       base)))
@@ -84,10 +91,13 @@
    (fine-channel :dimmer offset :fine-offset fine-offset :range-label "Intensity")))

 (defn color
-  [offset kwd & {:keys [hue label]}]
-  (-> (fine-channel :color offset :range-label (or label (clojure.string/capitalize (name kwd))))
-      (assoc :color (keyword kwd))
-      (cond-> hue (assoc :hue hue))))
+  [offset color & {:keys [hue function-label fine-offset]}]
+  {:pre (some? color)}
+  (let [color (keyword color)]
+    (-> (fine-channel :color offset :fine-offset fine-offset
+                      :function-type color :function-label (or function-label (clojure.string/capitalize (name color))))
+        (assoc :color (keyword color))
+        (cond-> hue (assoc :hue hue)))))

 (defn pan
   ([offset]


Start out with rainbow cues saturated.

Start out with rainbow cues saturated.

The variable affects the rendering of the cue buttons now, but was not
getting set until one of them was pressed, so the animation was
initially invisible.

--- src/afterglow/examples.clj
+++ src/afterglow/examples.clj
@@ -367,6 +367,7 @@
     (global-color-cue "purple" 5 0 :include-color-wheels? true)
     (global-color-cue "white" 6 0 :include-color-wheels? true)

+    (show/set-variable! :rainbow-saturation 100)
     (ct/set-cue! (:cue-grid *show*) 0 1
                  (let [color-param (params/build-color-param :s :rainbow-saturation :l 50 :h hue-bar)]
                    (cues/cue :color (fn [_] (global-color-effect color-param))


Tweak cues.

Tweak cues.


do (take n (generate)) rather than (take n (generate-all): new way exposes more randomness hopefully

--- test/babel/test/it.cljc
+++ test/babel/test/it.cljc
@@ -114,19 +114,19 @@
     (is (empty?
          (filter #(not (nil? %))
                  (let [expressions
-                       (generate-all {:synsem {:cat :verb
-                                               :sem {:tense :present}
-                                               :subcat '()}}
-                                     small)]
+                       (take do-this-many
+                             (repeatedly
+                              #(first (generate {:synsem {:cat :verb
+                                                          :sem {:tense :present}
+                                                          :subcat '()}}
+                                                small))))]
                    (pmap (fn [expr]
                            (if (empty? (parse (fo expr) small))
                              (do
                                (log/error (str "failed to parse: " (fo expr)))
-                             {:fo (fo expr)
-                              :expr (get-in expr [:synsem :sem])
-                              :sem (get-in (first (parse (fo expr) small))
-                                           [:synsem :sem])})
+                               {:fo (fo expr)
+                                :expr (get-in expr [:synsem :sem])
+                                :sem (get-in (first (parse (fo expr) small))
+                                             [:synsem :sem])})
                              (log/info (str "parse OK:" (fo expr)))))
-                         (if (= do-this-many :all)
-                           expressions
-                           (take do-this-many expressions)))))))))
+                         expressions)))))))