use real ip for rollbar

use real ip for rollbar

--- src/pc/rollbar.clj
+++ src/pc/rollbar.clj
@@ -80,7 +80,7 @@
   (merge {:request {:url (url-for request)
                     :method (str/upper-case (name request-method))
                     :query_string (or query-string "")
-                    :user_ip remote-addr}}
+                    :user_ip (get-in request [:headers "x-real-ip"] (:remote-addr request))}}
          (when (seq cust)
            {:person {:id (str (:cust/uuid cust))
                      :email (:cust/email cust)}})))


Update package litedocx.wpml

Update package litedocx.wpml

--- src/bakyeono/litedocx/wpml.clj
+++ src/bakyeono/litedocx/wpml.clj
@@ -281,31 +281,31 @@
   Examples:
   - TODO"
   [{:as params
-    :keys [page-width page-height page-orientation
-           page-margin-top page-margin-bottom page-margin-left page-margin-right
-           page-margin-header page-margin-footer
-           body]}]
-  ;; Document
-  (node :w:document word-xmlns
-        ;; w:body
-        (node :w:body {}
-              body
-              ;; Document Final Section Properties
-              (node :w:sectPr {}
-                    ;; Page Size
-                    (node :w:pgSz {:w:w (unit/convert page-width "dxa") ; Page Width
-                                                                        ; (default: 11907)
-                                   :w:h (unit/convert page-height "dxa") ; Page Height
-                                                                         ; (default: 16839)
-                                   :w:orient (name page-orientation) ; Page Orientation
-                                   :w:code 9}) ; Printer Paper Code
-                    ;; Page Margins
-                    (node :w:pgMar {:w:top page-margin-top ; (default: 720)
-                                    :w:bottom page-margin-bottom ; (default: 720)
-                                    :w:left page-margin-left ; (default: 720)
-                                    :w:right page-margin-right ; (default: 720)
-                                    :w:header page-margin-header ; (default: 0)
-                                    :w:footer page-margin-footer}))))) ; (default: 0)
+    :keys [page-width page-height page-orientation page-margin body]}]
+  (let [page-width (or page-width "210mm")
+        page-height (or page-height "297mm")
+        page-orientation (or page-orientation "portrait")
+        ;; TODO: Create page-margin maker.
+        page-margin (or page-margin [720 720 720 720 0 0])]
+    ;; Document
+    (node :w:document word-xmlns
+          ;; w:body
+          (node :w:body {}
+                body
+                ;; Document Final Section Properties
+                (node :w:sectPr {}
+                      ;; Page Size
+                      (node :w:pgSz {:w:w (unit/convert page-width "dxa") ; Page Width
+                                     :w:h (unit/convert page-height "dxa") ; Page Height
+                                     :w:orient (name page-orientation) ; Page Orientation
+                                     :w:code 9}) ; Printer Paper Code
+                      ;; Page Margins
+                      (node :w:pgMar {:w:top (get page-margin 0)
+                                      :w:bottom (get page-margin 1)
+                                      :w:left (get page-margin 2)
+                                      :w:right (get page-margin 3)
+                                      :w:header (get page-margin 4)
+                                      :w:footer (get page-margin 5)}))))))

 (defn- run-properties
   "Creates w:rPr tag node. Called by paragaph-style."


Add nav-switch experiments, updated dm experiments.

--- src/angelic/scripts/experiments_11aaai.clj
+++ src/angelic/scripts/experiments_11aaai.clj
@@ -4,6 +4,7 @@
            [angelic [env :as env] [hierarchy :as hierarchy] [sas :as sas]]
            [angelic.search.textbook :as textbook]
            [angelic.domains.discrete-manipulation :as dm]
+           [angelic.domains.nav-switch :as ns]
            [angelic.search.implicit
             [ah-astar :as aha]
             [sahtn :as sahtn]
@@ -14,7 +15,7 @@

 (def alg-forms
-     {:sahtn   (fn [m] `(sahtn/sahtn ~'init #{:nav :reach :discretem-tla}))
+     {:sahtn   (fn [m] `(sahtn/sahtn ~'init #{:nav :reach :discretem-tla '~'top '~'navh '~'navv}))
       :aha*    (fn [m] `(aha/ah-a* ~'init true))
       :dash-a* (fn [m] `(dao/implicit-dash-a*-opt ~'init :gather true :d true :s :eager :dir :right
                                                   :choice-fn rand-nth))})
@@ -68,6 +69,33 @@
           (util/sref-get hierarchy/*ref-counter*)
           (util/sref-get hierarchy/*plan-counter*)))

+(defmacro defresults [name es-maker]
+  (let [sn (util/symbol-cat '* name '-results*)]
+    `(do (defonce ~sn nil)
+         (defn ~(util/symbol-cat 'read- name '-results) []
+           (def ~sn
+                (experiments/experiment-set-results->dataset
+                 (experiments/read-experiment-set-results (~es-maker))))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;; Nav Switch experiments ;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+(defn make-ns-exp-set []
+  (experiments/make-experiment-set "11aaai-ns"
+    [:product
+     [:size   [5 10 20 50 100 200 500]]
+     [:alg    (keys alg-forms)]
+     [:rand   [0 1 2]]]
+    (fn [m]
+      `(ns/make-nav-switch-hierarchy
+        (ns/make-random-nav-switch-env ~(:size m) 20 ~(:rand m)) true))
+    (fn [m] ((util/safe-get alg-forms (:alg m)) m))
+    'angelic.scripts.experiments-11aaai 10 3600 512 false ::ExpResult))
+
+(defresults ns make-ns-exp-set)
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;; Discrete manipulation experiments ;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -77,13 +105,13 @@
   (experiments/make-experiment-set "11aaai-dm"
     [:product
      [:objects [1 2 3 4 5 6]]
-     [:rand    [1 2 3]]
-     [:alg     (keys alg-forms)]]
+     [:alg     (keys alg-forms)]
+     [:rand    [0 1 2 3 4]]]
     (fn [m]
       `(dm/make-discrete-manipulation-hierarchy
         (dm/make-random-hard-discrete-manipulation-env ~(:objects m) ~(:rand m))))
     (fn [m] ((util/safe-get alg-forms (:alg m)) m))
-    'angelic.scripts.experiments-11aaai 10 3600 512 false ::ExpResult))
+    'angelic.scripts.experiments-11aaai 10 3600 1024 false ::ExpResult))

 (defn make-dm-lama-exp-set []
   (experiments/make-experiment-set "11aaai-dm-lama"
@@ -96,13 +124,6 @@
     (fn [m] `(dm/solve-dmh-lama ~'init))
     'angelic.scripts.experiments-11aaai nil 3600 512 false ::ExpResult))

-(defmacro defresults [name es-maker]
-  (let [sn (util/symbol-cat '* name '-results*)]
-    `(do (defonce ~sn nil)
-         (defn ~(util/symbol-cat 'read- name '-results) []
-           (def ~sn
-                (experiments/experiment-set-results->dataset
-                 (experiments/read-experiment-set-results (~es-maker))))))))

 (defresults dm make-dm-exp-set)
 (defresults dm-lama make-dm-lama-exp-set)
@@ -113,6 +134,12 @@

 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;  Discrete charts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;

+
+(defn median-of [n items]
+  (assert (odd? n))
+  (let [ind (quot n 2)]
+    (when (> (count items) ind)
+      (nth (sort items) ind))))

 (defn make-dm-charts
@@ -122,7 +149,7 @@
                                ["time"  "runtime (s)" :secs]]]
        (-> (datasets/ds-summarize
             (datasets/ds-derive #(/ (:ms %) 1000) (filter :ms *dm-results*) :secs)
-            [:objects :alg] [[field util/mean field]])
+            [:objects :alg] [[field (fn [& args] (median-of 3 args)) #_util/mean field]])
            (datasets/ds->chart
             [:alg] :objects field
             {:term "solid dashed size 2.0,1.5"


Added a simple with-transaction macro to the datastore service.

--- src/appengine_magic/services/datastore.clj
+++ src/appengine_magic/services/datastore.clj
@@ -4,15 +4,31 @@
             Entity]))

-(defonce *default-datastore-service* (atom nil))
+(defonce *datastore-service* (atom nil))
+
+(defonce *current-transaction* nil)

-(defn- get-datastore-service [& {:keys [service]}]
-  (if (nil? service)
-      (do (when (nil? @*default-datastore-service*)
-            (reset! *default-datastore-service* (DatastoreServiceFactory/getDatastoreService)))
-          @*default-datastore-service*)
-      service))
+(defn get-datastore-service []
+  (when (nil? @*datastore-service*)
+    (reset! *datastore-service* (DatastoreServiceFactory/getDatastoreService)))
+  @*datastore-service*)
+
+
+(defprotocol EntityProtocol
+  "Entities are Clojure records which conform to the EntityProtocol. Each Entity
+   must have a key. If an entity record field has a :key metadata tag, then that
+   field becomes the key. If a record has no :key metadata tags, then a key is
+   automatically generated for it. In either case, the key becomes part of the
+   entity's metadata. Entity retrieval operations must set the :key metadata on
+   returned entity records."
+  (get-key-object [this] [this parent]
+    "Returns nil if no tag is specified in the record definition, and no :key
+     metadata exists. Otherwise returns a Key object. Specify optional entity
+     group parent.")
+  (save! [this] [this parent]
+    "Writes the given entity to the data store. Specify optional entity group
+    parent."))

 (defn- unqualified-name [sym]
@@ -49,22 +65,6 @@
          :else v)))

-(defprotocol EntityProtocol
-  "Entities are Clojure records which conform to the EntityProtocol. Each Entity
-   must have a key. If an entity record field has a :key metadata tag, then that
-   field becomes the key. If a record has no :key metadata tags, then a key is
-   automatically generated for it. In either case, the key becomes part of the
-   entity's metadata. Entity retrieval operations must set the :key metadata on
-   returned entity records."
-  (get-key-object [this] [this parent]
-    "Returns nil if no tag is specified in the record definition, and no :key
-     metadata exists. Otherwise returns a Key object. Specify optional entity
-     group parent.")
-  (save! [this] [this parent]
-    "Writes the given entity to the data store. Specify optional entity group
-    parent."))
-
-
 (defn get-key-object-helper [entity-record key-property datastore-entity-name &
                              {:keys [parent]}]
   (let [entity-record-metadata (meta entity-record)
@@ -75,14 +75,14 @@
      ;; neither exists: autogenerate
      (and (nil? key-property-value) (nil? metadata-key-value))
      nil
+     ;; metadata key exists
+     (and (not (nil? metadata-key-value)) (instance? Key metadata-key-value))
+     metadata-key-value
      ;; key property exists
      (not (nil? key-property-value))
      (if parent
          (KeyFactory/createKey (get-key-object parent) datastore-entity-name key-property-value)
          (KeyFactory/createKey datastore-entity-name key-property-value))
-     ;; metadata key exists
-     (and (not (nil? metadata-key-value)) (instance? Key metadata-key-value))
-     metadata-key-value
      ;; something's wrong
      :else (throw (RuntimeException.
                    "entity has no valid :key metadata, and has no fields marked :key")))))
@@ -142,3 +142,17 @@
          (save!-helper this# ~datastore-entity-name))
        (save! [this# parent#]
          (save!-helper this# ~datastore-entity-name :parent parent#)))))
+
+
+;;; Note that the code relies on the API's implicit transaction tracking, and
+;;; the *current-transaction* value is not used. Making it available just in
+;;; case.
+(defmacro with-transaction [& body]
+  `(binding [*current-transaction* (.beginTransaction (get-datastore-service))]
+     (try
+       (let [body-result# ~@body]
+         (.commit *current-transaction*)
+         body-result#)
+       (catch Throwable err#
+         (do (.rollback *current-transaction*)
+             (throw err#))))))


Log both snapshots and new messages; change format

--- src/org/timmc/heim_logger.clj
+++ src/org/timmc/heim_logger.clj
@@ -1,8 +1,9 @@
 (ns org.timmc.heim-logger
-  (:require [clojure.string :as str]
-            [aleph.http :as ah]
-            [manifold.stream :as s]
-            [cheshire.core :as json]))
+  (:require [aleph.http :as ah]
+            [cheshire.core :as json]
+            [clojure.java.io :as io]
+            [clojure.string :as str]
+            [manifold.stream :as s]))

 (defn pencode
   "Percent-encode for URLs (conservatively.)"
@@ -117,38 +118,82 @@
 (defn do-nothing
   [_session _msg])

-(defn log-message
-  "Log one message to file."
-  [session msg]
+(defn debug-print-message
+  [_session msg]
+  #_(println msg))
+
+(defn log-messages
+  "Log message datas to file."
+  [session event-datas]
   (let [w (-> session :state deref :logger :writer)]
     (binding [*out* w]
-      (prn msg))
+      (doseq [m event-datas]
+        (println (json/generate-string {:hl-type "message"
+                                        :data m}))))
     (.flush w)))
+
+(defn log-send-event
+  "Log a single new message to file."
+  [session send-msg]
+  (log-messages session [(get-in send-msg [:data])]))
+
+(defn log-snapshot-event
+  "Log an entire snapshot to file."
+  [session snapshot-msg]
+  (log-messages session (get-in snapshot-msg [:data :log])))

 (def logger-dispatch
   "Reactive dispatch overlay for logger."
   (merge
    (base-dispatch "hillbot")
    {"send-event"
-    #'log-message
+    #'log-send-event
+
+    "snapshot-event"
+    #'log-snapshot-event

     :unknown-type
     ;; (fn [session msg]
     ;;   (println "Unknown event type:" (:type msg)))
     do-nothing

+    :pre
+    #'debug-print-message
+
     :halt
     (fn [session _msg]
       (some-> session :state deref :logger :writer .close))}))

+;; Consider exporting to org.timmc/handy
+(defn max-1
+  "Find the largest element in coll with respect to the comparator,
+yielding it as the single element of a collection. When coll is
+empty, yields nil."
+  [comparator coll]
+  (when (seq coll)
+    (reduce (fn [accum item]
+              (if (neg? (comparator accum item))
+                item
+                accum))
+            (first coll)
+            (rest coll))))
+
+;; FIXME unused
+(defn find-last-logged
+  "Find the most recently logged message."
+  [logfile]
+  (with-open [rdr (io/reader logfile :encoding "UTF-8")]
+    (max-1 (fn compare-times [x y]
+             (- (get-in x ["data" "time"])
+                (get-in y ["data" "time"])))
+           (json/parsed-seq rdr))))
+
 (defn -main
   "Demo connection to heim"
-  [server room outfile]
-  (let [w (-> (java.io.FileOutputStream. outfile true)
-              (java.io.OutputStreamWriter. "UTF-8")
-              (java.io.BufferedWriter.))
+  [server room logfile]
+  (let [w (io/writer logfile :encoding "UTF-8" :append true)
         session (run server room
-                     {:logger {:out outfile
+                     {:logger {:path logfile
                                :writer w}}
                      logger-dispatch)]
     session))


Minor typo fixed

Minor typo fixed

--- src/clj/hellhound/components/cassandra.clj
+++ src/clj/hellhound/components/cassandra.clj
@@ -26,4 +26,4 @@
   []
   (let [host (or (environ/env :cassandra-host)
                  ["127.0.0.1"])]
-    (->CassandraClient host)))
+    (->Cassandra host)))


Fix a failure in the slamhound run.

Fix a failure in the slamhound run.

--- src/regarde/db.clj
+++ src/regarde/db.clj
@@ -1,6 +1,7 @@
 (ns regarde.db
   (:require [korma.db :as sql]
             [ragtime.core :refer [applied-migrations migrate-all]]
+            [environ.core]
             [ragtime.sql.files]))

 (defn db-name [env]


Reify process into interceptor collection

--- src/yada/core.clj
+++ src/yada/core.clj
@@ -422,30 +422,31 @@
               :request request
               :allowed-methods (:allowed-methods http-resource)
               :options options})
+        interceptors
+        [
+         available?
+         known-method?
+         uri-too-long?
+         TRACE
+         method-allowed?
+         malformed?
+         authentication
+         authorization
+         fetch
+         ;; TODO: Unknown or unsupported Content-* header
+         ;; TODO: Request entity too large - shouldn't we do this later,
+         ;; when we determine we actually need to read the request body?
+         select-representation
+         exists?
+         check-modification-time
+         if-match
+         invoke-method
+         compute-etag
+         create-response]
         ]

     (->
-     (d/chain
-      ctx
-      available?
-      known-method?
-      uri-too-long?
-      TRACE
-      method-allowed?
-      malformed?
-      authentication
-      authorization
-      fetch
-      ;; TODO: Unknown or unsupported Content-* header
-      ;; TODO: Request entity too large - shouldn't we do this later,
-      ;; when we determine we actually need to read the request body?
-      select-representation
-      exists?
-      check-modification-time
-      if-match
-      invoke-method
-      compute-etag
-      create-response)
+     (apply d/chain ctx interceptors)

      ;; Handle exits
      (d/catch clojure.lang.ExceptionInfo


improve varsaw arg docstring

improve varsaw arg docstring

--- src/overtone/sc/machinery/ugen/metadata/osc.clj
+++ src/overtone/sc/machinery/ugen/metadata/osc.clj
@@ -129,7 +129,7 @@
       {:name "VarSaw",
        :args [{:name "freq", :default 440.0 :doc "Frequency in Hertz"}
               {:name "iphase", :default 0.0 :doc "Initial phase offset in cycles ( 0..1 )"}
-              {:name "width", :default 0.5 :doc "Duty cycle from zero to one."}]
+              {:name "width", :default 0.5 :doc "Duty cycle from zero to one. (0 = downward sawtooth, 0.5 = triangle, 1 = upward sawtooth)"}]
        :rates #{:ar :kr}
        :doc "a variable duty cycle saw wave oscillator"}


rewrite part about how hn post bombed

rewrite part about how hn post bombed

--- src/pc/views/blog/product_hunt_wake_up_call.clj
+++ src/pc/views/blog/product_hunt_wake_up_call.clj
@@ -12,9 +12,9 @@
      [:p "On November 15 around 8am, I woke up to nearly 100 notifications on my phone.
          In my mostly-asleep state I assumed all the noises were just my alarm.
          When I finally got up and slid my phone open, I was shocked by what I found."]
-     [:p "The previous night, my friend Daniel and I shared our weekend project on with Hacker News.
-         Knowing that the project was still very young, we were interested in suggestions of what features to add next.
-         We received some feedback but nothing substantial."]
+     [:p "The previous night, my friend Daniel and I shared our weekend project on Hacker News.
+         The project was still very young so we were interested in suggestions of what features to add next.
+         Our post slid off of \"New\" quickly though; it bombed to say the least."]
      [:p "Later that night, a designer from Paris named Julie found our project and submitted it to Product Hunt.
          It quickly rose to #1 and attracted a slew of European traffic.
          Luckily, Daniel had previously set up alerts for us when someone signed up or tried chatting with us in the app.


Add bounded functions to queries

Add bounded functions to queries

--- src/clj/gtfve/data/queries.clj
+++ src/clj/gtfve/data/queries.clj
@@ -1,6 +1,15 @@
 (ns gtfve.data.queries
   (:require [datomic.api :as d]
             [gtfve.data.connection :refer [conn uri feed]]))
+
+(defn bounded?
+  ([[x1 y1 x2 y2]]
+   (fn [[px py]]
+     (and (> px x1) (> py y1)
+          (< px x2) (< px y2))))
+  ([[x1 y1 x2 y2] [px py]]
+   (and (> px x1) (> py y1)
+        (< px x2) (< px y2))))

 (defn pull
   "Use the Datomic Pull API to query the database."
@@ -20,14 +29,22 @@
     (set? e) (into #{} (map touch-all e))
     :else e))

-(defn stops-search
-  [name]
+(defn bounded-stops
+  [bbox]
   (let [db (d/db conn)]
-    (->> (d/datoms db :aevt :stop/name)
-         (filter #(re-find (re-pattern (str "(?i)" name)) (:v %)))
+    (->> (d/datoms db :aevt :stop/id)
          (map :e)
          (map (partial d/entity db))
+         (map (fn [e] (into [e]
+                            ((juxt :stop/latitude :stop/longitude) e))))
+         (filter (fn [[e & point]]
+                   (bounded? bbox point)))
+         (map first)
          (map d/touch))))
+
+(defn viewport
+  [bbox]
+  (bounded-stops bbox))

 (defn stops-search
   ([name]


Custom trains

Custom trains

--- src/clj/mokkameister/train.clj
+++ src/clj/mokkameister/train.clj
@@ -32,6 +32,11 @@

 (def ^:private coffee-train (partial train :coffee))

+(defn- train-custom [& wagons]
+  (->> (cons :steam_locomotive wagons)
+       (map key->emoji)
+       (apply str)))
+
 (defn rand-train []
   (rand-sexp
    (coffee-train)
@@ -60,10 +65,12 @@
    (train :nespresso :ali)
    (train :ali :gruff)
    (train :syringe :hocho)
-   ":steam_locomotive::coffee::coffee::running::running::running::dash:"
-   "All aboard the coffee train\n:steam_locomotive::coffee::coffee::running::running::running::dash:"
+   (train-custom :coffee :coffee :running :running :running :dash)
+   (train-custom :sun_with_face :lollipop :sun_with_face
+                 :lollipop :sun_with_face :lollipop :rainbow)
+   (str "All aboard the coffee train!\n"
+        (train-custom :coffee :coffee :running :running :running :dash))
    "Det er kaffi å få på kjøken!"
    "KEEP CALM THE COFFEE IS READY!"
    "M-m-m-m-m-microdosing! :syringe::syringe::syringe::exclamation:"
-   "PLEASE PROCEED IN ORDERLY FASHION TO RECEIVE COFFEE :syringe:"
-   ":steam_locomotive::sun_with_face::lollipop::sun_with_face::lollipop::sun_with_face::lollipop::sun_with_face::rainbow:"))
+   "PLEASE PROCEED IN ORDERLY FASHION TO RECEIVE COFFEE :syringe:"))


Refactor code

Refactor code

--- src/clj/salava/core/layout.clj
+++ src/clj/salava/core/layout.clj
@@ -30,10 +30,6 @@
    "/assets/leaflet/leaflet.js"
    "/js/ckeditor/ckeditor.js"])

-(defn gtm [ctx]
- (let [script (first (plugin-fun (get-plugins ctx) "block" "gtmscript"))]
-   (if script (script ctx) "")))
-
 (defn with-version [ctx resource-name]
   (let [version (get-in ctx [:config :core :asset-version])]
     (str resource-name "?_=" (or version (System/currentTimeMillis)))))
@@ -57,6 +53,10 @@

 (defn js-list [ctx]
     (map #(with-version ctx %) (conj asset-js "/js/salava.js")))
+
+(defn plugin-js [ctx]
+ (let [f (first (plugin-fun (get-plugins ctx) "block" "pluginjs"))]
+   (if f (f ctx) "")))

 (defn context-js [ctx]
@@ -128,8 +128,7 @@
        [:link {:rel "icon" :type "image/png" :href  (:png favicons)}]

        [:script {:type "text/javascript"} (context-js ctx)]
-       (include-js "/js/dataLayer.js")
-       (include-js (gtm ctx))]
+       (include-js "/js/dataLayer.js"))]

       [:body {:class (if (nil? (get-in ctx [:user])) "anon")}
        [:div#app]
@@ -138,7 +137,7 @@
        "<![endif]-->"
        (include-js "/assets/es6-shim/es6-shim.min.js" "/assets/es6-shim/es6-sham.min.js")
        (apply include-js (js-list ctx))
-       #_(include-js "https://backpack.openbadges.org/issuer.js")]))))
+       (apply include-js (plugin-js ctx))]))))

 (defn main-response [ctx current-user flash-message meta-tags]


don't pre-compute gmark text and deref cursor

Wait until the user asks for it.

--- src/cljs/greffe/doc_comps.cljs
+++ src/cljs/greffe/doc_comps.cljs
@@ -106,15 +106,16 @@
            (om/build element-attributes-component (:attrs elem))
            (when (:showButton state)
              (dom/div #js {:className "controls"}
-               (let [elem-as-text (gm/to-gmark elem tei)]
-                 (dom/a #js {:onClick
-                             (fn [ev]
-                               (om/set-state! owner :editContent elem-as-text)
-                               (om/set-state!
-                                 owner
-                                 :editText
-                                 (not (om/get-state owner :editText))))}
-                   "Editer"))))
+               (dom/a #js {:onClick
+                           (fn [ev]
+                             (om/set-state! owner
+                               :editContent (gm/to-gmark @elem tei))
+                             (om/set-state!
+                               owner
+                               :editText
+                               (not (om/get-state owner :editText))))}
+                 "Editer"))))
+
            (when (:editText state)
              (dom/textarea #js {:value (:editContent state)
                                 :cols "50" :rows "3"
@@ -130,7 +131,8 @@
                                                    mk/inner-tokens
                                                    (assoc el :content []))]
                                           new-el
-                                          el)))))})))
+                                          el)))))}))
+
          (when (pos? (count (:content elem)))
            (apply dom/div #js {:className "block-contents col-md-10"}
              (om/build-all element-component (:content elem)))))))))


Make storage protocol callback-based.

Make storage protocol callback-based.

--- src/cljs/nonojure/storage.cljs
+++ src/cljs/nonojure/storage.cljs
@@ -2,11 +2,11 @@
   (:require [jayq.util :refer [log]]))

 (defprotocol Storage
-  (load-progress [this])
-  (load-progress-for-puzzle [this id])
-  (save-auto! [this id progress])
-  (save-manual! [this id progress])
-  (solved! [this id solution]))
+  (load-progress [this callback])
+  (load-progress-for-puzzle [this id callback])
+  (save-auto! [this id progress callback])
+  (save-manual! [this id progress callback])
+  (solved! [this id solution callback]))

 (defn to-str [data]
   (.stringify js/JSON (clj->js data)))
@@ -30,40 +30,40 @@

 (extend-protocol Storage
   js/Storage
-  (load-progress [this]
-    (get-item this "progressAll"))
-  (load-progress-for-puzzle [this id]
-    (get-item this id))
-  (save-auto! [this id progress]
-    (update-progress this id {:auto progress} :in-progress))
-  (save-manual! [this id progress]
-    (update-progress this id {:manual progress} :in-progress))
-  (solved! [this id solution]
-    (update-progress this id {:solution solution} :solved)))
+  (load-progress [this callback]
+    (callback (get-item this "progressAll")))
+  (load-progress-for-puzzle [this id callback]
+    (callback (get-item this id)))
+  (save-auto! [this id progress callback]
+    (callback (update-progress this id {:auto progress} :in-progress)))
+  (save-manual! [this id progress callback]
+    (callback (update-progress this id {:manual progress} :in-progress)))
+  (solved! [this id solution callback]
+    (callback (update-progress this id {:solution solution} :solved))))

 (defn ^:export init []
   (let [log #(log (clj->js %))
         storage window/localStorage]
-    (log (load-progress storage))
+    (load-progress storage log)

     (log "Save auto mar")
-    (save-auto! storage "mar" {:left [[1] [2]] :top [[1]]})
-    (log (load-progress storage))
-    (log (load-progress-for-puzzle storage "mar"))
+    (save-auto! storage "mar" {:left [[1] [2]] :top [[1]]} log)
+    (load-progress storage log)
+    (load-progress-for-puzzle storage "mar" log)

     (log "Save auto asf")
-    (save-auto! storage "asf" {:left [[1] [2]] :top [[1]]})
-    (log (load-progress storage))
-    (log (load-progress-for-puzzle storage "asf"))
+    (save-auto! storage "asf" {:left [[1] [2]] :top [[1]]} log)
+    (load-progress storage log)
+    (load-progress-for-puzzle storage "asf" log)

     (log "Save manual mar")
-    (save-manual! storage "mar" {:left [[1] [2]] :top [[1]]})
-    (log (load-progress storage))
-    (log (load-progress-for-puzzle storage "mar"))
+    (save-manual! storage "mar" {:left [[1] [2]] :top [[1]]} log)
+    (load-progress storage log)
+    (load-progress-for-puzzle storage "mar" log)

     (log "Solving asf")
-    (solved! storage "asf" {:left [] :right []})
-    (log (load-progress storage))
-    (log (load-progress-for-puzzle storage "asf"))
+    (solved! storage "asf" {:left [] :right []} log)
+    (load-progress storage log)
+    (load-progress-for-puzzle storage "asf" log)

     ))


Simulation working

Simulation working

--- src/cljs/robot_kata/robot.cljs
+++ src/cljs/robot_kata/robot.cljs
@@ -1,9 +1,9 @@
 (ns robot-kata.robot

-  (:require [dommy.core          :refer [append!                                      ]]
-            [robot-kata.geometry :refer [calculate-next-position get-sensor-position  ]]
-            [robot-kata.image    :refer [get-2d-context get-color-name get-pixel-color]]
-            [robot-kata.svg      :refer [make-robot-graphic set-position!             ]] )
+  (:require [dommy.core          :refer [append!                                                             ]]
+            [robot-kata.geometry :refer [calculate-next-position calculate-position-delta get-sensor-position]]
+            [robot-kata.image    :refer [get-2d-context get-color-name get-pixel-color                       ]]
+            [robot-kata.svg      :refer [make-robot-graphic set-position!                                    ]] )

   (:require-macros [dommy.macros :refer [sel1]]) )

@@ -17,39 +17,32 @@

 (defn init-robot-svg [svg]
   (append! svg (make-robot-graphic robot-params))
-  #_(animate {:x 0 :y 0 :theta 135})
-  #_(animate-interval 1000 #(js/console.log "hi"))
-  (simulate {:x 400 :y 800 :theta 0} {:distance 0 :theta 0} (.now js/Date)) )
+  (simulate {:x 400 :y 800 :theta 0} {:trans 0 :rot 0} (.now js/Date)) )

-(defn animate [robot-position]
-  (js/setTimeout #(let [new-robot-position (calculate-next-position robot-position 5 0)]
-                    (set-position! (sel1 :#robot) new-robot-position)
-                    (js/console.log (get-color-name (get-pixel-color (get-2d-context (sel1 :#floor))
-                                                                     (get-sensor-position new-robot-position
-                                                                                robot-params ) )))
-                    (animate new-robot-position) )
-                 200 ) )
+(let [last-tick-time (atom 0)]
+  (defn time-for-control-tick? []
+    (let [current-time (.now js/Date)]
+      (if (> (- current-time @last-tick-time))
+        (swap! last-tick-time (fn [_] current-time)) ) ) ) )

-(defn animate-interval
-  ([period func] (animate-interval period func 0))
-  ([period func last-tick-time]
-     (js/requestAnimationFrame #(let [tick-time (.now js/Date)]
-                                  (if (> (- tick-time last-tick-time) period)
-                                    (do
-                                      (func)
-                                      (animate-interval period func tick-time) )
-                                    (animate-interval period func last-tick-time) ) )) ) )
+(defn get-new-robot-velocity [robot-position]
+  (let [color (get-color-name (get-pixel-color (get-2d-context (sel1 :#floor))
+                                               (get-sensor-position robot-position robot-params) ))]
+    (cond (= color "white") {:trans 50 :rot  0}
+          :else             {:trans  0 :rot 10} ) ) )

-(defn simulate [robot-position robot-velocity last-tick-time]
-  (let [tick-time          (.now js/Date)
-        elapsed-time       (min (- tick-time last-tick-time) 100)
-        new-robot-position (calculate-next-position robot-position
-                                                    (calculate-position-delta robot-velocity elapsed-time) ) ]
-    (set-position! (sel1 :#robot) new-robot-position)
+(defn continue-simulation? [] true)
+
+(defn simulate [previous-robot-position robot-velocity last-tick-time]
+  (let [tick-time      (.now js/Date)
+        elapsed-time   (min (- tick-time last-tick-time) 100)
+        robot-position (calculate-next-position previous-robot-position
+                                                (calculate-position-delta robot-velocity elapsed-time) ) ]
+    (set-position! (sel1 :#robot) robot-position)
     (if (continue-simulation?)
-      (js/requestAnimationFrame #(simulate new-robot-position
+      (js/requestAnimationFrame #(simulate robot-position
                                            (if (time-for-control-tick?)
-                                             (get-new-robot-velocity new-robot-position)
+                                             (get-new-robot-velocity robot-position)
                                              robot-velocity )
                                            tick-time ))  ) ) )


inline usage

inline usage

--- src/clojure/confgraph/core.clj
+++ src/clojure/confgraph/core.clj
@@ -21,12 +21,10 @@
   (let [re (re-pattern (str prefix ".*"))]
     (into {} (for [[src dst] raw-edges :when (re-matches re src)] (rootpath src)))))

-(defn usage []
-  (println "Supply at most a single filtering prefix.")
-  (System/exit 1))
-
 (defn -main [& args]
   (alter-var-root #'*read-eval* (constantly false))
-  (if (> (count args) 1) (usage))
+  (if (> (count args) 1)
+    (do (println "Supply at most a single filtering prefix.")
+        (System/exit 1)))
   (let [fe (edges (first args))]
     (prn fe)))


Add more entries to trigger translation table.

--- src/uxbox/view/ui/viewer/interactions.cljs
+++ src/uxbox/view/ui/viewer/interactions.cljs
@@ -17,6 +17,10 @@
   (case trigger
     :click :on-click
     :hover :on-hover
+    :doubleclick :on-double-click
+    :rightclick :on-context-menu
+    :mousein :on-mouse-enter
+    :mouseout :on-mouse-leave
     (throw (ex-info "not supported at this moment" {:trigger trigger}))))

 (defn- translate-ease


lein: add portal, lein-count, reveal

lein: add portal, lein-count, reveal

--- .lein/profiles.clj
+++ .lein/profiles.clj
@@ -2,7 +2,9 @@
 ;; Inspired heavily by http://dev.solita.fi/2014/03/18/pimp-my-repl.html
 ;; and a newer https://gist.github.com/rauhs/d5bb47c239b58cbf54e1d3d1de221031
 ;; pragma: allowlist nextline secret
-{:user {:signing {:gpg-key "5AA361214163F2CD"} ;; my keybase key
+{:portal {:dependencies [[djblue/portal "0.19.2"]]
+          :repl-options {:welcome (do (def p ((requiring-resolve 'portal.api/open))) (add-tap (requiring-resolve 'portal.api/submit)))}}
+ :user {:signing {:gpg-key "5AA361214163F2CD"} ;; my keybase key
         :deploy-repositories [["clojars" {:url "https://repo.clojars.org" :creds :gpg}]]
         :dependencies [
 ;?                         [org.clojure/tools.nrepl "0.2.13"]
@@ -40,6 +42,7 @@
 ;;                     (require 'spyscope.core)
                      ]
         :plugins [
+                  [lein-count "1.0.9"] ; -> `lein count` to count LoC
  ;;                   [lein-virgil "0.1.8"] ; auto-recompile and reload .java files from :java-source-paths into the REPL
  ;;                   [lein-libdir "0.1.1"] ; dependencies to ./lib/
                     ;[cider/cider-nrepl "0.9.1"]
@@ -76,7 +79,11 @@
                        ]}
 ;       :mirrors {#"clojars" {:name "clojars mirror" :url "https://clojars-mirror.tcrawley.org/repo/"}}
  }
- :repl {:dependencies [;[zcaudate/lucid.aether "1.4.4"]
+ :reveal {:dependencies [[vlaaad/reveal "1.3.216"]
+                         [org.openjfx/javafx-controls "17"]]
+          :repl-options {:nrepl-middleware [vlaaad.reveal.nrepl/middleware]}}
+ :repl {#_#_
+        :dependencies [;[zcaudate/lucid.aether "1.4.4"]
                        ;[zcaudate/lucid.core.inject "1.4.4"]
                        [zcaudate/lucid.core.debug "1.4.7"]
                        [zcaudate/lucid.package "1.4.7"]


Respond to POST requests

Respond to POST requests

--- src/bot/web.clj
+++ src/bot/web.clj
@@ -8,8 +8,16 @@
    :headers {"Content-Type" "text/plain"}
    :body (pr-str ["Hello" :from 'ClojureNauts])})

+(defn webhook []
+  "handles POST request from GitHub webhooks"
+  {:status 200
+   :headers {"Content-Type" "text/plain"}
+   :body "OK"})
+
 (defroutes app
   (GET "/" []
        (splash))
+  (POST "/" []
+        (webhook))
   (ANY "*" []
        (route/not-found (slurp (io/resource "404.html")))))


Remove boilerplate comments in core.cljs

--- src/reddio_frontend/core.cljs
+++ src/reddio_frontend/core.cljs
@@ -2,26 +2,13 @@
   (:require [reagent.core :as reagent :refer [atom]]
             [reddio-frontend.bridge :as bridge]))

-(enable-console-print!)
-
-(println "This text is printed from src/reddio-next/core.cljs. Go ahead and edit it and see reloading in action.")
-
-;; define your app data so that it doesn't get over-written on reload
-
-(defonce app-state (atom {:text "Hello world!"}))
-
 (defn hello-world []
   [:div
-   [:h1 (:text @app-state)]
-   [:h2 "Another one!"]
+   [:h1 "Hello world!"]
    [:> bridge/root-provider
     [:> bridge/home-top-subreddits]]])

 (reagent/render-component [hello-world]
                           (. js/document (getElementById "app")))

-(defn on-js-reload []
-  ;; optionally touch your app-state to force rerendering depending on
-  ;; your application
-  ;; (swap! app-state update-in [:__figwheel_counter] inc)
-)
+(defn on-js-reload [])


Parallelize TCP worker/boss group shutdown

Speeds up the netty core tests.

--- src/riemann/transport/tcp.clj
+++ src/riemann/transport/tcp.clj
@@ -131,15 +131,21 @@
                   (.childHandler pipeline-factory))

                 ; Start bootstrap
-                (.add channel-group (.channel (.bind bootstrap (InetSocketAddress. host port))))
+                (->> (InetSocketAddress. host port)
+                     (.bind bootstrap)
+                     (.channel)
+                     (.add channel-group))
                 (info "TCP server" host port "online")

                 ; fn to close server
                 (reset! killer
                         (fn killer []
-                          (-> channel-group .close .awaitUninterruptibly)
-                          (-> worker-group .shutdownGracefully .awaitUninterruptibly)
-                          (-> boss-group .shutdownGracefully .awaitUninterruptibly)
+                          (.. channel-group close awaitUninterruptibly)
+                          ; Shut down workers and boss concurrently.
+                          (let [w (.shutdownGracefully worker-group)
+                                b (.shutdownGracefully boss-group)]
+                            (.awaitUninterruptibly w)
+                            (.awaitUninterruptibly b))
                           (info "TCP server" host port "shut down")))))))

   (stop! [this]


Adding some newlines as a test of stash.

--- umm-spec-lib/src/cmr/umm_spec/umm_json.clj
+++ umm-spec-lib/src/cmr/umm_spec/umm_json.clj
@@ -112,3 +112,6 @@
   [schema json-str]
   (let [root-type-def (get-in schema [:definitions (:root schema)])]
     (parse-json schema (:root schema) root-type-def (json/decode json-str true))))
+
+
+


use more idiomatic lisp functions

use more idiomatic lisp functions

--- src/sort/merge.clj
+++ src/sort/merge.clj
@@ -8,8 +8,8 @@
         (empty? ys) (concat (reverse xs) sorted)
         :else (let [x (first xs) y (first ys)]
                 (if (< x y)
-                  (recur (drop 1 xs) ys (conj sorted x))
-                  (recur (drop 1 ys) xs (conj sorted y))))))))
+                  (recur (rest xs) ys (cons x sorted))
+                  (recur (rest ys) xs (cons y sorted))))))))

 (defn merge-sort [xs]
   (let [n (count xs)]


Whitespace.

Whitespace.

--- src/cavm/h2.clj
+++ src/cavm/h2.clj
@@ -315,8 +315,7 @@
    `strand` CHAR(1),
    FOREIGN KEY (`probemap_id`) REFERENCES `probemap` (`id`) ON DELETE CASCADE,
    FOREIGN KEY (`probe_id`) REFERENCES `probe` (`id`) ON DELETE CASCADE)"
-   "CREATE INDEX IF NOT EXISTS chrom_bin ON probe_position (`probemap_id`, `chrom`, `bin`)"
-   ])
+   "CREATE INDEX IF NOT EXISTS chrom_bin ON probe_position (`probemap_id`, `chrom`, `bin`)"])

 (def probe-gene-table
   ["CREATE TABLE IF NOT EXISTS `probe_gene` (
@@ -325,8 +324,7 @@
    `gene` VARCHAR(255),
     FOREIGN KEY (`probemap_id`) REFERENCES `probemap` (`id`) ON DELETE CASCADE,
     FOREIGN KEY (`probe_id`) REFERENCES `probe` (`id`) ON DELETE CASCADE)"
-   "CREATE INDEX IF NOT EXISTS probe_gene ON `probe_gene` (`probemap_id`, `gene`)"
-   ])
+   "CREATE INDEX IF NOT EXISTS probe_gene ON `probe_gene` (`probemap_id`, `gene`)"])

 ;
 ; feature tables
@@ -566,10 +564,10 @@

 (defn- table-writer-default [dir f]
   (f {:insert-field insert-field
-         :insert-score insert-scores
-         :insert-field-score insert-field-score
-         :encode score-encode
-         :key ahashable}))
+      :insert-score insert-scores
+      :insert-field-score insert-field-score
+      :encode score-encode
+      :key ahashable}))

 ;
 ; Table writer that updates one table at a time by writing to temporary files.


added comments button

added comments button

--- src/cljs/redditv/components/fullscreen-controls.cljs
+++ src/cljs/redditv/components/fullscreen-controls.cljs
@@ -20,6 +20,10 @@
                   :title "Play Previous Video"
                   :onClick #(playlist/select-prev app-state)})

+       (mdl/icon {:name "comment" :className "redditv-button-fullscreen noselect"
+                  :title "View Reddit Comments"
+                  :onClick #(playlist/open-current-video-comments app-state)})
+
        (mdl/icon {:name "fullscreen_exit" :className "redditv-button-fullscreen noselect"
                   :title "Exit Fullscreen"
                   :onClick #(swap! app-state assoc :fullscreen false)})


Add error handling to query functions

Add error handling to query functions

--- src/fivethreeonern/sqlite.cljs
+++ src/fivethreeonern/sqlite.cljs
@@ -6,17 +6,25 @@
 (defstate sqlite
   :start (.openDatabase node-module #js {:name "531.db" :location "default"} #(js/console.log "sql ok") #(js/console.log "sql error")))

-(defn execute-sql [tx final-cb [query & other-queries]]
-  (.executeSql tx query #js [] (fn [tx results]
-                                 (if (empty? other-queries)
-                                   (let [results (-> results .-rows .raw js->clj)]
-                                     (final-cb results))
-                                   (execute-sql tx final-cb other-queries)))))
+(defn- execute-sql [tx [query & other-queries] final-cb on-error]
+  (.executeSql tx query #js []
+               (fn [tx results]
+                 (if (empty? other-queries)
+                   (let [results (-> results .-rows .raw js->clj)]
+                     (final-cb results))
+                   (execute-sql tx other-queries final-cb on-error)))
+               on-error))

-(defn transaction [query-strings final-cb]
-  (.transaction @sqlite
-                (fn [tx]
-                  (execute-sql tx final-cb query-strings))))
+(defn transaction
+  ([query-strings final-cb]
+   (transaction query-strings final-cb (fn [_])))
+  ([query-strings final-cb on-error]
+   (.transaction @sqlite
+                 (fn [tx]
+                   (execute-sql tx query-strings final-cb on-error)))))

-(defn query [query-str cb]
-  (transaction [query-str] cb))
+(defn query
+  ([query-str cb]
+   (query query-str cb (fn [_])))
+  ([query-str cb on-error]
+   (transaction [query-str] cb on-error)))


Much better lshuffle, uses Java's shuffle with a custom random source.

--- src/clojush.clj
+++ src/clojush.clj
@@ -92,20 +92,12 @@
   (nth coll (. thread-local-random-generator (nextInt (count coll)))))

 (defn lshuffle
-  "Return a random permutation of coll. Implements a Fisher-Yates shuffle."
-  [coll]
-  (loop [i (dec (count coll))
-         shuf (vec coll)]
-    (if (zero? i)
-      (cond (vector? coll) shuf
-            (list? coll) (into (list) shuf)
-            :else
-            (seq shuf))
-      (recur (dec i)
-             (let [j (lrand-int (inc i))]
-               (assoc shuf
-                 j (shuf i)
-                 i (shuf j)))))))
+  "Return a random permutation of coll (Adapted from clojure.core)"
+  {:static true}
+  [^java.util.Collection coll]
+  (let [al (java.util.ArrayList. coll)]
+    (java.util.Collections/shuffle al thread-local-random-generator)
+    (clojure.lang.RT/vector (.toArray al))))

 (defn decompose
   "Returns a list of at most max-parts numbers that sum to number.


Computer player puts up a fight!

Computer player puts up a fight!

--- src/tttclj/web.clj
+++ src/tttclj/web.clj
@@ -5,15 +5,16 @@
             [compojure.route :refer [resources]]
             [org.httpkit.server :refer [run-server]]
             [tttclj.core :refer [create-game possible-moves successor game-over?]]
-            [tttclj.prep :refer [prep]]))
+            [tttclj.prep :refer [prep]]
+            [tttclj.solver :refer [alphabeta]]))

 (defn index [req]
   {:status 200
    :headers {"Content-Type" "text/html"}
    :body "Hello HTTP via Compojure!"})

-(defn- make-random-move [g]
-  (successor g (rand-nth (possible-moves g))))
+(defn- make-ai-move [g]
+  (successor g (alphabeta g 3)))

 (defn ws-handler [{:keys [ws-channel] :as req}]
   (println "Opened connection from" (:remote-addr req))
@@ -30,7 +31,7 @@
             (>! ws-channel (prep @game))
             (when-not (game-over? @game)
               (<! (timeout 500))
-              (swap! game make-random-move)
+              (swap! game make-ai-move)
               (>! ws-channel (prep @game))))
           (when-not (game-over? @game)
             (recur)))))))


Remove :sequence-end slot

Remove :sequence-end slot

The :sequence-end slot is a convenience during TPN construction,
but is not needed in the final output.

Signed-off-by: Tom Marble <b248307e886b80bf9cb4014595572a835b049bf4@info9.net>

--- src/pamela/tpn.clj
+++ src/pamela/tpn.clj
@@ -741,6 +741,7 @@
 ;; remove invalid slots
 ;; 1. Remove state :end-node if :sequence-end is not set
 ;; 2. add :end-node if :sequence-end is set
+;; 3. remove :sequence-end everywhere
 (defn remove-invalid-tpn-attributes []
   (doseq [uid (keys @*tpn-plan-map*)]
     (let [object (get-tpn-plan-map uid)
@@ -749,7 +750,12 @@
         (and (= :state tpn-type) end-node (not sequence-end))
         (update-tpn-plan-map! (dissoc object :end-node))
         (and (= :state tpn-type) sequence-end (not= end-node sequence-end))
-        (update-tpn-plan-map! (assoc object :end-node sequence-end))
+        (update-tpn-plan-map!
+          (assoc
+            (dissoc object :sequence-end)
+              :end-node sequence-end))
+        sequence-end
+        (update-tpn-plan-map! (dissoc object :sequence-end))
         ;; (and (#{:state :c-end :p-end} tpn-type) end-node)
         ;; (update-tpn-plan-map! (dissoc object :end-node))
         ;; (#{:c-end :p-end} tpn-type)


Fix validations namespace.

Fix validations namespace.

--- src/grafter/rdf/validation.clj
+++ src/grafter/rdf/validation.clj
@@ -1,8 +1,8 @@
 (ns grafter.rdf.validation
-  (:use [grafter.rdf]
-        [grafter.rdf.sesame])
   (:require [grafter.rdf.protocols :as pr]
-            [clojure.string :as st]))
+            [clojure.string :as st]
+            [grafter.rdf]
+            [grafter.rdf.sesame :as ses]))

 (defn blank? [v]
   (or (nil? v) (= "" v)))


use aget to prevent an advanced compilation bug

expected externs to solve that...

--- frontend/instrumentation.cljs
+++ frontend/instrumentation.cljs
@@ -33,7 +33,7 @@
 (def component-stats (atom {}))

 (defn react-id [x]
-  (let [id (.-_rootNodeID x)]
+  (let [id (aget x "_rootNodeID")]
     (assert id)
     id))


Added more tests.

Added more tests.

--- test/cmr/system_int_test/search/collection_multiple_conditions_aql_search_test.clj
+++ test/cmr/system_int_test/search/collection_multiple_conditions_aql_search_test.clj
@@ -31,4 +31,5 @@
            [] [{:dataSetId "Dataset2"} {:shortName "Long%"}] {}
            [] [{:dataSetId "Dataset2"} {:shortName "Long%" :pattern false}] {}
            [coll2 coll4] [{:dataSetId "Dataset2"} {:shortName "Long%" :pattern true}] {}
+           [] [{:dataSetId "Dataset1"} {:dataSetId "Dataset2"}] {}
            [coll1] [{:dataSetId "Dataset1"} {:shortName "SHORT"}] {:dataCenterId "PROV1"}))))


Added `componentWillUpdate` lifecycle wrapper...

Adds the `on-will-update` and `on-will-render` lifecycle wrapper fn's.
These can be used to prepare the page for an update, e.g. by adding or
removing a class to trigger an animation.

--- src/quiescent.cljs
+++ src/quiescent.cljs
@@ -80,12 +80,20 @@
               (when-let [f (aget (.-props this) "onWillMount")]
                 (binding [*component* this]
                   (f)))))
+          :componentWillUpdate
+          (fn [_ _]
+            (this-as this
+              (when-let [f (aget (.-props this) "onWillUpdate")]
+                (binding [*component* this]
+                  (f)))))
           :componentWillUnmount
           (fn []
             (this-as this
               (when-let [f (aget (.-props this) "onWillUnmount")]
                 (binding [*component* this]
                   (f)))))}))
+
+

 (defn on-update
   "Wrap a component, specifying a function to be called on the
@@ -112,8 +120,9 @@
   The function will be passed the rendered DOM node."
   [child f]
   (WrapperComponent #js {:wrappee child
-                         :onUpdate f
-                         :onMount f}))
+                         :onMount f
+                         :onUpdate f}))
+

 (defn on-will-mount
   "Wrap a component, specifying a function to be called on the
@@ -124,6 +133,26 @@
   (WrapperComponent #js {:wrappee child
                          :onWillMount f}))

+(defn on-will-update
+  "Wrap a component, specifying a function to be called on the
+  componentWillUpdate lifecycle event.
+
+  The function will be called with no arguments."
+  [child f]
+  (WrapperComponent #js {:wrappee child
+                         :onWillUpdate f}))
+
+(defn on-will-render
+  "Wrap a component, specifying a function to be called on the
+  componentWillMount AND the componentWillUpdate lifecycle events.
+
+  The function will be called with no arguments."
+  [child f]
+  (WrapperComponent #js {:wrappee child
+                         :onWillMount f
+                         :onWillUpdate f}))
+
+
 (defn on-will-unmount
   "Wrap a component, specifying a function to be called on the
   componentWillUnmount lifecycle event.
@@ -133,6 +162,7 @@
   (WrapperComponent #js {:wrappee child
                          :onWillUnmount f}))

+
 (defn render
   "Given a ReactJS component, immediately render it, rooted to the
   specified DOM node."


Bump garden-watcher to 0.3.1, fixes missing dependencty

--- src/leiningen/new/chestnut.clj
+++ src/leiningen/new/chestnut.clj
@@ -39,7 +39,7 @@
 (doseq [opt valid-options]
   (eval
    `(defn ~(symbol (str opt "?")) [opts#]
-     (some #{~(str "--" opt) ~(str "+" opt)} opts#))))
+      (some #{~(str "--" opt) ~(str "+" opt)} opts#))))

 (defn om? [props]
   (and (not (reagent? props))
@@ -65,13 +65,13 @@
     (om? opts)       (conj '[org.omcljs/om "1.0.0-alpha47"])
     (om-next? opts)  (conj '[org.omcljs/om "1.0.0-alpha47"])
     (rum? opts)      (conj '[rum "0.10.8"])
-    (garden? opts)   (conj '[lambdaisland/garden-watcher "0.3.0"])))
+    (garden? opts)   (conj '[lambdaisland/garden-watcher "0.3.1"])))

 (defn project-plugins [opts]
   (cond-> []
-          (sass? opts) (conj "lein-sassc \"0.10.4\""
-                             "lein-auto \"0.1.3\"")
-          (less? opts) (conj "lein-less \"1.7.5\"")))
+    (sass? opts) (conj "lein-sassc \"0.10.4\""
+                       "lein-auto \"0.1.3\"")
+    (less? opts) (conj "lein-less \"1.7.5\"")))

 (defn project-prep-tasks [name opts]
   (cond-> ["compile" ["cljsbuild" "once" "min"]]
@@ -79,8 +79,8 @@

 (defn project-uberjar-hooks [opts]
   (cond-> []
-          (less? opts) (conj "leiningen.less")
-          (sass? opts) (conj "leiningen.sassc")))
+    (less? opts) (conj "leiningen.less")
+    (sass? opts) (conj "leiningen.sassc")))

 (defn extra-dev-components [name opts]
   (cond-> []
@@ -152,10 +152,10 @@
            "test/cljs/chestnut/core_test.cljs"
            "test/cljs/chestnut/test_runner.cljs"
            "test/cljc/chestnut/common_test.cljc"]
-          (less? opts) (conj "src/less/style.less")
-          (sass? opts) (conj "src/scss/style.scss")
-          (garden? opts) (conj "src/clj/chestnut/styles.clj")
-          (not (or (less? opts) (sass? opts))) (conj "resources/public/css/style.css")))
+    (less? opts) (conj "src/less/style.less")
+    (sass? opts) (conj "src/scss/style.scss")
+    (garden? opts) (conj "src/clj/chestnut/styles.clj")
+    (not (or (less? opts) (sass? opts))) (conj "resources/public/css/style.css")))

 (defn format-files-args
   "Returns a list of pairs (vectors). The first element is the file name to


initialize REPL after build config

initialize REPL after build config

otherwise custom resolve settings will be ignored initially
and may cause weird things in the REPL

--- src/main/shadow/cljs/devtools/server/worker/impl.clj
+++ src/main/shadow/cljs/devtools/server/worker/impl.clj
@@ -132,10 +132,11 @@
                       ;; temp hack for repl/setup since it needs access to repl-init-ns but configure wasn't called yet
                       :shadow.build/config build-config
                       })
+              (build/configure :dev build-config)
               ;; FIXME: this should be done on session-start
               ;; only keeping it until everything uses new repl-system
-              (repl/setup)
-              (build/configure :dev build-config))
+              ;; must be done after config in case config has non-default resolve settings
+              (repl/setup))

           extra-config-files
           (reduce


Fix bug - wrong name!

Fix bug - wrong name!

--- services/tnseq-phase0-b.clj
+++ services/tnseq-phase0-b.clj
@@ -1,10 +1,10 @@

-{:name "rnaseq-phase0-b",
+{:name "tnseq-phase0-b",
  :path "",

  :graph {:strtscratch {:type "func"
                        :name "start-scratch-space"
-                       :args ["#1"]}
+                       :args ["#1"]} ; EID
          :bcstats {:type "func"
                    :name "collect-barcode-stats"}
          :wrtstats {:type "func"


+TODO

+TODO

--- src/babel/english/grammar.cljc
+++ src/babel/english/grammar.cljc
@@ -208,6 +208,8 @@
                                               :sem head-sem}}
                               :rule "nbar"}))

+                   ;; TODO: do we need noun-phrase1? its only distinction is its' [:sem :mod] = '()
+                   ;; and I'm not sure why this distinction is necessary.
                    (unify-check c10
                            comp-specs-head
                            (let [number-agreement (atom :top)


whitespace

whitespace

--- src/babel/english/grammar.cljc
+++ src/babel/english/grammar.cljc
@@ -174,8 +174,8 @@
 (def unmodified {:modified false})

 (def grammar (list (unify-check h21
-                           {:rule "adjective-phrase"
-                            :synsem {:cat :adjective}})
+                                {:rule "adjective-phrase"
+                                 :synsem {:cat :adjective}})

                    (unify-check h21
                                 (let [head-synsem {:cat :intensifier}]


added predicate leave for partire

added predicate leave for partire

--- src/babel/english/lexicon.cljc
+++ src/babel/english/lexicon.cljc
@@ -916,10 +916,19 @@
    "learn" {:synsem {:cat :verb
                      :sem {:pred :learn}}}

-   "leave" {:english {:past "left"}
-            :synsem {:cat :verb
-                     :sem {:pred :leave-behind
-                           :obj {:place false}}}}
+   "leave" [
+
+            {:english {:past "left"}
+             :synsem {:cat :verb
+                      :sem {:pred :leave-behind
+                            :obj {:place false}}}}
+
+            {:english {:note "(on a trip)"
+                       :past "left"}
+             :synsem {:cat :verb
+                      :sem {:pred :leave}}}
+
+                         ]

    "lie" {:synsem {:cat :verb
                 :sem {:pred :lie}}}


added s'appeler

added s'appeler

--- src/babel/francais/lexicon.clj
+++ src/babel/francais/lexicon.clj
@@ -496,6 +496,23 @@
 ;                                       :reflexive true
 ;                                       :sem subject-semantics}}}})

+  "s'appeler" (let [subject-semantics (ref {:human true})
+                    subject-agr (ref :top)]
+                {:synsem {:cat :verb
+                          :essere true
+                          :français {:futuro-stem "appell"
+                                     :boot-stem "appell"}
+                          :sem {:pred :be-called
+                                :reflexive true
+                                :subj subject-semantics
+                                :obj subject-semantics}
+                          :subcat {:1 {:agr subject-agr
+                                       :sem subject-semantics}
+                                   :2 {:agr subject-agr
+                                       :pronoun true
+                                       :reflexive true
+                                       :sem subject-semantics}}}})
+
   "s'endormir" (let [subject-semantics (ref {:human true})
                     subject-agr (ref :top)]
                 {:synsem {:cat :verb


fix courir

fix courir

--- src/babel/francais/lexicon.clj
+++ src/babel/francais/lexicon.clj
@@ -126,8 +126,35 @@

    "couper" {:synsem {:cat :verb
                       :sem {:pred :cut}}}
-
-   "courir" {:synsem {:cat :verb
+
+   ;; TODO: with 'courir', *everything* is an exception: this can probably be done better via
+   ;; morphology (francais/morphology/verbs.clj). Surely there are some regularities that we are missing.
+   "courir" {:français {:past-participle "couru"
+                        :present {:1sing "cours"
+                                  :2sing "cours"
+                                  :3sing "court"
+                                  :1plur "courons"
+                                  :2plur "courez"
+                                  :3plur "courent"}
+                        :imperfect {:1sing "courais"
+                                    :2sing "courais"
+                                    :3sing "courait"
+                                    :1plur "courions"
+                                    :2plur "couriez"
+                                    :3plur "couraient"}
+                        :future {:1sing "courrai"
+                                 :2sing "courras"
+                                 :3sing "courra"
+                                 :1plur "courrons"
+                                 :2plur "courrez"
+                                 :3plur "courront"}
+                        :conditional  {:1sing "courrais"
+                                       :2sing "courrais"
+                                       :3sing "courrait"
+                                       :1plur "courrions"
+                                       :2plur "courriez"
+                                       :3plur "courraient"}}
+             :synsem {:cat :verb
                       :sem {:pred :run}}}

    "créer" {:synsem {:cat :verb


fix typo

fix typo

--- src/babel/italiano/lexicon.clj
+++ src/babel/italiano/lexicon.clj
@@ -859,14 +859,14 @@
       "dipingere" {:synsem {:cat :verb
                             :sem {:pred :paint}}
                    :italiano {:passato "dipinto"}}
-
-      "dire" (let [shared-part-of-dire
-                   {:synsem {:cat :verb}
-                    :italiano {:italiano "dicere"
-                               :drop-e false
-                               :passato "detto"
-                               :future-stem "dir"
-                               :present {:2plur "dite"}}}]
+
+   "dire" (let [shared-part-of-dire
+                {:synsem {:cat :verb}
+                 :italiano {:infinitive "dicere"
+                            :drop-e false
+                            :passato "detto"
+                            :future-stem "dir"
+                            :present {:2plur "dite"}}}]
                [(merge shared-part-of-dire
                        {:synsem {:pred :say}})
                 (merge shared-part-of-dire


finished annotating utils.clj; merging feature branch.

--- src/clj/forma/matrix/utils.clj
+++ src/clj/forma/matrix/utils.clj
@@ -1,37 +1,24 @@
 (ns forma.matrix.utils
   (:require [incanter.core :as i]))

-;; Useful general functions
-
-(defn variance-matrix
-  "construct a variance-covariance matrix from a given matrix X"
-  [X]
-  (let [product (i/mmult (i/trans X) X)]
-    (i/solve (if (seq? product)
-               product
-               (vector product)))))
-
-(defn column-matrix
-  "create an incanter column matrix of the supplied value with length
-  `x`."
-  [val cols]
-  (i/matrix val cols 1))
-
-(def ones-column (partial column-matrix 1))
-
-(defn above-x? [x] (partial < x))
+;; Useful general functions for matrix operations or filters. The
+;; first functions should be very simple functions used as composites
+;; or filters.  The latter functions should be matrix operations that
+;; deal with arbitrarily large, multi-dimensional matrices.

 (defn replace-val
-  "replaces values in a vector [vec] that are (<, >, >=, <=, =) to [in-val]
-  with the value supplied in [out-val]"
+  "replaces values in a vector `vec` that are (<, >, >=, <=, =) relative
+  to `in-val` with the value supplied in `out-val`"
   [vec rel in-val out-val]
   (map #(if (rel % in-val) out-val %) vec))

 (defn replace-in
- "replace the value in coll at index idx with value val"
+ "replace the value in `coll` at index `idx` with value `val`"
   [coll idx val]
   {:post [(= (count coll) (count %))]}
   (concat (take idx coll) (list val) (drop (inc idx) coll)))
+
+(defn above-x? [x] (partial < x))

 (defn average
   "average of a list"
@@ -52,3 +39,24 @@
                 (empty? tup-seq)) (persistent! v)
                 (= idx pos) (recur (inc idx) (rest tup-seq) (conj! v val))
                 :else       (recur (inc idx) tup-seq (conj! v placeholder))))))
+
+;; Multi-dimensional matrix operations
+
+(defn variance-matrix
+  "construct a variance-covariance matrix from a given matrix `X`. If the
+  value of the matrix multiplication yields an integer value, then we
+  vectorize.  This would be equivalent to the var function in incanter.stats."
+  [X]
+  (let [product (i/mmult (i/trans X) X)]
+    (i/solve (if (seq? product)
+               product
+               (vector product)))))
+
+(defn column-matrix
+  "create an incanter column matrix of the supplied value with length
+  `x`."
+  [val cols]
+  (i/matrix val cols 1))
+
+(def ones-column (partial column-matrix 1))
+


make Encrypted Portals show updated code gate strengths immediately

--- src/clj/game/cards-agendas.clj
+++ src/clj/game/cards-agendas.clj
@@ -129,8 +129,8 @@
                           (reduce (fn [c server]
                                     (+ c (count (filter (fn [ice] (and (has? ice :subtype "Code Gate")
                                                                        (:rezzed ice))) (:ices server)))))
-                                  0 (flatten (seq (:servers corp))))))
-
+                                  0 (flatten (seq (:servers corp)))))
+                    (update-all-ice))
     :events {:pre-ice-strength {:req (req (has? target :subtype "Code Gate"))
                                 :effect (effect (ice-strength-bonus 1))}}}


Man in business suit levitating with hand with index and middle fingers crossed with reversed hand with middle finger raised

--- src/clj/mokkameister/train.clj
+++ src/clj/mokkameister/train.clj
@@ -60,7 +60,7 @@
    (train-coffee :scream)
    (train-coffee :female_health_worker)
    (train-coffee :smoking)
-   (train-coffee :fingers_crossed)
+   (train-coffee :hand_with_index_and_middle_fingers_crossed)
    (train-coffee :battery)
    (train-coffee :bomb)
    (train-coffee :zap)


:unicorn_face::coffee::tada::coffee::tada::coffee::rainbow:

--- src/clj/mokkameister/train.clj
+++ src/clj/mokkameister/train.clj
@@ -81,6 +81,7 @@
                  :sun_with_face :lollipop :sun_with_face :rainbow)
    ":bullettrain_front::coffee::scream_cat::coffee::scream_cat::dash:"
    ":tractor::cow2::cow::cow2::cow::cow2::cow::cow2::dash:"
+   ":unicorn_face::coffee::tada::coffee::tada::coffee::tada::coffee::rainbow:"
    (str "All aboard the coffee train!\n"
         (train-custom :coffee :coffee :running :running :running :dash))
    "Det er kaffi å få på kjøken!"


Display doc-lb entries as one of two different icons, depending on whether they are selected

--- src/clj_ns_browser/browser.clj
+++ src/clj_ns_browser/browser.clj
@@ -13,7 +13,7 @@
             [seesaw.rsyntax]
             [clojure.java.browse]
             [clojure.java.shell]
-            [clojure.java.io]
+            [clojure.java.io :as io]
             [clojure.pprint]
             [clojure.set :as set]
             [clojure.string :as str]
@@ -209,51 +209,50 @@
                                               :special-forms ns-special-forms %1 %2 %3)
                             })

-;; Create maps from names of doc-lb entries to files containing icon
-;; images, and the other direction (to efficiently map selections back
-;; to names, which are used everywhere else in the code).
-
-;;(def icon-dir "file:/Users/jafinger/clj/clj-ns-browser/resources/")
-;;
-;;(def name-to-icon-desc
-;;  {"Doc" (str icon-dir "icon-doc.png"),
-;;   "Source" (str icon-dir "icon-source.png"),
-;;   "Examples" (str icon-dir "icon-examples.png"),
-;;   "Comments" (str icon-dir "icon-comments.png"),
-;;   "See alsos" (str icon-dir "icon-see-also.png"),
-;;   "Value" (str icon-dir "icon-value.png"),
-;;   "Meta" (str icon-dir "icon-meta.png")})
-
-(def name-to-icon-desc
-  {"Doc" (str (clojure.java.io/resource "icon-doc.png")),
-   "Source" (str (clojure.java.io/resource "icon-source.png")),
-   "Examples" (str (clojure.java.io/resource "icon-examples.png")),
-   "Comments" (str (clojure.java.io/resource "icon-comments.png")),
-   "See alsos" (str (clojure.java.io/resource "icon-see-also.png")),
-   "Value" (str (clojure.java.io/resource "icon-value.png")),
-   "Meta" (str (clojure.java.io/resource "icon-meta.png"))})
+
+;; Icons to display when the corresponding strings are selected or not
+;; in the doc-lb listbox.
+(def name-to-icon-descriptions
+  {"Doc"
+   {:icon-unselected (str (io/resource "icon-doc.png")),
+    :icon-selected (str (io/resource "icon-doc-selected-90x24.png"))}
+   "Source"
+   {:icon-unselected (str (io/resource "icon-source.png")),
+    :icon-selected (str (io/resource "icon-source-selected-90x24.png"))}
+   "Examples"
+   {:icon-unselected (str (io/resource "icon-examples.png")),
+    :icon-selected (str (io/resource "icon-examples-selected-90x24.png"))}
+   "Comments"
+   {:icon-unselected (str (io/resource "icon-comments.png")),
+    :icon-selected (str (io/resource "icon-comments-selected-90x24.png"))}
+   "See alsos"
+   {:icon-unselected (str (io/resource "icon-see-also.png")),
+    :icon-selected (str (io/resource "icon-see-also-selected-90x24.png"))}
+   "Value"
+   {:icon-unselected (str (io/resource "icon-value.png")),
+    :icon-selected (str (io/resource "icon-value-selected-90x24.png"))}
+   "Meta"
+   {:icon-unselected (str (io/resource "icon-meta.png")),
+    :icon-selected (str (io/resource "icon-meta-selected-90x24.png"))}
+   })

 (defn update-vals-in-map [m f]
   (reduce (fn [m [k v]]
             (assoc m k (f v)))
           m m))

-(def name-to-icon (update-vals-in-map name-to-icon-desc icon))
-
-(def icon-desc-to-name (set/map-invert name-to-icon-desc))
-
-(defn icon-to-name [i]
-  (icon-desc-to-name (str i)))
-
-(def doc-lb-name-list ["Doc" "Source" "Examples"
+(def name-to-selected-icon
+  (update-vals-in-map name-to-icon-descriptions
+                      (fn [v] (icon (:icon-selected v)))))
+
+(def name-to-unselected-icon
+  (update-vals-in-map name-to-icon-descriptions
+                      (fn [v] (icon (:icon-unselected v)))))
+
+(def doc-lb-value-list ["Doc" "Source" "Examples"
                         "Comments" "See alsos" "Value" "Meta"])
-(def doc-lb-value-list (map name-to-icon doc-lb-name-list))
 (def doc-lb-value-set (set doc-lb-value-list))
 (def doc-lb-cur-order (atom doc-lb-value-list))
-
-;; Needed for handling drag and drop to reorder items in the doc-lb
-;; listbox.
-(def icon-flavor (seesaw.dnd/local-object-flavor javax.swing.ImageIcon))

 (def all-buttons-with-atoms
@@ -290,7 +289,7 @@

 (defn clojuredocs-offline-mode! []
   (let [f (clojuredocs-snapshot-filename)]
-    (if (.exists (clojure.java.io/as-file f))
+    (if (.exists (io/as-file f))
       ;; TBD: Consider trying to handle errors while reading the file.
       (let [s (with-out-str (cd-client.core/set-local-mode! f))]
         [:ok s])
@@ -630,6 +629,14 @@

 ;; Init functions called during construction of a frame with its widget hierarchy

+(defn render-doc-lb-item [renderer info]
+  (let [{:keys [value selected?]} info]
+    (config! renderer
+             :icon (if selected?
+                     (name-to-selected-icon value)
+                     (name-to-unselected-icon value))
+             :text "")))
+
 (defn init-before-bind
   [root]
   (letfn [(id [kw] (select-id root kw))]
@@ -645,11 +652,12 @@
     (config! (id :ns-entries-lbl) :text "0")
     (config! (id :doc-lb)
              :model doc-lb-value-list
+             :renderer render-doc-lb-item
              :drag-enabled? true
              :drop-mode :insert
              :transfer-handler
              (seesaw.dnd/default-transfer-handler
-               :import [icon-flavor
+               :import [seesaw.dnd/string-flavor
                         (fn [{:keys [target data drop? drop-location] :as m}]
                           ;; Ignore anything dropped onto the list that is
                           ;; not in the original set of list elements.
@@ -663,7 +671,8 @@
                               (reset! doc-lb-cur-order new-order)
                               (config! target :model new-order))))]
                :export {:actions (constantly :copy)
-                        :start   (fn [c] [icon-flavor (selection c)])}))
+                        :start   (fn [c] [seesaw.dnd/string-flavor
+                                          (selection c)])}))
     (config! (id :edit-btn) :enabled? false)
     (config! (id :browse-btn) :enabled? false)
     (config! (id :clojuredocs-online-rb) :selected? true)
@@ -694,7 +703,7 @@
     (config! (id :doc-ta) :text "                                                                        ")
     (selection! (id :ns-cbx) "loaded")
     (selection! (id :vars-cbx) "Vars - public")
-    (selection! (id :doc-lb) (name-to-icon "Doc"))))
+    (selection! (id :doc-lb) "Doc")))

 (defn init-after-bind
@@ -728,7 +737,7 @@
          ))
      (selection! (id :ns-cbx) "loaded")
      (selection! (id :vars-cbx) "Vars - public")
-     (selection! (id :doc-lb) (name-to-icon "Doc")))))
+     (selection! (id :doc-lb) "Doc"))))

 (defn bind-all
@@ -950,14 +959,14 @@
       (b/filter (fn [[doc-tf doc-lb]]
 ;;                  (printf "(class doc-lb)=%s (seq doc-lb)='%s' (map ... doc-lb)='%s'\n"
 ;;                          (class doc-lb) (seq doc-lb)
-;;                          (seq (map icon-to-name doc-lb)))
+;;                          (seq doc-lb))
 ;;                  (flush)
                   (not (or (nil? doc-tf)  (= "" doc-tf)
                            (nil? doc-lb) (= "" doc-lb)))))
       (b/transform
         (fn [[doc-tf doc-lb]]
           (future
-            (let [s (render-doc-text doc-tf (map icon-to-name doc-lb))]
+            (let [s (render-doc-text doc-tf doc-lb)]
               (when-not (= s (config (id :doc-ta) :text))
                 (invoke-soon (config! (id :doc-ta) :text s))))))))
     ;;
@@ -992,8 +1001,7 @@
     ;; new text in doc-ta => dis/enable browser button
     (b/bind
       (id :doc-ta)
-      (b/transform (fn [o] (map icon-to-name
-                                (selection (id :doc-lb) {:multi? true}))))
+      (b/transform (fn [o] (selection (id :doc-lb) {:multi? true})))
       (b/transform (fn [o]
         (if (empty? (set/intersection (set o)
                                       #{"Examples" "See alsos" "Comments"}))
@@ -1013,7 +1021,7 @@
 ;;                     (printf "update edit-btn: (class o)=%s o='%s'\n"
 ;;                             (class o) (seq o))
 ;;                     (flush)
-                     (let [doc-set (set (map icon-to-name o))]
+                     (let [doc-set (set o)]
                        (or (doc-set "Source") (doc-set "All")))))
       (b/transform (fn [o]
         (when o (if (meta-when-file (config (id :doc-tf) :text))
@@ -1027,8 +1035,7 @@
     (b/bind
       (apply b/funnel [(id :doc-tf)
                        (b/selection (id :doc-lb) {:multi? true})])
-      (b/transform (fn [[doc-tf doc-lb]] [(config (id :doc-tf) :text)
-                                          (map icon-to-name doc-lb)]))
+      (b/transform (fn [[doc-tf doc-lb]] [(config (id :doc-tf) :text) doc-lb]))
       (b/transform (fn [[fqn doc-lb-sel]]
                      (let [doc-set (set doc-lb-sel)]
                        (if (or (doc-set "Value") (doc-set "All"))
@@ -1046,7 +1053,7 @@
       (b/selection (id :doc-lb) {:multi? true})
       (b/transform (fn [o]
         (when (= (.getName (type (id :doc-ta))) "org.fife.ui.rsyntaxtextarea.RSyntaxTextArea")
-          (let [selected-set (set (map icon-to-name o))]
+          (let [selected-set (set o)]
             (if (= selected-set
                    (set/intersection selected-set
                                      #{"Source" "Examples" "Meta"}))
@@ -1058,7 +1065,7 @@
       (id :browse-btn-atom)
       (b/notify-soon)
       (b/transform (fn [& oo]
-        (let [o (icon-to-name (selection (id :doc-lb)))]
+        (let [o (selection (id :doc-lb))]
           (when-let [fqn (config (id :doc-tf) :text)]
             (future
               (case o


Made sure disposed-callback's are fired even when diffing.

--- src/clojure/freactive/dom.cljs
+++ src/clojure/freactive/dom.cljs
@@ -67,14 +67,25 @@

 (declare get-transition)

+(defn- init-element-meta! [state element-spec]
+  (let [m (meta element-spec)]
+    (set! (.-meta state) m)
+    (when-let [on-disposed (get m :node-disposed)]
+      (set! (.-disposed-callback state) on-disposed))))
+
+(defn- reset-element-spec! [state spec]
+  (when-let [on-disposed (.-disposed-callback state)]
+    (on-disposed)
+    (set! (.-disposed-callback state) nil))
+  (set! (.-element-spec state) spec)
+  (init-element-meta! state spec))
+
 (defn- init-element-state! [dom-node element-spec]
   (let [node-id (str auto-node-id)
         state (ElementState. node-id false element-spec nil)]
     (set! auto-node-id (inc auto-node-id))
     (set-attr! dom-node "data-freactive-id" node-id)
-    (when-let [m (meta element-spec)]
-      (when-let [on-disposed (get m :node-disposed)]
-        (set! (.-disposed-callback state) on-disposed)))
+    (init-element-meta! state element-spec)
     (aset element-state-lookup node-id state)
     ;;(set! element-state-lookup (assoc element-state-lookup dom-node state))
     state))
@@ -87,9 +98,6 @@
 (defn- unregister-from-parent-state [parent-state child-key]
   (when-let [child-states (.-child-states parent-state)]
     (js-delete child-states child-key)))
-
-(defn- reset-element-spec! [dom-node spec]
-  (set! (.-element-spec (get-element-state dom-node)) spec))

 (defprotocol IRemove
   (-remove! [x]))
@@ -456,9 +464,8 @@
     (doseq [child-key to-remove]
       (js-delete child-states child-key))))

-(defn- replace-attrs! [node old-attrs new-attrs]
-  (let [node-state (get-element-state node)
-        old-style (:style old-attrs)
+(defn- replace-attrs! [node node-state old-attrs new-attrs]
+  (let [old-style (:style old-attrs)
         new-style (:style new-attrs)]
     (dispose-attrs node-state)
     (replace-attrs!* node node-state
@@ -573,7 +580,8 @@
       cur-child)))

 (defn- try-diff [parent vdom cur-dom-node top-level]
-  (let [cur-vdom (get-virtual-dom cur-dom-node)
+  (let [cur-state (get-element-state cur-dom-node)
+        cur-vdom (get-virtual-dom (.-element-spec cur-state))
         ;;vdom (normalize-virtual-element vdom)
         ]
     (if (keyword-identical? (first vdom) (first cur-vdom))
@@ -582,10 +590,11 @@
         (let [old-attrs? (second cur-vdom)
               new-attrs? (second vdom)
               new-attrs (when (map? new-attrs?) new-attrs?)]
+          (reset-element-spec! cur-state vdom)
           (replace-attrs! cur-dom-node
+                          cur-state
                           (when (map? old-attrs?) old-attrs?)
                           new-attrs)
-          (reset-element-spec! cur-dom-node vdom)
           (let [new-children (if new-attrs (nnext vdom) (next vdom))
                 dangling-child (try-diff-subseq cur-dom-node (.-firstChild cur-dom-node) new-children)]
             (loop [cur-child dangling-child]
@@ -593,6 +602,7 @@
                 (let [next-sib (.-nextSibling cur-child)]
                   (remove-dom-node cur-child)
                   (recur next-sib)))))
+          ()
           cur-dom-node))
       (do
         ;(println "build hit" (first vdom) (first cur-vdom))
@@ -837,9 +847,9 @@
           (bind-attr! node k v state))
         (when children
           (append-children! node children))
-        (when-let [m (meta virtual-dom)]
-          (when-let [node-created (get m :node-created)]
-            (node-created node)))
+        ;(when-let [m (.-meta state)]
+        ;  (when-let [node-created (get m :node-created)]
+        ;    (node-created node)))
         node))))

 (defn mount! [element child]


Disabled diffing by default before pushing snapshot to Clojars.

--- src/clojure/freactive/dom.cljs
+++ src/clojure/freactive/dom.cljs
@@ -543,7 +543,7 @@
 (defn- text-node? [dom-node]
   (identical? (.-nodeType dom-node) 3))

-(def enable-diffing true)
+(def enable-diffing false)

 (defn- register-element-with-parent [parent new-elem]
   (when-not (text-node? new-elem)


Fixing how jobs start is called.

Fixing how jobs start is called.

--- src/cmr/metadata_db/system.clj
+++ src/cmr/metadata_db/system.clj
@@ -49,8 +49,8 @@
     (db-holder/set-db! db)

     (when-not (or (re-find #"MemoryDB" (str (type db)))
-                  (:skip-background-jobs started-system)
-                  (jobs/start db)))
+                  (:skip-background-jobs started-system))
+      (jobs/start db))

     (info "Metadata DB started")
     started-system))


Turn off :def-emits-var if evaluating ns, require, require-macros, import, load-file

Fixes #531

--- planck-cljs/src/planck/repl.cljs
+++ planck-cljs/src/planck/repl.cljs
@@ -1660,7 +1660,7 @@
 (defn- load-form?
   "Determines if the expression is a form that loads code."
   [expression-form]
-  (call-form? expression-form '#{require require-macros}))
+  (call-form? expression-form '#{ns require require-macros import load load-file}))

 (defn- def-form?
   "Determines if the expression is a def expression which returns a Var."
@@ -1688,10 +1688,10 @@
             {:ns initial-ns}
             (select-keys @app-env [:verbose :checked-arrays :static-fns :fn-invoke-direct])
             (if expression?
-              (merge {:context       :expr
-                      :def-emits-var true}
-                (when (load-form? expression-form)
-                  {:source-map true}))
+              (merge {:context       :expr}
+                (if (load-form? expression-form)
+                  {:source-map true}
+                  {:def-emits-var true}))
               (merge {:source-map true}
                 (when (:cache-path @app-env)
                   {:cache-source (cache-source-fn source-text)}))))


Added defnail macro

Added defnail macro

--- src/de/kotka/gorilla/nails.clj
+++ src/de/kotka/gorilla/nails.clj
@@ -27,33 +27,38 @@
   (:import
      com.martiansoftware.nailgun.NGContext))

-(gen-class
-  :name    de.kotka.gorilla.nails.DocLookup
-  :prefix  DocLookup-
-  :methods [#^{:static true}
-            [nailMain [com.martiansoftware.nailgun.NGContext] void]]
-  :main    false)
+(defmacro defnail
+  "Define a new Nail of the given name. A suitable class with the
+  .nailMain method is generated. The arguments is a command line
+  arguments specification vector suitable for with-command-line.
+  The body will be installed as the body .nailMain method with the
+  command-line arguments available according to the specification
+  and the nailgun context as 'nailContext'."
+  [nail usage arguments & body]
+  `(do
+     (gen-class
+       :name    ~(symbol (str "de.kotka.gorilla.nails." (name nail)))
+       :prefix  ~(symbol (str (name nail) "-"))
+       :methods [#^{:static true}
+                 [~'nailMain [com.martiansoftware.nailgun.NGContext] ~'void]]
+       :main    false)

-(defn DocLookup-nailMain
-  [#^NGContext context]
-  (with-command-line (.getArgs context)
-    "Usage: ng de.kotka.gorilla.nails.DocString [options] [--] symbol ..."
-    [[namespace n "Lookup the symbols in the given namespace." "user"]
-     symbols]
-    (binding [*out* (-> context .out java.io.OutputStreamWriter.)]
-      (print (doc-lookup namespace symbols))
-      (flush))))
+     (defn ~(symbol (str (name nail) "-nailMain"))
+       ~usage
+       [~(with-meta 'nailContext {:tag 'NGContext})]
+       (with-command-line (.getArgs ~'nailContext)
+         ~usage
+         ~arguments
+         ~@body))))

-(gen-class
-  :name    de.kotka.gorilla.nails.NamespaceInfo
-  :prefix  NamespaceInfo-
-  :methods [#^{:static true}
-            [nailMain [com.martiansoftware.nailgun.NGContext] void]]
-  :main    false)
+(defnail DocLookup
+  "Usage: ng de.kotka.gorilla.nails.DocString [options] [--] symbol ..."
+  [[namespace n "Lookup the symbols in the given namespace." "user"]
+   symbols]
+  (print (doc-lookup namespace symbols))
+  (flush))

-(defn NamespaceInfo-nailMain
-  [#^NGContext context]
-  (with-command-line (.getArgs context)
-    "Usage: ng de.kotka.gorilla.nails.NamespaceInfo namespace ..."
-    [namespaces]
-    (println (clj->vim (map #(-> % symbol find-ns ns-info) namespaces)))))
+(defnail NamespaceInfo
+  "Usage: ng de.kotka.gorilla.nails.NamespaceInfo [--] namespace ..."
+  [namespaces]
+  (println (clj->vim (map #(-> % symbol find-ns ns-info) namespaces))))


add db constructors and rework schema

add db constructors and rework schema

--- src/mvMusic/library/schema.clj
+++ src/mvMusic/library/schema.clj
@@ -4,85 +4,114 @@
             [clojure.java.jdbc :as jdb]
             [mvMusic.log :as log]))

-(def db
+(defn get-db-spec
+  [& path]
+  "return db spec map. If path is given then db spec for that path will be
+  returned, otherwise cfg-map will determine location."
   {:classname "org.h2.Driver"
    :subprotocol "h2"
-   :subname (str (:temp-folder cfg-map)
-                 (if (= (last (:temp-folder cfg-map)) \/)
-                   ""
-                   \/)
-                 "library")})
+   :subname (if (first path)
+              path
+              (str (:temp-folder cfg-map)
+                   (if (= (last (:temp-folder cfg-map)) \/)
+                     ""
+                     \/)
+                   "library"))})

 (def folders
   (jdb/create-table-ddl :folders
-                        [:folder "VARCHAR(1024)" "PRIMARY KEY"]
-                        [:mod_date :TIMESTAMP]
-                        [:image :bool]))
+                        [:folder "VARCHAR" "PRIMARY KEY"]
+                        [:mod_date :TIMESTAMP]))

 (def songs
   (jdb/create-table-ddl :songs
-                        [:file "VARCHAR(1024) PRIMARY KEY"]
+                        [:file :nvarchar "PRIMARY KEY"]
                         [:track :integer]
-                        [:disc :integer]
-                        [:title "VARCHAR(1024)"]
-                        [:artist "VARCHAR(1024)"]
-                        [:time :integer]
-                        [:published :integer]
-                        [:extension "VARCHAR(1024)"]
-                        [:genre "VARCHAR(1024)"]
-                        [:folder "VARCHAR(1024)"]
-                        [:filename "VARCHAR(1024)"]
-                        [:path "VARCHAR(1024)"]
-                        [:mod_date :TIMESTAMP]
-                        [:album_title "VARCHAR(1024)"]
-                        [:album_artist "VARCHAR(1024)"]
-                        [:album_sort "VARCHAR(1024)"]
-                        [:image :bool]
-                        [:other :text]
+                        [:track_total :integer]
+                        [:disc_no :integer]
+                        [:disc_total :integer]
+                        [:title :nvarchar]
+                        [:artist :nvarchar]
+                        [:album :nvarchar]
+                        [:album_artist :nvarchar]
+                        [:year :integer]
+                        [:genre :nvarchar]
+                        [:comment :nvarchar]
+                        [:composer :nvarchar]
+                        [:original_artist :nvarchar]
+                        [:remixer :nvarchar]
+                        [:conductor :nvarchar]
+                        [:bmp :integer]
+                        [:grouping :nvarchar]
+                        [:isrc :nvarchar]
+                        [:record-label :nvarchar]
+                        [:encoder :nvarchar]
+                        [:lyricist :nvarchar]
+                        [:lyrics :nclob]
+                        [:artwork_mime :nvarchar]
+                        [:artwork_data :blob]
+                        [:bit_rate :integer]
+                        [:channels :integer]
+                        [:encoding_type :nvarchar]
+                        [:format :nvarchar]
+                        [:sample_rate :integer]
+                        [:length :integer]
+                        [:variable_bit_rate :boolean]
+                        [:folder :nvarchar]
+                        [:filename :nvarchar]
+                        [:mod_date :timestamp]
                         ["foreign key(folder) REFERENCES folders(folder)"]))

-(def downloads
-  (jdb/create-table-ddl :downloads
-                        [:file "VARCHAR(1024) PRIMARY KEY"]
-                        [:name "VARCHAR(1024)"]
-                        [:created :timestamp]
-                        [:filetypes "VARCHAR(1024)"]
-                        [:bitrates "VARCHAR(1024)"]))
-
-(def songs-downloads
-  (jdb/create-table-ddl :songs_downloads
-                        [:song_file (str "VARCHAR(1024) REFERENCES songs(file)"
-                                         " ON DELETE CASCADE")]
-                        [:download_file
-                         (str "VARCHAR(1024) REFERENCES downloads(file)"
-                              " ON DELETE CASCADE")]))
+;(def downloads
+;  (jdb/create-table-ddl :downloads
+;                        [:file "VARCHAR(1024) PRIMARY KEY"]
+;                        [:name "VARCHAR(1024)"]
+;                        [:created :timestamp]
+;                        [:filetypes "VARCHAR(1024)"]
+;                        [:bitrates "VARCHAR(1024)"]))
+;
+;(def songs-downloads
+;  (jdb/create-table-ddl :songs_downloads
+;                        [:song_file (str "VARCHAR(1024) REFERENCES songs(file)"
+;                                         " ON DELETE CASCADE")]
+;                        [:download_file
+;                         (str "VARCHAR(1024) REFERENCES downloads(file)"
+;                              " ON DELETE CASCADE")]))

 (def folder-idx "CREATE INDEX songs_folder ON songs (folder)")

-(defn create-db
-  "Creates database and all tables and indexes at location in cfg-map. "
-  [db]
+(defn create-db!
+  "Creates database and all tables and indexes using db-spec"
+  [db-spec]
   (try
-    (jdb/db-do-commands db folders songs downloads songs-downloads)
-    (jdb/db-do-commands db folder-idx)
+    (jdb/db-do-commands db-spec folders songs)
+    (jdb/db-do-commands db-spec folder-idx)
     (catch Exception ex
       (log/error ex "Error creating database")
       #_(System/exit (:db-failure exit-codes)))))

-(defn drop-db
+(defn drop-db!
   "Deletes database file if it exists. Returns true if database deleted. Nil if
   database didn't exist."
-  [db]
+  [db-spec]
   (try
     (jdb/db-do-commands db "DROP ALL OBJECTS DELETE FILES")
     (catch Exception ex
       (log/error ex "Error creating database")
       #_(System/exit (:db-failure exit-codes)))))

-(defn recreate-db
+(defn recreate-db!
   "Deletes database and then recreates all tables and indexes without data."
-  [db]
-  (drop-db db)
-  (create-db db))
+  [db-spec]
+  (drop-db! db-spec)
+  (create-db! db-spec))
+
+(defn get-db!
+  "Get db-spec and create db if it doesn't already exist."
+  [& db-spec]
+  (let [db-spec (if (empty? db-spec)  (get-db-spec) (first db-spec))]
+    (when-not (.exists (io/as-file (str (:subname (get-db-spec)) ".mv.db")))
+      (create-db! db-spec))
+    db-spec))


Add demo information

Add demo information

--- src/obb_demo/views/player.cljs
+++ src/obb_demo/views/player.cljs
@@ -196,4 +196,11 @@
        (rotate-panel game-data)
        [:button.btn.btn-default {:on-click (partial reset-turn game-data)} "Reset turn"]]
       [:div.col-lg-5
-        [boardground/render {} game-data]]]))
+        [boardground/render {} game-data]]
+      [:div.col-lg-5
+       [:div.jumbotron
+        [:h1 "Demo"]
+        [:p "This is a demo that showcases the gameplay of Orion's Belt against a simple AI."]
+        [:p "It's your turn to play. Perform your actions and then click Play turn."]
+        [:p "Pro tip: on the top menu you can see CPU vs CPU and also the
+            traits of all units."]]]]))


Fix vector's movement type

Fix vector's movement type

--- src/obb_rules/units/vector.clj
+++ src/obb_rules/units/vector.clj
@@ -17,6 +17,6 @@
    :category :medium
    :displacement :air

-   :movement-type :diagonal
+   :movement-type :normal
    :movement-cost 3})


Added variation.

Added variation.

--- src/overtunes/songs/at_all.clj
+++ src/overtunes/songs/at_all.clj
@@ -79,10 +79,28 @@
     (even-melody# (map ionian [-1 -2 -3 0 0 -3 0 1 0 -3])))
   (rhythm-n-bass# timing (take 8 (cycle progression))))

+(defn variation# [timing]
+  (-> timing
+    (speed-up 2)
+    (from 9)
+    (even-melody# (map ionian [11 11 12 9 7]))
+    (from 11)
+    (even-melody# (map ionian [8 8 9 8 3]))
+    (from 11)
+    (even-melody# (map ionian [8 8 9 6 4]))
+    (from 11)
+    (even-melody# (map ionian [11 11 12 11 8])))
+  (first-bit# timing))
+
+
 (defn final-chord# [timing]
+  (-> timing
+    (from -1)
+    (speed-up 2)
+    (even-melody# (map ionian [11 13 14])))
   (at (timing 0)
       (chord# (update-in I [:i] raise))))

-(defn play# [] (-> (bpm 120) (from 2) intro# first-bit# (speed-up 3/2) first-bit# final-chord#))
+(defn play# [] (-> (bpm 120) (from 2) intro# first-bit# (speed-up 3/2) variation# final-chord#))

-; (play#)
+;(play#)


Document the functions.

Document the functions.

--- src/overtunes/songs/before.clj
+++ src/overtunes/songs/before.clj
@@ -2,54 +2,75 @@
   (:use [overtone.live])
   (:use [overtunes.instruments.organ-cornet]))

-(def melody [:C3 :Bb3 :Eb3 :G3 :Eb3])
+(def melody
+  "A crude five-note melody that will constantly fall out of time when played
+  over chords at a rate of two notes per chord."
+  [:C3 :Bb3 :Eb3 :G3 :Eb3])

-(def start [    (chord :Eb3 :major)
-                (chord :Eb3 :major)
-                (chord :C3  :minor7)
-                (chord :C3  :minor7)
-                (chord :Eb3 :major)
-                (chord :Eb3 :major)
-                (chord :C3  :minor7)
-                (chord :C3  :minor7)])
+(def start
+  "In which we establish the key and give the syncopated relationship between
+  the melody and the chords time to become apparent."
+  [(chord :Eb3 :major)
+   (chord :Eb3 :major)
+   (chord :C3  :minor7)
+   (chord :C3  :minor7)
+   (chord :Eb3 :major)
+   (chord :Eb3 :major)
+   (chord :C3  :minor7)
+   (chord :C3  :minor7)])

-(def middle [   (chord :G3  :minor)
-                (chord :G3  :minor)
-                (chord :Ab3 :major)
-                (chord :Bb3 :major)
-                (chord :Eb4 :major)
-                (chord :Eb4 :major)
-                (chord :Bb3 :major)
-                (chord :Bb3 :major)])
+(def middle
+  "In which we let the melody combine in interesting ways with a developing
+  progression that builds and then resolves."
+  [(chord :G3  :minor)
+   (chord :G3  :minor)
+   (chord :Ab3 :major)
+   (chord :Bb3 :major)
+   (chord :Eb4 :major)
+   (chord :Eb4 :major)
+   (chord :Bb3 :major)
+   (chord :Bb3 :major)])

-(def variation [(chord :G3  :minor)
-                (chord :G3  :minor)
-                (chord :Ab3 :major)
-                (chord :Ab3 :major)
-                (chord :G3  :minor)
-                (chord :G3  :minor)
-                (chord :Ab3 :major)
-                (chord :Ab3 :major)
-                (chord :G3  :minor)
-                (chord :G3  :minor)
-                (chord :Ab3 :major)
-                (chord :Bb3 :major)
-                (chord :Eb4 :major)
-                (chord :Eb4 :major)
-                (chord :Bb3 :major)
-                (chord :Bb3 :major)])
+(def variation
+  "In which we elaborate on the middle section."
+  [(chord :G3  :minor)
+   (chord :G3  :minor)
+   (chord :Ab3 :major)
+   (chord :Ab3 :major)
+   (chord :G3  :minor)
+   (chord :G3  :minor)
+   (chord :Ab3 :major)
+   (chord :Ab3 :major)
+   (chord :G3  :minor)
+   (chord :G3  :minor)
+   (chord :Ab3 :major)
+   (chord :Bb3 :major)
+   (chord :Eb4 :major)
+   (chord :Eb4 :major)
+   (chord :Bb3 :major)
+   (chord :Bb3 :major)])

-(def finish [   (chord :Eb4 :major)
-                (chord :Eb4 :major)])
+(def finish
+  "In which we return to root for resolution at the end of the piece."
+  [(chord :Eb4 :major)
+   (chord :Eb4 :major)])

-(defn beat-length [metro] (- (metro 1) (metro 0)))
+(defn beat-length
+  "Determines the beat length in milliseconds of metro."
+  [metro] (- (metro 1) (metro 0)))

-(defn play-note [tone instrument duration]
+(defn play-note
+  "Plays a single tone on instrument for duration, assuming an instrument that
+  takes a frequency in Hz and a duration in seconds.
+  (play-note :C4 organ-cornet 1.2)"
+  [tone instrument duration]
   (let [ seconds (/ duration 1000)
          frequency (midi->hz (note tone))]
     (instrument frequency seconds)))

 (defn play-chord [tones instrument duration]
+  "Plays a seq of tones as a chord on instrument for duration.
+  (play-chord (chord :C4 :major) organ-cornet 1.2)"
   (if (not (empty? tones))
     (let [ root (first tones)
            bass [(- root 12) (- root 24)]
@@ -57,32 +78,43 @@
       (doall (map (fn [tone] (play-note tone instrument duration)) with-bass)))))

 (defn play-progression [progression metro start]
-  (let [ beats-per-chord 4
+  "Plays a seq of chords for two beats each on the cornet.
+  Takes a metronome and a starting beat in addition to the chord progression.
+  (play-progression [root fourth fifth] metro (metro))"
+  (let [ beats-per-chord 2
          duration (* beats-per-chord (beat-length metro))]
     (when-not (empty? progression)
       (at (metro start) (play-chord (first progression) organ-cornet duration))
       (play-progression (rest progression) metro (+ start beats-per-chord)))))

 (defn play-melody [melody metro start]
-  (let [ beats-per-note 2
-         duration (* beats-per-note (beat-length metro))]
+  "Plays a seq of notes on the cornet.
+  Takes a metronome and a starting beat in addition to the melody.
+  (play-progression [root fourth fifth] metro (metro))"
+  (let [duration (beat-length metro)]
     (when-not (empty? melody)
       (at (metro start) (play-note (first melody) organ-cornet duration))
-      (play-melody (rest melody) metro (+ start beats-per-note)))))
+      (play-melody (rest melody) metro (+ start 1)))))

 (defn cycle-n
-  "Returns a new seq which is cycled n times
+  "Returns a new seq which is cycled n times.
   (cycle-n 2 [1 2 3]) ;=> [1 2 3 1 2 3]"
   [n s]
   (take (* (count s) n) (cycle s)))

 (defn play [chords metro]
-  (let [ melody-line (cycle-n (/ (count chords) 2.5) melody)]
+  "Play the melody over the chords to metro's time."
+  (let
+  [ repetitions-per-chord (/ (count melody) 2)
+    melody-line (cycle-n (/ (count chords) repetitions-per-chord) melody)]
   (do
     (play-melody melody-line metro (metro))
     (play-progression (concat chords finish) metro (metro)))))

 (defn full-version []
-  (play (concat start middle start middle variation) (metronome 60)))
+  "The full version of 'Before', grave."
+  (play (concat start middle start middle variation) (metronome 30)))

-(defn short-version [] (play variation (metronome 200)))
+(defn short-version []
+  "A short version of 'Before', adante."
+  (play variation (metronome 100)))