Revert "Temporarily take out this test for CI."

This reverts commit 059d3133b8ca72db4f6b09b378835fbb384efd28.

error: pathspec 'test/onyx/coordinator/coordinator_ha_test.clj' did not match any file(s) known to git

--- test/onyx/coordinator/coordinator_ha_test.clj
+++ test/onyx/coordinator/coordinator_ha_test.clj
@@ -0,0 +1,148 @@
+(ns onyx.coordinator.coordinator-ha-test
+  (:require [midje.sweet :refer :all]
+            [zookeeper ]
+            [onyx.queue.hornetq-utils :as hq-util]
+            [onyx.api])
+  (:import [org.apache.curator.test TestingServer]
+           [org.hornetq.core.server.embedded EmbeddedHornetQ]))
+
+(def config (read-string (slurp (clojure.java.io/resource "test-config.edn"))))
+
+;; Need to run ZK and HQ away from the Coordinator because the
+;; Coordinator will start and stop these services as it
+;; comes on and offline.
+(def zk-server (TestingServer. (:spawn-port (:zookeeper config))))
+
+(def hq-server
+  (doto (EmbeddedHornetQ.)
+    (.setConfigResourcePath
+     (str (clojure.java.io/resource
+           (first (:configs (:hornetq config))))))
+    (.start)))
+
+(def n-messages 2000)
+
+(def batch-size 1320)
+
+(def echo 1000)
+
+(def id (str (java.util.UUID/randomUUID)))
+
+(def in-queue (str (java.util.UUID/randomUUID)))
+
+(def out-queue (str (java.util.UUID/randomUUID)))
+
+(def onyx-port (+ 10000 (rand-int 10000)))
+
+(def hq-config {"host" "localhost"
+                "port" 5445})
+
+(def coord-opts
+  {:hornetq/mode :udp
+   :hornetq.udp/cluster-name (:cluster-name (:hornetq config))
+   :hornetq.udp/group-address (:group-address (:hornetq config))
+   :hornetq.udp/group-port (:group-port (:hornetq config))
+   :hornetq.udp/refresh-timeout (:refresh-timeout (:hornetq config))
+   :hornetq.udp/discovery-timeout (:discovery-timeout (:hornetq config))
+   :zookeeper/address (:address (:zookeeper config))
+   :onyx/id id
+   :onyx.coordinator/host "localhost"
+   :onyx.coordinator/port onyx-port
+   :onyx.coordinator/revoke-delay 5000})
+
+(def peer-opts
+  {:hornetq/mode :udp
+   :hornetq.udp/cluster-name (:cluster-name (:hornetq config))
+   :hornetq.udp/group-address (:group-address (:hornetq config))
+   :hornetq.udp/group-port (:group-port (:hornetq config))
+   :hornetq.udp/refresh-timeout (:refresh-timeout (:hornetq config))
+   :hornetq.udp/discovery-timeout (:discovery-timeout (:hornetq config))
+   :zookeeper/address (:address (:zookeeper config))
+   :onyx/id id})
+
+(def onyx-server (onyx.api/start-distributed-coordinator coord-opts))
+
+(def onyx-port-2 (+ 10000 (rand-int 10000)))
+
+(def coord-opts-2
+  {:hornetq/mode :udp
+   :hornetq.udp/cluster-name (:cluster-name (:hornetq config))
+   :hornetq.udp/group-address (:group-address (:hornetq config))
+   :hornetq.udp/group-port (:group-port (:hornetq config))
+   :hornetq.udp/refresh-timeout (:refresh-timeout (:hornetq config))
+   :hornetq.udp/discovery-timeout (:discovery-timeout (:hornetq config))
+   :zookeeper/address (:address (:zookeeper config))
+   :onyx/id id
+   :onyx.coordinator/host "localhost"
+   :onyx.coordinator/port onyx-port-2
+   :onyx.coordinator/revoke-delay 5000})
+
+(def onyx-server-2 (future (onyx.api/start-distributed-coordinator coord-opts-2)))
+
+(def conn (onyx.api/connect :distributed coord-opts))
+
+(hq-util/create-queue! hq-config in-queue)
+(hq-util/create-queue! hq-config out-queue)
+
+(hq-util/write-and-cap! hq-config in-queue (map (fn [x] {:n x}) (range n-messages)) echo)
+
+(defn my-inc [{:keys [n] :as segment}]
+  (assoc segment :n (inc n)))
+
+(def catalog
+  [{:onyx/name :in
+    :onyx/ident :hornetq/read-segments
+    :onyx/type :input
+    :onyx/medium :hornetq
+    :onyx/consumption :concurrent
+    :hornetq/queue-name in-queue
+    :hornetq/host "localhost"
+    :hornetq/port 5445
+    :onyx/batch-size batch-size}
+
+   {:onyx/name :inc
+    :onyx/fn :onyx.coordinator.coordinator-ha-test/my-inc
+    :onyx/type :transformer
+    :onyx/consumption :concurrent
+    :onyx/batch-size batch-size}
+
+   {:onyx/name :out
+    :onyx/ident :hornetq/write-segments
+    :onyx/type :output
+    :onyx/medium :hornetq
+    :onyx/consumption :concurrent
+    :hornetq/queue-name out-queue
+    :hornetq/host "localhost"
+    :hornetq/port 5445
+    :onyx/batch-size batch-size}])
+
+(def workflow {:in {:inc :out}})
+
+(def v-peers (onyx.api/start-peers conn 1 peer-opts))
+
+(onyx.api/submit-job conn {:catalog catalog :workflow workflow})
+
+(Thread/sleep 7000)
+(onyx.api/stop-distributed-coordinator onyx-server)
+
+(def onyx-server (future (onyx.api/start-distributed-coordinator coord-opts)))
+(Thread/sleep 7000)
+
+(onyx.api/stop-distributed-coordinator onyx-server-2)
+
+(def results (hq-util/consume-queue! hq-config out-queue echo))
+
+(doseq [v-peer v-peers]
+  ((:shutdown-fn v-peer)))
+
+(onyx.api/shutdown conn)
+(onyx.api/stop-distributed-coordinator onyx-server)
+
+(.stop hq-server)
+(.stop zk-server)
+
+(let [expected (set (map (fn [x] {:n (inc x)}) (range n-messages)))]
+  (fact (set (butlast results)) => expected)
+  (fact (last results) => :done))
+
+


add ring middleware

add ring middleware

error: pathspec 'src/yeller_clojure_client/ring.clj' did not match any file(s) known to git

--- src/yeller_clojure_client/ring.clj
+++ src/yeller_clojure_client/ring.clj
@@ -0,0 +1,33 @@
+(ns yeller-clojure-client.ring
+  (:require [yeller-clojure-client :as yeller]
+            [ring.util.request :as req]))
+
+(defn format-extra [options request]
+  {:url (req/url request)
+   :custom-data
+   {:params (merge (:query-params request) (:form-params request))
+    :session (:session request)
+    :context (:yeller/context request)}
+   :environment (:environment options "development")})
+
+(defn wrap-ring
+  [handler options]
+  "wraps a ring handler in middleware that sends exceptions to yeller.
+   takes a map of options.
+   Required:
+   {:token \"your api token here\"}
+   Optional:
+   {:environment \"development\"} ; the name of the environment this ring app is running in
+
+   if you attach a :yeller/context key in your request map (that contains a further map) in your ring request,
+   then this middleware will send that off to yeller upon exceptions as well (useful for e.g. user ids, current
+   datomic T etc)."
+  (let [client (yeller/client options)]
+    (fn [request]
+      (try
+        (handler request)
+        (catch Throwable t
+          (yeller/report
+            client
+            t
+            (format-extra options request)))))))


Tests for sms routing

Tests for sms routing

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/turbovote-http-api.git/'


Problem: no Server implementation in Clojure

Solution: Implement minimal server.

Pitfalls: server relies on the changes that are not yet commited
to zproto that are currently being implemented by @mknoszlig. They
will be commited shortly

error: pathspec 'main/clojure/src/org/zproto/server.clj' did not match any file(s) known to git

--- main/clojure/src/org/zproto/server.clj
+++ main/clojure/src/org/zproto/server.clj
@@ -0,0 +1,137 @@
+(ns server
+  (:require [zeromq.zmq           :as zmq]
+            [org.zproto.hydra-msg :as msg])
+  (:import [org.zproto HydraMsg]))
+
+(alter-var-root #'*out* (constantly *out*))
+
+(declare get-latest-post)
+(declare get-all-tags)
+(declare get-single-tag)
+
+
+(defn get-latest-post
+  [& more]
+  ;;
+  ;; Fill in the blanks
+  ;;
+  (println more))
+(defn get-all-tags
+  [& more]
+  ;;
+  ;; Fill in the blanks
+  ;;
+  (println more))
+(defn get-single-tag
+  [& more]
+  ;;
+  ;; Fill in the blanks
+  ;;
+  (println more))
+
+
+(defprotocol HydraServer
+  (hello    [this])
+  (get-tags [this])
+  (get-tag  [this tag])
+  (get-post [this post-id])
+  (goodbye  [this])
+  (invalid  [this]))
+
+(defn next-state
+  [atom expected-state next-state]
+  (swap! atom
+         (fn [st]
+           (if (= st expected-state)
+             next-state
+             (throw (RuntimeException. (str "Expected to have "
+                                            expected-state
+                                            " state, but had "
+                                            next-state)))))))
+
+(defn ensure-state
+  [atom expected-state]
+  (when (not (= @atom expected-state))
+    (throw (RuntimeException.
+            (str "Expected to have "
+                 expected-state
+                 " state, but had "
+                 next-state)))))
+
+(defrecord Server [socket state]
+  HydraServer
+  (hello [this]
+    ;; (next-state state :start :connected)
+    (let [response (HydraMsg. HydraMsg/HELLO_OK)]
+      (get-latest-post response)
+      (.send response socket)
+      (.destroy response)
+      ))
+
+  (get-tags [this]
+    (ensure-state state :connected)
+    (let [response (HydraMsg. HydraMsg/GET_TAGS_OK)]
+      (get-all-tags response)
+      (.send response socket)
+      (.destroy response)
+      ))
+
+  (get-tag [this tag-id]
+    (ensure-state state :connected)
+    (let [response (HydraMsg. HydraMsg/GET_TAG_OK)]
+      (get-single-tag response tag-id)
+      (.send response socket)
+      (.destroy response)
+      ))
+
+  (invalid [this]
+    (.send (HydraMsg. HydraMsg/INVALID) socket))
+  )
+
+
+
+
+
+(defn match-msg
+  [server ^HydraMsg msg]
+  (let [id (.id msg)]
+    (println (= id HydraMsg/HELLO))
+    (cond
+     (= id HydraMsg/HELLO)    (hello    server)
+     (= id HydraMsg/GET_TAGS) (get-tags server)
+     (= id HydraMsg/GET_TAG)  (get-tag  server (.tag msg))
+
+     :default                 (invalid  server)
+
+     ;; HydraMsg/GET_POST
+     ;; HydraMsg/GET_POST_OK
+     ;; HydraMsg/GOODBYE
+     ;; HydraMsg/GOODBYE_OK
+     ;; HydraMsg/INVALID
+     ;; HydraMsg/FAILED
+     ))
+  )
+
+(defn server-loop
+  [socket]
+  (let [server (Server. socket (atom :start))]
+    (loop []
+      (when-let [received (msg/recv socket)]
+        (try
+          (match-msg server received)
+          (catch Exception e
+            (.printStackTrace e))
+          ))
+      (if (not (.isInterrupted (Thread/currentThread)))
+        (recur)
+        (println "Server shutdown")
+        ))))
+
+
+(comment
+  (def c (msg/client-socket "tcp://127.0.0.1:12345"))
+  (def s (msg/server-socket "tcp://127.0.0.1:12345"))
+  (def sf (future (server-loop s)))
+  (server-loop s)
+  (msg/hello c)
+  )


rm midi.soundfont dependency

rm midi.soundfont dependency

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jtbeckha/alda.git/'


finished player

finished player

error: pathspec 'src/frontend/player.cljs' did not match any file(s) known to git

--- src/frontend/player.cljs
+++ src/frontend/player.cljs
@@ -0,0 +1,26 @@
+(ns moomoo-frontend.player
+  (:require [moomoo-frontend.globals :as g]))
+
+(defn while-playing []
+  )
+
+(defn duration []
+  (if-let [current-sound (.getSoundById js/soundManager
+                                          (:current-sound-id @g/app-state))]
+    (.-duration current-sound)
+    0))
+
+(defn load-track! [sound-url sound-id on-load-callback]
+  (.createSound js/soundManager #js {:id sound-id
+                                     :url sound-url
+                                     :autoLoad true
+                                     :onload on-load-callback}))
+
+(defn play-track! [sound-id position on-finish-callback]
+  (swap! g/app-state assoc :current-sound-id sound-id)
+  (.play js/soundManager sound-id #js {:whileplaying while-playing
+                                       :onfinish on-finish-callback
+                                       :volume (:volume @g/app-state)
+                                       :from (if (> (duration) position)
+                                               position
+                                               (duration))}))


Add new outline branch.

Add new outline branch.

error: pathspec 'src/videotest/falling/outline_core.clj' did not match any file(s) known to git

--- src/videotest/falling/outline_core.clj
+++ src/videotest/falling/outline_core.clj
@@ -0,0 +1,176 @@
+(ns videotest.falling.outline-core
+  (:require
+   [clojure.math.numeric-tower :as math]
+   [quil.applet :as qa :refer [applet-close]]
+   [quil.core :as q]
+   [quil.middleware :as m]
+   [videotest.falling.cv :as cv]
+   [videotest.falling.cv-draw :as cv-draw]
+   [videotest.falling.triangles :as tri])
+  (:import
+   [org.opencv.core Core CvType Mat MatOfKeyPoint Point Size]
+   [org.opencv.features2d FeatureDetector KeyPoint]
+   [org.opencv.imgproc Imgproc]
+   [java.nio ByteBuffer ByteOrder]))
+
+(def CAM-DEV-NUM 0)
+(def CAM-SIZE (cv/camera-frame-size CAM-DEV-NUM))
+(def CAM-WIDTH  (int (:width  CAM-SIZE)))
+(def CAM-HEIGHT (int (:height CAM-SIZE)))
+
+;; Optoma Projector
+;; (def DISPLAY-WIDTH 1280.0)
+;; (def DISPLAY-HEIGHT 800.0)
+
+;; Lenovo
+(def DISPLAY-WIDTH 1366.0)
+(def DISPLAY-HEIGHT 768.0)
+
+;; pixCnt1 is the number of bytes in the pixel buffer
+;; pixCnt2 is the number of integers in the PImage pixels buffer
+(def PIX-CNT1 (int (* DISPLAY-WIDTH DISPLAY-HEIGHT 4)))
+(def PIX-CNT2 (int (* DISPLAY-WIDTH DISPLAY-HEIGHT)))
+
+(def ALPHA-STILL 100.0)
+
+;; 32 works
+(def NUM-COL-BINS 64.0)
+(def DISPLAY-BIN-SIZE (/ DISPLAY-WIDTH NUM-COL-BINS))
+(def NUM-ROW-BINS (/ DISPLAY-HEIGHT DISPLAY-BIN-SIZE))
+
+(def DISPLAY-BIN-SIZE-X2  (* DISPLAY-BIN-SIZE 2.0))
+(def DISPLAY-BIN-SIZE-2   (/ DISPLAY-BIN-SIZE 2.0))
+
+(def CAM-BIN-SIZE   (/ CAM-WIDTH NUM-COL-BINS))
+(def CAM-BIN-SIZE-2 (/ CAM-BIN-SIZE 2.0))
+(defn display->cam [display-x-or-y]
+  (* display-x-or-y (/ CAM-WIDTH DISPLAY-WIDTH)))
+
+
+(defn mat
+  ([]
+     (Mat.))
+  ([h w t]
+     (Mat. (int h) (int w) t)))
+
+;; bArray is the temporary byte array buffer for OpenCV cv::Mat.
+;; iArray is the temporary integer array buffer for PImage pixels.
+(defn setup []
+  (q/frame-rate 60)
+  (let [tri-pts     (tri/triangle-points NUM-COL-BINS NUM-ROW-BINS
+                                         DISPLAY-BIN-SIZE)
+        tri-orients (tri/triangle-orientations tri-pts)
+        tri-glyphs  (tri/triangle-glyphs DISPLAY-BIN-SIZE-X2
+                                         tri-pts tri-orients)]
+   {:b-array (byte-array PIX-CNT1)
+    :i-array (int-array PIX-CNT2)
+    :frame-mat  (mat CAM-HEIGHT CAM-WIDTH CvType/CV_8UC3)
+    :output-mat (mat DISPLAY-HEIGHT DISPLAY-WIDTH CvType/CV_8UC4)
+    :gray-mat   (mat)
+    :rgba-mat   (mat CAM-HEIGHT CAM-WIDTH CvType/CV_8UC4)
+    :drawn-mat  (mat DISPLAY-HEIGHT DISPLAY-WIDTH CvType/CV_8UC4)
+    :camera  (cv/camera CAM-DEV-NUM)
+    :p-image (q/create-image DISPLAY-WIDTH DISPLAY-HEIGHT :rgb)
+    :triangle-points       tri-pts
+    :triangle-orientations tri-orients
+    :triangle-glyphs       tri-glyphs
+    :color-record {}
+    :previous-color-record {}}))
+
+(defn update-rgba [{:keys [rgba-mat frame-mat] :as state}]
+  (assoc-in state [:rgba-mat] (cv/BGR->RGBA! frame-mat rgba-mat)))
+
+(defn update-color-record
+  [{:keys [triangle-points rgba-mat color-record] :as state}]
+  (let [cam->color (fn [col row]
+                     (let [cam-x (+ (* col CAM-BIN-SIZE)
+                                    CAM-BIN-SIZE-2)
+                           cam-y (+ (* row CAM-BIN-SIZE)
+                                    CAM-BIN-SIZE)]
+                       (vec (.get rgba-mat cam-y cam-x))))
+        colors (reduce (fn [memo [col row :as coords]]
+                         (assoc-in memo [coords] (cam->color col row)))
+                       {}
+                       (keys triangle-points))]
+    (assoc-in state [:color-record] colors)))
+
+(defn update-previous-color-record
+  [{:keys [color-record] :as state}]
+  (assoc-in state [:previous-color-record] color-record))
+
+(defn color-changed? [current-color previous-color]
+  (if (nil? previous-color)
+    false
+    (let [changes (map (comp math/abs -)
+                       current-color previous-color)
+          channel-change-big-enough? #(< 50 %)]
+      (some channel-change-big-enough? changes))))
+
+(defn color-with-alpha [c alpha]
+  (conj (vec (take 3 c)) alpha))
+
+(defn mosaic-color [current-color previous-color]
+  (if (color-changed? current-color previous-color)
+    current-color
+    (color-with-alpha current-color ALPHA-STILL)))
+
+(defn draw-mosaic
+  [tri-points tri-glyphs drawn-mat
+   color-record previous-color-record
+   [col row :as coords]]
+  (let [[display-x display-y] (tri-points coords)
+        c (mosaic-color (color-record coords) (previous-color-record coords))]
+    (cv-draw/draw-poly-with-pts drawn-mat c (tri-glyphs coords))))
+
+(defn overlay-triangles
+  [{:keys [drawn-mat rgba-mat triangle-points triangle-glyphs
+           color-record previous-color-record] :as state}]
+  (dorun
+   (map (partial draw-mosaic
+                 triangle-points
+                 triangle-glyphs
+                 drawn-mat
+                 color-record
+                 previous-color-record)
+        (keys triangle-points)))
+  state)
+
+(defn update-drawn-p-image [state]
+  (let [{:keys [drawn-mat output-mat b-array i-array]} state]
+    (update-in state [:p-image] #(cv/mat->p-img drawn-mat output-mat b-array i-array %))))
+
+(defn update [state]
+  (-> state
+      (cv/update-frame)
+      (update-rgba)
+      (update-color-record)
+      (overlay-triangles)
+      (update-drawn-p-image)
+      (update-previous-color-record)))
+
+(defn draw [state]
+  (let [{:keys [p-image frame-mat]} state]
+    (q/background 255)
+    (q/push-matrix)
+    (q/translate DISPLAY-WIDTH 0)
+    (q/scale -1 1)
+    (when p-image
+      (q/image p-image 0 0))
+    (q/pop-matrix)))
+
+(defn on-close
+  ([{:keys [camera]}]
+     (println "closing sketch and releasing camera...")
+     (when-not (nil? camera)
+       (.release camera))))
+
+(q/defsketch videotest
+  :title "Video Test"
+  :size [DISPLAY-WIDTH DISPLAY-HEIGHT]
+  :setup setup
+  :update update
+  :draw draw
+  :on-close on-close
+  :middleware [m/fun-mode])
+
+(.setResizable (.frame videotest) true)


[WM] revert update-in-response addition

[WM] revert update-in-response addition

The backend component isn't putting things under this key anyway. Maybe
it should?

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/voting-method-http-api.git/'


Adds functions to start and stop a server

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/govindkrjoshi/minesweeper.git/'


Cast no shadow

Cast no shadow


Refactor for better organization

Refactor for better organization

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bsmr-clojure/Nightcode.git/'


Improve maintainability by refactoring consumer to use RabbitMQ auto-recovery and Java concurrency patterns instead of using blocking consumer and manual channel recovery

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Yleisradio/rabid.git/'


add materials

add materials

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/kenichirow/recipes.git/'


Begin explorations of euler problems

Begin explorations of euler problems

error: pathspec 'src/shokunin/euler.clj' did not match any file(s) known to git

--- src/shokunin/euler.clj
+++ src/shokunin/euler.clj
@@ -0,0 +1,22 @@
+(ns shokunin.euler)
+
+;; sum of multiples of 3 and 5 < 1000
+;; https://projecteuler.net/problem=1
+
+(defn multiple-seq [n]
+  (iterate (partial + n) n))
+
+(let [x3 (take-while #(< % 1000) (multiple-seq 3))
+      x5 (take-while #(< % 1000) (multiple-seq 5))
+      xs (clojure.set/union (set x3) (set x5))]
+  (apply + xs))
+
+
+;; sum of even fibonacci numbers below 4 million
+;; https://projecteuler.net/problem=2
+
+(defn fib-seq
+  ([] (fib-seq 1 1))
+  ([x y] (cons x (lazy-seq (fib-seq y (+ x y))))))
+
+(def answer-2 (reduce + (filter even? (take-while (fn [x] (< x 4000000)) (fib-seq)))))


Added a time keeper namespace to allow programmatic control of time during tests.

error: pathspec 'src/cmr/common/time_keeper.clj' did not match any file(s) known to git

--- src/cmr/common/time_keeper.clj
+++ src/cmr/common/time_keeper.clj
@@ -0,0 +1,47 @@
+(ns cmr.common.time-keeper
+  "This namespace provides a function for retrieving the current time. This is used instead of
+  clj-time.core/now directly so that tests can have programmatic control over the current time."
+  (:require [clj-time.core :as t]
+            [cmr.common.log :refer (debug info warn error)]))
+
+(def time-override
+  "Contains the current time to return if it is overriden."
+  (atom nil))
+
+(defn set-current-time!
+  "Sets the current time. This should only be used for testing."
+  [t]
+  (warn "Current time set to" t)
+  (reset! time-override t))
+
+(defn freeze-time!
+  "Sets the current time to whatever the real time is now."
+  []
+  (set-current-time! (t/now)))
+
+(defn advance-time!
+  "Increases the time override by a number of seconds"
+  [num-secs]
+  (swap! time-override t/plus (t/seconds num-secs))
+  (warn "Current time set to" @time-override))
+
+(defn clear-current-time!
+  "Clears the current time if one was set."
+  []
+  (reset! time-override nil))
+
+(defn freeze-resume-time-fixture
+  "This is a clojure.test fixture that will freeze time then clear any time override at the end
+  of the test."
+  []
+  (fn [f]
+    (try
+      (freeze-time!)
+      (f)
+      (finally
+        (clear-current-time!)))))
+
+(defn now
+  "Returns the current time"
+  []
+  (or @time-override (t/now)))


JSON encoder for all errors

JSON encoder for all errors

Not just clojure.lang.ExceptionInfo

This is added so that JSON encoding errors don't mask underlying issues.

--- src/yada/body.clj
+++ src/yada/body.clj
@@ -291,11 +291,15 @@
    :id id
    :error error})

-(cheshire.generate/add-encoder clojure.lang.ExceptionInfo
+
+(cheshire.generate/add-encoder java.lang.Exception
                                (fn [ei jg]
                                  (cheshire.generate/encode-map
-                                  {:error (str ei)
-                                   :data (pr-str (ex-data ei))} jg)))
+                                  (merge
+                                   {:error (str ei)}
+                                   (when (instance? clojure.lang.ExceptionInfo ei)
+                                     {:data (pr-str (ex-data ei))}))
+                                  jg)))

 ;; TODO: Check semantics, is this right? Shouldn't we be encoding to json here?
 (defmethod render-error "application/json"


Removing vestiges of old API

Removing vestiges of old API

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/delitescere/yada.git/'


Extend walk over resources as well as handlers

--- src/yada/walk.clj
+++ src/yada/walk.clj
@@ -3,14 +3,17 @@
 (ns yada.walk
   (:require
    [clojure.walk :refer [postwalk]])
-  (:import [yada.handler Handler]))
+  (:import [yada.handler Handler]
+           [yada.resource Resource]))

 ;; Functions to update inner routes

 (defn update-routes [routes f & args]
   (postwalk
    (fn [x]
-     (if (instance? Handler x)
+     (if (or
+          (instance? Handler x)
+          (instance? Resource x))
        (apply f x args)
        x))
    routes))


Clapping Music example

Clapping Music example

... with apologies to Steve Reich.

error: pathspec 'src/overtone/examples/compositions/clapping_music.clj' did not match any file(s) known to git

--- src/overtone/examples/compositions/clapping_music.clj
+++ src/overtone/examples/compositions/clapping_music.clj
@@ -0,0 +1,39 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Something like Steve Reich - Clapping Music ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ns overtone.examples.compositions.clapping-music
+  (:use overtone.live))
+
+(def clap (sample (freesound-path 48310)))
+(def clap2 (sample (freesound-path 132676)))
+;(clap)
+;(clap2)
+
+(def pattern-1 [1 1 1 0 1 1 0 1 0 1 1 0])
+(def pattern-2 [1 1 1 0 1 1 0 1 0 1 1 0])
+
+(defn my-cycle []
+  (def pattern-2 (let [e (last pattern-2)
+                       p (drop-last pattern-2)]
+                   (vec (conj p e)))))
+
+(def m (metronome 32))
+
+(defn play-beat [beat]
+  (dorun (map (fn [i]
+                (let [v (if (= 0 (mod i 3)) 1 0.5)]
+                  (when (= (pattern-1 i) 1)
+                    (at (m (+ (/ i 12.0) beat)) (clap 1 0 0 (* v 0.33))))
+                  (when (= (pattern-2 i) 1)
+                    (at (m (+ (/ i 12.0) beat)) (clap2 1 0 0 v)))))
+              (range 12))))
+
+(defn player [beat]
+  (when (= (mod beat 4) 0) (my-cycle))
+  (play-beat beat)
+  (apply-at (m (inc beat)) #'player (inc beat) []))
+
+(player (m))
+
+;(defn player [beat] "stop")


Organizing thoughts and starting with first protocol.

error: pathspec 'src/overtone/protocols.clj' did not match any file(s) known to git

--- src/overtone/protocols.clj
+++ src/overtone/protocols.clj
@@ -0,0 +1,48 @@
+(ns overtone.protocols)
+
+; This is a holding ground for working on protocols...
+
+; One goal of defining protocols is to clearly delineate various groups of functionality
+; so we can improve the composability of the Overtone building blocks.  Our current synths
+; and instruments grew out of a desire to make things easy to use, but we need to refactor them
+; split-up unneccessarily bound aspects of functionality, and then create new "easy" APIs using
+; these refactored elements.
+
+; Synths:
+; * synth definition
+; * filling of default parameter settings from atoms
+; * mixed argument handling (in-order and/or keyword)
+; * implements trigger, ctl and kill for single synth instances
+; * places synth nodes in a default synth group
+
+; Instruments:
+; * allocates an output bus for all instances of the synth
+; * appends the out ugen to automatically use the correct bus
+; * allocates groups:
+;  - inst container group
+;  - instance group
+;  - fx group
+;  - mixer group
+; * implements ctl and kill for all instances
+
+; This bundling of functionality has led to two issues that demonstrate some of the problems:
+; * Many synth designs are very general purpose, and with different settings they can sound like completely different instruments.  Currently the only way to use the same instrument multiple times in Overtone with different parameter settings is to copy/paste the definst and give it a new name.  Instead we need a way to build a "voice" given a synth design and a set of parameters.
+
+; * Audio samples and the memory buffers they get loaded into can be single or multi-channel, but a synth design must have a fixed number of channels.  We need to define a protocol for triggering synths so we can implement higher level "meta-synths" that can actually use multiple underlying synths depending on the number of channels of the input buffer.  Many other kinds of meta-synths could exist that either generate synth definitions on the fly, or choose the most appropriate for the task.
+
+; To begin lets try to isolate this functionality into simpler units:
+; * synth definition
+; * mixed argument handling
+; * stored default parameters
+; * trigger, control and kill
+; * bus allocation and automatic append of out ugen
+; * automatic instance placement
+; * group allocation and fx/mixer setup
+
+
+(defprotocol ISynthNode
+  (trigger [this & params] "Instantiate the synth and start playing.")
+  (control [this & params] "Modify control parameters of a synth.")
+  (kill    [this] "Stop and delete a synth instance."))
+
+


Add very simple tests for bzzz.state

Add very simple tests for bzzz.state

error: pathspec 'test/bzzz/state_test.clj' did not match any file(s) known to git

--- test/bzzz/state_test.clj
+++ test/bzzz/state_test.clj
@@ -0,0 +1,43 @@
+(ns bzzz.state-test
+  (:use clojure.test
+        [bzzz.state :as state])
+  (:import (java.util HashMap Map))
+  (:import (bzzz.java.query TermPayloadClojureScoreQuery)))
+
+(defn ro-get-in [k & [default]]
+  (get-in TermPayloadClojureScoreQuery/EXPR_GLOBAL_STATE_RO k default))
+
+(defn ro-get-in-equals-to [k target]
+  (is (= (ro-get-in k) target)
+      (str "Global State: " TermPayloadClojureScoreQuery/EXPR_GLOBAL_STATE_RO)))
+
+(deftest test-state
+  (testing "cleanup the global RO state leaves no key behind"
+    (TermPayloadClojureScoreQuery/replace_expr_global_state_ro
+      (new java.util.HashMap))
+    (is (= TermPayloadClojureScoreQuery/EXPR_GLOBAL_STATE_RO {})))
+
+  (testing "merge overwrites keys with their values"
+    (state/ro-merge { :foo 1, :bar 2 })
+    (is (= (ro-get-in [:foo]) 1))
+    (is (= (ro-get-in [:bar]) 2)))
+
+  (testing "deep-merge keeps nested keys untouched"
+    (state/ro-deep-merge { :foo { :bar 1 }})
+    (state/ro-deep-merge { :foo { :baz 2 }})
+    (is (= (ro-get-in [:foo :bar]) 1) (str "Wrong nested key " TermPayloadClojureScoreQuery/EXPR_GLOBAL_STATE_RO))
+    (is (= (ro-get-in [:foo :baz]) 2) (str "Wrong nested key " TermPayloadClojureScoreQuery/EXPR_GLOBAL_STATE_RO))
+
+    (testing "while always the value associated if the key exists"
+      (state/ro-deep-merge { :foo { :baz 3 }})
+      (is (= (ro-get-in [:foo :baz]) 3) (str "Wrong nested key " TermPayloadClojureScoreQuery/EXPR_GLOBAL_STATE_RO))))
+
+  (testing "rename changes the key name"
+    (let [test-data {:foo 1, :bar 2, :baz 3}]
+      (state/ro-merge { :before-key test-data })
+      (is (= (ro-get-in [:before-key]) test-data))
+      (state/ro-rename-key :before-key :after-key)
+      (is (= (ro-get-in [:after-key]) test-data))
+
+      (testing "and makes sure the old key doesn't exist anymore"
+        (is (nil? (ro-get-in [:before-key])))))))


Create queue only once

Create queue only once

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/acessocard/microscope.git/'


Added list of supported languages for syntagmata.

error: pathspec 'src/hxgm30/language/syntagmata/lang/core.clj' did not match any file(s) known to git

--- src/hxgm30/language/syntagmata/lang/core.clj
+++ src/hxgm30/language/syntagmata/lang/core.clj
@@ -0,0 +1,19 @@
+(ns hxgm30.language.syntagmata.lang.core)
+
+(def supported
+  [:afrikaans
+   :arabic
+   :chinese
+   :english
+   :french
+   :german
+   :hindi
+   :japanese
+   :korean
+   :oldenglish
+   :oldnorse
+   :pie
+   :russian
+   :sanskrit
+   :scots
+   :spanish])


Abstract out function to calculate station coordinates.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/cstorey/nrod.git/'


Problem 0115.

Problem 0115.

error: pathspec 'src/project_euler/problem_0115.clj' did not match any file(s) known to git

--- src/project_euler/problem_0115.clj
+++ src/project_euler/problem_0115.clj
@@ -0,0 +1,64 @@
+(ns project-euler.problem_0115
+  (:require [clojure.math.combinatorics :as combo]))
+
+;; NOTE: This is a more difficult version of Problem 114.
+
+;; A row measuring n units in length has red blocks with a minimum length of m units placed on it,
+;; such that any two red blocks (which are allowed to be different lengths)
+;; are separated by at least one black square.
+
+;; Let the fill-count function, F(m, n), represent the number of ways that a row can be filled.
+
+;; For example, F(3, 29) = 673135 and F(3, 30) = 1089155.
+
+;; That is, for m = 3, it can be seen that n = 30 is the smallest value for which
+;; the fill-count function first exceeds one million.
+
+;; In the same way, for m = 10, it can be verified that F(10, 56) = 880711
+;; and F(10, 57) = 1148904,
+;; so n = 57 is the least value for which the fill-count function first exceeds one million.
+
+;; For m = 50, find the least value of n for which the fill-count function first exceeds one million.
+
+;;==================================================================
+
+;; From problem 0114 :
+;;------------------------------------------------------------------
+(defn compositions [n m]
+  (if (< n m)
+    [[]]
+    (concat (compositions n (inc m))
+            (map (fn [cs] (conj cs m))
+                 (compositions (- n m 1) m))
+            )))
+
+(def mz-count-positions
+  (memoize
+   (fn [n blks]
+     (if (empty? blks)
+       1
+       (let [nb (count blks)
+             sum (reduce + blks)
+             [b & bs] blks]
+         (->> (range (- n sum nb -2))
+              (map (fn [i] (mz-count-positions (- n b i 1) bs)))
+              (reduce +')
+              ))))))
+
+
+(defn count-ways [n m]
+  (->> (compositions n m)
+       (map (fn [blks]
+              (*' (combo/count-permutations blks)
+                  (mz-count-positions n blks))))
+       (reduce +')))
+
+;;-------------------------------------------------------------------
+
+(defn solution []
+  (->> (iterate inc 50)
+       (filter (fn [n] (> (count-ways n 50) 1000000)))
+       (first)
+       ))
+
+;;-> 168


fixed define-syntax

fixed define-syntax

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/parenode/clojure-cup-2015.git/'


Stop trying to make ping response text/plain

It didn't work and isn't a big deal really.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/turbovote-http-api.git/'


[WM] remove debugging println

[WM] remove debugging println

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/voting-method-works.git/'


chore(core): update core version to 2.8.22

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/B2W-BIT/restQL-server.git/'


Fixing restql-core-java integration.

Fixing restql-core-java integration.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/B2W-Digital/pdg-core.git/'


Version 106-SNAPSHOT

Version 106-SNAPSHOT

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/tobias/clojars-web.git/'


Version 0.19.0-SNAPSHOT

Version 0.19.0-SNAPSHOT

--- project.clj
+++ project.clj
@@ -1,4 +1,4 @@
-(defproject clojars-web "0.18.1"
+(defproject clojars-web "0.19.0-SNAPSHOT"
   :min-lein-version "2.0.0"
   :dependencies [[org.clojure/clojure "1.7.0"]
                  [org.clojure/tools.cli "0.2.1"]


Version 0.18.0

Version 0.18.0

--- project.clj
+++ project.clj
@@ -1,4 +1,4 @@
-(defproject clojars-web "0.18.0-SNAPSHOT"
+(defproject clojars-web "0.18.0"
   :dependencies [[org.clojure/clojure "1.6.0"]
                  [org.clojure/tools.cli "0.2.1"]
                  [yeller-clojure-client "1.2.1"]


Release 0.13.6

Release 0.13.6

--- project.clj
+++ project.clj
@@ -1,4 +1,4 @@
-(defproject clojars-web "0.13.5"
+(defproject clojars-web "0.13.6"
   :dependencies [[org.clojure/clojure "1.4.0"]
                  [org.clojure/tools.cli "0.2.1"]
                  [org.apache.maven/maven-model "3.0.4"


Bump version to 0.8.8-SNAPSHOT.

Bump version to 0.8.8-SNAPSHOT.

--- project.clj
+++ project.clj
@@ -1,4 +1,4 @@
-(defproject clojars-web "0.8.7"
+(defproject clojars-web "0.8.8-SNAPSHOT"
   :dependencies [[org.clojure/clojure "1.4.0"]
                  [org.clojure/tools.cli "0.2.1"]
                  [org.apache.maven/maven-model "3.0.4"


Upgraded flyway!

Upgraded flyway!

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/couragelabs/coachbot.git/'


update to newer packages

update to newer packages

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jeelabs/jet.git/'


Prepare to enumerate child components

Prepare to enumerate child components

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/kkroening/jackal.git/'


Added first draft of ancient-clj.repository namespace.

error: pathspec 'src/ancient_clj/repository.clj' did not match any file(s) known to git

--- src/ancient_clj/repository.clj
+++ src/ancient_clj/repository.clj
@@ -0,0 +1,31 @@
+(ns ^{ :doc "Repository Access for ancient-clj."
+       :author "Yannick Scherer" }
+  ancient-clj.repository)
+
+;; ## Protocol
+
+(defprotocol Repository
+  (retrieve-metadata-xml! [this group-id artifact-id]
+    "Retrieve the version metadata XML file as a String."))
+
+(extend-protocol Repository
+  clojure.lang.AFunction
+  (retrieve-metadata-xml! [f group-id artifact-id]
+    (f group-id artifact-id))
+  nil
+  (retrieve-metadata-xml! [this group-id artifact-id]
+    nil))
+
+;; ## Registry
+
+(defmulti create-repository
+  "Create entity implementing the `Repository` protocol for the given
+   repository map (`:url`, `:credentials`, ...)"
+  (fn [{:keys [^String url] :as options}]
+    (when url
+      (let [i (.indexOf url ":/")]
+        (when-not (neg? i)
+          (.substring url 0 i)))))
+  :default nil)
+
+(defmethod create-repository nil [& _] nil)


Move anti_forgery ns from repository to here

error: pathspec 'src/cider_ci/auth/anti_forgery.clj' did not match any file(s) known to git

--- src/cider_ci/auth/anti_forgery.clj
+++ src/cider_ci/auth/anti_forgery.clj
@@ -0,0 +1,70 @@
+; Copyright © 2013 - 2016 Dr. Thomas Schank <Thomas.Schank@AlgoCon.ch>
+; Licensed under the terms of the GNU Affero General Public License v3.
+; See the "LICENSE.txt" file provided with this software.
+
+
+(ns cider-ci.auth.anti-forgery
+  (:require
+    [crypto.random]
+
+    [clj-logging-config.log4j :as logging-config]
+    [clojure.tools.logging :as logging]
+    [logbug.debug :as debug :refer [I> I>>]]
+    [logbug.ring :refer [wrap-handler-with-logging]]
+    [logbug.thrown :as thrown]
+    ))
+
+(def cookie-name "cider-ci_anti-forgery-token")
+
+(defn cookie [token]
+  {:value token
+   :http-only true
+   :path "/"
+   })
+
+(defn- session-authentication? [request]
+  (not (#{"basic-auth"}
+         (-> request :authenticated-user :authentication-method))))
+
+(defn- safe-request? [request]
+  (boolean
+    ( #{:get :head :options :trace} (-> request :request-method keyword))))
+
+(defn- valid-forgery-protection? [token request]
+  (boolean
+    (when token
+      (when-let [x-csrf-header-token (-> request :headers (get "x-csrf-token"))]
+        (= x-csrf-header-token token)))))
+
+(def response-401
+  (let [msg "Mutating requests must either authenticate
+            with basic authentication or provide a valid
+            X-CSRF-TOKEN header!"]
+    response-401 {:status 401
+                  :body msg
+                  :headers {"WWW-Authenticate" msg}}))
+
+(defn process [request handler]
+  (logging/info request)
+  (let [token (or (-> request :cookies (get cookie-name nil) :value)
+                  (crypto.random/base64 32))
+        resp (if (and (not (safe-request? request))
+                      (not (valid-forgery-protection? token request))
+                      (session-authentication? request))
+               response-401
+               (handler (assoc-in request [:cider-ci_anti-forgery-token] token))
+               )]
+    (if (and (session-authentication? request)
+             (not (valid-forgery-protection? token request)))
+      (assoc-in resp [:cookies cookie-name] (cookie token))
+      resp)))
+
+(defn wrap [handler]
+  (fn [request]
+    (process request handler)))
+
+;#### debug ###################################################################
+;(logging-config/set-logger! :level :debug)
+;(logging-config/set-logger! :level :info)
+;(debug/debug-ns 'cider-ci.auth.anti-forgery)
+;(debug/debug-ns *ns*)


Add project.clj

Add project.clj

error: pathspec 'project.clj' did not match any file(s) known to git

--- project.clj
+++ project.clj
@@ -0,0 +1,7 @@
+(defproject clojure-tips "0.1.0-SNAPSHOT"
+  :description "FIXME: write description"
+  :url "http://example.com/FIXME"
+  :license {:name "Eclipse Public License"
+            :url "http://www.eclipse.org/legal/epl-v10.html"}
+  :dependencies [[org.clojure/clojure "1.6.0"]
+                 [org.clojure/core.async "0.1.346.0-17112a-alpha"]])


WIP Begin refactor of dispatch.cljs. References #11.

--- src/cljs/library/dispatch.cljs
+++ src/cljs/library/dispatch.cljs
@@ -90,3 +90,65 @@
      (when-let [fns (get-listeners id)]
        (doseq [f (map :fn fns)] (f id data))
        (collect-garbage fns))))
+
+
+
+
+
+(comment
+
+  (do
+    (let [recorded-reactions (atom [])
+          reaction (react-to #{:do-something} #(swap! recorded-reactions conj [%1 %2]))]
+      ;; Did we get a reaction back?
+      (assert reaction)
+      (fire :do-something)
+      ;; Did the reactor catch the event?
+      (assert (= [[:do-something nil]] @recorded-reactions))
+      (fire :do-something)
+      ;; Did the reactor catch the event a second time?
+      (assert (= [[:do-something nil] [:do-something nil]] @recorded-reactions))
+      (fire :something-else)
+      ;; Did we ignore events we're not reacting to?
+      (assert (= [[:do-something nil]] @recorded-reactions))
+      (reset! recorded-reactions [])
+      (fire :do-something 17)
+      ;; Does event data arrive intact?
+      (assert (= [[:do-something 17]] @recorded-reactions))
+      (reset! recorded-reactions [])
+      (delete-reaction reaction)
+      (fire :do-something 17)
+      ;; Does deleting a reaction cause us to stop reacting?
+      (assert (= [] @recorded-reactions)))
+    (let [recorded-reactions (atom #{})
+          reaction-once (react-to 1 #{:do-something}
+                                  #(swap! recorded-reactions conj [1 %1 %2]))
+          reaction-twice (react-to 2 #{:do-something}
+                                   #(swap! recorded-reactions conj [2 %1 %2]))]
+      (fire :do-something 1)
+      ;; Did both reactions react?
+      (assert (= #{[1 :do-something 1] [2 :do-something 1]} @recorded-reactions))
+      (fire :do-something 2)
+      ;; Did only the second reaction react?
+      (assert (= #{[1 :do-something 1] [2 :do-something 1] [2 :do-something 2]}
+                 @recorded-reactions))
+      (fire :do-something 3)
+      ;; Did nothing change?
+      (assert (= #{[1 :do-something 1] [2 :do-something 1] [2 :do-something 2]}
+                 @recorded-reactions)))
+    true
+    )
+
+;;  (is (= (fire :)))
+
+;;  (fire :do-something {:a :b}) ;; event-id, event-data
+;; ;;  (fire [:something-else 2])   ;; event-id
+
+;;   (react-to #{:do-something} #(...reactor...)) ;; event-pred, reactor function
+  ;;=> #<Reaction:0xabcdef01>
+
+  ;; (react-to 1 #{...} #)
+
+  ;; (delete-reaction #<Reaction:0xabcdef01>) ;; reaction
+  ;; 2
+  )


runs query after clicking range set button

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/starcity-properties/odin.git/'


check-for-urls should run before check-for-low-cardinality

--- src/metabase/driver/generic_sql/sync.clj
+++ src/metabase/driver/generic_sql/sync.clj
@@ -121,9 +121,9 @@
     (->> (sel :many Field :table_id table-id :active true)
          (pmap (fn [field]
                  (try
+                   (check-for-urls korma-table field)
                    (check-for-low-cardinality korma-table field)
                    (check-for-large-average-length korma-table field)
-                   (check-for-urls korma-table field)
                    (catch Throwable e
                      (log/warn (format "Caught exception when syncing field '%s.%s':" table-name (:name field)) e)))))
          dorun)