Update FileBlobStore.

Update FileBlobStore.

--- src/vault/blob/store/file.clj
+++ src/vault/blob/store/file.clj
@@ -5,25 +5,25 @@
     [clojure.string :as string]
     [vault.blob.core :as blob :refer [BlobStore]])
   (:import
-    (java.io
-      File)))
+    java.io.File
+    java.util.Date))

 ;; HELPER FUNCTIONS

-(defn- hashid->file
+(defn- id->file
   ^File
   [root id]
   (let [id (blob/hash-id id)
         {:keys [algorithm digest]} id]
     (io/file root
-             (name algorithm)
-             (subs digest 0 3)
-             (subs digest 3 6)
-             (subs digest 6))))
+      (name algorithm)
+      (subs digest 0 3)
+      (subs digest 3 6)
+      (subs digest 6))))

-(defn- file->hashid
+(defn- file->id
   [root file]
   (let [root (str root)
         file (str file)]
@@ -44,7 +44,7 @@

 (defn- enumerate-files
-  "Generates a lazy sequence of file blobs contained in a root directory."
+  "Generates a lazy sequence of blob files contained in a root directory."
   [^File root]
   ; TODO: intelligently skip entries based on 'after'
   (flatten
@@ -53,6 +53,16 @@
         (for-files [midfix-dir prefix-dir]
           (for-files [blob midfix-dir]
             blob))))))
+
+
+(defmacro ^:private when-blob-file
+  "This is an unhygenic macro which binds the blob file to 'file' and executes
+  the body only if it exists."
+  [store id & body]
+  `(let [~(with-meta 'file {:tag 'java.io.File})
+         (id->file (:root ~store) ~id)]
+     (when (.exists ~'file)
+       ~@body)))

@@ -66,29 +76,27 @@
 (extend-protocol BlobStore
   FileBlobStore

-  (-list [this opts]
+  (enumerate [this opts]
     (->> (enumerate-files (:root this))
-         (map (partial file->hashid (:root this)))
+         (map (partial file->id (:root this)))
          (blob/select-ids opts)))

-  (-stat [this id]
-    (let [file (hashid->file (:root this) id)]
-      (when (.exists file)
-        {:size (.length file)
-         :stored-at (java.util.Date. (.lastModified file))
-         :location (.toURI file)})))
+  (stat [this id]
+    (when-blob-file this id
+      {:size (.length file)
+       :stored-at (Date. (.lastModified file))
+       :location (.toURI file)}))

-  (-open [this id]
-    (let [file (hashid->file (:root this) id)]
-      (when (.exists file)
-        (byte-streams/to-input-stream file))))
+  (open [this id]
+    (when-blob-file this id
+      (io/input-stream file)))

-  (-store! [this blob]
+  (store! [this blob]
     (let [{:keys [id content]} blob
-          file (hashid->file (:root this) id)]
+          file (id->file (:root this) id)]
       (when-not (.exists file)
         (io/make-parents file)
         ; For some reason, io/copy is much faster than byte-streams/transfer here.
@@ -96,10 +104,18 @@
         (.setWritable file false false))))

-  (-remove! [this id]
-    (let [file (hashid->file (:root this) id)]
-      (when (.exists file)
-        (.delete file)))))
+  (delete! [this id]
+    (when-blob-file this id
+      (.delete file)))
+
+
+  (destroy!! [this]
+    (let [rm-r (fn rm-r [^File path]
+                 (when (.isDirectory path)
+                   (doseq [child (.listFiles path)]
+                     (rm-r child)))
+                 (.delete path))]
+      (rm-r (:root this)))))

 (defn file-store


renamed vars

renamed vars

--- clojure/atbash-cipher/atbash.clj
+++ clojure/atbash-cipher/atbash.clj
@@ -1,19 +1,23 @@
 (ns atbash
-  (require [clojure.string :as string :only [lower-case]]))
+  (require [clojure.string :as string :only [lower-case join]]))

-(let [letters (map char (range
-                         (int \a)
-                         (inc (int \z))))
-      total-letters (dec (count letters))]
+(let [alphabet (map #(str (char %)) (range
+                                    (int \a)
+                                    (inc (int \z))))
+      alphabet-set (set alphabet)
+      total (dec (count alphabet))]
   (defn- transpose
     [c]
-    (->> c
-         (.indexOf letters)
-         (- total-letters)
-         (nth letters)))
+    (if (contains? alphabet-set c)
+      (let [complement (- total (.indexOf alphabet c))]
+        (nth alphabet complement))
+      c))
   (defn encode
     [s]
     (->> s
          string/lower-case
+         (re-seq #"\w")
          (map transpose)
-         (apply str))))
+         (partition 5 5 [])
+         (map #(apply str %))
+         (string/join " "))))


Remove security where not needed

Remove security where not needed


Bug fix

Bug fix

--- src/cia/models.clj
+++ src/cia/models.clj
@@ -86,7 +86,7 @@
    })

 (def Scope
-  (s/either "inclusive" "exclusive"))
+  (s/enum "inclusive" "exclusive"))

 (def HighMedLow
   "See http://stixproject.github.io/data-model/1.2/stixVocabs/HighMediumLowVocab-1.0/"


Use threading macro

Use threading macro

--- src-cljs/game_of_life/core.cljs
+++ src-cljs/game_of_life/core.cljs
@@ -6,11 +6,6 @@
           :when (not= 0 dx dy)]
       [(+ dx x) (+ dy y)])))

-;; collect all neighbors of live cells
-;; count their frequencies
-;; if frequency == 2 then add to living set ONLY if alive
-;; if frequency == 3 then add to living set
-;;
 (defn should-live?
   [living-cells [location occurrence-count]]
   (or (= occurrence-count 3)
@@ -18,7 +13,10 @@
            (contains? living-cells location))))

 (defn advance [living-cells]
-  (set
-    (map first
-      (filter (partial should-live? living-cells)
-              (frequencies (mapcat neighbors living-cells))))))
+  (->>
+    living-cells
+    (mapcat neighbors)
+    (frequencies)
+    (filter (partial should-live? living-cells))
+    (map first)
+    set))


CMR-4283: Added more tests.

CMR-4283: Added more tests.

--- system-int-test/test/cmr/system_int_test/search/service/service_association_test.clj
+++ system-int-test/test/cmr/system_int_test/search/service/service_association_test.clj
@@ -266,3 +266,99 @@
            (dissociate-service-fn token concept-id request-json))

         au/dissociate-by-concept-ids [{:concept-id coll-concept-id}]))))
+
+(deftest dissociate-services-with-mixed-response-test
+  (e/grant-registered-users (s/context) (e/coll-catalog-item-id "PROV1"))
+  (testing "dissociate service with mixed success and failure response"
+    (let [coll1 (d/ingest "PROV1" (dc/collection {:entry-title "ET1"}))
+          coll2 (d/ingest "PROV1" (dc/collection {:entry-title "ET2"}))
+          coll3 (d/ingest "PROV1" (dc/collection {:entry-title "ET3"}))
+          token (e/login (s/context) "user1")
+          service-name "service1"
+          {:keys [concept-id]} (su/ingest-service-with-attrs {:Name service-name})
+          assert-service-associated (partial su/assert-service-associated-with-query
+                                              token {:service-concept-id concept-id})]
+
+      (index/wait-until-indexed)
+      (au/associate-by-concept-ids token concept-id [{:concept-id (:concept-id coll1)}
+                                                        {:concept-id (:concept-id coll2)
+                                                         :revision-id (:revision-id coll2)}])
+      (assert-service-associated [coll1 coll2])
+
+      (let [response (au/dissociate-by-concept-ids
+                      token concept-id
+                      [{:concept-id "C100-P5"} ;; non-existent collection
+                       {:concept-id (:concept-id coll1)} ;; success
+                       {:concept-id (:concept-id coll2) :revision-id 1} ;; success
+                       {:concept-id (:concept-id coll3)}])] ;; no service association
+
+        (su/assert-service-dissociation-response-ok?
+         {["C100-P5"] {:errors ["Collection [C100-P5] does not exist or is not visible."]}
+          ["C1200000012-PROV1"] {:concept-id "SA1200000016-CMR" :revision-id 2}
+          ["C1200000013-PROV1" 1] {:concept-id "SA1200000017-CMR" :revision-id 2}
+          ["C1200000014-PROV1"]
+          {:warnings [(format "Service [%s] is not associated with collection [C1200000014-PROV1]."
+                              concept-id)]}}
+         response)
+        (assert-service-associated [])))))
+
+(deftest associate-dissociate-service-with-collections-test
+  ;; Grant all collections in PROV1
+  (e/grant-registered-users (s/context) (e/coll-catalog-item-id "PROV1"))
+  (let [[coll1 coll2 coll3] (doall (for [n (range 1 4)]
+                                     (d/ingest "PROV1" (dc/collection))))
+        [coll1-id coll2-id coll3-id] (map :concept-id [coll1 coll2 coll3])
+        token (e/login (s/context) "user1")
+        {service1-concept-id :concept-id} (su/ingest-service-with-attrs {:Name "service1"})
+        {service2-concept-id :concept-id} (su/ingest-service-with-attrs {:Name "service2"})
+        assert-service-association (fn [concept-id colls]
+                                     (su/assert-service-associated-with-query
+                                      token {:service-concept-id concept-id} colls))]
+    (index/wait-until-indexed)
+
+    ;; associate service1 to coll1, service2 to coll2
+    ;; both :concept-id and :concept_id works as keys
+    (au/associate-by-concept-ids token service1-concept-id [{:concept_id coll1-id}])
+    (au/associate-by-concept-ids token service2-concept-id [{:concept-id coll2-id}])
+    (index/wait-until-indexed)
+    ;; verify association
+    (assert-service-association service1-concept-id [coll1])
+    (assert-service-association service2-concept-id [coll2])
+
+    ;; associate service1 to coll1 again
+    (au/associate-by-concept-ids token service1-concept-id [{:concept-id coll1-id}])
+    (index/wait-until-indexed)
+    ;; verify association
+    (assert-service-association service1-concept-id [coll1])
+    (assert-service-association service2-concept-id [coll2])
+
+    ;; associate service1 to coll2
+    (au/associate-by-concept-ids token service1-concept-id [{:concept-id coll2-id}])
+    (index/wait-until-indexed)
+    ;; verify association
+    (assert-service-association service1-concept-id [coll1 coll2])
+    (assert-service-association service2-concept-id [coll2])
+
+    ;; associate service2 to coll1, coll2 and coll3
+    (au/associate-by-concept-ids token service2-concept-id [{:concept-id coll1-id}
+                                                            {:concept-id coll2-id}
+                                                            {:concept-id coll3-id}])
+    (index/wait-until-indexed)
+    ;; verify association
+    (assert-service-association service1-concept-id [coll1 coll2])
+    (assert-service-association service2-concept-id [coll1 coll2 coll3])
+
+    ;; dissociate service1 from coll1
+    (au/dissociate-by-concept-ids token service1-concept-id [{:concept-id coll1-id}])
+    (index/wait-until-indexed)
+    ;; verify association
+    (assert-service-association service1-concept-id [coll2])
+    (assert-service-association service2-concept-id [coll1 coll2 coll3])
+
+    ;; dissociate service2 from coll1 and coll2
+    (au/dissociate-by-concept-ids token service2-concept-id [{:concept-id coll1-id}
+                                                             {:concept-id coll2-id}])
+    (index/wait-until-indexed)
+    ;; verify association
+    (assert-service-association service1-concept-id [coll2])
+    (assert-service-association service2-concept-id [coll3])))


[scamp] remove deprecated 'comm-test-world

--- modules/scamp/test/scamp/core_test.clj
+++ modules/scamp/test/scamp/core_test.clj
@@ -54,12 +54,6 @@
         (is (= result
                [[:message-envelope "stuffier-node" :forwarded-subscription "allergen-free-node"]]))))))

-(def comm-test-world
-  (-> core/new-world
-      (core/add-new-node (core/node-contact-address->node "node-id0"))
-      (core/subscribe-new-node "node-id1" "node-id0")
-      (core/subscribe-new-node "node-id2" "node-id1")
-      (core/subscribe-new-node "node-id3" "node-id2")))

 (deftest do-comm-test
   (scamp-test


CMR-5806 CMR-5809 Updates doc string.

CMR-5806 CMR-5809 Updates doc string.

--- common-app-lib/src/cmr/common_app/api/routes.clj
+++ common-app-lib/src/cmr/common_app/api/routes.clj
@@ -227,7 +227,7 @@
       ((ring-json/wrap-json-response handler) request))))

 (defn add-security-header-response-handler
-  ""
+  "Adds a number of security related response headers."
   [handler]
   (fn [{context :request-context :as request}]
     (if-let [request-id (cxt/context->request-id context)]


Eliminate reflection from encode/decode paths.

--- core/src/immutant/codecs.clj
+++ core/src/immutant/codecs.clj
@@ -17,7 +17,8 @@
   (:require [clojure.tools.reader.edn :as edn]
             [clojure.tools.reader     :as r]
             [immutant.internal.util   :as u])
-  (:import [org.projectodd.wunderboss.codecs BytesCodec Codecs None StringCodec]))
+  (:import [org.projectodd.wunderboss.codecs BytesCodec Codec Codecs None StringCodec]
+           java.nio.ByteBuffer))

 (defmacro data-readers []
   (if (resolve 'clojure.core/*data-readers*)
@@ -46,7 +47,7 @@
      (decode [data#]
        (~decode data#))))

-(defonce ^:private codecs
+(defonce ^:private ^Codecs codecs
   (-> (Codecs.)
     (.add None/INSTANCE)))

@@ -77,9 +78,9 @@
      :type :bytes
      :encode (fn [data]
                (if-let [write (u/try-resolve 'clojure.data.fressian/write)]
-                 (let [data (write data :footer? true)
-                       bytes (byte-array (.remaining data))]
-                   (.get data bytes)
+                 (let [^ByteBuffer encoded (write data :footer? true)
+                       bytes (byte-array (.remaining encoded))]
+                   (.get encoded bytes)
                    bytes)
                  (throw (IllegalArgumentException.
                           "Can't encode fressian. Add org.clojure/data.fressian to your dependencies."))))
@@ -117,7 +118,7 @@
   []
   (into #{} (map #(-> % .name keyword) (.codecs codecs))))

-(defn lookup-codec
+(defn ^Codec lookup-codec
   [name-or-content-type]
   (if-let [codec (.forName codecs (name name-or-content-type))]
     codec


Remove print statement

Remove print statement

--- src/angelic/search/action_set/stratified.clj
+++ src/angelic/search/action_set/stratified.clj
@@ -208,7 +208,7 @@
                         :structural (fn [last-a state] (improved-inf-stratified-applicable-actions last-a state tsi cav-map actions))                       :dynamic    (fn [last-a state] (dynamic-inf-stratified-applicable-actions last-a state tsi av-map ap-map actions))
                         :fluid    (fn [last-a state] (dynamic-fluid-inf-stratified-applicable-actions last-a state tsi av-map ap-map goal-map cg-map icg-map actions))
                         )]
-    (println tsi)
+;    (println tsi)
     (when (#{:dynamic :fluid} type) #_ (println av-map)
       (doseq [a (:actions sas-problem), pp (:precond-map a)]
         (.put ap-map pp (cons a (.get ap-map pp)))))


Throttle probe write messages.

Throttle probe write messages.

--- src/cavm/h2.clj
+++ src/cavm/h2.clj
@@ -579,6 +579,7 @@
           row-count (atom 0)
           data (matrix-fn)
           warnings (atom (meta data))
+          last-log (atom 0)
           inserts (lazy-mapcat #(do
                                   (swap! row-count max (count (force (:rows %))))
                                   (load-field-feature
@@ -592,7 +593,10 @@
         (jdbcd/transaction
           (doseq [[insert-type values] insert-batch]
             (when (= :insert-field insert-type)
-              (trace "writing" (:name values)))
+              (let [t (System/currentTimeMillis)]
+                (when (> (- t @last-log) 10000)
+                  (trace "writing" (:name values))
+                  (reset! last-log t))))
             (let [values (run-delays values)
                   values (if (and (= :insert-field insert-type)
                                   (> (.length ^String (:name values)) max-probe-length))


Add some tests to decide if a symbol is free

--- test/desdemona/query_test.clj
+++ test/desdemona/query_test.clj
@@ -153,3 +153,17 @@

     #{'x}
     '(clojure.core.logic/featurec x {:ip "10.0.0.1"})))
+
+(deftest free-sym-tests
+  (is (#'q/free-sym 'x))
+  (is (#'q/free-sym 'y))
+
+  (is (not (#'q/free-sym 'clojure.core/=))
+      "fully qualified")
+  (is (not (#'q/free-sym 1))
+      "not a symbol"))
+
+    ;; #{'x}
+    ;; '(clojure.core.logic/conde
+    ;;   [(clojure.core.logic/featurec x {:ip "10.0.0.1"})
+    ;; (clojure.core.logic/featurec x {:type "egress"})])


Show new lifecycle duration spec examples

--- src/demo/cache.clj
+++ src/demo/cache.clj
@@ -10,7 +10,7 @@
 (cache/put foo :a 1)

 ;;; Override its time-to-live
-(cache/put foo :a 1 {:ttl 1, :units :hours})
+(cache/put foo :a 1 {:ttl [1 :hour]})

 ;;; Add all the entries in the map to the cache
 (cache/put-all foo {:b 2, :c 3})
@@ -79,7 +79,7 @@

 ;;; Other than the function to be memoized, arguments are the same as
 ;;; for the cache function.
-(def memoized-fn (cache/memo slow-fn "memo", :ttl 5, :units :minutes))
+(def memoized-fn (cache/memo slow-fn "memo", :ttl [5 :minutes]))

 ;;; Invoking the memoized function fills the cache with the result
 ;;; from the slow function the first time it is called.


Tweaks to reactive updating.

Tweaks to reactive updating.

--- src/clojure/freactive/experimental/dom2.cljs
+++ src/clojure/freactive/experimental/dom2.cljs
@@ -364,11 +364,15 @@
                                  (set! (.-updating state) false)
                                  (if (.-disposed state)
                                    (remove-dom-node cur)
-                                   (let [new-node (replace-child parent new-elem cur)]
+                                   (let [new-elem (if (.-dirty state)
+                                                    (do
+                                                      (set! (.-dirty false))
+                                                      (add-watch* child-ref state (.-invalidate state))
+                                                      @child-ref)
+                                                    new-elem)
+                                         new-node (replace-child parent new-elem cur)]
                                      (set! (.-cur-element state) new-node)
-                                     (show-node new-node nil)
-                                     (when (.-dirty state)
-                                       (queue-animation animate))))))
+                                     (show-node new-node nil)))))
                     ;(set! (.-cur-element state)
                     ;      (transition-element
                     ;        parent


Integrate latency measurement in out-fn.

--- src/dsbdp/main.clj
+++ src/dsbdp/main.clj
@@ -24,7 +24,7 @@
       [parallel-processing :refer :all]
       [processing-fn-utils :as utils]))
   (:import
-    (dsbdp Counter ExperimentHelper ProcessingLoop)
+    (dsbdp Counter ExperimentHelper LatencyProbe LatencyProbeCollector ProcessingLoop)
     (java.lang.management ManagementFactory ThreadInfo ThreadMXBean)
     (java.util HashMap Map))
   (:gen-class))
@@ -257,7 +257,17 @@
     (println "Using args:" arguments)
     (let [in-cntr (Counter.)
           out-cntr (Counter.)
-          out-fn (fn [_] (.inc out-cntr))
+          latency-probe-collector (LatencyProbeCollector.)
+          out-fn (if (:latency options)
+                   (do
+                     (println "Creating latency measurement out-fn.")
+                     (fn [^LatencyProbe lp]
+                       (.done lp)
+                       (.addProbe latency-probe-collector lp)
+                       (.inc out-cntr)))
+                   (do
+                     (println "Creating default measurement out-fn.")
+                     (fn [_] (.inc out-cntr))))
           delta-cntr (delta-counter)
           ^String scenario (:scenario options)
           in-data (prepare-in-data options)


Move out-fn closer to pipeline definition.

--- src/dsbdp/main.clj
+++ src/dsbdp/main.clj
@@ -107,8 +107,6 @@
     (let [in-cntr (Counter.)
           out-cntr (Counter.)
           delta-cntr (delta-counter)
-          out-fn (fn [_ _]
-                   (.inc out-cntr))
           ^String scenario (:scenario options)
           in-data-arg (:in-data options)
           in-data (if (not (nil? in-data-arg))
@@ -169,6 +167,8 @@
                                                          synthetic-high-throughput-self-adaptivity-processing-fns)
                        nil))
           _ (println "Proc-fns:" @proc-fns)
+          out-fn (fn [_ _]
+                   (.inc out-cntr))
           pipeline (if (and
                          (not (nil? in-data))
                          (not (.endsWith scenario "-direct")))


asterix bug fixed

asterix bug fixed

--- src/etaoin/api.clj
+++ src/etaoin/api.clj
@@ -306,9 +306,9 @@
 (defn set-window-size
   "Sets new size for a window. Absolute precision is not guaranteed."
   ([driver {:keys [width height]}]
-   (set-window-size driver width height))
+   (set-window-size* driver width height))
   ([driver width height]
-   (set-window-size driver width height)))
+   (set-window-size* driver width height)))

 (defmulti ^:private set-window-position* dispatch-driver)

@@ -329,9 +329,9 @@
   "Sets new position for a window. Absolute precision is not
   guaranteed."
   ([driver {:keys [x y]}]
-   (set-window-position driver x y))
+   (set-window-position* driver x y))
   ([driver x y]
-   (set-window-position driver x y)))
+   (set-window-position* driver x y)))

 ;;
 ;; navigation


last minute tweaks to made with post

last minute tweaks to made with post

--- src/pc/views/blog/ideas_are_made_with_precursor.clj
+++ src/pc/views/blog/ideas_are_made_with_precursor.clj
@@ -49,7 +49,9 @@
    :body
    (list
     [:article
-     [:p "As our user base continues to grow, I'm exceedingly curious to see how people use Precursor. "
+     [:p "As our user base continues to grow, I'm exceedingly curious to see how people use "
+         [:a {:href "/" :title "What's Precursor?"} "Precursor"]
+         ". "
          "Recently we've been using an internal playback tool to learn about this. "]
      [:p "Today I'm excited to announce that we're now making this tool accessible to everyone. "
          "I've curated the following examples to showcase some of its key uses. "]]
@@ -57,7 +59,9 @@
     [:article
      [:h3 "Keep moving forward."]
      [:p  "This doc is a unique glimpse at the discipline it takes to be a great designer. "
-          "Eddie is well known for his exceptional work, and rather than waste time with elaborate sketches, he quickly opts for a simple placeholder to get the job done. "]]
+          "Eddie is known for "
+          [:a {:href "https://dribbble.com/shots/1754995-SF-Dribbble-Meetup" :title "SF Dribbble Meetup"} "exceptional illustrations"]
+          ", but he saves time here using placeholders, rather than elaborate sketches. "]]
     [:figure (common/card "lobanovskiy" "17592197312062?cx=1044&cy=580&z=0.45&replay=true")]

     [:article
@@ -99,17 +103,16 @@

     [:article
      [:h3 "I'd wear that."]
-     [:p  "To thank these users for letting me show their work, I'm sending them all custom t-shirts displaying their prototypes. "
-          "I want to do this again soon, so if you make something cool, let me know. "
-          "Email "
-          [:a {:href (format "mailto:%s" (email/email-address "hi"))} "hi@precursorapp.com"]
-          " with a link and if it gets featured, you'll get a one-of-a-kind t-shirt! "]]
+     [:p  "To thank them for showing off their work, everyone featured today will recieve a free t-shirt displaying their unique designs. "]
+     [:p  "Got something interesting, made with " [:a {:href "/new" :title "Make something."} "Precursor"] "? "]
+     [:p  "Tweet " [:a {:href "https://twitter.com/PrecursorApp"} "@PrecursorApp"] " and include #madewithprecursor. "
+          " I'll send you a one-of-a-kind shirt for free, if we feature it next time! "]]
     [:figure [:img {:src (external-cdn-path "/blog/ideas-are-made-with-precursor/eddie-shirt.png")}]]

     [:article.blogpost-author
      [:p
       "Written by "
       [:a {:href "https://twitter.com/dannykingme"} "Danny King"]
-      "."]]
+      ". "]]

     )})


added naître

added naître

--- src/babel/francais/lexicon.cljc
+++ src/babel/francais/lexicon.cljc
@@ -490,6 +490,20 @@
                                :3plur "mettent"}

                      :sem {:pred :put}}}
+
+  "naître" {:synsem {:cat :verb
+                     :future-stem "naîtr"
+                     :imperfect-stem "naiss"
+                     :passato "né"
+                     :present {:1sing "nais"
+                               :2sing "nais"
+                               :3sing "naît"
+                               :1plur "naissons"
+                               :2plur "naissez"
+                               :3plur "naissent"}
+
+                     :sem {:pred :be-born}}}
+
   "nous"
   (let [common {:synsem {:case :top
                          :agr {:person :1st


distinguish vp-aux with phrasal complement vs. non phrasal complement

--- src/babel/italiano/grammar.cljc
+++ src/babel/italiano/grammar.cljc
@@ -7,8 +7,10 @@
    [babel.over :refer [intersection-with-identity]]
    [babel.parse :as parse]
    [babel.ug :refer [comp-modifies-head comp-specs-head
-                     head-principle root-is-comp
-                     root-is-head root-is-head-root subcat-1-principle
+                     head-principle
+                     root-is-comp root-is-comp-root
+                     root-is-head root-is-head-root
+                     subcat-1-principle
                      subcat-1-1-principle subcat-2-principle
                      subcat-1-1-principle-comp-subcat-1
                      subcat-2-2-principle
@@ -399,15 +401,27 @@
                                      :infl :infinitive
                                      :cat :verb}})
                    (unifyc h21a
-                           root-is-comp
-                           {:rule "vp-aux"
+                           root-is-comp-root
+                           {:rule "vp-aux-phrasal-complement"
                             :head {:phrasal false
                                    :synsem {:aux true}}
+                            :comp {:phrasal true}
                             :synsem {:aux true
                                      :infl :present
                                      :sem {:tense :past}
                                      :cat :verb}})

+                   (unifyc h21a
+                           root-is-comp
+                           {:rule "vp-aux-nonphrasal-complement"
+                            :head {:phrasal false
+                                   :synsem {:aux true}}
+                            :comp {:phrasal false}
+                            :synsem {:aux true
+                                     :infl :present
+                                     :sem {:tense :past}
+                                     :cat :verb}})
+
                    ;; this rule is kind of complicated and made more so by
                    ;; dependence on auxilary sense of "avere" which supplies the
                    ;; obj-agr agreement between the object and the main (non-auxilary) verb.


vp-aux-phrasal-complement-passato: infl shared between parent and head

--- src/babel/italiano/grammar.cljc
+++ src/babel/italiano/grammar.cljc
@@ -425,16 +425,18 @@
                                      :infl :infinitive
                                      :cat :verb}})
                    (unify h21a
-                           root-is-comp-root
-                           {:rule "vp-aux-phrasal-complement-passato"
-                            :head {:phrasal false
-                                   :synsem {:aux true}}
-                            :comp {:phrasal true}
-                            :synsem {:aux true
-                                     :infl :present
-                                     :sem {:tense :past
-                                           :aspect :simple}
-                                     :cat :verb}})
+                          root-is-comp-root
+                          (let [infl (atom :top)]
+                            {:rule "vp-aux-phrasal-complement-passato"
+                             :head {:phrasal false
+                                    :synsem {:aux true
+                                             :infl infl}}
+                             :comp {:phrasal true}
+                             :synsem {:aux true
+                                      :infl infl
+                                      :sem {:tense :past
+                                            :aspect :simple}
+                                      :cat :verb}}))

                    (unify h21a
                           root-is-comp


add :applied feature to diagnose where rules are being applied

--- src/babel/italiano/lexicon.cljc
+++ src/babel/italiano/lexicon.cljc
@@ -205,12 +205,14 @@
                                 :sem sem}}}}))

       (default ;; a verb's first argument's case is nominative.
-       {:synsem {:cat :verb
+       {:applied {:subcat-1-is-nom true}
+        :synsem {:cat :verb
                  :subcat {:1 {:cat :noun
                               :case :nom}}}})

       (default ;; a verb's second argument's case is accusative.
-       {:synsem {:cat :verb
+       {:applied {:subcat-2-is-acc true}
+        :synsem {:cat :verb
                  :subcat {:2 {:cat :noun
                               :case :acc}}}})

@@ -387,7 +389,8 @@
       ;; <category-independent>
       (default ;; morphology looks in :italiano, so share relevant grammatical pieces of
        ;; info (:agr, :cat, :infl, and :essere) there so it can see them.
-       (:agreement defaults))
+       (unify (:agreement defaults)
+              {:applied {:agreement-defaults true}}))
       ;; </category-independent>

       phonize2 ;; for each value v of each key k, set the [:italiano :italiano] of v to k, if not already set


and elim left todo

and elim left todo

--- src/latte/prop.clj
+++ src/latte/prop.clj
@@ -1,4 +1,5 @@
 (ns latte.prop
+  (:refer-clojure :exclude [and or not])
   (:require [clj-by.example :refer [example do-for-example]])
   (:require [latte.core :as latte :refer [defterm term type-of defthm
                                           lambda forall assume proof]])
@@ -63,18 +64,18 @@
        (assume [f absurd]
          (qed (f A))))

-(defterm neg
+(defterm not
   "Logical negation."
   [[A :type]]
   (==> A absurd))

 (example
- (type-of neg) => '(Π [A ✳] ✳))
+ (type-of not) => '(Π [A ✳] ✳))

 (defthm absurd-intro
   "Introduction rule for absurdity."
   [[A :type]]
-  (==> A (neg A)
+  (==> A (not A)
        absurd))

 ;; (proof absurd-intro
@@ -86,39 +87,39 @@
 (proof absurd-intro
        :script
        (assume [x A
-                y (neg A)]
+                y (not A)]
          (have concl absurd :by (y x))
          (qed concl)))

-(defthm impl-neg-neg
+(defthm impl-not-not
   "The if half of double negation."
   [[A :type]]
-  (==> A (neg (neg A))))
+  (==> A (not (not A))))

 ;; (neg (neg A))
 ;; = (==> (neg A) absurd)
 ;; = (==> (==> A absurd) absurd)

-(proof impl-neg-neg
+(proof impl-not-not
        :script
        (assume [x A
-                H (neg A)]
+                H (not A)]
          (have step1 absurd :by (H x))
-         (have step2 (neg (neg A)) :discharge [H step1])
+         (have step2 (not (not A)) :discharge [H step1])
          (qed step2)))

-(defterm land
+(defterm and
   "logical conjunction."
   [[A :type] [B :type]]
   (forall [C :type]
     (==> (==> A B C)
       C)))

-(defthm land-intro
+(defthm and-intro
   "Introduction rule for logical conjunction."
   [[A :type] [B :type]]
   (==> A B
-       (land A B)))
+       (and A B)))

 ;; (proof land-intro
 ;;        :term
@@ -128,7 +129,7 @@
 ;;              (lambda [z (==> A B C)]
 ;;                z x y)))))

-(proof land-intro
+(proof and-intro
        :script
        (assume [x A
                 y B
@@ -141,5 +142,24 @@
          (have concl (land A B) :discharge [C step3])
          (qed concl)))

+(defthm and-elim-left
+  "Elimination rule for logical conjunction.
+   This one only keeps the left-side of the conjunction"
+  [[A :type] [B :type]]
+  (==> (and A B)
+       A))
+
+;; (proof and-elim-left
+;;        :script
+;;        (assume [H1 (and A B)
+;;                 x A
+;;                 y B]
+;;          (have <a> (==> (==> A B A) A) :by (H1 A))
+;;          (have <b> (==> A (==> B A))
+
+;;          (assume [H2 (==> A B A)]
+;;            (have <b> A :by ((<a>) H2))
+;;            (qed <b>))))

+


Fix crash on collections. TODO: extracting collection contents is not done yet.

--- src/bltool/data/backloggery.clj
+++ src/bltool/data/backloggery.clj
@@ -74,9 +74,9 @@
 (defn- bl-extract-games [body]
   (->> body (tag-seq :section)
        (filter #(= "gamebox" (:class (:attrs %))))
-       (map gamebox-to-game)
-       ; filter out any collections; these will have an HTML tag as the name rather than a string
-       (filter :platform)))
+       ; filter out collections - they don't have edit links, so (first (tag-seq :a)) will be nil
+       (filter #(first (tag-seq :a %)))
+       (map gamebox-to-game)))

 (defmethod read-games "backloggery" [_]
   (let [user (:bl-name *opts*)


implement Surat City Grid

implement Surat City Grid

--- src/clj/game/cards-upgrades.clj
+++ src/clj/game/cards-upgrades.clj
@@ -323,6 +323,24 @@
                                         (assoc card :zone '(:discard))))}
       :events {:pre-steal-cost ab :run-ends nil}})

+   "Surat City Grid"
+   {:events
+    {:rez {:req (req (let [serv (card->server state card)
+                           servcards (concat (:ices serv) (:contents serv))]
+                       (and (= (card->server state target) serv)
+                            (not= (:cid target) (:cid card))
+                            (seq (filter #(not (rezzed? %)) servcards)))))
+           :effect (effect (resolve-ability
+                             {:optional
+                              {:prompt (msg "Rez another card in or protecting " (zone->name (second (:zone card)))
+                                            " with Surat City Grid?")
+                               :yes-ability {:prompt "Choose a card to rez"
+                                             :choices {:req #(= (card->server state %) (card->server state card))}
+                                             :msg (msg "rez " (:title target) ", lowering the rez cost by 2 [Credits]")
+                                             :effect (effect (rez-cost-bonus -2)
+                                                             (rez target))}}}
+                            card nil))}}}
+
    "The Twins"
    {:abilities [{:label "Reveal and trash a copy of the ICE just passed from HQ"
                  :req (req (and this-server


use the new grouping

use the new grouping

--- src/clj/just_married/labels.clj
+++ src/clj/just_married/labels.clj
@@ -40,7 +40,7 @@

 (defn gen-table
   [addresses]
-  (let [grouped-addresses (partition-all n-cols addresses)]
+  (let [grouped-addresses (group-addresses addresses)]
     (into [:pdf-table
            table-options
            (repeat n-cols 20)]


small fix

small fix

--- src/clj/salava/badge/routes.clj
+++ src/clj/salava/badge/routes.clj
@@ -29,7 +29,8 @@
              (layout/main ctx "/import")
              #_(layout/main ctx "/export")
              (layout/main ctx "/receive/:id")
-             (layout/main ctx "/application"))
+             (layout/main ctx "/application")
+             (layout/main ctx "/user/endorsements"))

     (context "/obpv1/badge" []
              :tags  ["badge"]
@@ -334,11 +335,11 @@
                    :current-user current-user
                    (ok (e/update-status! ctx (:id current-user) user_badge_id endorsement-id status)))

-             (POST "endorsement/edit/:endorsement-id" []
+             (POST "/endorsement/edit/:endorsement-id" []
                    :return {:status (s/enum "success" "error")}
                    :path-params [endorsement-id :- Long]
                    :body-params [content :- s/Str
-                                 user_badge_id :- s/Str]
+                                 user_badge_id :- s/Int]
                    :summary "Edit endorsement"
                    :auth-rules access/authenticated
                    :current-user current-user


Focus the REPL input field

Focus the REPL input field

--- src/cljs/cljs_webrepl/core.cljs
+++ src/cljs/cljs_webrepl/core.cljs
@@ -26,6 +26,15 @@
 (defn pprint-str [data]
   (with-out-str (fipp/pprint data)))

+(defn trigger
+  "Returns a reagent class that can be used to easily add triggers
+  from the map in `props`, such as :component-did-mount.  See
+  `reagent.core/create-class` for more information."
+  [props content]
+  (r/create-class
+   (-> {:display-name "trigger"}
+       (merge props)
+       (assoc :reagent-render (fn [_ content] content)))))

 (defn clipboard [child]
   (let [clipboard-atom (atom nil)]
@@ -39,6 +48,10 @@
                                   (.destroy @clipboard-atom)
                                   (reset! clipboard-atom nil)))
       :reagent-render         (fn [child] child)})))
+
+(defn focus-node [node]
+  (-> (r/dom-node node)
+      (.focus)))

 (defn on-repl-eval [state num [ns expression]]
   (when num
@@ -271,14 +284,16 @@
      [:form {:action "#" "autoComplete" "off"}
       [mdl/upgrade
        [:div.wide.mdl-textfield.mdl-js-textfield.mdl-textfield--floating-label
-        [:input.wide.mdl-textfield__input
-         {:type         :text
-          :id           "input"
-          :autocomplete "off"
-          :value        input
-          :disabled     (not is-init?)
-          :on-change    #(swap! state input-on-change %)
-          :on-key-down  #(input-key-down state %)}]
+        [trigger
+         {:component-did-mount focus-node}
+         [:input.wide.mdl-textfield__input
+          {:type         :text
+           :id           "input"
+           :autocomplete "off"
+           :value        input
+           :disabled     (not is-init?)
+           :on-change    #(swap! state input-on-change %)
+           :on-key-down  #(input-key-down state %)}]]
         ^{:key is-init?}
         [:label.mdl-textfield__label {:for "input"}
          (str ns "=>")]]]]]))


Completed setting the data of the stradella-bass

--- src/cljs/omnibar/accordion.cljs
+++ src/cljs/omnibar/accordion.cljs
@@ -1,134 +1,147 @@
 (ns omnibar.accordion)
+
+(def major-3rd-note
+  {:name "major-3rd-note" :text "Major 3rd note" :notes [
+            {:id "accord-bass-maj-3rd-d-flat"
+             :midi [49]        ;; the midi notes that are played when the button is pressed.
+             :text "Db"        ;; what should appear on the bass button
+             :position [0 0]   ;; where it appears on the accordion bass board
+             :class "r0 c0"}   ;; knowing the row and column can help css styling, so you can highligh the whole row and columns of a selected note.
+            {:id "accord-bass-maj-3rd-a-flat" :midi [44] :text "Ab" :position [0 1] :class "r0 c1"}
+            {:id "accord-bass-maj-3rd-e-flat" :midi [39] :text "Eb" :position [0 2] :class "r0 c2"}
+            {:id "accord-bass-maj-3rd-b-flat" :midi [46] :text "Bb" :position [0 3] :class "r0 c3"}
+            {:id "accord-bass-maj-3rd-f" :midi [41] :text "F" :position [0 4] :class "r0 c4"}
+            {:id "accord-bass-maj-3rd-c" :midi [48] :text "C" :position [0 5] :class "r0 c5"}
+            {:id "accord-bass-maj-3rd-g" :midi [43] :text "G" :position [0 6] :class "r0 c6"}
+            {:id "accord-bass-maj-3rd-d" :midi [38] :text "D" :position [0 7] :class "r0 c7"}
+            {:id "accord-bass-maj-3rd-a" :midi [45] :text "A" :position [0 8] :class "r0 c8"}
+            {:id "accord-bass-maj-3rd-e" :midi [40] :text "E" :position [0 9] :class "r0 c9"} ;; same as accord-bass-root-f-flat
+            {:id "accord-bass-maj-3rd-b" :midi [47] :text "B" :position [0 10] :class "r0 c10"}
+            {:id "accord-bass-maj-3rd-f-sharp" :midi [42] :text "F#" :position [0 11] :class "r0 c11"}
+            {:id "accord-bass-maj-3rd-c-sharp" :midi [49] :text "C#" :position [0 12] :class "r0 c12"}
+            {:id "accord-bass-maj-3rd-g-sharp" :midi [44] :text "G#" :position [0 13] :class "r0 c13"}
+            {:id "accord-bass-maj-3rd-d-sharp" :midi [39] :text "D#" :position [0 14] :class "r0 c14"}
+            {:id "accord-bass-maj-3rd-a-sharp" :midi [46] :text "A#" :position [0 15] :class "r0 c15"}
+            {:id "accord-bass-maj-3rd-e-sharp" :midi [41] :text "E#" :position [0 16] :class "r0 c16"}
+            {:id "accord-bass-maj-3rd-b-sharp" :midi [48] :text "B#" :position [0 17] :class "r0 c17"}
+            {:id "accord-bass-maj-3rd-double-sharp" :midi [43] :text "F##" :position [0 18] :class "r0 c18"}
+            {:id "accord-bass-maj-3rd-double-sharp" :midi [38] :text "C##" :position [0 19] :class "r0 c19"}]})
+
+(def root-note
+  {:name "root-note" :text "Root note" :notes [
+            {:id "accord-bass-root-b-double-flat" :midi [45] :text "Bbb" :position [1 0] :class "r1 c0"}
+            {:id "accord-bass-root-f-flat" :midi [40] :text "Fb" :position [1 1] :class "r1 c1"} ;; same as accord-bass-maj-3rd-e
+            {:id "accord-bass-root-c-flat" :midi [35] :text "Cb" :position [1 2] :class "r1 c2"}
+            {:id "accord-bass-root-g-flat" :midi [42] :text "Gb" :position [1 3] :class "r1 c3"}
+            {:id "accord-bass-root-d-flat" :midi [37] :text "Db" :position [1 4] :class "r1 c4"}
+            {:id "accord-bass-root-a-flat" :midi [44] :text "Ab" :position [1 5] :class "r1 c5"}
+            {:id "accord-bass-root-e-flat" :midi [39] :text "Eb" :position [1 6] :class "r1 c6"}
+            {:id "accord-bass-root-b-flat" :midi [34] :text "Bb" :position [1 7] :class "r1 c7"}
+            {:id "accord-bass-root-f" :midi [41] :text "F" :position [1 8] :class "r1 c8"}
+            {:id "accord-bass-root-c" :midi [36] :text "C" :position [1 9] :class "r1 c9"}
+            {:id "accord-bass-root-g" :midi [43] :text "G" :position [1 10] :class "r1 c10"}
+            {:id "accord-bass-root-d" :midi [38] :text "D" :position [1 11] :class "r1 c11"}
+            {:id "accord-bass-root-a" :midi [45] :text "A" :position [1 12] :class "r1 c12"}
+            {:id "accord-bass-root-e" :midi [40] :text "E" :position [1 13] :class "r1 c13"}
+            {:id "accord-bass-root-b" :midi [35] :text "B" :position [1 14] :class "r1 c14"}
+            {:id "accord-bass-root-f-sharp" :midi [42] :text "F#" :position [1 15] :class "r1 c15"}
+            {:id "accord-bass-root-c-sharp" :midi [37] :text "C#" :position [1 16] :class "r1 c16"}
+            {:id "accord-bass-root-g-sharp" :midi [44] :text "G#" :position [1 17] :class "r1 c17"}
+            {:id "accord-bass-root-d-sharp" :midi [39] :text "D#" :position [1 18] :class "r1 c18"}
+            {:id "accord-bass-root-a-sharp" :midi [34] :text "A#" :position [1 19] :class "r1 c19"}]})
+
+(def major-chord
+   {:name "major-chord" :text "Major chord" :notes [
+            {:id "accord-bass-maj-chord-d-flat" :midi [57 61 64] :text "Db" :position [2 0] :class "r2 c0"}
+            {:id "accord-bass-maj-chord-a-flat" :midi [64 68 71] :text "Ab" :position [2 1] :class "r2 c1"}
+            {:id "accord-bass-maj-chord-e-flat" :midi [59 63 66] :text "Eb" :position [2 2] :class "r2 c2"}
+            {:id "accord-bass-maj-chord-b-flat" :midi [54 58 61] :text "Bb" :position [2 3] :class "r2 c3"}
+            {:id "accord-bass-maj-chord-f" :midi [61 65 68] :text "F" :position [2 4] :class "r2 c4"}
+            {:id "accord-bass-maj-chord-c" :midi [56 60 63] :text "C" :position [2 5] :class "r2 c5"}
+            {:id "accord-bass-maj-chord-g" :midi [63 67 70] :text "G" :position [2 6] :class "r2 c6"}
+            {:id "accord-bass-maj-chord-d" :midi [58 62 65] :text "D" :position [2 7] :class "r2 c7"}
+            {:id "accord-bass-maj-chord-a" :midi [65 69 72] :text "A" :position [2 8] :class "r2 c8"}
+            {:id "accord-bass-maj-chord-e" :midi [60 64 67] :text "E" :position [2 9] :class "r2 c9"}
+            {:id "accord-bass-maj-chord-b" :midi [55 59 62] :text "B" :position [2 10] :class "r2 c10"}
+            {:id "accord-bass-maj-chord-f-sharp" :midi [62 66 69] :text "F#" :position [2 11] :class "r2 c11"}
+            {:id "accord-bass-maj-chord-c-sharp" :midi [57 61 64] :text "C#" :position [2 12] :class "r2 c12"}
+            {:id "accord-bass-maj-chord-g-sharp" :midi [64 68 71] :text "G#" :position [2 13] :class "r2 c13"}
+            {:id "accord-bass-maj-chord-d-sharp" :midi [59 63 66] :text "D#" :position [2 14] :class "r2 c14"}
+            {:id "accord-bass-maj-chord-a-sharp" :midi [54 58 61] :text "A#" :position [2 15] :class "r2 c15"}
+            {:id "accord-bass-maj-chord-e-sharp" :midi [61 65 68] :text "E#" :position [2 16] :class "r2 c16"}
+            {:id "accord-bass-maj-chord-b-sharp" :midi [56 60 63] :text "B#" :position [2 17] :class "r2 c17"}
+            {:id "accord-bass-maj-chord-f-double-sharp" :midi [63 67 70] :text "F##" :position [2 18] :class "r2 c18"}
+            {:id "accord-bass-maj-chord-c-double-sharp" :midi [70 74 77] :text "C##" :position [2 19] :class "r2 c19"}]})
+
+(def minor-chord
+   {:name "minor-chord" :text "Minor chord" :notes [
+            {:id "accord-bass-minor-chord-d-flat" :midi [57 60 64] :text "Db" :position [3 0] :class "r3 c0"}
+            {:id "accord-bass-minor-chord-a-flat" :midi [64 67 71] :text "Ab" :position [3 1] :class "r3 c1"}
+            {:id "accord-bass-minor-chord-e-flat" :midi [59 62 66] :text "Eb" :position [3 2] :class "r3 c2"}
+            {:id "accord-bass-minor-chord-b-flat" :midi [54 57 61] :text "Bb" :position [3 3] :class "r3 c3"}
+            {:id "accord-bass-minor-chord-f" :midi [61 64 68] :text "F" :position [3 4] :class "r3 c4"}
+            {:id "accord-bass-minor-chord-c" :midi [56 59 63] :text "C" :position [3 5] :class "r3 c5"}
+            {:id "accord-bass-minor-chord-g" :midi [63 66 70] :text "G" :position [3 6] :class "r3 c6"}
+            {:id "accord-bass-minor-chord-d" :midi [58 61 65] :text "D" :position [3 7] :class "r3 c7"}
+            {:id "accord-bass-minor-chord-a" :midi [65 68 72] :text "A" :position [3 8] :class "r3 c8"}
+            {:id "accord-bass-minor-chord-e" :midi [60 63 67] :text "E" :position [3 9] :class "r3 c9"}
+            {:id "accord-bass-minor-chord-b" :midi [55 58 62] :text "B" :position [3 10] :class "r3 c10"}
+            {:id "accord-bass-minor-chord-f-sharp" :midi [62 65 69] :text "F#" :position [3 11] :class "r3 c11"}
+            {:id "accord-bass-minor-chord-c-sharp" :midi [57 60 64] :text "C#" :position [3 12] :class "r3 c12"}
+            {:id "accord-bass-minor-chord-g-sharp" :midi [64 67 71] :text "G#" :position [3 13] :class "r3 c13"}
+            {:id "accord-bass-minor-chord-d-sharp" :midi [59 62 66] :text "D#" :position [3 14] :class "r3 c14"}
+            {:id "accord-bass-minor-chord-a-sharp" :midi [54 57 61] :text "A#" :position [3 15] :class "r3 c15"}
+            {:id "accord-bass-minor-chord-e-sharp" :midi [61 64 68] :text "E#" :position [3 16] :class "r3 c16"}
+            {:id "accord-bass-minor-chord-b-sharp" :midi [56 59 63] :text "B#" :position [3 17] :class "r3 c17"}
+            {:id "accord-bass-minor-chord-f-double-sharp" :midi [63 66 70] :text "F##" :position [3 18] :class "r3 c18"}
+            {:id "accord-bass-minor-chord-c-double-sharp" :midi [70 73 77] :text "C##" :position [3 19] :class "r3 c19"}]})
+
+(def dominant-7th-chord
+   {:name "dominant-7th-chord" :text "Dominant 7th chord" :notes [
+            {:id "accord-bass-dominant-7th-d-flat" :midi [57 61 67] :text "Db" :position [4 0] :class "r4 c0"}
+            {:id "accord-bass-dominant-7th-a-flat" :midi [64 68 74] :text "Ab" :position [4 1] :class "r4 c1"}
+            {:id "accord-bass-dominant-7th-e-flat" :midi [59 63 69] :text "Eb" :position [4 2] :class "r4 c2"}
+            {:id "accord-bass-dominant-7th-b-flat" :midi [54 58 64] :text "Bb" :position [4 3] :class "r4 c3"}
+            {:id "accord-bass-dominant-7th-f" :midi [61 65 71] :text "F" :position [4 4] :class "r4 c4"}
+            {:id "accord-bass-dominant-7th-c" :midi [56 60 66] :text "C" :position [4 5] :class "r4 c5"}
+            {:id "accord-bass-dominant-7th-g" :midi [63 67 73] :text "G" :position [4 6] :class "r4 c6"}
+            {:id "accord-bass-dominant-7th-d" :midi [58 62 68] :text "D" :position [4 7] :class "r4 c7"}
+            {:id "accord-bass-dominant-7th-a" :midi [65 69 75] :text "A" :position [4 8] :class "r4 c8"}
+            {:id "accord-bass-dominant-7th-e" :midi [60 64 70] :text "E" :position [4 9] :class "r4 c9"}
+            {:id "accord-bass-dominant-7th-b" :midi [55 59 65] :text "B" :position [4 10] :class "r4 c10"}
+            {:id "accord-bass-dominant-7th-f-sharp" :midi [62 66 72] :text "F#" :position [4 11] :class "r4 c11"}
+            {:id "accord-bass-dominant-7th-c-sharp" :midi [57 61 67] :text "C#" :position [4 12] :class "r4 c12"}
+            {:id "accord-bass-dominant-7th-g-sharp" :midi [64 68 74] :text "G#" :position [4 13] :class "r4 c13"}
+            {:id "accord-bass-dominant-7th-d-sharp" :midi [59 63 69] :text "D#" :position [4 14] :class "r4 c14"}
+            {:id "accord-bass-dominant-7th-a-sharp" :midi [54 58 64] :text "A#" :position [4 15] :class "r4 c15"}
+            {:id "accord-bass-dominant-7th-e-sharp" :midi [61 65 71] :text "E#" :position [4 16] :class "r4 c16"}
+            {:id "accord-bass-dominant-7th-b-sharp" :midi [56 60 66] :text "B#" :position [4 17] :class "r4 c17"}
+            {:id "accord-bass-dominant-7th-f-double-sharp" :midi [63 67 73] :text "F##" :position [4 18] :class "r4 c18"}
+            {:id "accord-bass-dominant-7th-c-double-sharp" :midi [70 74 80] :text "C##" :position [4 19] :class "r4 c19"}]})
+
+(def diminished-7th-chord
+   {:name "diminished-7th-chord" :text "Diminished 7th chord" :notes [
+            {:id "accord-bass-diminished-7th-d-flat" :midi [54 57 60] :text "Db" :position [5 0] :class "r5 c0"}
+            {:id "accord-bass-diminished-7th-a-flat" :midi [61 64 67] :text "Ab" :position [5 1] :class "r5 c1"}
+            {:id "accord-bass-diminished-7th-e-flat" :midi [56 59 62] :text "Eb" :position [5 2] :class "r5 c2"}
+            {:id "accord-bass-diminished-7th-b-flat" :midi [51 54 57] :text "Bb" :position [5 3] :class "r5 c3"}
+            {:id "accord-bass-diminished-7th-f" :midi [58 61 64] :text "F" :position [5 4] :class "r5 c4"}
+            {:id "accord-bass-diminished-7th-c" :midi [53 56 59] :text "C" :position [5 5] :class "r5 c5"}
+            {:id "accord-bass-diminished-7th-g" :midi [60 63 66] :text "G" :position [5 6] :class "r5 c6"}
+            {:id "accord-bass-diminished-7th-d" :midi [55 58 61] :text "D" :position [5 7] :class "r5 c7"}
+            {:id "accord-bass-diminished-7th-a" :midi [62 65 68] :text "A" :position [5 8] :class "r5 c8"}
+            {:id "accord-bass-diminished-7th-e" :midi [57 60 63] :text "E" :position [5 9] :class "r5 c9"}
+            {:id "accord-bass-diminished-7th-b" :midi [52 55 58] :text "B" :position [5 10] :class "r5 c10"}
+            {:id "accord-bass-diminished-7th-f-sharp" :midi [59 62 65] :text "F#" :position [5 11] :class "r5 c11"}
+            {:id "accord-bass-diminished-7th-c-sharp" :midi [54 57 60] :text "C#" :position [5 12] :class "r5 c12"}
+            {:id "accord-bass-diminished-7th-g-sharp" :midi [61 64 67] :text "G#" :position [5 13] :class "r5 c13"}
+            {:id "accord-bass-diminished-7th-d-sharp" :midi [56 59 62] :text "D#" :position [5 14] :class "r5 c14"}
+            {:id "accord-bass-diminished-7th-a-sharp" :midi [51 54 57] :text "A#" :position [5 15] :class "r5 c15"}
+            {:id "accord-bass-diminished-7th-e-sharp" :midi [58 61 64] :text "E#" :position [5 16] :class "r5 c16"}
+            {:id "accord-bass-diminished-7th-b-sharp" :midi [53 56 59] :text "B#" :position [5 17] :class "r5 c17"}
+            {:id "accord-bass-diminished-7th-f-double-sharp" :midi [60 63 66] :text "F##" :position [5 18] :class "r5 c18"}
+            {:id "accord-bass-diminished-7th-c-double-sharp" :midi [67 70 73] :text "C##" :position [5 19] :class "r5 c19"}]})

 ;;"Typical 120-button Stradella bass system. This is the left-hand manual system found on most unisonoric accordions today" -- wikipedia
 (def stradella-bass
-  [{:name "major-3rd-note" :text "Major 3rd note" :notes [
-            {:id "major-3rd-d-flat"
-             :midi []
-             :text "Db"
-             :class "r0 c0"} ;; knowing the row and column can help css styling, so you can highligh the whole row and columns of a selected note.
-            {:id "major-3rd-a-flat"       :midi [] :text "Ab"  :class "r0 c1"}
-            {:id "major-3rd-e-flat"       :midi [] :text "Eb"  :class "r0 c2"}
-            {:id "major-3rd-b-flat"       :midi [] :text "Bb"  :class "r0 c3"}
-            {:id "major-3rd-f"            :midi [] :text "F"   :class "r0 c4"}
-            {:id "major-3rd-c"            :midi [] :text "C"   :class "r0 c5"}
-            {:id "major-3rd-g"            :midi [] :text "G"   :class "r0 c6"}
-            {:id "major-3rd-d"            :midi [] :text "D"   :class "r0 c7"}
-            {:id "major-3rd-a"            :midi [] :text "A"   :class "r0 c8"}
-            {:id "major-3rd-e"            :midi [] :text "E"   :class "r0 c9"}
-            {:id "major-3rd-b"            :midi [] :text "B"   :class "r0 c10"}
-            {:id "major-3rd-f-sharp"      :midi [] :text "F#"  :class "r0 c11"}
-            {:id "major-3rd-c-sharp"      :midi [] :text "C#"  :class "r0 c12"}
-            {:id "major-3rd-g-sharp"      :midi [] :text "G#"  :class "r0 c13"}
-            {:id "major-3rd-d-sharp"      :midi [] :text "D#"  :class "r0 c14"}
-            {:id "major-3rd-a-sharp"      :midi [] :text "A#"  :class "r0 c15"}
-            {:id "major-3rd-e-sharp"      :midi [] :text "E#"  :class "r0 c16"}
-            {:id "major-3rd-b-sharp"      :midi [] :text "B#"  :class "r0 c17"}
-            {:id "major-3rd-double-sharp" :midi [] :text "F##" :class "r0 c18"}
-            {:id "major-3rd-double-sharp" :midi [] :text "C##" :class "r0 c19"}]}
-   {:name "root-note" :text "Root note" :notes [
-            {:id "root-b-double-flat" :midi [] :text "Bbb" :class "r1 c0"}
-            {:id "root-f-flat" :midi [] :text "Fb" :class "r1 c1"}
-            {:id "root-c-flat" :midi [] :text "Cb" :class "r1 c2"}
-            {:id "root-g-flat" :midi [] :text "Gb" :class "r1 c3"}
-            {:id "root-d-flat" :midi [] :text "Db" :class "r1 c4"}
-            {:id "root-a-flat" :midi [] :text "Ab" :class "r1 c5"}
-            {:id "root-e-flat" :midi [] :text "Eb" :class "r1 c6"}
-            {:id "root-b-flat" :midi [] :text "Bb" :class "r1 c7"}
-            {:id "root-f" :midi [] :text "F" :class "r1 c8"}
-            {:id "root-c" :midi [] :text "C" :class "r1 c9"}
-            {:id "root-g" :midi [] :text "G" :class "r1 c10"}
-            {:id "root-d" :midi [] :text "D" :class "r1 c11"}
-            {:id "root-a" :midi [] :text "A" :class "r1 c12"}
-            {:id "root-e" :midi [] :text "E" :class "r1 c13"}
-            {:id "root-b" :midi [] :text "B" :class "r1 c14"}
-            {:id "root-f-sharp" :midi [] :text "F#" :class "r1 c15"}
-            {:id "root-c-sharp" :midi [] :text "C#" :class "r1 c16"}
-            {:id "root-g-sharp" :midi [] :text "G#" :class "r1 c17"}
-            {:id "root-d-sharp" :midi [] :text "D#" :class "r1 c18"}
-            {:id "root-a-sharp" :midi [] :text "A#" :class "r1 c19"}]}
-   {:name "major-chord" :text "Major cho rd" :notes [
-            {:id "major-chord-" :midi [] :text "Db"  :class "r2 c0"}
-            {:id "major-chord-" :midi [] :text "Ab"  :class "r2 c1"}
-            {:id "major-chord-" :midi [] :text "Eb"  :class "r2 c2"}
-            {:id "major-chord-" :midi [] :text "Bb"  :class "r2 c3"}
-            {:id "major-chord-" :midi [] :text "F"   :class "r2 c4"}
-            {:id "major-chord-" :midi [] :text "C"   :class "r2 c5"}
-            {:id "major-chord-" :midi [] :text "G"   :class "r2 c6"}
-            {:id "major-chord-" :midi [] :text "D"   :class "r2 c7"}
-            {:id "major-chord-" :midi [] :text "A"   :class "r2 c8"}
-            {:id "major-chord-" :midi [] :text "E"   :class "r2 c9"}
-            {:id "major-chord-" :midi [] :text "B"   :class "r2 c10"}
-            {:id "major-chord-" :midi [] :text "F#"  :class "r2 c11"}
-            {:id "major-chord-" :midi [] :text "C#"  :class "r2 c12"}
-            {:id "major-chord-" :midi [] :text "G#"  :class "r2 c13"}
-            {:id "major-chord-" :midi [] :text "D#"  :class "r2 c14"}
-            {:id "major-chord-" :midi [] :text "A#"  :class "r2 c15"}
-            {:id "major-chord-" :midi [] :text "E#"  :class "r2 c16"}
-            {:id "major-chord-" :midi [] :text "B#"  :class "r2 c17"}
-            {:id "major-chord-" :midi [] :text "F##" :class "r2 c18"}
-            {:id "major-chord-" :midi [] :text "C##" :class "r2 c19"}]}
-   {:name "minor-chord" :text "Minor chord" :notes [
-            {:id "minor-chord-" :midi [] :text "Db"  :class ""}
-            {:id "minor-chord-" :midi [] :text "Ab"  :class ""}
-            {:id "minor-chord-" :midi [] :text "Eb"  :class ""}
-            {:id "minor-chord-" :midi [] :text "Bb"  :class ""}
-            {:id "minor-chord-" :midi [] :text "F"   :class ""}
-            {:id "minor-chord-" :midi [] :text "C"   :class ""}
-            {:id "minor-chord-" :midi [] :text "G"   :class ""}
-            {:id "minor-chord-" :midi [] :text "D"   :class ""}
-            {:id "minor-chord-" :midi [] :text "A"   :class ""}
-            {:id "minor-chord-" :midi [] :text "E"   :class ""}
-            {:id "minor-chord-" :midi [] :text "B"   :class ""}
-            {:id "minor-chord-" :midi [] :text "F#"  :class ""}
-            {:id "minor-chord-" :midi [] :text "C#"  :class ""}
-            {:id "minor-chord-" :midi [] :text "G#"  :class ""}
-            {:id "minor-chord-" :midi [] :text "D#"  :class ""}
-            {:id "minor-chord-" :midi [] :text "A#"  :class ""}
-            {:id "minor-chord-" :midi [] :text "E#"  :class ""}
-            {:id "minor-chord-" :midi [] :text "B#"  :class ""}
-            {:id "minor-chord-" :midi [] :text "F##" :class ""}
-            {:id "minor-chord-" :midi [] :text "C##" :class ""}]}
-   {:name "dominant-7th-chord" :text "Dominant 7th chord" :notes [
-            {:id "dominant-7th-" :midi [] :text "Db"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "Ab"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "Eb"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "Bb"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "F"   :class ""}
-            {:id "dominant-7th-" :midi [] :text "C"   :class ""}
-            {:id "dominant-7th-" :midi [] :text "G"   :class ""}
-            {:id "dominant-7th-" :midi [] :text "D"   :class ""}
-            {:id "dominant-7th-" :midi [] :text "A"   :class ""}
-            {:id "dominant-7th-" :midi [] :text "E"   :class ""}
-            {:id "dominant-7th-" :midi [] :text "B"   :class ""}
-            {:id "dominant-7th-" :midi [] :text "F#"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "C#"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "G#"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "D#"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "A#"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "E#"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "B#"  :class ""}
-            {:id "dominant-7th-" :midi [] :text "F##" :class ""}
-            {:id "dominant-7th-" :midi [] :text "C##" :class ""}]}
-   {:name "diminished-7th-chord" :text "Diminished 7th chord" :notes [
-            {:id "diminished-7th-" :midi [] :text "Db"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "Ab"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "Eb"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "Bb"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "F"   :class ""}
-            {:id "diminished-7th-" :midi [] :text "C"   :class ""}
-            {:id "diminished-7th-" :midi [] :text "G"   :class ""}
-            {:id "diminished-7th-" :midi [] :text "D"   :class ""}
-            {:id "diminished-7th-" :midi [] :text "A"   :class ""}
-            {:id "diminished-7th-" :midi [] :text "E"   :class ""}
-            {:id "diminished-7th-" :midi [] :text "B"   :class ""}
-            {:id "diminished-7th-" :midi [] :text "F#"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "C#"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "G#"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "D#"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "A#"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "E#"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "B#"  :class ""}
-            {:id "diminished-7th-" :midi [] :text "F##" :class ""}
-            {:id "diminished-7th-" :midi [] :text "C##" :class ""}]}])
-
+  [major-3rd-note root-note major-chord minor-chord dominant-7th-chord diminished-7th-chord])


Add default meta data to imports from vubis

--- src/mdr2/views.clj
+++ src/mdr2/views.clj
@@ -214,6 +214,7 @@
   (doseq [[_ p] productions]
     (-> p
      prod/parse
+     prod/add-default-meta-data
      ;; FIXME: does it make sense to update? What if the production
      ;; has already been archived? I think we should probably make
      ;; sure the production is in certain valid states


(dbscan) written; not fully tested

(dbscan) written; not fully tested

--- src/pscan/core.clj
+++ src/pscan/core.clj
@@ -1,6 +1,7 @@
 (ns pscan.core
   (:require [clojure.core.reducers :as r]
-            [clojure.set :as s]))
+            [clojure.set :as s]
+            [clojure.string :refer [split]]))

 (def pts [[0 0] [0 1] [0 2] [1 0] [1 1] [1 2] [2 0] [2 1] [2 2]])

@@ -40,7 +41,7 @@
           (let [current-point (first s)
                 result (region-query current-point pts metric eps)
                 size (count result)
-                unvisited-result (filter #(not (v %)) result)]
+                unvisited-result (remove #(v %) result)]

             (if (>= size min-pts) ;new core point
               (recur (disj (into s unvisited-result) current-point)
@@ -51,13 +52,44 @@
                      c
                      (conj v current-point)))))))))

+(defn dbscan
+  "Run dbscan
+   Not fully tested"
+  [pts metric eps min-pts]
+
+  (loop [points pts
+         visited #{}
+         clusters #{}]
+    (let [pt (first points)]
+      (cond
+        (empty? points)
+          clusters
+        (visited pt)
+          (recur (rest points)
+                 (conj visited pt)
+                 clusters)
+        :else
+          (let [{new-cluster :core, new-visited :visited}
+                (expand-cluster pt pts metric eps min-pts visited)]
+            (recur (rest points)
+                   new-visited
+                   (conj clusters new-cluster)))))))

+(defn parse-float
+  "Parse string s as float"
+  [s]
+  (Float/parseFloat s))

 (comment

 (def star [[0 0] [0 1] [1 0] [-1 0] [0 -1]])
+
+(def blobs [[0 0] [0 1] [1 0] [1 1]
+            [100 100] [101 100] [100 101] [101 101]])
+
+(dbscan blobs r2-dist 2 3)

 (expand-cluster [0 0] star r2-dist 1 2 #{})


add swap-state

add swap-state

--- src/quiescent.cljs
+++ src/quiescent.cljs
@@ -99,3 +99,12 @@
   "Get the \"value\" key of a component's state."
   ([] (get-state *component*))
   ([component] (aget (.-state component) "value")))
+
+(defn swap-state
+  "Set the \"value\" key of a component's state."
+  [component tx-fn]
+  (let [old-state (get-state component)
+        new-state (tx-fn old-state)]
+    (when (not= old-state new-state)
+      (set-state component new-state))))
+


getting internal vars via strings (for advanced comp)

--- src/show/core.cljs
+++ src/show/core.cljs
@@ -121,12 +121,12 @@
   (js/React.renderComponent component dom))

 (defn- local-method [component name]
-  (when-let [props (.. component -__show_base)]
-    (name (.-lifecycle_methods props))))
+  (when-let [props (aget component "__show_base")]
+    (name (aget props "lifecycle_methods"))))

 (defn- local-mixins [component name]
-  (when-let [props (.. component -__show_base)]
-    (map name (filter name (.-mixins props)))))
+  (when-let [props (aget component "__show_base")]
+    (map name (filter name (aget props "mixins")))))

 (defn- execute-mixin-methods [component name & props]
   (let [mixins (local-mixins component name)]


Namespace doc string

Namespace doc string

--- src/such/casts.clj
+++ src/such/casts.clj
@@ -1,4 +1,9 @@
 (ns such.casts
+  "Some Clojure functions require specific types of arguments, such as a symbol
+   representing a namespace. But, for many purposes, you'd just as soon that
+   function work with the namespace itself. You can use these functions to
+   convert from what you've got to what Clojure wants. Or you can use them to
+   build more accepting variants of those Clojure functions."
   (:use such.types))

 (defn- badtype [name]


insights: refactor - clearly mark unexterned props

--- src-cljs/frontend/components/insights.cljs
+++ src-cljs/frontend/components/insights.cljs
@@ -9,7 +9,7 @@
             [frontend.models.repo :as repo-model]
             [frontend.models.user :as user-model]
             [frontend.state :as state]
-            [frontend.utils :as utils :refer-macros [inspect]]
+            [frontend.utils :as utils :refer-macros [inspect] :refer [unexterned-prop]]
             [frontend.utils.github :as gh-utils]
             [frontend.utils.vcs-url :as vcs-url]
             [frontend.routes :as routes]
@@ -21,16 +21,6 @@
   (:require-macros [cljs.core.async.macros :as am :refer [go go-loop alt!]]
                    [frontend.utils :refer [html defrender]]))

-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;                                                                         ;;
-;; Note that unexterned properties must be accessed with `aget` instead of ;;
-;; the `.-` or `..` shortcut notations.                                    ;;
-;;                                                                         ;;
-;; Google closure compiler with "advanced" optimizations will mangle       ;;
-;; unexterned field names.                                                 ;;
-;;                                                                         ;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

 (def svg-info
   {:width 425
@@ -119,29 +109,29 @@
     ;; top bars enter and update
     (-> bars-join
         (.select ".top")
-        (.attr #js {"xlink:href" #(utils/uri-to-relative (aget % "build_url"))
-                    "xlink:title" #(let [duration-str (datetime/as-duration (aget % "build_time_millis"))]
+        (.attr #js {"xlink:href" #(utils/uri-to-relative (unexterned-prop % "build_url"))
+                    "xlink:title" #(let [duration-str (datetime/as-duration (unexterned-prop % "build_time_millis"))]
                                      (gstring/format "%s in %s"
-                                                     (gstring/toTitleCase (aget % "outcome"))
+                                                     (gstring/toTitleCase (unexterned-prop % "outcome"))
                                                      duration-str))})
         (.select "rect.bar")
-        (.attr #js {"class" #(str "bar " (aget % "outcome"))
-                    "y" #(y-pos-scale (aget % "build_time_millis"))
-                    "x" #(x-scale (aget % "build_num"))
+        (.attr #js {"class" #(str "bar " (unexterned-prop % "outcome"))
+                    "y" #(y-pos-scale (unexterned-prop % "build_time_millis"))
+                    "x" #(x-scale (unexterned-prop % "build_num"))
                     "width" (.rangeBand x-scale)
-                    "height" #(- y-zero (y-pos-scale (aget % "build_time_millis")))}))
+                    "height" #(- y-zero (y-pos-scale (unexterned-prop % "build_time_millis")))}))

     ;; bottom bar enter and update
     (-> bars-join
         (.select ".bottom")
-        (.attr #js {"xlink:href" #(utils/uri-to-relative (aget % "build_url"))
-                    "xlink:title" #(let [duration-str (datetime/as-duration (aget % "queued_time_millis"))]
+        (.attr #js {"xlink:href" #(utils/uri-to-relative (unexterned-prop % "build_url"))
+                    "xlink:title" #(let [duration-str (datetime/as-duration (unexterned-prop % "queued_time_millis"))]
                                      (gstring/format "Queue time %s" duration-str))})
         (.select "rect.bar")
         (.attr #js {"y" y-zero
-                    "x" #(x-scale (aget % "build_num"))
+                    "x" #(x-scale (unexterned-prop % "build_num"))
                     "width" (.rangeBand x-scale)
-                    "height" #(- (y-neg-scale (aget % "queued_time_millis")) y-zero)}))
+                    "height" #(- (y-neg-scale (unexterned-prop % "queued_time_millis")) y-zero)}))

     ;; bars exit
     (-> bars-join


Don't assume order.

Don't assume order.

--- modules/core/src/test/clojure/org/immutant/core/processors/test/AppJarScanningProcessor.clj
+++ modules/core/src/test/clojure/org/immutant/core/processors/test/AppJarScanningProcessor.clj
@@ -25,4 +25,5 @@

 (deftest jar-scanning-should-ignore-dev
   (let [accepted (.getJarFiles (AppJarScanningProcessor.) (VFS/getChild (.toURI (io/resource "lib"))))]
-    (is (= ["acceptable.jar" "acceptable-as-well.jar"] (map #(.getName %) accepted)))))
+    (is (= #{"acceptable.jar" "acceptable-as-well.jar"}
+           (set (map #(.getName %) accepted))))))


integer selector elements

integer selector elements

--- src/structural_typing/guts/compile/compile_path.clj
+++ src/structural_typing/guts/compile/compile_path.clj
@@ -132,75 +132,75 @@
 (defmethod clojure.core/print-method RangePathElement [o, ^java.io.Writer w]
   (.write w (:printable o)))

-(defn desired-range [{:keys [inclusive-start desired-count]} sequence]
-  ;; This works with infinite sequences
-  (->> sequence
-       (drop inclusive-start)
-       (take desired-count)
-       vec))
-
-(defn extract-desired-range-as-exvals [exval {:keys [inclusive-start] :as selector-element}]
-  (->> (:leaf-value exval)
-       (desired-range selector-element)
-       (mapv #(update-exval exval {:conj-path %1 :descend-into %2})
-             (drop inclusive-start (range)))))
-
-(defn indexes-of-out-of-range-elements [actual-count {:keys [inclusive-start desired-count]}]
-  (let [shortfall (- desired-count actual-count)]
-    (take shortfall
-          (drop (+ inclusive-start actual-count)
-                (range)))))
-
-(defn padding:nil-exval [exval index]
-  (update-exval exval {:conj-path index :descend-into nil}))
-
-(defn padding:shouldbe-present-oopsie [exval index]
-  (first (errcase explain/oopsies:shouldbe-present exval {:conj-path index
-                                                          :descend-into ::missing-value})))
-
-(defn pad-the-end [actual-values exval {:keys [:reject-missing?] :as selector-element}]
-  (let [missing-indexes (indexes-of-out-of-range-elements (count actual-values)
-                                                          selector-element)
-        padder (if reject-missing? padding:shouldbe-present-oopsie padding:nil-exval)]
-    (into actual-values (map padder (repeat exval) missing-indexes))))
-
-(defn perhaps-rejecting-nil [mixture selector-element]
-  (mapv #(if (rejected-nil? (:leaf-value %) selector-element)
-           (first (errcase explain/oopsies:shouldbe-not-nil % {}))
-           %)
-        mixture))
-
-(defn descend [mixture next-fn]
-  (reduce (fn [so-far x]
-            (if (wrap/oopsie? x)
-              (conj so-far x)
-              (into so-far (next-fn x))))
-          []
-          mixture))
-
-(defn range-select* [{:keys [inclusive-start desired-count] :as selector-element} {:keys [leaf-value] :as exval} next-fn]
-  (cond (rejected-nil? leaf-value selector-element)
-        (errcase explain/oopsies:should-not-be-applied-to-nil exval {:conj-path selector-element})
-
-        (nil? leaf-value)
-        (pad-the-end [] exval selector-element)
-
-        (not (sequential? leaf-value))
-        (errcase explain/oopsies:shouldbe-sequential exval {:conj-path selector-element})
-
-        :else
-        (-> exval
-            (extract-desired-range-as-exvals selector-element)
-            (pad-the-end exval selector-element)
-            (perhaps-rejecting-nil selector-element)
-            (descend next-fn))))
-
-(extend-type RangePathElement
+(letfn [(desired-range [{:keys [inclusive-start desired-count]} sequence]
+          ;; This works with infinite sequences
+          (->> sequence
+               (drop inclusive-start)
+               (take desired-count)
+               vec))
+
+        (extract-desired-range-as-exvals [exval {:keys [inclusive-start] :as selector-element}]
+          (->> (:leaf-value exval)
+               (desired-range selector-element)
+               (mapv #(update-exval exval {:conj-path %1 :descend-into %2})
+                     (drop inclusive-start (range)))))
+
+        (indexes-of-out-of-range-elements [actual-count {:keys [inclusive-start desired-count]}]
+          (let [shortfall (- desired-count actual-count)]
+            (take shortfall
+                  (drop (+ inclusive-start actual-count)
+                        (range)))))
+
+        (padding:nil-exval [exval index]
+          (update-exval exval {:conj-path index :descend-into nil}))
+
+        (padding:shouldbe-present-oopsie [exval index]
+          (first (errcase explain/oopsies:shouldbe-present exval {:conj-path index
+                                                                  :descend-into ::missing-value})))
+
+        (pad-the-end [actual-values exval {:keys [:reject-missing?] :as selector-element}]
+          (let [missing-indexes (indexes-of-out-of-range-elements (count actual-values)
+                                                                  selector-element)
+                padder (if reject-missing? padding:shouldbe-present-oopsie padding:nil-exval)]
+            (into actual-values (map padder (repeat exval) missing-indexes))))
+
+        (perhaps-rejecting-nil [mixture selector-element]
+          (mapv #(if (rejected-nil? (:leaf-value %) selector-element)
+                   (first (errcase explain/oopsies:shouldbe-not-nil % {}))
+                   %)
+                mixture))
+
+        (descend [mixture next-fn]
+          (reduce (fn [so-far x]
+                    (if (wrap/oopsie? x)
+                      (conj so-far x)
+                      (into so-far (next-fn x))))
+                  []
+                  mixture))
+
+        (range-select* [{:keys [inclusive-start desired-count] :as selector-element} {:keys [leaf-value] :as exval} next-fn]
+          (cond (rejected-nil? leaf-value selector-element)
+                (errcase explain/oopsies:should-not-be-applied-to-nil exval {:conj-path selector-element})
+
+                (nil? leaf-value)
+                (pad-the-end [] exval selector-element)
+
+                (not (sequential? leaf-value))
+                (errcase explain/oopsies:shouldbe-sequential exval {:conj-path selector-element})
+
+                :else
+                (-> exval
+                    (extract-desired-range-as-exvals selector-element)
+                    (pad-the-end exval selector-element)
+                    (perhaps-rejecting-nil selector-element)
+                    (descend next-fn))))]
+
+  (extend-type RangePathElement
     sp/StructurePath
     (select* [this exval next-fn]
       (range-select* this exval next-fn))

-    (transform* [& _] (no-transform!)))
+    (transform* [& _] (no-transform!))))

 (letfn [(range-boom! [fmt [inclusive-start exclusive-end]]


CMR-1412 - fixed typo

CMR-1412 - fixed typo

--- system-int-test/test/cmr/system_int_test/search/collection_concept_revision_search_test.clj
+++ system-int-test/test/cmr/system_int_test/search/collection_concept_revision_search_test.clj
@@ -72,7 +72,7 @@

         coll1-tombstone (merge coll1-2 {:deleted true :revision-id (inc (:revision-id coll1-2))})

-        ;; Ingest collection once, tombstone, then ingest again (latest should deleted=false.
+        ;; Ingest collection once, tombstone, then ingest again - latest should be deleted=false.
         coll2-1 (d/ingest "PROV1" umm-coll2)

         coll2-tombstone {:concept-id (:concept-id coll2-1)


CMR-1803: Get rid of comment block

CMR-1803: Get rid of comment block

--- system-int-test/test/cmr/system_int_test/search/collection_highlights_search_test.clj
+++ system-int-test/test/cmr/system_int_test/search/collection_highlights_search_test.clj
@@ -18,12 +18,6 @@
   "Helper for creating and ingesting a collection"
   [n & attribs]
   (d/ingest "PROV1" (dc/collection (apply merge {:entry-title (str "coll" n)} attribs))))
-
-(comment
-  (def search-options {:keyword "Findme"})
-  (get-search-results-summaries {:keyword "Findme"})
-  (search/find-concepts-json :collection (merge {:include-highlights true} search-options))
-  )

 (defn- get-search-results-summaries
   "Returns a list of the highlighted-summary-snippets fields from a list of search results."


(SERVER-2287) Add OID for custom CLI auth extension

This commit adds a custom extension in Puppet's arc that is checked when
allowing access to the certificate_status(es) endpoint. This extension
should only be added to the master's cert by the CLI tool's generate
command, and it is disallowed when signing other certs with this CA.

This is a cherry-pick of d7d1d79862ade7acacb75e3ea5400f948da18abb from
the 6.0.x branch.

--- src/clj/puppetlabs/puppetserver/certificate_authority.clj
+++ src/clj/puppetlabs/puppetserver/certificate_authority.clj
@@ -139,6 +139,11 @@
 (def ppAuthCertExt
   "The OID for the extension with shortname 'ppPrivCertExt'."
   "1.3.6.1.4.1.34380.1.3")
+
+;; Extension that is checked when allowing access to the certificate_status(es)
+;; endpoint. Should only be present on the puppet master cert.
+(def cli-auth-oid
+  "1.3.6.1.4.1.34380.1.3.39")

 (def puppet-short-names
   "A mapping of Puppet extension short names to their OIDs. These appear in
@@ -169,7 +174,8 @@
    :pp_apptier          "1.3.6.1.4.1.34380.1.1.24"
    :pp_hostname         "1.3.6.1.4.1.34380.1.1.25"
    :pp_authorization    "1.3.6.1.4.1.34380.1.3.1"
-   :pp_auth_role        "1.3.6.1.4.1.34380.1.3.13"})
+   :pp_auth_role        "1.3.6.1.4.1.34380.1.3.13"
+   :pp_cli_auth         cli-auth-oid})

 (def netscape-comment-value
   "Standard value applied to the Netscape Comment extension for certificates"
@@ -567,6 +573,7 @@
   [extension :- Extension]
   (let [oid (:oid extension)]
     (or
+      (and (utils/subtree-of? ppAuthCertExt oid) (not (= cli-auth-oid oid)))
       (utils/subtree-of? ppRegCertExt oid)
       (utils/subtree-of? ppPrivCertExt oid))))


Fix schema->resources

Fix schema->resources

Envelope gives strings, not keywords

--- src/cljs/gc_api_browser/schema_select.cljs
+++ src/cljs/gc_api_browser/schema_select.cljs
@@ -84,7 +84,6 @@
 (defn schema->resources [schema]
   (->> (vals (:definitions schema))
        (map :envelope)
-       (map name)
        sort))

 (defn read-as-text [file c]


remove duplicate counter allocation functionality - use overtone.libs.counters instead

--- src/overtone/sc/machinery/server/comms.clj
+++ src/overtone/sc/machinery/server/comms.clj
@@ -1,10 +1,9 @@
 (ns overtone.sc.machinery.server.comms
   (:use [overtone.sc.machinery.server osc-validator]
-        [overtone.libs.event]
+        [overtone.libs event counters]
         [overtone.util.lib :only [uuid deref!]])
   (:require [overtone.util.log :as log]))

-(defonce server-sync-id* (atom 0))
 (defonce osc-debug*     (atom false))
 (defonce server-osc-peer*        (ref nil))

@@ -45,19 +44,11 @@
       (log/debug (str "Sending: " path [args])))
     (apply validated-snd @server-osc-peer* path args)))

-(defn- update-server-sync-id
-  "update osc-sync-id*. Increments by 1 unless it has maxed out
-  in which case it resets it to 0."
-  []
-  (swap! server-sync-id* (fn [cur] (if (= Integer/MAX_VALUE cur)
-                                    0
-                                    (inc cur)))))
-
 (defn on-server-sync
   "Registers the handler to be executed when all the osc messages generated
    by executing the action-fn have completed. Returns result of action-fn."
   [action-fn handler-fn]
-  (let [id (update-server-sync-id)
+  (let [id (next-id ::server-sync-id)
         key (uuid)]
     (on-event "/synced"
               (fn [msg] (when (= id (first (:args msg)))
@@ -85,7 +76,7 @@
   synchronise with its completion. The action-fn can sync using the fn server-sync.
   Returns the result of action-fn."
   [action-fn]
-  (let [id (update-server-sync-id)
+  (let [id (next-id ::server-sync-id)
         prom (promise)
         key (uuid)]
     (on-event "/synced"
@@ -102,7 +93,7 @@
   "Blocks current thread until all osc messages in action-fn have completed.
   Returns result of action-fn."
   [action-fn]
-  (let [id (update-server-sync-id)
+  (let [id (next-id ::server-sync-id)
         prom (promise)
         key (uuid)]
     (on-event "/synced"


set groupId

set groupId

--- hyrax/project.clj
+++ hyrax/project.clj
@@ -1,4 +1,4 @@
-(defproject hyrax "0.0.1-SNAPSHOT"
+(defproject org.funtastic/hyrax "0.0.1-SNAPSHOT"
   :description "A library of tools for distributed coordination via RabbitMQ and MongoDB."
   :url "http://example.com/FIXME"
   :license {:name "Apache License"


trying to comment out halt-with-interrupt to see if it helps

--- storm-multicore/src/clj/backtype/storm/daemon/executor.clj
+++ storm-multicore/src/clj/backtype/storm/daemon/executor.clj
@@ -656,7 +656,7 @@
       (shutdown
         [this]
         (log-message "Shutting down executor " component-id ":" (pr-str executor-id))
-        (disruptor/halt-with-interrupt! (:receive-queue executor-data))
+;        (disruptor/halt-with-interrupt! (:receive-queue executor-data))
 ;        (disruptor/halt-with-interrupt! (:batch-transfer-queue executor-data))
         (doseq [h handlers]
           (.interrupt h)


Add incremental OpenNLP single sentence test fn.

--- src/dsbdp/experiment_helper.clj
+++ src/dsbdp/experiment_helper.clj
@@ -127,3 +127,9 @@
   (doseq [sentence (get-sentences in-str)]
     (opennlp-single-sentence-direct-test-fn sentence)))

+(def opennlp-single-sentence-inc-test-fns
+  [(fn [in _] (tokenize in))
+   (fn [_ out] (pos-tag out))
+   (fn [_ out] (chunker out))
+   (fn [_ out] (phrases out))])
+


Small fixes to test.

Small fixes to test.

--- test/onyx/coordinator/linear_cluster_sim.clj
+++ test/onyx/coordinator/linear_cluster_sim.clj
@@ -7,7 +7,8 @@
             [datomic.api :as d]
             [onyx.system :refer [onyx-coordinator]]
             [onyx.extensions :as extensions]
-            [onyx.coordinator.sim-test-utils :as sim-utils]))
+            [onyx.coordinator.sim-test-utils :as sim-utils]
+            [onyx.api]))

 (def cluster (atom {}))

@@ -147,7 +148,7 @@
   (let [cluster-val @cluster
         n (count cluster-val)
         victim (nth (keys cluster-val) (rand-int n))]
-    (let [pulse (:pulse-node (extensions/read-place (:sync components) victim))]
+    (let [pulse (:peer-node (extensions/read-place (:sync components) (:node victim)))]
       (extensions/delete (:sync components) pulse)
       (future-cancel (get cluster-val victim))
       (swap! cluster dissoc victim))))


Cleanup

Cleanup

--- src/holston/tasting_service.clj
+++ src/holston/tasting_service.clj
@@ -11,11 +11,14 @@
           (:id (search-func name))))))

 (defn- add-beer [beer-name brewery-name]
-  (import-named-entity beer-name repo/get-beer (fn [] (repo/add-beer beer-name (import-named-entity brewery-name repo/get-brewery (fn [] (repo/add-brewery brewery-name)))))))
+  (fn [] (repo/add-beer beer-name (import-named-entity brewery-name repo/get-brewery (fn [] (repo/add-brewery brewery-name))))))
+
+(defn- add-or-select-beer [beer-name brewery-name]
+  (import-named-entity beer-name repo/get-beer (add-beer beer-name brewery-name)))

 (defn add-tasting [raw-tasting user-id]
   (repo/add-tasting
-   {:beer_id (add-beer (:beer-name raw-tasting) (:brewery raw-tasting))
+   {:beer_id (add-or-select-beer (:beer-name raw-tasting) (:brewery raw-tasting))
     :user_rating (:rating raw-tasting)
     :user_id user-id}))


CMR-1855 Adds expected conversion for DIF 9 platform instruments.

--- umm-spec-lib/src/cmr/umm_spec/test/expected_conversion.clj
+++ umm-spec-lib/src/cmr/umm_spec/test/expected_conversion.clj
@@ -95,8 +95,9 @@
   [umm-coll _]
   (-> umm-coll
       (update-in [:TemporalExtents] dif-temporal)
-      ;; DIF 9 does not support Platform Type or Characteristics.
-      (update-in-each [:Platforms] assoc :Type nil :Characteristics nil)))
+      ;; DIF 9 does not support Platform Type or Characteristics. The mapping for Instruments is
+      ;; unable to be implemented as specified.
+      (update-in-each [:Platforms] assoc :Type nil :Characteristics nil :Instruments nil)))

 ;; DIF 10


Add missing type hint to clojure example

Following GH623

--- language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/rx_examples.clj
+++ language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/rx_examples.clj
@@ -27,7 +27,9 @@

 (defn hello
   [& args]
-  (-> (Observable/from args)
+  (->
+    ; type hint required due to `Observable/from` overloading
+    (Observable/from ^java.lang.Iterable args)
     (.subscribe (rx/action [v] (println (str "Hello " v "!"))))))

 ; To see output


Update core/run; add mutation operator

Update core/run; add mutation operator

--- clojure/src/evolution_programs/core.clj
+++ clojure/src/evolution_programs/core.clj
@@ -13,6 +13,8 @@
   (fn [p g]
     (doseq [l loggers] (l p g))))

+(def max-generations 20)
+
 (defn run
   ([objective limits]
    (run objective limits (spread-logger (console/logger) (chart/logger))))
@@ -21,8 +23,9 @@
      objective
      selection/binary-tournament-without-replacement
      (partial recombination/crossover
-              (partial crossover/simulated-binary-with-limits limits))
-     (ga/terminate-max-generations? 20)
+              (partial crossover/simulated-binary-with-limits limits)
+              (partial mutation/parameter-based-with-limits limits max-generations))
+     (ga/terminate-max-generations? max-generations)
      (random-generators/generate-population 20 limits)
      logger)))


Clean up tests for add-tags-txd

Clean up tests for add-tags-txd

This is what I set out to do some commits ago, but then got interrupted
by other considerations. Pull out some structure from the properties.
Add comments. Rearrange the definitions.

Note that I started using all uppercase for procedure arguments (already
in one of the preceding commits). [1] hinted me to this convention from
Emacs Lisp and I find it quite helpful. Especially when I want to use a
word both as an argument name and as a regular word.

Pulling out common structure meant writing a macro. Of course I could
have made it a function, but that wouldn't have been as pretty. Alas, I
can't use it for the fact about "transacting doesn't modify", since this
one has a slightly different setup than the others.

[1] https://groups.google.com/d/msg/clojure/kBXoYuL2bP0/5QQSrN1lrNkJ

--- test/clj/theatralia/database/t_txd_gen.clj
+++ test/clj/theatralia/database/t_txd_gen.clj
@@ -1,5 +1,7 @@
 (ns theatralia.database.t-txd-gen
   (:require [midje.sweet :refer :all]
+            [clojure.data :refer [diff]]
+            [clojure.pprint :refer [pprint]]
             [clojure.test.check :as tc]
             [clojure.test.check.generators :as gen]
             [clojure.test.check.properties :as prop]
@@ -8,50 +10,116 @@
             [datomic.api :as d]
             [theatralia.database.canned-queries :as qcan]
             [theatralia.database.component :as db-component]
-            [theatralia.database.txd-gen :as nut])) ; namespace under test
+            [theatralia.database.txd-gen :as txd-gen]))
+
+;;;; General purpose helpers
+
+(defn contains-all?
+  "Returns true iff all elements from seq NEEDLES are contained in seq
+  HAYSTACK."
+  [haystack needles]
+  (every? (set haystack) needles))
+
+;;;; Auxiliary functions for testing against Datomic

 ;;; Credits: http://yellerapp.com/posts/2014-05-07-testing-datomic.html

-(defn empty-db-conn
+(defn fresh-db
+  "Returns a db that contains only the example data."
   []
   (-> (db-component/make-database "datomic:mem://theatralia-test")
       component/start
-      :conn))
+      :conn
+      d/db))

-(defn empty-db
-  []
-  (d/db (empty-db-conn)))
-
-(defn suppose [db txd]
+(defn suppose
+  "Returns a db that looks as if TXD had been transacted into DB."
+  [db txd]
   (:db-after (d/with db txd)))

-(defn contains-all? [haystack needles]
-  (every? (set haystack) needles))
+;;;; Sugar for easier testing of txd-gen/add-tags-txd
+
+(defn add-tags-txd-only
+  "Like txd-gen/add-tags-txd, but returns only the second element of the tuple
+  returned by the add-tags-txd."
+  [& args]
+  ((apply txd-gen/add-tags-txd args) 1))
+
+(defmacro tags-txd-prop
+  "Template for shortening the definition of a property of txd-gen/add-tags-txd
+  slighly. BODY will be evaluated in a context where ARGS are filled as follows:
+
+    0 (tags)      – a generator generating a vector of strings to be used as
+                    tags
+    1 (owner-eid) – the eid of the user for whom we're adding tags
+    2 (db-before) - the database value before adding TAGS
+    3 (db-after)  – the database value after adding TAGS"
+  [args & body]
+  `(let [db-before# (fresh-db)
+         owner-eid# (qcan/username->eid db-before# "sandbox")
+         ~(subvec args 1 3) [owner-eid# db-before#]]
+     (prop/for-all [tags# (gen/vector gen/string)]
+       (let [db-after# (suppose db-before#
+                                (add-tags-txd-only db-before# tags# owner-eid#))
+             ~(args 0) tags#
+             ~(args 3) db-after#]
+         ~@body))))
+
+;;;; Tests for txd-gen/add-tags-txd

 (facts "about add-tags-txd"
   (fact "After transacting the results, all tags should be present in DB."
-    (let [db (empty-db)
-          owner-eid (qcan/username->eid db "sandbox")]
-      (prop/for-all [tags (gen/vector gen/string-ascii)]
-        (let [db-after (suppose db ((nut/add-tags-txd db tags owner-eid) 1))
-              tags-after (qcan/tags-of-user-eid db-after owner-eid)]
-          (contains-all? tags-after tags))))
+    (tags-txd-prop [tags owner-eid db-before db-after]
+      (let [tags-after (qcan/tags-of-user-eid db-after owner-eid)]
+        (contains-all? tags-after tags)))
+    => (quick-check 100))
+
+  (fact "Transacting the generated data doesn't modify pre-existing tags."
+    (let [db-0 (fresh-db)
+          owner-eid (qcan/username->eid db-0 "sandbox")]
+      (prop/for-all [tags-1 (gen/vector gen/string)
+                     tags-2 (gen/vector gen/string)]
+        (let [db-1 (suppose db-0 (add-tags-txd-only db-0 tags-1 owner-eid))
+              tag-eids-1 (d/q '[:find [?e ...] :where [?e :tag/text _]] db-1)
+              db-2 (suppose db-1 (add-tags-txd-only db-1 tags-2 owner-eid))
+              [tag-info-1 tag-info-2] (mapv #(d/pull-many % '[*] tag-eids-1)
+                                            [db-1 db-2])]
+          (= tag-info-1 tag-info-2))))
     => (quick-check 100))

   (fact "After transacting once, the result of the second run should be empty."
-    (let [db (empty-db)
-          owner-eid (qcan/username->eid db "sandbox")]
-      (prop/for-all [tags (gen/vector gen/string)]
-        (let [db-after (suppose db ((nut/add-tags-txd db tags owner-eid) 1))
-              new-txd ((nut/add-tags-txd db-after tags owner-eid) 1)]
-          (empty? new-txd))))
+    (tags-txd-prop [tags owner-eid db-before db-after]
+      (let [new-txd (add-tags-txd-only db-after tags owner-eid)]
+        (empty? new-txd)))
     => (quick-check 100))

   (fact "There should be no two tags with the same text and owner-eid."
-    (let [db (empty-db)
-          owner-eid (qcan/username->eid db "sandbox")]
-      (prop/for-all [tags (gen/vector gen/string)]
-        (let [db-after (suppose db ((nut/add-tags-txd db tags owner-eid) 1))
-              tags-after (qcan/tags-of-user-eid db-after owner-eid)]
-          (apply distinct? tags-after))))
+    (tags-txd-prop [tags owner-eid db-before db-after]
+      (let [tags-after (qcan/tags-of-user-eid db-after owner-eid)]
+        (apply distinct? tags-after)))
     => (quick-check 100)))
+
+(comment
+
+  (require '[theatralia.database.t-txd-gen :as t])
+  (require '[theatralia.database.canned-queries :as qcan])
+  (require '[theatralia.database.txd-gen :as txd-gen])
+
+  (def db-0 (t/fresh-db))
+  (def oeid (qcan/username->eid db-0 "sandbox"))
+  (def res-1 (txd-gen/add-tags-txd db-0 ["a"] oeid))
+
+
+  )
+
+  ; add-material-txd
+  ; Properties:
+  ;  - None should have nil? for a value.
+  ;  - Hm, this one is pretty borinrc/clj/theatralia/routes.clj
+  ;  - What do we want to do if same material transacted twice?
+  ;  - Tags already present shouldn't be modified.
+  ;  - Shouldn't add more tags than we want.
+  ;  - Make sure that when there are some tags already in the database and our
+  ;    list of tags contains some of these, the ones that are contained are left
+  ;    in peace and the extra ones are added.
+  ;  - What about duplicate tags in tags?


Update pe-classifier config to use a templated version

--- configs/pe-classifier/pe-classifier.clj
+++ configs/pe-classifier/pe-classifier.clj
@@ -1,7 +1,7 @@
-(defproject puppetlabs.packages/pe-classifier "0.3.3"
+(defproject puppetlabs.packages/pe-classifier "{{{pe-classifier-version}}}"
   :description "Release artifacts for classifier"
   :pedantic? :abort
-  :dependencies [[puppetlabs/classifier "0.3.3"]
+  :dependencies [[puppetlabs/classifier "{{{pe-classifier-version}}}"]
                  [puppetlabs/trapperkeeper-webserver-jetty9 "0.5.1"]]

   :uberjar-name "classifier-release.jar"


Add caching of ASTs

Add caching of ASTs

This should hopefully improve performance quite a bit.  I'm hoping that
this will allow us to perform the refactorings synchronously rather than
asynchronously.

--- src/refactor_nrepl/analyzer.clj
+++ src/refactor_nrepl/analyzer.clj
@@ -5,6 +5,9 @@
             [clojure.tools.analyzer.jvm :as ana.jvm]
             [clojure.tools.namespace.parse :refer [read-ns-decl]])
   (:import java.io.PushbackReader))
+
+;;; The structure here is {ns {content-hash ast}}
+(def ^:private ast-cache (atom {}))

 ;; these two fns could go to clojure.tools.namespace.parse: would worth a pull request
 (defn get-alias [as v]
@@ -14,8 +17,8 @@

 (defn parse-ns
   "Returns tuples with the ns as the first element and
-   a map of the aliases for the namespace as the second element
-   in the same format as ns-aliases"
+  a map of the aliases for the namespace as the second element
+  in the same format as ns-aliases"
   [body]
   (let [ns-decl (read-ns-decl (PushbackReader. (java.io.StringReader. body)))
         aliases (->> ns-decl
@@ -30,13 +33,33 @@
 (defn- noop-macroexpand-1 [form]
   form)

-(defn string-ast [string]
+(defn- get-ast-from-cache
+  [ns file-content]
+  (-> (get @ast-cache ns)
+      (get (hash file-content))))
+
+(defn- update-ast-cache
+  [file-content ns ast]
+  (swap! ast-cache update-in [ns] merge {(hash file-content) ast})
+  ast)
+
+(defn- build-ast
+  [ns aliases]
+  (binding [ana/macroexpand-1 noop-macroexpand-1]
+    (assoc-in (ana.jvm/analyze-ns ns) [0 :alias-info] aliases)))
+
+(defn- cachable-ast [file-content]
+  (let [[ns aliases] (parse-ns file-content)]
+    (when ns
+      (if-let [cached-ast (get-ast-from-cache ns file-content)]
+        cached-ast
+        (update-ast-cache file-content ns (build-ast ns aliases))))))
+
+(defn string-ast
+  [file-content]
   (try
-    (let [[ns aliases] (parse-ns string)]
-      (binding [ana/macroexpand-1 noop-macroexpand-1]
-        (when ns
-          (assoc-in (ana.jvm/analyze-ns ns) [0 :alias-info] aliases))))
+    (cachable-ast file-content)
     (catch Exception ex
-      (println "error when building AST for" (first (parse-ns string)))
+      (println "error when building AST for" (first (parse-ns file-content)))
       (.printStackTrace ex)
       [])))