Add fixed buttons, update devcard layout

--- src-cljs/frontend/components/pieces/button.cljs
+++ src-cljs/frontend/components/pieces/button.cljs
@@ -12,13 +12,15 @@
                (default: :secondary)
   :disabled? - If true, the button is disabled. (default: false)
   :size      - The size of the button. One of #{:full :small}.
-               (default: :full)"
-  [{:keys [on-click kind disabled? size]
+               (default: :full)
+  :fixed?    - If true, the button is fixed width. (default: false)"
+  [{:keys [on-click kind disabled? size fixed?]
     :or {kind :secondary size :full}}
    content]
   (component
     (html
      [:button {:class (remove nil? [(name kind)
+                                    (when fixed? "fixed")
                                     (case size
                                       :full nil
                                       :small "small")])
@@ -54,14 +56,16 @@
                    (default: :secondary)
   :size          - The size of the button. One of #{:full :small}.
                    (default: :full)
+  :fixed?        - If true, the button is fixed width. (default: false)
   :target        - Specifies where to display the linked URL."
-  [{:keys [href on-click kind size target]
+  [{:keys [href on-click kind size fixed? target]
     :or {kind :secondary size :full}}
    content]
   (component
     (html
      [:a.exception
       {:class (remove nil? [(name kind)
+                            (when fixed? "fixed")
                             (case size
                               :full nil
                               :small "small")])
@@ -102,6 +106,7 @@
   :disabled?    - If true, the button is disabled. (default: false)
   :size         - The size of the button. One of #{:full :small}.
                   (default: :full)
+  :fixed?       - If true, the button is fixed width. (default: false)
   :loading-text - Text to display indicating that the button action is in
                   progress. (default: \"...\")
   :success-text - Text to display indicating that the button action was
@@ -109,7 +114,7 @@
   :failed-text  - Text to display indicating that the button action failed.
                   (default: \"Failed\")
   :bordered?    - (optional) Adds a border to the button."
-  [{:keys [kind disabled? size failed-text success-text loading-text on-click bordered?]
+  [{:keys [kind disabled? size fixed? failed-text success-text loading-text on-click bordered?]
     :or {kind :secondary size :full disabled? false}}
    content]
   (forms/managed-button
@@ -123,6 +128,7 @@
              :disabled disabled?
              :on-click on-click
              :class (remove nil? [(name kind)
+                                  (when fixed? "fixed")
                                   (case size
                                     :full nil
                                     :small "small")
@@ -132,9 +138,9 @@
 (dc/do
   (defn button-display [columns]
     (html
-     [:div {:style {:display "flex"}}
+     [:div {:style {:display "flex" :justify-content "space-between"}}
       (for [column columns]
-        [:div.primary {:style {:margin-right "2em"}}
+        [:div {:style {:margin-right "2em"}}
          (for [button column]
            [:div {:style {:margin-bottom "1em"}}
             button])])]))
@@ -163,7 +169,11 @@

     The **Full** size button is the default. A **Small** size is available to
     use in table rows, card headers, and anywhere vertical space is at a
-    premium."
+    premium.
+
+
+    ## Widths
+    Button widths vary based on button text length and can have a **Fixed** attribute which if set to **true** will make the button fixed width for cases like modal footers where we want 2 buttons."

     (button-display
      [;; Primary buttons
@@ -173,7 +183,11 @@
        (button {:disabled? true
                 :kind :primary
                 :on-click #(js/alert "Clicked!")}
-               "Disabled")
+               "Default Disabled")
+       (button {:fixed? true
+                :kind :primary
+                :on-click #(js/alert "Clicked!")}
+               "Default Fixed")
        (button {:kind :primary
                 :size :small
                 :on-click #(js/alert "Clicked!")}
@@ -185,6 +199,9 @@
        (button {:disabled? true
                 :on-click #(js/alert "Clicked!")}
                "Secondary Disabled")
+       (button {:fixed? true
+                :on-click #(js/alert "Clicked!")}
+               "Secondary Fixed")
        (button {:size :small
                 :on-click #(js/alert "Clicked!")}
                "Secondary Small")]
@@ -197,6 +214,10 @@
                 :kind :danger
                 :on-click #(js/alert "Clicked!")}
                "Danger Disabled")
+       (button {:fixed? true
+                :kind :danger
+                :on-click #(js/alert "Clicked!")}
+               "Danger Fixed")
        (button {:kind :danger
                 :size :small
                 :on-click #(js/alert "Clicked!")}
@@ -210,6 +231,10 @@
                 :kind :flat
                 :on-click #(js/alert "Clicked!")}
                "Flat Disabled")
+       (button {:fixed? true
+                :kind :flat
+                :on-click #(js/alert "Clicked!")}
+               "Flat Fixed")
        (button {:kind :flat
                 :size :small
                 :on-click #(js/alert "Clicked!")}
@@ -226,13 +251,14 @@
     which matches the Small size of text buttons."

     (button-display
-     [[(icon {:label "Icon"
+     [;; Icon buttons
+      [(icon {:label "Icon"
               :on-click #(js/alert "Clicked!")}
-             [:i.octicon.octicon-repo-forked])]
-      [(icon {:label "Icon Disabled"
-              :disabled? true
-              :on-click #(js/alert "Clicked!")}
-             [:i.octicon.octicon-repo-forked])]]))
+             [:i.octicon.octicon-repo-forked])
+       (icon {:label "Icon Disabled"
+                     :disabled? true
+                     :on-click #(js/alert "Clicked!")}
+                    [:i.octicon.octicon-repo-forked])]]))

   (defcard link-buttons
     "A **link-button** looks like a button, but is actually a link.
@@ -253,7 +279,11 @@
      [;; Primary link buttons
       [(link {:kind :primary
               :href "#"}
-             "Primary Link")
+             "Default Link")
+       (link {:fixed? true
+              :kind :primary
+              :href "#"}
+             "Default Link Fixed")
        (link {:kind :primary
               :href "#"
               :size :small}
@@ -262,6 +292,9 @@
       ;; Secondary link buttons
       [(link {:href "#"}
              "Secondary Link")
+       (link {:fixed? true
+              :href "#"}
+             "Secondary Link Fixed")
        (link {:href "#"
               :size :small}
              "Small Secondary Link")]
@@ -270,6 +303,10 @@
       [(link {:kind :danger
               :href "#"}
              "Danger Link")
+       (link {:fixed? true
+              :kind :danger
+              :href "#"}
+             "Danger Link Fixed")
        (link {:kind :danger
               :href "#"
               :size :small}
@@ -279,6 +316,10 @@
       [(link {:kind :flat
               :href "#"}
              "Flat Link")
+       (link {:fixed? true
+              :kind :flat
+              :href "#"}
+             "Flat Link Fixed")
        (link {:kind :flat
               :href "#"
               :size :small}
@@ -295,12 +336,17 @@
   (defcard bordered-buttons
     "Buttons with borders are used in the header. This is design debt."
     (button-display
-     [[(managed-button {:bordered? true
+     [
+      [(managed-button {:bordered? true
                         :size :small}
-                       "Bordered Button")]
-      [(icon {:label "Bordered GitHub icon button"
+                       "Bordered Button")
+       (managed-button {:fixed? true
+                        :bordered? true
+                        :size :small}
+                       "Bordered Button Fixed")
+       (icon {:label "Bordered GitHub icon button"
               :bordered? true}
-             (icon/github))]
-      [(icon-link {:label "Bordered Bitbucket icon link-button"
+             (icon/github))
+       (icon-link {:label "Bordered Bitbucket icon link-button"
                    :bordered? true}
                   (icon/bitbucket))]])))


kibit suggestion

kibit suggestion

--- test/cljs/scrabble/views_test.cljs
+++ test/cljs/scrabble/views_test.cljs
@@ -4,4 +4,4 @@

 (t/deftest main-panel-test
-  (t/is (> (count ((views/main-panel))) 0)))
+  (t/is (pos? (count ((views/main-panel))))))


[#15]: lifecycle functions return states touched

start/stop/suspend/resume would now return all the states they effected/touched

--- src/mount/core.clj
+++ src/mount/core.clj
@@ -1,6 +1,6 @@
 (ns mount.core
   (:require [clojure.tools.macro :as macro]
-            [clojure.tools.logging :refer [info warn debug error]]))
+            [clojure.tools.logging :refer [info]]))

 ;; (defonce ^:private session-id (System/currentTimeMillis))
 (defonce ^:private mount-state 42)
@@ -44,45 +44,50 @@
       `(defonce ~(with-meta state (merge (meta state) s-meta))
          (NotStartedState. ~(str state))))))

-(defn- up [var {:keys [ns name start started? resume suspended?]}]
+(defn- record! [{:keys [ns name]} f done]
+  (let [state (f)]
+    (swap! done conj (str ns "/" name))
+    state))
+
+(defn- up [var {:keys [ns name start started? resume suspended?] :as state} done]
   (when-not started?
     (let [s (try (if suspended?
                    (do (info ">> resuming.. " name)
-                       (resume))
+                       (record! state resume done))
                    (do (info ">> starting.. " name)
-                       (start)))
+                       (record! state start done)))
                  (catch Throwable t
                    (throw (RuntimeException. (str "could not start [" name "] due to") t))))]
       (intern ns (symbol name) s)
       (alter-meta! var assoc :started? true :suspended? false))))

-(defn- down [var {:keys [ns name stop started? suspended?]}]
+(defn- down [var {:keys [ns name stop started? suspended?] :as state} done]
   (when (or started? suspended?)
     (info "<< stopping.. " name)
     (when stop
       (try
-        (stop)
+        (record! state stop done)
         (catch Throwable t
           (throw (RuntimeException. (str "could not stop [" name "] due to") t)))))
     (intern ns (symbol name) (NotStartedState. name)) ;; (!) if a state does not have :stop when _should_ this might leak
     (alter-meta! var assoc :started? false :suspended? false)))

-(defn- sigstop [var {:keys [ns name started? suspend resume]}]
+(defn- sigstop [var {:keys [ns name started? suspend resume] :as state} done]
   (when (and started? resume)        ;; can't have suspend without resume, but the reverse is possible
     (info ">> suspending.. " name)
     (when suspend                    ;; don't suspend if there is only resume function (just mark it :suspended?)
-      (let [s (try (suspend)
+      (let [s (try (record! state suspend done)
                    (catch Throwable t
                      (throw (RuntimeException. (str "could not suspend [" name "] due to") t))))]
         (intern ns (symbol name) s)))
     (alter-meta! var assoc :started? false :suspended? true)))

-(defn- sigcont [var {:keys [ns name start started? resume suspended?]}]
+(defn- sigcont [var {:keys [ns name start started? resume suspended?] :as state} done]
   (when (instance? NotStartedState var)
     (throw (RuntimeException. (str "could not resume [" name "] since it is stoppped (i.e. not suspended)"))))
   (when suspended?
     (info ">> resuming.. " name)
-    (let [s (try (resume)
+    (let [s (try (record! state resume done)
                  (catch Throwable t
                    (throw (RuntimeException. (str "could not resume [" name "] due to") t))))]
       (intern ns (symbol name) s)
@@ -117,10 +122,12 @@
          (sort-by :order))))

 (defn- bring [states fun order]
-  (->> states
-       (sort-by (comp :order meta) order)
-       (map #(fun % (meta %)))
-       doall))
+  (let [done (atom #{})]
+    (->> states
+         (sort-by (comp :order meta) order)
+         (map #(fun % (meta %) done))
+         doall)
+    @done))

 (defn merge-lifecycles
   "merges with overriding _certain_ non existing keys.
@@ -155,23 +162,22 @@

 (defn start [& states]
   (let [states (or (seq states) (all-without-subs))]
-    (bring states up <)
-    :started))
+    {:started (bring states up <)}))

 (defn stop [& states]
-  (let [states (or states (find-all-states))]
-    (doall (map unsub states))     ;; unmark substitutions marked by "start-with"
-    (bring states down >)
-    (doall (map rollback! states)) ;; restore to origin from "start-with"
-    :stopped))
+  (let [states (or states (find-all-states))
+        _ (doall (map unsub states))     ;; unmark substitutions marked by "start-with"
+        stopped (bring states down >)]
+    (doall (map rollback! states))       ;; restore to origin from "start-with"
+    {:stopped stopped}))

 (defn stop-except [& states]
   (let [all (set (find-all-states))
-        states (remove (set states) all)]
-    (doall (map unsub states))     ;; unmark substitutions marked by "start-with"
-    (bring states down >)
-    (doall (map rollback! states)) ;; restore to origin from "start-with"
-    :stopped))
+        states (remove (set states) all)
+        _ (doall (map unsub states))     ;; unmark substitutions marked by "start-with"
+        stopped (bring states down >)]
+    (doall (map rollback! states))       ;; restore to origin from "start-with"
+    {:stopped stopped}))

 (defn start-with-args [xs & states]
   (reset! -args xs)
@@ -194,10 +200,8 @@

 (defn suspend [& states]
   (let [states (or (seq states) (all-without-subs))]
-    (bring states sigstop <)
-    :suspended))
+    {:suspended (bring states sigstop <)}))

 (defn resume [& states]
   (let [states (or (seq states) (all-without-subs))]
-    (bring states sigcont <)
-    :resumed))
+    {:resumed (bring states sigcont <)}))


Manually resolve call

Manually resolve call

--- src/caesium/crypto/secretbox.clj
+++ src/caesium/crypto/secretbox.clj
@@ -47,7 +47,7 @@
   You only want this to manage the output byte array yourself. Otherwise,
   you want [[secretbox-open-easy]]."
   [^bytes out ^bytes ctext ^bytes nonce ^bytes key]
-  (let [clen (buflen ctext)
+  (let [clen (long (buflen ctext))
         res (.crypto_secretbox_open_easy sodium out ctext clen nonce key)]
     (if (= res 0)
       out


Add digest test for unknown algorithm type.

--- test/vault/blob/digest_test.clj
+++ test/vault/blob/digest_test.clj
@@ -52,7 +52,8 @@

 (deftest content-hashing
   (testing "digest/hash"
-    (is (= blob-id (digest/hash :sha256 blob-content)))))
+    (is (= blob-id (digest/hash :sha256 blob-content)))
+    (is (thrown? IllegalArgumentException (digest/hash :foo blob-content)))))

 (deftest hash-id-selection


Update link in gathering

Update link in gathering

--- src/clj/chili_dog_night/data.clj
+++ src/clj/chili_dog_night/data.clj
@@ -73,7 +73,7 @@
                           ", and "
                           (link-to "https://en.wikipedia.org/wiki/Vanity_(singer)" "Vanity")
                           [:br]
-                          (link-to "https://www.youtube.com/watch?v=sWsnd4IUJdY" "Gene Simmons")
+                          (link-to "https://www.youtube.com/watch?v=vJsy434hg5c" "Gene Simmons")
                           " and "
                           (link-to "https://en.wikipedia.org/wiki/Zionism" "Zionism")
                           [:br]


fix typo

fix typo

--- test/clj/fractals/datalog_test.clj
+++ test/clj/fractals/datalog_test.clj
@@ -253,7 +253,7 @@
                                   (into []))
                              ))
                [:user/email "john@example.com"]))))
-  @(d/transact (:conn datomic-social-db) (gen-users-wilth-upvotes 10))
+  @(d/transact (:conn datomic-social-db) (gen-users-with-upvotes 10))
   (is (= (count (d/q '[:find ?id :where
                        [?id :user/email]]
                      (d/db (:conn datomic-social-db))))


have Bank Job end the run and mark it successful (triggering Desperado)

--- src/clj/game/cards-resources.clj
+++ src/clj/game/cards-resources.clj
@@ -50,10 +50,15 @@

    "Bank Job"
    {:data {:counter 8}
-    :abilities [{:label "Take any number of [Credits] on Bank Job"
+    :abilities [{:req (req (and (:run @state) (= (:position run) 0)))
+                 :label "Take any number of [Credits] on Bank Job"
                  :prompt "How many [Credits]?" :choices :counter :msg (msg "gain " target " [Credits]")
                  :effect (req (gain state side :credit target)
-                              (when (= target (:counter card)) (trash state :runner card {:unpreventable true})))}]}
+                              (register-successful-run state side (:server run))
+                              (swap! state update-in [:runner :prompt] rest)
+                              (handle-end-run state side)
+                              (when (= target (:counter card))
+                                (trash state :runner card {:unpreventable true})))}]}

    "Beach Party"
    {:effect (effect (gain :max-hand-size 5)) :leave-play (effect (lose :max-hand-size 5))
@@ -690,7 +695,7 @@
    "Wireless Net Pavilion"
    {:effect (effect (trash-resource-bonus -2))
     :leave-play (effect (trash-resource-bonus 2))}
-
+
    "Woman in the Red Dress"
    {:events {:runner-turn-begins
              {:msg (msg "reveal " (:title (first (:deck corp))) " on the top of R&D")
@@ -719,4 +724,3 @@
                                 (remove-watch ref (keyword (str "zona-sul-shipping" (:cid card))))
                                 (trash ref :runner card)
                                 (system-msg ref side "trashes Zona Sul Shipping for being tagged")))))}})
-


change London Library and The Supplier to use card targeting for hosting

--- src/clj/game/cards-resources.clj
+++ src/clj/game/cards-resources.clj
@@ -271,11 +271,8 @@

    "London Library"
    {:abilities [{:label "Install a non-virus program on London Library" :cost [:click 1]
-                 :prompt "Choose a non-virus program to install on London Library"
-                 :choices (req (filter #(and (= (:type %) "Program")
-                                             (not (has? % :subtype "Virus"))
-                                             (<= (:memoryunits %) (:memory runner)))
-                                       (:hand runner)))
+                 :prompt "Choose a non-virus program from your Grip to install on London Library"
+                 :choices {:req #(and (= (:type %) "Program") (not (has? % :subtype "Virus")) (= (:zone %) [:hand]))}
                  :msg (msg "host " (:title target))
                  :effect (effect (runner-install target {:host-card card :no-cost true}))}
                 {:label "Add a program hosted on London Library to your Grip" :cost [:click 1]
@@ -590,8 +587,9 @@

    "The Supplier"
    {:abilities [{:label "Host a resource or piece of hardware" :cost [:click 1]
-                 :prompt "Choose a card to host on The Supplier"
-                 :choices (req (filter #(#{"Resource" "Hardware"} (:type %)) (:hand runner)))
+                 :prompt "Choose a resource or piece of hardware from your Grip to host on The Supplier"
+                 :choices {:req #(and (or (= (:type %) "Hardware") (= (:type %) "Resource"))
+                                      (= (:zone %) [:hand]))}
                  :effect (effect (host card target)) :msg (msg "host " (:title target) "")}]
     :events {:runner-turn-begins
              {:prompt "Choose a card on The Supplier to install"


test check remaining counters on daily casts

--- src/clj/test/cards-resources.clj
+++ src/clj/test/cards-resources.clj
@@ -263,6 +263,7 @@
       )))

 (deftest daily-casts
+  "Play and tick through all turns of daily casts"
   (do-game
     (new-game (default-corp) (default-runner [(qty "Daily Casts" 3)]))
     (take-credits state :corp)
@@ -273,4 +274,14 @@
       ;End turn
       (take-credits state :runner)
       (take-credits state :corp)
+      (is (= 6 (get-in (refresh dc) [:counter])))
+      (take-credits state :runner)
+      (take-credits state :corp)
+      (is (= 4 (get-in (refresh dc) [:counter])))
+      (take-credits state :runner)
+      (take-credits state :corp)
+      (is (= 2 (get-in (refresh dc) [:counter])))
+      (take-credits state :runner)
+      (take-credits state :corp)
+      (is (nil? (get-in @state [:runner :rig :resource 0])))
       )))


Shadow for not found hexagon

Shadow for not found hexagon


Add keys-in utility for getting all map keys

--- src/cljs/hacker_agent/utils.cljs
+++ src/cljs/hacker_agent/utils.cljs
@@ -14,3 +14,11 @@
   (let [re (new js/RegExp "^https?\\:\\/\\/(www\\.)?([^\\/:?#]+)(?:[\\/:?#]|$)")
         matches (.exec re url)]
     (and matches (get matches 2))))
+
+(defn keys-in [m]
+  (if (map? m)
+    (mapcat (fn [[k v]]
+              (let [sub (keys-in v)]
+                (conj sub k)))
+            m)
+    []))


Production API.

Production API.

--- src/cljs/town_client/config.cljs
+++ src/cljs/town_client/config.cljs
@@ -1,6 +1,6 @@
 (ns town-client.config)

-(def url-base "http://localhost:8079")
+(def url-base "http://dev.hel.fi/kenenkaupunki/api/")

 (def aggregates
   {:asuminen #{"paikka-tai-alue-asuinrakentamiselle"


comments

comments

--- src/cljs/triboard/game_loop.cljs
+++ src/cljs/triboard/game_loop.cljs
@@ -16,6 +16,9 @@
 (def ai-move-delay 1000)

 (defn ai-computation
+  "Run the computation of the AI asynchronously:
+   * Wait 500ms to start (animation might be frozen otherwise)
+   * Wait 1s to play the move (avoid moves being played too fast)"
   [game]
   (let [out-chan (chan 1)]
     (go


Add logic to save xtra data

Add logic to save xtra data

--- src/cljs/whatishistory/core.cljs
+++ src/cljs/whatishistory/core.cljs
@@ -76,6 +76,25 @@
                               (swap! app-atom assoc :defn-obj defn)))
     (.fail new-defn-promise #(js/alert "Something went wrong :/. Please try again."))))

+
+(defn save-xtra-info [app-atom]
+  (let [acl (js/Parse.ACL.)
+        curr-user (js/Parse.User.current)
+        defn-obj (get @app-atom :defn-obj)
+        profession (get @app-atom :profession)
+        country (get @app-atom :country)]
+    (if-not (nil? defn-obj)
+      (do
+        (.setPublicReadAccess acl false)
+        (.setPublicWriteAccess acl false)
+        (.setACL defn-obj acl)
+        (.set defn-obj "authorProfession" profession)
+        (.set defn-obj "authorCountry" country)
+        (def save-defn-promise (.save defn-obj))
+        (.then save-defn-promise #(js/console.log "saved xtra data"))
+        (.fail save-defn-promise (fn [err]
+                                 (js/console.error err)))))))
+
 (defn get-defn [idx]
   (js/Parse.Cloud.run
    "getDefn"
@@ -148,7 +167,8 @@
       [:div
        [:button {:class "button-primary submit"
                  :on-click (fn [evt]
-                             (.preventDefault evt))} "Submit"]]]])
+                             (.preventDefault evt)
+                             (save-xtra-info app-atom))} "Submit"]]]])

 (defn defn-form [app-atom]
   [defn-form-content app-atom])
@@ -202,10 +222,10 @@
           much time. Ten minutes should be sufficient. It is also
           okay to cite another author who you feel expresses things
           clearly."]
-     [xtra-info-form app-atom]
-     ; (if (nil? (get @app-atom :defn-obj))
-     ;   [defn-form app-atom]
-     ;   [xtra-info-form app-atom])
+     ;[xtra-info-form app-atom]
+     (if (nil? (get @app-atom :defn-obj))
+       [defn-form app-atom]
+       [xtra-info-form app-atom])
      ]))

 ; (defn about-page []


[style]

[style]

--- test/og/clj_dstream/test_utils.clj
+++ test/og/clj_dstream/test_utils.clj
@@ -1,5 +1,7 @@
 (ns og.clj-dstream.test-utils
   (:require [og.clj-dstream.core :as core]))
+
+(def TWO_PI (* 2 (Math/PI)))

 (defn cluster-count [state]
   (->> state
@@ -18,10 +20,8 @@
        (filter #(= label %))
        count))

-
-(def TWO_PI (* 2 (Math/PI)))
-
-(defn sample-ring-2d [r-inner r-outer & {:keys [offsets] :or {offsets [0.0 0.0]}}]
+(defn sample-ring-2d [r-inner r-outer
+                      & {:keys [offsets] :or {offsets [0.0 0.0]}}]
   "Random [x y] inside a ring centered
   about origin with minor/major radii"
   (let [r     (+ r-inner
@@ -32,7 +32,8 @@
     [(+ x (first offsets)) (+ y (second offsets))]))

-(defn sample-ring-arc-2d [theta-start theta-end r-inner r-outer & {:keys [offsets] :or {offsets [0.0 0.0]}}]
+(defn sample-ring-arc-2d [theta-start theta-end r-inner r-outer
+                          & {:keys [offsets] :or {offsets [0.0 0.0]}}]
   "Random [x y] inside a ring centered
   about origin with minor/major radii
   and with degree range"
@@ -52,11 +53,9 @@
     [rand-x rand-y]))

 (defn sample-rect [sizes offsets]
-  (let [stuff (vec (map-indexed (fn [idx offset]
-                                  (+ offset
-                                     (rand (get sizes idx))))
-                                offsets))]
-
-    stuff)
-
-  )+  (vec
+    (map-indexed
+      (fn [idx offset]
+        (+ offset
+           (rand (get sizes idx))))
+      offsets)))


Fix links in markdown generated for quil-site.

--- dev-resources/utils/docs.clj
+++ dev-resources/utils/docs.clj
@@ -14,6 +14,22 @@
            (com.vladsch.flexmark.ext.wikilink WikiLinkExtension)
            (com.vladsch.flexmark.util.options MutableDataSet)))

+(defn as-url [str]
+  (-> str
+      (string/lower-case)
+      (string/replace  #"[ &,?]" {" " "-"
+                                  "," ""
+                                  "&" "and"
+                                  "?" "_q"})))
+
+(defn wiki-link
+  [function-name category subcategory]
+  (str "/api"
+       (when category (str "/" (as-url category)))
+       (when subcategory (str "/" (as-url subcategory)))
+       "#"
+       function-name))
+
 (def md-extensions
   [(TablesExtension/create)
    (AutolinkExtension/create)
@@ -27,7 +43,7 @@

 (defn- md-renderer
   "Create a Markdown renderer."
-  []
+  [function->category-info]
   (.. (HtmlRenderer/builder
        (doto (MutableDataSet.)
          (.set AnchorLinkExtension/ANCHORLINKS_ANCHOR_CLASS "md-anchor")
@@ -41,19 +57,21 @@
             (reify LinkResolver
               (resolveLink [_this _node _ctx link]
                 (if (= (.getLinkType link) WikiLinkExtension/WIKI_LINK)
-                  (ResolvedLink. LinkType/LINK
-                                 (.getUrl link)
+                  (let [function-name (.getUrl link)
+                        {:keys [category subcategory]} (get function->category-info function-name)]
+                    (ResolvedLink. LinkType/LINK
+                                 (wiki-link function-name category subcategory)
                                  nil
-                                 LinkStatus/UNCHECKED)
+                                 LinkStatus/UNCHECKED))
                   link))))))
       (extensions md-extensions)
       (build)))

 (defn markdown-to-html
   "Parse the given string as Markdown and return HTML."
-  [markdown]
+  [markdown function->category-info]
   (->> (.parse md-container markdown)
-       (.render (md-renderer))))
+       (.render (md-renderer function->category-info))))

 (defn read-all [file feature]
   (let [reader (->> file
@@ -70,7 +88,7 @@
   (filter #(-> % second meta (contains? :category))
           all-forms))

-(defn get-full-meta [[type name docstring args :as form]]
+(defn get-full-meta [function->category-info [type name docstring args :as form]]
   (let [mt (meta name)
         args (if (vector? args)
                #{args}
@@ -78,17 +96,35 @@
     (assoc mt
            :args args
            :name name
-           :docstring (markdown-to-html docstring)
+           :docstring (markdown-to-html docstring function->category-info)
            :what (if (= 'defmacro type) :macro :fn)
            :link (link-to-processing-reference mt))))

+(defn get-category-info [[type symbol docstring args :as form]]
+  [(name symbol)
+   (select-keys (meta symbol) [:category :subcategory])])
+
+(def files ["quil/core.cljc" "quil/middleware.cljc"])
+
+(defn get-function->category-info []
+  (let [clj (->> (mapcat #(read-all % :clj) files)
+                get-public-fns
+                (map get-category-info)
+                (into {}))
+        cljs (->> (mapcat #(read-all % :cljs) files)
+                 get-public-fns
+                 (map get-category-info)
+                 (into {}))]
+    (merge clj cljs)))
+
 (defn get-metas [forms type]
-  (->> forms
-       get-public-fns
-       (map get-full-meta)
-       (map #(assoc % :type type))
-       (map #(vector (:name %) %))
-       (into {})))
+  (let [function->category-info (get-function->category-info)]
+    (->> forms
+         get-public-fns
+         (map #(get-full-meta function->category-info %))
+         (map #(assoc % :type type))
+         (map #(vector (:name %) %))
+         (into {}))))

 (defn merge-metas [meta1 meta2]
   (let [args1 (:args meta1)
@@ -105,8 +141,6 @@
 (defn merge-all-metas [metas1 metas2]
   (merge-with merge-metas metas1 metas2))

-(def files ["quil/core.cljc" "quil/middleware.cljc"])
-
 (defn generate-metas-to-file [file]
   (let [clj (-> (mapcat #(read-all % :clj) files)
                 (get-metas :clj))


Setting an application name.

Setting an application name.


travis isn't that fast..

travis isn't that fast..

--- test/io/sarnowski/swagger1st/performance_test.clj
+++ test/io/sarnowski/swagger1st/performance_test.clj
@@ -66,6 +66,6 @@
         [mean-time] (:mean result)]
     (c/report-result result :verbose)

-    (is (< mean-time (microseconds 200)))
+    (is (< mean-time (microseconds 1000)))

-    (set-log-level! "TRACE")))+    (set-log-level! "TRACE")))


Patch to ensure #{} in record-pass

Patch to ensure #{} in record-pass


CMR-1955: Addressing code review comments.

--- common-lib/src/cmr/common/mime_types.clj
+++ common-lib/src/cmr/common/mime_types.clj
@@ -83,6 +83,8 @@
    :serf             "application/serf+xml"})

 (defn format->mime-type
+  "Converts a format structure (a keyword or map containing :format and :version) to a mime type.
+   The mime type will contain a version if the format does."
   [format]
   (if (map? format)
     (with-version (format->mime-type (:format format)) (:version format))


Remove function to get url to statement

Remove function to get url to statement

--- src/discuss/references/main.cljs
+++ src/discuss/references/main.cljs
@@ -33,21 +33,6 @@
   [reference-id]
   (let [url (str (:base config/api) (get-in config/api [:get :reference-usages]) "/" reference-id)]
     (com/ajax-get url {} reference-usage-handler)))
-
-(defn get-statement-handler
-  "Processes response and changes view with given url."
-  [response]
-  (let [res (com/process-response response)]
-    (com/ajax-get-and-change-view (:url res) :default)))
-
-(defn get-statement-url
-  "Given an issue-id, statement-id and attitude, query statement url inside the discussion."
-  [statement agree]
-  (let [issue-id (get-in statement [:issue :uid])
-        statement-id (get-in statement [:statement :uid])
-        pre-url (get-in config/api [:get :statement-url])
-        url (clojure.string/join "/" [pre-url issue-id statement-id agree])]
-    (com/ajax-get url {} get-statement-handler)))

 ;;;; Interaction with integrated references


Add macroexpand-all and prettyprint to main ns.

--- src/uxbox/main.clj
+++ src/uxbox/main.clj
@@ -6,6 +6,8 @@

 (ns uxbox.main
   (:require [clojure.tools.namespace.repl :as repl]
+            [clojure.walk :refer [macroexpand-all]]
+            [clojure.pprint :refer [pprint]]
             [mount.core :as mount]
             [buddy.core.codecs :as codecs]
             [buddy.core.nonce :as nonce]


Whitespace tweak

Whitespace tweak

--- src/io/aviso/rook/dispatcher.clj
+++ src/io/aviso/rook/dispatcher.clj
@@ -324,7 +324,7 @@
 (def standard-resolvers
   "A map of keyword -> (function of symbol returning a function of
   request)."
-  {:request (constantly identity)
+  {:request     (constantly identity)
    :request-key request-key-resolver
    :header      header-arg-resolver
    :param       param-arg-resolver})


env validation testing

env validation testing

--- test/celestial/test/persistency.clj
+++ test/celestial/test/persistency.clj
@@ -42,6 +42,8 @@
        (throws ExceptionInfo (with-m? {:username '("must be present")}))
      (validate-user {:username "foo" :password "bar" :roles admin})  =>
        (throws ExceptionInfo (with-m? {:envs '("must be present")}))
+     (validate-user {:username "foo" :password "bar" :roles admin :envs [""]})  =>
+       (throws ExceptionInfo (with-m? {:envs '(({0 ("must be a keyword")}))} ))
      (validate-user {:username "foo" :password "bar" :roles ["foo"] :envs []})  =>
        (throws ExceptionInfo (with-m? {:roles '(({0 ("role must be either #{:celestial.roles/anonymous :celestial.roles/user :celestial.roles/admin}")}))} ))
       )


Fix failing tests

Fix failing tests

--- test/cljs/replumb/require_test.cljs
+++ test/cljs/replumb/require_test.cljs
@@ -113,34 +113,21 @@
 (h/read-eval-call-test e/*target-opts*
   ["(find-doc \"[^(]newline[^s*]\")"]
   (let [result (unwrap-result @_res_)
-        expected "-------------------------
-*flush-on-newline*
-  When set to true, output will be flushed whenever a newline is printed.
-
-  Defaults to true.
-"]
+        expected #"-------------------------\s+\*flush-on-newline\*\s+When set to true"]
     (is (success? @_res_) (str _msg_ "should succeed"))
     (is (valid-eval-result? result) (str _msg_ "should  be a valid result"))
-    (is (= expected result) (str _msg_ "should return valid docstring"))))
+    (is (re-find expected result) (str _msg_ "should return valid docstring"))))

 (h/read-eval-call-test e/*target-opts*
   ["(require 'clojure.string)"
    "(find-doc \"[^(]newline[^s*]\")"]
   (let [result (unwrap-result @_res_)
-        expected "-------------------------
-*flush-on-newline*
-  When set to true, output will be flushed whenever a newline is printed.
-
-  Defaults to true.
--------------------------
-trim-newline
-([s])
-  Removes all trailing newline \\n or return \\r characters from
-  string.  Similar to Perl's chomp.
-"]
-    (is (success? @_res_) (str _msg_ "should succeed"))
-    (is (valid-eval-result? result) (str _msg_ "should be a valid result"))
-    (is (= expected result) (str _msg_ "should return valid docstring"))))
+        expected1 #"-------------------------\s+\*flush-on-newline\*\s+When set to true"
+        expected2 #"-------------------------\s+trim-newline\s\(\[s\]\)\s+Removes all trailing newline"]
+    (is (success? @_res_) (str _msg_ "should succeed"))
+    (is (valid-eval-result? result) (str _msg_ "should be a valid result"))
+    (is (re-find expected1 result) (str _msg_ "should return expected (part 1) docstring"))
+    (is (re-find expected2 result) (str _msg_ "should return expected (part 2) docstring"))))

 (h/read-eval-call-test e/*target-opts*
   ["(load-file \"foo/load.clj\")"]


[test] clean up put/predict rpc test

[test] clean up put/predict rpc test

--- test/og/clj_dstream/server_test.clj
+++ test/og/clj_dstream/server_test.clj
@@ -39,18 +39,23 @@
   (println "\n 2 init state: " (clt/rpc-init-state))
   (println "\n 2 put data: " (clt/rpc-put-data [{::core/raw-datum {::core/position-value [0.2, 0.4] ::core/value 1.0}}])))

-(deftest test-put-data
+(deftest test-put-and-predict-data
   (println "set props: " (clt/rpc-set-props props))
   (println "init state: " (clt/rpc-init-state))
-  (let [put-data (map (fn [i]
-                        (let [p-data {::core/raw-datum
-                                      {::core/position-value [(- 0.8 (/ i 100)) 0.4]
-                                       ::core/value          1.0}}]
-                          (println "\nput data idx: " i
-                                   "\nput result state: " (clt/rpc-put-data [p-data]))
-                          p-data))
-                      (range 1000))]
-    (doseq [p-datum put-data]
-      (println "The datum:")
-      (clojure.pprint/pprint (::core/raw-datum p-datum))
-      (println "Get cluster result: " (clt/rpc-predict-cluster-or-outlier (::core/raw-datum p-datum))))))
+  (let [put-data (map
+                   (fn [i]
+                     (let [p-data {::core/raw-datum
+                                   {::core/position-value [(- 0.8 (/ i 10000)) 0.4]
+                                    ::core/value          1.0}}]
+                       (clt/rpc-put-data [p-data])
+                       p-data))
+                   (range 100))
+        clusters (->>
+                   put-data
+                   (map
+                     (fn [d]
+                       (clt/rpc-predict-cluster-or-outlier
+                         (::core/raw-datum d))))
+                   set)]
+    (is (= 1 (count clusters)))
+    (is (not= nil (first (vec clusters))))))


Updated required statements in files

Updated required statements in files

--- test/research_clojure/core_test.clj
+++ test/research_clojure/core_test.clj
@@ -2,6 +2,6 @@
   (:require [clojure.test :refer :all]
             [research-clojure.core :refer :all]))

-(deftest a-test
+(deftest chooseSentences-test
   (testing "FIXME, I fail."
     (is (= 0 1))))


Fix duplicate declaration

Fix duplicate declaration

--- src-cljs/frontend/utils/github.cljs
+++ src-cljs/frontend/utils/github.cljs
@@ -42,9 +42,6 @@
        "/settings/connections/applications/"
        (aget js/window "renderContext" "githubClientId")))

-(defn http-endpoint []
-  (config/github-endpoint))
-
 (defn login-url [login]
   (str (http-endpoint) "/" login))


Add fullscreen mode on apple devices

Add fullscreen mode on apple devices

--- src/clj/clojuredocs/site/common.clj
+++ src/clj/clojuredocs/site/common.clj
@@ -107,6 +107,7 @@
   [:html5
    [:head
     [:meta {:name "viewport" :content "width=device-width, maximum-scale=1.0"}]
+    [:meta {:name "apple-mobile-web-app-capable" :content "yes"}]
     [:title (or title "Community-Powered Clojure Documentation and Examples | ClojureDocs")]
     font-awesome-link
     bootstrap-link
@@ -166,7 +167,27 @@
     clojuredocs-script
     (when (env/bool :cljs-dev)
       [:script "goog.require(\"clojuredocs.main\");"])
-    ($ga-script-tag config/ga-tracking-id)]])
+    ($ga-script-tag config/ga-tracking-id)
+    [:script
+     "if((\"standalone\" in window.navigator) && window.navigator.standalone){
+var noddy, remotes = false;
+
+document.addEventListener('click', function(event) {
+
+noddy = event.target;
+
+while(noddy.nodeName !== \"A\" && noddy.nodeName !== \"HTML\") {
+noddy = noddy.parentNode;
+}
+
+if('href' in noddy && noddy.href.indexOf('http') !== -1 && (noddy.href.indexOf(document.location.host) !== -1 || remotes))
+{
+event.preventDefault();
+document.location.href = noddy.href;
+}
+
+},false);
+}"]]])

 (defn $avatar [{:keys [email login avatar-url] :as user}]
   [:a {:href (str "/u/" login)}


Fix defcomponent-2 expansion

Fix defcomponent-2 expansion

For complex subscription specs it was very wrong

--- src/clj/comic_reader/macro_util.clj
+++ src/clj/comic_reader/macro_util.clj
@@ -49,7 +49,7 @@

 (defmacro defcomponent-2 [name subscriptions & body]
   `(do
-     (defn ~name ~subscriptions
+     (defn ~name ~(mapv subscription-symbol subscriptions)
        ~@body)

      (defn ~(container-name name) []


Removed direct function call to getStartIdx.

--- src/clj/forma/hadoop/jobs/forma.clj
+++ src/clj/forma/hadoop/jobs/forma.clj
@@ -1,20 +1,18 @@
 (ns forma.hadoop.jobs.forma
   (:use cascalog.api)
   (:require [cascalog.ops :as c]
-            [forma.utils :as utils]
             [forma.matrix.walk :as w]
             [forma.date-time :as date]
             [forma.hadoop.io :as io]
             [forma.hadoop.predicate :as p]
             [forma.trends.analysis :as a]
-            [forma.source.modis :as modis])
-  (:import [forma.schema TimeSeries]))
+            [forma.source.modis :as modis]))

 (defn short-trend-shell
   "a wrapper to collect the short-term trends into a form that can be
   manipulated from within cascalog."
   [{:keys [est-start est-end t-res long-block window]} ts-series]
-  (let [ts-start (.getStartIdx ts-series)
+  (let [ts-start (io/get-start-idx ts-series)
         [start end] (date/relative-period t-res ts-start [est-start est-end])]
     [(->> (io/get-vals ts-series)
           (a/collect-short-trend start end long-block window)
@@ -27,7 +25,7 @@
   time-series (and cofactors) to extract the long-term trends and
   t-statistics from the time-series."
   [{:keys [est-start est-end t-res long-block window]} ts-series & cofactors]
-  (let [ts-start (.getStartIdx ts-series)
+  (let [ts-start (io/get-start-idx ts-series)
         [start end] (date/relative-period t-res ts-start [est-start est-end])]
     (->> (a/collect-long-trend start end
                                (io/get-vals ts-series)


Updated API commands

Updated API commands

--- src/clj_jenkins_api/jenkins_api.clj
+++ src/clj_jenkins_api/jenkins_api.clj
@@ -128,22 +128,22 @@
 (defn start-job
     "Start the given job via Jenkins API."
     [jenkins-url jenkins-auth include-jenkins-reply? job-name]
-    (job-related-command jenkins-url jenkins-auth include-jenkins-reply? job-name "start_job"))
+    (job-related-command jenkins-url jenkins-auth include-jenkins-reply? job-name "build"))

 (defn enable-job
     "Enable the given job via Jenkins API."
     [jenkins-url jenkins-auth include-jenkins-reply? job-name]
-    (job-related-command jenkins-url jenkins-auth include-jenkins-reply? job-name "enable_job"))
+    (job-related-command jenkins-url jenkins-auth include-jenkins-reply? job-name "enable"))

 (defn disable-job
     "Disable the given job via Jenkins API."
     [jenkins-url jenkins-auth include-jenkins-reply? job-name]
-    (job-related-command jenkins-url jenkins-auth include-jenkins-reply? job-name "disable_job"))
+    (job-related-command jenkins-url jenkins-auth include-jenkins-reply? job-name "disable"))

 (defn delete-job
     "Delete the given job via Jenkins API."
     [jenkins-url jenkins-auth include-jenkins-reply? job-name]
-    (job-related-command jenkins-url jenkins-auth include-jenkins-reply? job-name "delete_job"))
+    (job-related-command jenkins-url jenkins-auth include-jenkins-reply? job-name "delete"))

 (defn replace-placeholder
     [string placeholder-name value]


Add ^long typehints for bit-and calls

Add ^long typehints for bit-and calls

--- src/cljc/mikron/runtime/buffer.cljc
+++ src/cljc/mikron/runtime/buffer.cljc
@@ -338,7 +338,7 @@

 (defn put-ubyte
   "Puts an unsigned byte `value` to `buffer`."
-  #?(:clj {:inline (fn [buffer value] `(put-byte ~buffer (unchecked-byte (bit-and ~value 0xFF))))})
+  #?(:clj {:inline (fn [buffer value] `(put-byte ~buffer (unchecked-byte (bit-and ~(with-meta value {:tag 'long}) 0xFF))))})
   [^Buffer buffer ^long value]
   (put-byte buffer (unchecked-byte (bit-and value 0xFF))))

@@ -350,7 +350,7 @@

 (defn put-ushort
   "Puts an unsigned short `value` to `buffer`."
-  #?(:clj {:inline (fn [buffer value] `(put-short ~buffer (unchecked-short (bit-and ~value 0xFFFF))))})
+  #?(:clj {:inline (fn [buffer value] `(put-short ~buffer (unchecked-short (bit-and ~(with-meta value {:tag 'long}) 0xFFFF))))})
   [^Buffer buffer ^long value]
   (put-short buffer (unchecked-short (bit-and value 0xFFFF))))

@@ -364,7 +364,7 @@

 (defn put-uint
   "Puts an unsigned int `value` to `buffer`."
-  #?(:clj {:inline (fn [buffer value] `(put-int ~buffer (unchecked-int (bit-and ~value 0xFFFFFFFF))))})
+  #?(:clj {:inline (fn [buffer value] `(put-int ~buffer (unchecked-int (bit-and ~(with-meta value {:tag 'long}) 0xFFFFFFFF))))})
   [^Buffer buffer ^long value]
   (put-int buffer (unchecked-int (bit-and value 0xFFFFFFFF))))


Fix hiccup tag

Fix hiccup tag

--- src/cljs/cglossa/search_inputs.cljs
+++ src/cljs/cglossa/search_inputs.cljs
@@ -51,7 +51,7 @@
   [:button.btn.add-phrase-btn {:on-click #()} "Or..."])

 (defn- language-select [languages selected-language]
-  [select {:value selected-language}
+  [:select {:value selected-language}
    (for [language languages]
      [:option {:key (:value language) :value (:value language)} (:text language)])])


Make the read handler more nuanced

Make the read handler more nuanced

Now it only resets the imgs if the source has changed (either the comic
or the site it comes from).

--- src/cljs/comic_reader/handlers.cljs
+++ src/cljs/comic_reader/handlers.cljs
@@ -28,11 +28,15 @@
    :read
    (fn [db [page site location]]
      (api/get-comic-urls site location)
-     (assoc db
-            :page page
-            :site site
-            :location location
-            :comic-imgs [])))
+     (if (and (= (:site db) site)
+              (= (get-in db [:location  :comic])
+                 (get-in      location [:comic])))
+       (assoc db :location location)
+       (assoc db
+              :page page
+              :site site
+              :location location
+              :comic-imgs []))))

   (rf/register-handler
    :site-list


use textContent instead of innerHTML

use textContent instead of innerHTML

fixes #60

--- src/cljs/netrunner/deckbuilder.cljs
+++ src/cljs/netrunner/deckbuilder.cljs
@@ -499,7 +499,7 @@
               (for [i (range (count matches))]
                 [:div {:class (if (= i (:selected state)) "selected" "")
                        :on-click (fn [e] (-> ".deckedit .qty" js/$ .select)
-                                         (om/set-state! owner :query (.. e -target -innerHTML))
+                                         (om/set-state! owner :query (.. e -target -textContent))
                                          (om/set-state! owner :selected i))}
                  (:title (nth matches i))])]))]]))))


Fix a mis-placed type hint.

Fix a mis-placed type hint.

--- src/clojure/cav/mtj/core/matrix.clj
+++ src/clojure/cav/mtj/core/matrix.clj
@@ -208,8 +208,8 @@

 (extend-protocol PSameShape
   Vector
-  (same-shape? [a ^Vector b]
-    (and (instance? Vector b) (= (.size a) (.size b))))
+  (same-shape? [a b]
+    (and (instance? Vector b) (= (.size a) (.size ^Vector b))))

   Matrix
   (same-shape? [a b]


Consume alternative key inputs

Consume alternative key inputs

--- src/clojure/paredit_widget/core.clj
+++ src/clojure/paredit_widget/core.clj
@@ -149,7 +149,9 @@
   (reify KeyListener
     (keyReleased [this e] nil)
     (keyTyped [this e]
-      (when (and @enable-advanced? (special-chars (str (.getKeyChar e))))
+      (when (and @enable-advanced?
+                 (or (special-chars (str (.getKeyChar e)))
+                     (advanced-alternative-keymap (convert-key-event e))))
         (.consume e)))
     (keyPressed [this e]
       (when-not (.isConsumed e)


Add specs for project-parent-query-attributes

--- src/com/wsscode/pathom/connect.cljc
+++ src/com/wsscode/pathom/connect.cljc
@@ -502,6 +502,10 @@
                          (if (contains? output key)
                            [key]
                            (mapv first (first (resolve-plan (assoc-in env [:ast :key] key)))))))))))
+
+(s/fdef project-parent-query-attributes
+  :args (s/cat :env ::p/env)
+  :ret ::attributes-set)

 ;; readers


Fix references

Fix references

--- src/com/wsscode/pathom/connect.cljc
+++ src/com/wsscode/pathom/connect.cljc
@@ -1251,11 +1251,11 @@
   resolver that will make a batch by running many in parallel, using `n` as the concurrency
   number."
   [n]
-  (fn [{::pc/keys [resolve] :as resolver}]
+  (fn [{::keys [resolve] :as resolver}]
     (assoc resolver
-      ::pc/batch? true
-
-      ::pc/resolve
+      ::batch? true
+
+      ::resolve
       (batch-resolver
         (fn [env inputs]
           (go


Remove clone display.

Remove clone display.

--- src/org/playasophy/wonderdome/display/clone.clj
+++ src/org/playasophy/wonderdome/display/clone.clj
@@ -1,21 +0,0 @@
-(ns org.playasophy.wonderdome.display.clone
-  (:require
-    [org.playasophy.wonderdome.display.core :as display]))
-
-
-(defrecord CloneDisplay
-  []
-
-  display/Display
-
-  (set-colors!
-    [this colors]
-    (dorun (map #(display/set-colors! % colors) (vals this)))
-    nil))
-
-
-(defn clone
-  "Creates a new display which broadcasts colors to any displays associated with
-  it as keys in the record."
-  [& displays]
-  (apply assoc (CloneDisplay.) displays))


bind: Improve markup used in the API docs

--- src/main/workflo/macros/bind.clj
+++ src/main/workflo/macros/bind.clj
@@ -17,28 +17,32 @@
   :ret ::sb/paths)

 (defn property-binding-paths
-  "Takes a property and an optional path of parents. Returns
+  "Takes a `property` and an optional `path` of parents. Returns
    a flat vector of all paths inside the property and its
    parents. Each path is again a sequence of properties,
    starting from the leaf (e.g. a regular property) and
    ending at the root of the query.

-   As an example, the query [{user [user [name email]]]}]
+   As an example, the query `[{user [user [name email]]]}]`
    would result in the query AST

-       [{:name user :type :join
-         :join-source {:name user :type :property}
-         :join-target [{:name user/name :type :property}
-                       {:name user/email :type :property}]}].
+   ```clojure
+   [{:name user :type :join
+     :join-source {:name user :type :property}
+     :join-target [{:name user/name :type :property}
+                   {:name user/email :type :property}]}].
+   ```

-   Calling property-binding-paths with the join property
-   would result in two paths, one for user -> user/name and
-   one for user -> user/email:
+   Calling `property-binding-paths` with the join property
+   would result in two paths, one for `user -> user/name` and
+   one for `user -> user/email`:

-     [({:name user/name :type :property}
-       {:name user :type :join :join-source ... :join-target ...})
-      ({:name user/email :type :property}
-       {:name user :type :join :join-source ... :join-target ...})]."
+   ```clojure
+   [({:name user/name :type :property}
+     {:name user :type :join :join-source ... :join-target ...})
+    ({:name user/email :type :property}
+     {:name user :type :join :join-source ... :join-target ...})].
+   ```"
   ([property]
    (property-binding-paths property nil))
   ([property path]
@@ -59,7 +63,7 @@
   :ret ::sb/paths)

 (defn binding-paths
-  "Returns a vector of all property binding paths for a query
+  "Returns a vector of all property binding paths for a `query`
    or parsed query."
   [query]
   (transduce (map property-binding-paths) concat []
@@ -85,13 +89,13 @@
    value of the corresponding property from a query result
    and bind it to the name of the property.

-   E.g. for a property path (a b/c d) (simplified notation with
-   only the property names), it would return {{{a :a} :b/c} :d},
-   allowing to destructure a map like {:d {:b/c {:a <val>}}}
-   and bind a to <val>.
+   E.g. for a property path `(a b/c d)` (simplified notation with
+   only the property names), it would return `{{{a :a} :b/c} :d}`,
+   allowing to destructure a map like `{:d {:b/c {:a <val>}}}`
+   and bind a to `<val>`.

-   It treats backref properties like _b/c in joins special by
-   binding to b instead of c (the regular case) or _b."
+   It treats backref properties like `_b/c` in joins special by
+   binding to `b` instead of `c` (the regular case) or `_b`."
   [[leaf & path]]
   (loop [form {(or (some-> leaf :alias simplified-name symbol)
                    (if (util/backref-attr? (:name leaf))
@@ -110,13 +114,13 @@
   :ret ::core-specs/map-bindings)

 (defn query-bindings
-  "Takes a query or parsed query and returns map bindings to
+  "Takes a `query` or parsed query and returns map bindings to
    be applied to the corresponding query result in order to
    destructure and bind all possible properties in the query
    result to their names.

-   E.g. for a query [a :as b c [d e {f [g :as h]}]] it
-   would return {b :a, d :c/d, e :c/e, f :f, {{h :g} :f}}."
+   E.g. for a query `[a :as b c [d e {f [g :as h]}]]` it
+   would return `{b :a, d :c/d, e :c/e, f :f, {{h :g} :f}}`."
   [query]
   (let [paths    (binding-paths query)
         bindings (mapv path-bindings paths)
@@ -141,7 +145,7 @@
        ~@body)))

 (defmacro with-query-bindings
-  "Takes a query, a query result and an arbitrary code block.
+  "Takes a `query`, a query `result` and an arbitrary code `body`.
    Wraps the code block so that all possible bindings derived
    from the query are bound to the values in the query result."
   [query result & body]


Update bench/dev code for use new generate function.


Better named variable, info is now a trace

--- src/onyx/peer/task_lifecycle.clj
+++ src/onyx/peer/task_lifecycle.clj
@@ -324,9 +324,9 @@
   [{:keys [onyx.core/windows onyx.core/window-state onyx.core/state-log] :as event}]
   (when (seq windows)
     (swap! window-state
-           (fn [state]
-             (let [replayed-state (state-extensions/playback-log-entries state-log event state)]
-               (info "REPLAYED STATE:" replayed-state)
+           (fn [wstate]
+             (let [replayed-state (state-extensions/playback-log-entries state-log event wstate)]
+               (trace (:onyx.core/task-id event) "replayed state:" replayed-state)
                replayed-state))))
   event)


Minor code re-arrange.

Minor code re-arrange.

--- src/onyx/peer/task_lifecycle.clj
+++ src/onyx/peer/task_lifecycle.clj
@@ -564,12 +564,30 @@
   (let [timeout (event->pub-liveness event)]
     (fn [state]
       (let [timed-out (upstream-timed-out-peers (m/subscriber (get-messenger state)) timeout)]
-        ; (when (empty? timed-out)
-        ;   (when-let [timeout-warnings (seq (upstream-timed-out-peers (m/subscriber (get-messenger state)) (/ timeout 4)))]
-        ;     (info (:onyx.core/log-prefix event) "Upstream peers are > 1/4 of the way to being timed out." upstream-timeout-warnings)))
         (->> timed-out
              (reduce evict-peer! state)
              (advance))))))
+
+(defn build-read-batch [{:keys [onyx.core/task-map] :as event}]
+  (if (input-task? event)
+    (let [batch-size (long (:onyx/batch-size task-map))
+          batch-timeout (long (ms->ns (arg-or-default :onyx/batch-timeout task-map)))
+          apply-watermark (if-let [watermark-fn (:assign-watermark-fn event)]
+                            (fn [state]
+                              (->> (get-event state)
+                                   (:onyx.core/batch)
+                                   (run! (fn [seg] (t/process-watermark! state (watermark-fn seg)))))
+                              state)
+                            (if (satisfies? p/WatermarkedInput (:onyx.core/input-plugin event))
+                              (fn [state]
+                                (t/process-watermark! state (p/watermark (get-input-pipeline state)))
+                                state)
+                              identity))]
+      (fn [state]
+        (-> state
+            (read-batch/read-input-batch batch-size batch-timeout)
+            (apply-watermark))))
+    read-batch/read-function-batch))

 (def state-fn-builders
   {:recover [{:lifecycle :lifecycle/poll-recover
@@ -614,26 +632,7 @@
    :process-batch [{:lifecycle :lifecycle/before-batch
                     :builder (fn [event] (build-lifecycle-invoke-fn event :lifecycle/before-batch))}
                    {:lifecycle :lifecycle/read-batch
-                    :builder (fn [{:keys [onyx.core/task-map] :as event}]
-                               (if (input-task? event)
-                                 (let [batch-size (long (:onyx/batch-size task-map))
-                                       batch-timeout (long (ms->ns (arg-or-default :onyx/batch-timeout task-map)))
-                                       apply-watermark (if-let [watermark-fn (:assign-watermark-fn event)]
-                                                         (fn [state]
-                                                           (->> (get-event state)
-                                                                (:onyx.core/batch)
-                                                                (run! (fn [seg] (t/process-watermark! state (watermark-fn seg)))))
-                                                           state)
-                                                         (if (satisfies? p/WatermarkedInput (:onyx.core/input-plugin event))
-                                                           (fn [state]
-                                                             (t/process-watermark! state (p/watermark (get-input-pipeline state)))
-                                                             state)
-                                                           identity))]
-                                   (fn [state]
-                                     (-> state
-                                         (read-batch/read-input-batch batch-size batch-timeout)
-                                         (apply-watermark))))
-                                 read-batch/read-function-batch))}
+                    :builder build-read-batch}
                    {:lifecycle :lifecycle/check-publisher-heartbeats
                     :builder build-check-publisher-heartbeats}
                    {:lifecycle :lifecycle/after-read-batch


Call out to lifecycle fns.

Call out to lifecycle fns.

--- src/onyx/peer/task_lifecycle.clj
+++ src/onyx/peer/task_lifecycle.clj
@@ -188,7 +188,8 @@

 (defn inject-batch-resources [event]
   (let [cycle-params {:onyx.core/lifecycle-id (java.util.UUID/randomUUID)}]
-    (merge event cycle-params (l-ext/inject-batch-resources* event))))
+    (merge event cycle-params ((:onyx.core/compiled-pre-batch-fn event)
+                               (l-ext/inject-batch-resources* event)))))

 (defn read-batch [event]
   (let [rets (p-ext/read-batch event)]
@@ -268,7 +269,8 @@
   (merge event (p-ext/write-batch event)))

 (defn close-batch-resources [event]
-  (merge event (l-ext/close-batch-resources* event)))
+  (merge event ((:onyx.core/compiled-post-batch-fn event)
+                (l-ext/close-batch-resources* event))))

 (defn launch-aux-threads!
   [messenger event outbox-ch seal-ch completion-ch task-kill-ch]
@@ -381,7 +383,7 @@
   (let [matched (filter #(= (:lifecycle/task %) task-name) lifecycles)]
     (reduce
      (fn [f lifecycle]
-       (comp (operation/kw->fn (get lifecycle kw)) f))
+       (comp (fn [x] ((operation/kw->fn (get lifecycle kw)) x lifecycle)) f))
      identity
      matched)))

@@ -447,7 +449,9 @@
                            :onyx.core/state (atom {:timeout-pool buckets})}

             ex-f (fn [e] (handle-exception e restart-ch outbox-ch job-id))
-            pipeline-data (merge pipeline-data (l-ext/inject-lifecycle-resources* pipeline-data))]
+            pipeline-data (merge pipeline-data
+                                 ((:onyx.core/compiled-pre-fn pipeline-data)
+                                  (l-ext/inject-lifecycle-resources* pipeline-data)))]

         (while (and (first (alts!! [kill-ch task-kill-ch] :default true))
                     (not (:onyx.core/start-lifecycle? (munge-start-lifecycle pipeline-data))))
@@ -482,7 +486,7 @@
   (stop [component]
     (taoensso.timbre/info (format "[%s] Stopping Task LifeCycle for %s" id (:onyx.core/task (:pipeline-data component))))
     (when-let [event (:pipeline-data component)]
-      (l-ext/close-lifecycle-resources* event)
+      ((:onyx.core/compiled-post-fn event) (l-ext/close-lifecycle-resources* event))

       ;; Ensure task operations are finished before closing peer connections
       (close! (:seal-ch component))


cleanup

cleanup

git-svn-id: 03a8048b5ee8463be5048a3801110fb50f378627@8782 d0ab736e-dc22-4aeb-8dc9-08def0aa14fd

--- acqEngine/src/org/micromanager/acq_engine.clj
+++ acqEngine/src/org/micromanager/acq_engine.clj
@@ -338,7 +338,7 @@
     (doall
       (loop [burst-seqs bursts-per-camera-channel i 0]
         (check-for-serious-error)
-        (if (and (not (@state :stop))
+        (when (and (not (@state :stop))
                  (not (apply = nil burst-seqs))
                  (or (pos? (core getRemainingImageCount))
                      (core isSequenceRunning)))
@@ -358,9 +358,9 @@
                      :stop true
                      :circular-buffer-overflow true))
             (let [event+ (update-in event [:channel-index] make-multicamera-channel cam-chan)]
-              (.put out-queue (make-TaggedImage (annotate-image image event+ @state (burst-time (:tags image) @state))))
-              (recur (update-in burst-seqs [cam-chan] next) (inc i))))
-          nil))))
+              (.put out-queue (make-TaggedImage (annotate-image image event+ @state
+                                                                (burst-time (:tags image) @state))))
+              (recur (update-in burst-seqs [cam-chan] next) (inc i))))))))
   (burst-cleanup)) ;; burst is done!

 (defn collect-snap-image [event out-queue]


Fix defapplet macro's use of bind-applets.

--- src/rosado/processing/applet.clj
+++ src/rosado/processing/applet.clj
@@ -3,25 +3,26 @@
         [clojure.contrib.java-utils :only [as-str]])
   (:import (java.awt Frame)))

-(defn bind-applet [f]
-  (fn [this & args]
-    (binding [*applet* this] (apply f args))))
+(defn- bind-applets
+  "Turn the method map into something one that update-proxy can use."
+  [methods [method-name f]]
+  (assoc methods (name method-name)
+         (fn [this & args]
+           (binding [*applet* this]
+             (apply f args)))))

 (defmacro defapplet
   "Define an applet. Takes an app-name and a map of options."
   [app-name & opts]
   (let [options (assoc (apply hash-map opts) :name (str app-name))
-        fns (dissoc options :name :title :height :width)
-        ;; TODO: fix this to automatically bind *applet* in fns
-        ;; methods (zipmap (map name (keys fns))
-        ;;                 (map bind-applet (vals fns)))
-        ]
+        fns (dissoc options :name :title :height :width)]
     `(def ~app-name
           (let [frame# (atom nil)
+                methods# (reduce bind-applets {} ~fns)
                 prx# (proxy [processing.core.PApplet
                              clojure.lang.IMeta] []
                        (meta [] (assoc ~options :frame frame#)))]
-            (update-proxy prx# ~fns)
+            (update-proxy prx# methods#)
             prx#))))

 (defn run [applet]
@@ -40,3 +41,10 @@
 (defn stop [applet]
   (.destroy applet)
   (.hide @(:frame ^applet)))
+
+(comment ;; Usage:
+  (defapplet growing-triangle
+    :draw (fn [] (line 10 10 (frame-count) 100)))
+
+  (run growing-triangle)
+  (stop growing-triangle))


fixes https://github.com/status-im/status-react/issues/1016

--- src/status_im/chat/handlers.cljs
+++ src/status_im/chat/handlers.cljs
@@ -272,9 +272,9 @@
                              (status/call-function!
                                {:chat-id  chat-id
                                 :function :init}))]
-        (dispatch [:load-requests! chat-id])
+       (dispatch [:load-requests! chat-id])
         ;; todo rewrite this. temporary fix for https://github.com/status-im/status-react/issues/607
-        #_(dispatch [:load-commands! chat-id])
+       #_(dispatch [:load-commands! chat-id])
         (if-not commands-loaded?
           (dispatch [:load-commands! chat-id call-init-command])
           (do
@@ -389,10 +389,11 @@

 (defn delete-chat!
   [_ [_ chat-id]]
-  (let [{:keys [debug?]} (chats/get-by-id chat-id)]
-    (if debug?
-      (chats/delete chat-id)
-      (chats/set-inactive chat-id))))
+  (let [{:keys [debug? group-chat]} (chats/get-by-id chat-id)]
+    (when group-chat
+      (if debug?
+        (chats/delete chat-id)
+        (chats/set-inactive chat-id)))))

 (defn remove-pending-messages!
   [_ [_ chat-id]]


sequence-max-lengths

sequence-max-lengths

git-svn-id: 03a8048b5ee8463be5048a3801110fb50f378627@10875 d0ab736e-dc22-4aeb-8dc9-08def0aa14fd

--- acqEngine/src/org/micromanager/mm.clj
+++ acqEngine/src/org/micromanager/mm.clj
@@ -15,7 +15,7 @@

 (ns org.micromanager.mm
   (:import [org.micromanager.navigation MultiStagePosition]
-           [mmcorej Configuration DoubleVector Metadata StrVector]
+           [mmcorej Configuration DeviceType DoubleVector Metadata StrVector]
            [org.json JSONArray JSONObject]
            [java.text SimpleDateFormat]
            [org.micromanager.navigation MultiStagePosition StagePosition]
@@ -196,7 +196,8 @@
    :focus           (core getFocusDevice)
    :xy-stage        (core getXYStageDevice)
    :autofocus       (core getAutoFocusDevice)
-   :image-processor (core getImageProcessorDevice)})
+   :image-processor (core getImageProcessorDevice)
+   :galvo           (core getGalvoDevice)})

 (defn config-struct
   "Creates a map of properties from a given Micro-Manager Configuration
@@ -428,3 +429,45 @@
     (doseq [item doubles]
       (.add v item))
     v))
+
+(defn all-properties []
+  (for [dev (.getLoadedDevices mmc) prop (.getDevicePropertyNames mmc dev)]
+    [dev prop]))
+
+(defn property-sequence-max-lengths []
+  (into {}
+        (for [[dev prop :as property] (all-properties)]
+          [property
+           (if (.isPropertySequenceable mmc dev prop)
+             (.getPropertySequenceMaxLength mmc dev prop)
+             0)])))
+
+(defn all-cameras []
+  (seq (.getLoadedDevicesOfType mmc DeviceType/CameraDevice)))
+
+(defn exposure-sequence-max-lengths []
+  (into {}
+        (for [camera (all-cameras)]
+          [camera
+           (if (.isExposureSequenceable mmc camera)
+             (.getExposureSequenceMaxLength mmc camera)
+             0)])))
+
+(defn all-z-stages []
+  (seq (.getLoadedDevicesOfType mmc DeviceType/StageDevice)))
+
+(defn stage-sequence-max-lengths []
+  (into {}
+        (for [stage (all-z-stages)]
+          [stage
+           (if (.isStageSequenceable mmc stage)
+             (.getStageSequenceMaxLength mmc stage)
+             0)])))
+
+(defn sequence-max-lengths []
+  {:camera-exposures (exposure-sequence-max-lengths)
+   :stages (slice-sequence-max-lengths)
+   :properties (property-sequence-max-lengths)})
+
+
+


CMR-2362: Reset db to in-memory

CMR-2362: Reset db to in-memory

--- dev-system/dev/user.clj
+++ dev-system/dev/user.clj
@@ -41,13 +41,13 @@
   ;; MAKE SURE YOU DISABLE SYMANTEC ANTIVIRUS BEFORE STARTING THE TESTS WITH EXTERNAL DB
   ;; Renable them when you're done

-  ; (system/set-dev-system-db-type! :in-memory)
-  (system/set-dev-system-db-type! :external)
+  (system/set-dev-system-db-type! :in-memory)
+  ; (system/set-dev-system-db-type! :external)

   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

-  ; (system/set-dev-system-message-queue-type! :in-memory)
-  (system/set-dev-system-message-queue-type! :external)
+  (system/set-dev-system-message-queue-type! :in-memory)
+  ; (system/set-dev-system-message-queue-type! :external)

   (let [s (system/create-system)]
     (alter-var-root #'system


Fix indendation

Fix indendation

--- src/discuss/communication/auth.cljs
+++ src/discuss/communication/auth.cljs
@@ -10,11 +10,11 @@
   (let [{:keys [nickname uid token]} (lib/process-response response)
         last-api-call (lib/get-last-api)]
      (lib/store-multiple-values-to-app-state!
-     [['user/nickname nickname]
-      ['user/token token]
-      ['user/id uid]
-      ['user/logged-in? true]
-      ['layout/view :default]])
+      [['user/nickname nickname]
+       ['user/token token]
+       ['user/id uid]
+       ['user/logged-in? true]
+       ['layout/view :default]])
      (when (seq last-api-call)
        (comlib/ajax-get last-api-call))))


InternationalizedText's parent-with-id can be far

Add an arity to internationalized-text->ltree to explicitly provide the
parent_with_id.

--- src/vip/data_processor/db/translations/util.clj
+++ src/vip/data_processor/db/translations/util.clj
@@ -57,23 +57,29 @@
              :parent_with_id parent-with-id
              :value value})))))))

-(defn internationalized-text->ltree [column-name]
-  (let [xml-element (column->xml-elment column-name)]
-    (fn [idx-fn base-path row]
-      (let [value (get row column-name)]
-        (when-not (str/blank? value)
-          (let [index (idx-fn)
-                text-path (str base-path "." xml-element "." index ".Text.0")
-                lang-path (str text-path ".language")]
-            (list
-             {:path text-path
-              :simple_path (path->simple-path text-path)
-              :parent_with_id (id-path base-path)
-              :value value}
-             {:path lang-path
-              :simple_path (path->simple-path lang-path)
-              :parent_with_id (id-path base-path)
-              :value "en"})))))))
+(defn internationalized-text->ltree
+  ([column-name]
+   (fn [idx-fn base-path row]
+     (let [parent-with-id (id-path base-path)
+           ltree-fn (internationalized-text->ltree column-name parent-with-id)]
+       (ltree-fn idx-fn base-path row))))
+  ([column-name parent-with-id]
+   (let [xml-element (column->xml-elment column-name)]
+     (fn [idx-fn base-path row]
+       (let [value (get row column-name)]
+         (when-not (str/blank? value)
+           (let [index (idx-fn)
+                 text-path (str base-path "." xml-element "." index ".Text.0")
+                 lang-path (str text-path ".language")]
+             (list
+              {:path text-path
+               :simple_path (path->simple-path text-path)
+               :parent_with_id parent-with-id
+               :value value}
+              {:path lang-path
+               :simple_path (path->simple-path lang-path)
+               :parent_with_id parent-with-id
+               :value "en"}))))))))

 (defn external-identifiers->ltree
   [idx-fn parent-path row]


Fix embed-stamp for new D4J methods

Fix embed-stamp for new D4J methods

--- src/main/resources/sandbox-init.clj
+++ src/main/resources/sandbox-init.clj
@@ -19,7 +19,7 @@
         (doseq [[t d i] (partition 3 fields)] (.appendField builder t d i))
         builder)))

-  (defn embed-stamp [embed] (do (.withTimestamp embed (java.time.LocalDateTime/ofInstant (java.time.Instant/now) (java.time.ZoneId/of "UTC"))) embed))
+  (defn embed-stamp [embed] (do (.withTimestamp embed (java.time.Instant/now) ) embed))

   ;; Simple embed function bindings
   (def embed-field (memfn appendField title desc inline))


ignore some more classpath files

ignore some more classpath files

--- src/main/shadow/build/classpath.clj
+++ src/main/shadow/build/classpath.clj
@@ -1067,6 +1067,8 @@
           ;; closure compiler support and test files
           #"^com/google/javascript"
           #"^jdk/nashorn/*"
+          ;; ignore shipped builds (UI, babel-worker, etc)
+          #"^shadow/.+/dist"
           ;; just in case the :output-dir of a dev build is on the classpath
           #"^public/"
           #"cljs-runtime/"}


less strict classpath handling

less strict classpath handling

--- src/main/shadow/build/classpath.clj
+++ src/main/shadow/build/classpath.clj
@@ -632,8 +632,10 @@
     (and (.isFile file) (util/is-jar? (.getName file)))
     (find-jar-resources cp file)

+    ;; silently ignore all other cases since we can't do anything with them anyways
+    ;; don't throw since java doesn't throw either
     :else
-    (throw (ex-info "classpath entry that is not a directory or jar file" {:entry file}))))
+    {}))

 (defn should-exclude-classpath [exclude ^File file]
   (let [abs-path (.getAbsolutePath file)]


Added a second table to the test case.

Added a second table to the test case.

--- test/blutwurst/database_test.clj
+++ test/blutwurst/database_test.clj
@@ -1,6 +1,7 @@
 (ns blutwurst.database-test
   (:require [clojure.test :refer :all]
-            [blutwurst.database :refer :all])
+            [blutwurst.database :refer :all]
+            [clojure.data :refer :all])
     (:import (java.sql DriverManager)))

 (def connection-string "jdbc:h2:mem:test")
@@ -15,7 +16,11 @@
 (defn create-test-tables [connection-string]
   (let [connection (DriverManager/getConnection connection-string)
         statement (.createStatement connection)
-        table-creation-sql ["create schema dbo" "create table dbo.Person (ID int, Name varchar(100))"]]
+        table-creation-sql [
+                            "create schema dbo"
+                            "create table dbo.Person (ID int, Name varchar(100))"
+                            "create table dbo.Purchase (ID int, Amount decimal, PurchasedByID int not null, foreign key (PurchasedByID) references dbo.Person(ID))"
+                            ]]
     (doseq [i table-creation-sql]
       (.execute statement i))
     (.close statement)
@@ -31,15 +36,19 @@
 (deftest table-graph-tests
   (testing "Connects to an in-memory database and returns basic table list."
      (let [spec {:connection-string connection-string}
-           table-graph (retrieve-table-graph spec)]
+           table-graph (retrieve-table-graph spec)
+           expected {:tables '({:name
+
+                              "PURCHASE", :schema "DBO", :columns ({:name
+                                      "PURCHASEDBYID", :type "INTEGER", :length
+                                      10} {:name "AMOUNT", :type "DECIMAL",
+                                      :length 65535} {:name "ID", :type
+                                      "INTEGER", :length 10})} {:name "PERSON",
+                              :schema "DBO", :columns ({:name "NAME", :type
+                                      "VARCHAR", :length 100} {:name "ID", :type
+                                      "INTEGER", :length 10})})}]
        (println table-graph)
-       (is (= {
-               :tables '({
-                          :name "PERSON"
-                          :schema "DBO"
-                          :columns ({:name "NAME" :type "VARCHAR" :length 100}
-                                    {:name "ID" :type "INTEGER" :length 10})
-                          })
-               }
-              table-graph)))
+       ;(println (diff expected table-graph))
+
+       (is (= expected table-graph)))
    ))


Add a test requiring the existence of a test data folder

--- test/comic_reader/sites_test.clj
+++ test/comic_reader/sites_test.clj
@@ -1,6 +1,7 @@
 (ns comic-reader.sites-test
   (:require [clojure.test :refer :all]
-            [comic-reader.sites :refer :all]))
+            [comic-reader.sites :refer :all]
+            [clojure.java.io :as io]))

 (deftest base-name-test
   (is (= (base-name "abc.123")
@@ -43,9 +44,29 @@
           (str "Contents of `sites/" site ".clj'"
                " cannot be empty")))))

+(defn site-test-folder [site-name]
+  (format "test/%s" site-name))
+
+(defn has-test-folder? [site-name]
+  (some->
+   site-name
+   site-test-folder
+   io/resource
+   io/as-file
+   .exists))
+
+(defn error-must-have-test-data [site-name]
+  (is false
+      (str "There must be a site test data folder at "
+           "`resources/test/" site-name "'")))
+
 (defn testdef-form [site-name]
   `(deftest ~(symbol (str site-name "-test"))
-     (expect-opts-are-map ~site-name)))
+     (expect-opts-are-map ~site-name)
+     (if (has-test-folder? ~site-name)
+       (do
+         )
+       (error-must-have-test-data ~site-name))))

 (defmacro defsite-tests []
   (try


indentation

indentation

--- test/greybear/websocket_test.clj
+++ test/greybear/websocket_test.clj
@@ -4,7 +4,11 @@
         [greybear.model :only [make-move]]))

 (fact "on-message make-move makes a new move and calls refresh"
-  (let [message "{\"cmd\": \"make-move\", \"game_id\": 142, \"user_id\": 3, \"x\": 1, \"y\": 3}"]
+  (let [message (str "{\"cmd\": \"make-move\", "
+                     "\"game_id\": 142, "
+                     "\"user_id\": 3,"
+                     "\"x\": 1,"
+                     "\"y\": 3}")]
     (on-message nil message) => nil
     (provided
       (make-move 142 "1-3") => anything


cosets.clj: documented an apparently missed result from table-generator


Add additional arity to the cats.core/mempty for allow user specified context.


First placeholders for v-event-handlers.

--- src/cbfg/core.cljs
+++ src/cbfg/core.cljs
@@ -64,6 +64,35 @@

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

+(def v-event-handlers
+  {"ago"
+   {:beg (fn [v actx args]
+           (let [[child-actx] args] 1))
+    :end (fn [v actx args]
+           (let [[child-actx result] args] 2))}
+   "aclose"
+   {:beg (fn [v actx args]
+           (let [[ch] args] 1))
+    :end (fn [v actx args]
+           (let [[ch result] args] 2))}
+   "atake"
+   {:beg (fn [v actx args]
+           (let [[ch] args] 1))
+    :end (fn [v actx args]
+           (let [[ch result] args] 2))}
+   "aput"
+   {:beg (fn [v actx args]
+           (let [[ch msg] args] 1))
+    :end (fn [v actx args]
+           (let [[ch msg result] args] 2))}
+   "aalts"
+   {:beg (fn [v actx args]
+           (let [[chs] args] 1))
+    :end (fn [v actx args]
+           (let [[chs result] args] 2))}})
+
+;; ------------------------------------------------
+
 (defn example-add [actx x y delay]
   (ago example-add actx
        (<! (timeout delay))


Change many atoms -> few atoms

Change many atoms -> few atoms

--- src/tomato/core.clj
+++ src/tomato/core.clj
@@ -7,21 +7,19 @@
   (:gen-class))

-; [TODO] too many atoms
-(def timer (atom nil))
-(def interval (atom nil))
-(def started (atom nil))
-(def in-state (atom nil))
 (def counted (atom 1))
-(def message-id (atom nil))
-
+(def state (atom {:timer      nil
+                  :interval   nil
+                  :started    nil
+                  :mode       nil
+                  :message-id nil}))

 (defn secs [s] (* s 1000))
 (defn mins [m] (* m (secs 60)))
-(def states {:pomodoro {:text #(str "업무로 돌아올 시간입니다! " @counted), :next :relax, :during (mins 5)}
-             :relax    {:text #(str "쉴 시간입니다!"), :next :pomodoro, :during (secs 30)}})
+(def modes {:pomodoro {:text #(str "업무로 돌아올 시간입니다! " @counted), :next :relax, :during (mins 5)}
+            :relax    {:text #(str "쉴 시간입니다!"), :next :pomodoro, :during (secs 30)}})

@@ -30,9 +28,10 @@
   (future (while true (do (Thread/sleep ms) (callback)))))

 (defn set-timeout [callback ms]
-  (reset! timer (future (do (Thread/sleep ms)
-                            (reset! timer nil)
-                            (callback)))))
+  (swap! state assoc :timer (future (do (Thread/sleep ms)
+                                        (swap! state assoc :timer nil)
+                                        (callback)))))
+

 (defn send-m
   ([message] (telegram/send-text (config/get :token) (config/get :chat-id) message))
@@ -45,10 +44,10 @@

 (defn elapsed-time []
-  (- (System/currentTimeMillis) @started))
+  (- (System/currentTimeMillis) (:started @state)))

 (defn base-time []
-  (:during (@in-state states)))
+  (:during ((:mode @state) modes)))

 (defn ms->sec [ms]
   (quot ms 1000))
@@ -61,48 +60,52 @@
   (edit-m message id)
   )

+(defn session-alive? []
+  (nil? (:timer @state)))

-(defn send-remaining []
-  (if (nil? @message-id)
+
+
+(defn send-remaining [byline]
+  (if (nil? (:message-id @state))
     (->> (send-m (str "남은 시간은 " (remaining-time) "초"))
          :result
          :message_id
-         (reset! message-id))
-    (edit-message @message-id (str "남은 시간은 " (remaining-time) "/" (ms->sec (base-time)) "초"))))
+         (swap! state assoc :message-id))
+    (edit-message (:message-id @state) (str "남은 시간은 " (remaining-time) "/" (ms->sec (base-time)) "초 by " byline))))

 (defn goto-x [key]
-  (let [state (key states)]
-    (send-m ((:text state)))
-    (reset! in-state key)
-    (reset! started (System/currentTimeMillis))
-    (reset! message-id nil)
-    (when-not (nil? @interval) (future-cancel @interval))
-    (reset! interval (set-interval #(send-remaining) (secs 10)))
+  (let [mode (key modes)]
+    (send-m ((:text mode)))
+    (swap! state assoc :mode key)
+    (swap! state assoc :started (System/currentTimeMillis))
+    (swap! state assoc :message-id nil)
+    (when-not (nil? (:interval @state)) (future-cancel (:interval @state)))
+    (swap! state assoc :interval (set-interval #(send-remaining "interval") (secs 10)))
     (when (= key :relax)
       (swap! counted inc))
-    (set-timeout #(goto-x (:next state)) (:during state))))
+    (set-timeout #(goto-x (:next mode)) (:during mode))))

 ;;;; Handlers
 (defn timer-start []
-  (if (nil? @timer)
+  (if (session-alive?)
     (goto-x :pomodoro)
     (send-m "이미 세션이 진행중입니다")))

 (defn check-remaining []
-  (if-not (nil? @timer)
-    (send-remaining)
+  (if-not (session-alive?)
+    (send-remaining "manual")
     (send-m "타이머가 돌고 있지 않음")))

 (defn cancel-timer []
-  (if-not (nil? @timer)
+  (if-not (session-alive?)
     (do
-      (future-cancel @timer)
-      (future-cancel @interval)
-      (reset! timer nil)
-      (send-m (str @in-state " 취소되었습니다")))
+      (future-cancel (:timer @state))
+      (future-cancel (:interval @state))
+      (swap! state assoc :timer nil)
+      (send-m (str (:mode @state) " 취소되었습니다")))
     (send-m "진행중인 태스크가 없습니다")))

 (defn count-counted []


What did I JUST say about being stupid

What did I JUST say about being stupid


Story ID: develop Summary: - updated app/change-locale mutation to change top-level react key

--- src/untangled/client/mutations.cljs
+++ src/untangled/client/mutations.cljs
@@ -1,5 +1,6 @@
 (ns untangled.client.mutations
   (:require [om.next :as om]
+            [untangled.dom :refer [unique-key]]
             [untangled.client.impl.data-fetch :as df]
             [untangled.i18n.core :as i18n]
             [untangled.client.logging :as log]))
@@ -72,8 +73,10 @@
 (defmethod mutate 'app/clear-error [{:keys [state]} _ _]
   {:action #(swap! state assoc :last-error nil)})

-(defmethod mutate 'app/change-locale [_ _ {:keys [lang]}]
-  (reset! i18n/*current-locale* lang))
+(defmethod mutate 'app/change-locale [{:keys [state]} _ {:keys [lang]}]
+  {:action (fn []
+             (reset! i18n/*current-locale* lang)
+             (swap! state assoc :react-key (unique-key)))})

 (defmethod mutate 'tx/fallback [env _ {:keys [action execute] :as params}]
   (if execute


fix collision.util

fix collision.util

--- src/voke/system/collision/util.cljs
+++ src/voke/system/collision/util.cljs
@@ -63,7 +63,7 @@
   by us during this tick."
   [entity]
   ; The collision system should only be killing :destroyed-on-contact entities.
-  (assert (get-in entity [:collision :destroyed-on-contact]))
+  (assert (get-in entity [:component/collision :collision/destroyed-on-contact]))

   (when-not (contains? @dead-entities (entity :entity/id))
     (swap! dead-entities conj (entity :entity/id))
@@ -71,7 +71,7 @@

 (s/fdef remove-entity!
   :args (s/and (s/cat :entity :entity/entity)
-               #(-> % :entity :collision :destroyed-on-contact true?)))
+               #(-> % :entity :component/collision :collision/destroyed-on-contact true?)))

 (defn get-updated-entity-center
   "Returns the given entity, with its most up-to-date center value spliced in.


Swap var order for Story.

Swap var order for Story.

--- src/isla/story.clj
+++ src/isla/story.clj
@@ -11,7 +11,7 @@
 (defprotocol Playable
   (move [this direction]))

-(defrecord Story [rooms player]
+(defrecord Story [player rooms]
   Playable
   (move [this direction]
     (println "move!")))
@@ -33,10 +33,12 @@

         rooms (name-into-objs (extract-by-class ctx (:type (get types "room"))))
         player (val (first (extract-by-class ctx (:type (get types "_player")))))]
+
+    (Story. player rooms)))
+
 (defn get-story-ctx []
   {"my" (interpreter/instantiate-type (get types "_player"))})

-    (Story. rooms player)))
 ;; keys+vals -> vals
 (defn name-into-objs [objs]
   (map (fn [x] (assoc (val x) :name (key x)))


Use new utils fns.

Use new utils fns.

--- src/isla/story.clj
+++ src/isla/story.clj
@@ -4,6 +4,7 @@
   (:require [isla.parser :as parser])
   (:require [isla.interpreter :as interpreter])
   (:require [isla.story-utils :as story-utils])
+  (:require [mrc.utils :as utils])
   (:require [isla.library :as library]))

 (declare types name-into-objs extract-by-class get-story-ctx seq-to-hash resolve-)
@@ -64,12 +65,11 @@
   (let [command (first (str/split command-str #" "))
         arguments-str (second (str/split command-str #" " 2))
         arguments-vec (if (nil? arguments-str) [nil] [arguments-str])]
-    (clojure.lang.Reflector/invokeInstanceMethod story command (to-array arguments-vec))))
-
+    (utils/run-method story command arguments-vec)))

 (defn get-story-ctx []
-  {"my" (interpreter/instantiate-type (get types "_player"))})
+  {"my" (story-utils/instantiate-type (get types "_player"))})

 ;; keys+vals -> vals
 (defn name-into-objs [objs]


better meta-data for documentation

better meta-data for documentation

--- src/latte/core.clj
+++ src/latte/core.clj
@@ -4,6 +4,8 @@

   Users (as opposed to developpers) of the framework should
   mostly depend on this namespace."
+
+  (:require [clojure.pprint :as pp])

   (:require [latte.utils :as u])
   (:require [latte.presyntax :as stx])
@@ -34,6 +36,13 @@
     (when (not (vector? params))
       (throw (ex-info "Parameters of defterm must be a vector." {:def-name def-name :params params})))
     [def-name doc params body]))
+
+(defn mk-doc [content explanation]
+  (str "\n"
+       (with-out-str
+         (pp/pprint content))
+       "\n"
+       explanation))

 (defmacro defterm
   "Defines a mathematical term composed of a `name`, and optional (but highly recommended)
@@ -63,7 +72,9 @@
             (let [name# (name def-name)]
               `(do
                  (def ~def-name ~quoted-def)
-                 (alter-meta! (var ~def-name)  (fn [m#] (assoc m# :doc ~doc)))
+                 (alter-meta! (var ~def-name)  (fn [m#] (assoc m#
+                                                               :doc (mk-doc (quote ~body) ~doc)
+                                                               :arglists (list (quote ~params)))))
                  [:defined :term ~name#]))))))))

 (defn parse-defthm-args [args]
@@ -106,7 +117,9 @@
           (let [name# (name def-name)]
             `(do
                (def ~def-name ~quoted-def)
-               (alter-meta! (var ~def-name)  (fn [m#] (assoc m# :doc ~doc)))
+               (alter-meta! (var ~def-name)  (fn [m#] (assoc m#
+                                                             :doc (mk-doc (quote ~ty) ~doc)
+                                                             :arglists (list (quote ~params)))))
                [:declared :theorem ~name#])))))))


indentation fix

indentation fix

--- src/latte/core.clj
+++ src/latte/core.clj
@@ -309,7 +309,7 @@
   "Provides a proof of theorem named `thm-name` using the given proof `steps`.
   This version only checks if the proof is correct or not, use the [[proof]] function
   for actually registering the proof."
-  {:style/indent [2 :form :form [1]]}
+  {:style/indent [1 :form [1]]}
   [thm-name & steps]
   (let [def-env defenv/empty-env
         [status thm] (if (symbol? thm-name)
@@ -333,7 +333,7 @@

 (defn proof
   "Provides a proof of theorem named `thm-name` using the given proof `steps`."
-  {:style/indent [2 :form :form [1]]}
+  {:style/indent [1 :form [1]]}
   [thm-name & steps]
   (let [def-env defenv/empty-env
         [status thm] (if (symbol? thm-name)


or-assoc special

or-assoc special

--- src/latte/prop.clj
+++ src/latte/prop.clj
@@ -658,6 +658,31 @@
                         <a> <d>)))
   (qed <e>))

+(defspecial or-assoc%
+  "Associativity of disjunction, a special that subsumes both
+[[or-assoc]] and [[or-assoc-conv]]."
+  [def-env ctx or-term]
+  (let [[status ty] (ty/type-of-term def-env ctx or-term)]
+    (if (= status :ko)
+      (throw (ex-info "Cannot type term." {:special 'latte.prop/or-assoc%
+                                           :term or-term
+                                           :from ty}))
+      (let [[status A B] (decompose-or-type def-env ctx ty)]
+        (if (= status :ko)
+          (throw (ex-info "Not an `or`-type." {:special 'latte.prop/or-assoc%
+                                               :term or-term
+                                               :type ty}))
+          (let [[status A1 A2] (decompose-or-type def-env ctx A)]
+            (if (= status :ok)
+              [(list #'or-assoc A1 A2 B) or-term]
+              (let [[status B1 B2] (decompose-or-type def-env ctx B)]
+                (if (= status :ok)
+                  [(list #'or-assoc-conv A B1 B2) or-term]
+                  (throw (ex-info "Not an associative `or`-type."
+                                  {:special 'latte.prop/or-assoc%
+                                   :term or-term
+                                   :type ty})))))))))))
+
 (definition <=>
   "Logical equivalence or 'if and only if'."
   [[A :type] [B :type]]


don't create unnecessary team txes

don't create unnecessary team txes

--- src/pc/billing.clj
+++ src/pc/billing.clj
@@ -61,24 +61,26 @@
                                         (conj acc u)
                                         acc))
                                     #{}))]
-      (let [t @(d/transact (pcd/conn) [{:db/id (d/tempid :db.part/tx)
-                                        :transaction/team (:db/id team)
-                                        :transaction/broadcast true}
-                                       (pcd/replace-many (:db/id plan)
-                                                         :plan/active-custs
-                                                         (set (map :db/id active-custs)))])
-            before-custs (:plan/active-custs (d/entity (:db-before t) (:db/id plan)))
-            after-custs (:plan/active-custs (d/entity (:db-after t) (:db/id plan)))
-            ;; 1 is the minimum, so that's what we want to compare for billing purposes
-            paid-before-count (max 1 (count before-custs))
-            paid-after-count (max 1 (count after-custs))]
-        (when (and (not= paid-before-count paid-after-count)
-                   (:plan/paid? plan))
-          ;; We'll send an email after Stripe sends out their webhook
-          (stripe/update-quantity (:plan/stripe-customer-id plan) (:plan/stripe-subscription-id plan) paid-after-count)
-          (stripe/create-invoice (:plan/stripe-customer-id plan)
-                                 :description (str "Number of active users changed from "
-                                                   paid-before-count " to " paid-after-count)))))))
+      (when (not= (set (map :db/id active-custs))
+                  (set (map :db/id (:plan/active-custs plan))))
+        (let [t @(d/transact (pcd/conn) [{:db/id (d/tempid :db.part/tx)
+                                          :transaction/team (:db/id team)
+                                          :transaction/broadcast true}
+                                         (pcd/replace-many (:db/id plan)
+                                                           :plan/active-custs
+                                                           (set (map :db/id active-custs)))])
+              before-custs (:plan/active-custs (d/entity (:db-before t) (:db/id plan)))
+              after-custs (:plan/active-custs (d/entity (:db-after t) (:db/id plan)))
+              ;; 1 is the minimum, so that's what we want to compare for billing purposes
+              paid-before-count (max 1 (count before-custs))
+              paid-after-count (max 1 (count after-custs))]
+          (when (and (not= paid-before-count paid-after-count)
+                     (:plan/paid? plan))
+            ;; We'll send an email after Stripe sends out their webhook
+            (stripe/update-quantity (:plan/stripe-customer-id plan) (:plan/stripe-subscription-id plan) paid-after-count)
+            (stripe/create-invoice (:plan/stripe-customer-id plan)
+                                   :description (str "Number of active users changed from "
+                                                     paid-before-count " to " paid-after-count))))))))

 (defn set-active-users-cron []
   (let [now (time/now)


Handle state-seqs for :inbox & expose deletions.

--- src/pepa/model.clj
+++ src/pepa/model.clj
@@ -367,7 +367,11 @@
 ;;; Sequence Number Stuff

 (def ^:private sequenced-tables
-  [:pages :page_images :files :documents :document_tags :document_pages :inbox])
+  [:pages :page_images
+   :files
+   :documents :document_tags :document_pages
+   :inbox
+   :deletions])

 (defn valid-seqs? [seq]
   (and (every? (set sequenced-tables) (keys seq))
@@ -434,7 +438,20 @@
                    (mapv :document)))}))

 (defmethod changed-entities* :files [db _ seq-num]
-  {:files (mapv :id (db/query db ["SELECT id FROM files WHERE state_seq > ?" seq-num]))})
+  {:files (->> (db/query db ["SELECT id FROM files WHERE state_seq > ?" seq-num])
+               (mapv :id)
+               (set))})
+
+(defmethod changed-entities* :inbox [db _ seq-num]
+  {:inbox (->> (db/query db ["SELECT page FROM inbox WHERE state_seq > ?" seq-num])
+               (mapv :page)
+               (set))})
+
+(defmethod changed-entities* :deletions [db _ seq-num]
+  {:deletions (reduce (fn [deletions {:keys [id entity]}]
+                        (update-in deletions [(-> entity name keyword)] (comp set conj) id))
+                      {}
+                      (db/query db ["SELECT id, entity FROM deletions WHERE state_seq > ?" seq-num]))})

 (defn changed-entities
   "Takes a seqs-map and returns a map containing IDs for all changed


new experimental component creation

new experimental component creation

--- src/pump/core.cljs
+++ src/pump/core.cljs
@@ -2,6 +2,25 @@
   (:require-macros [pump.def-macros :refer [defr]])
   (:require [pump.template :refer [html]]
             [pump.utils :refer [wrap-functions]]))
+
+(defn component [state-ref render-fn & {:keys [did-mount will-unmount]}]
+  (let [inner-mount (fn []
+                   (add-watch state-ref :react
+                              (fn [_ _ old new]
+                                (when (not= old new)
+                                  (this-as this (.forceUpdate this)))
+                                (if did-mount
+                                  (did-mount)))))
+        inner-unmount (fn []
+                        (remove-watch state-ref :react)
+                        (if will-unmount
+                          (will-unmount)))
+        comp (React/createClass
+              (js-obj "render" (fn []
+                                 (this-as this
+                                   (render-fn this (.-props this))))
+                      "componentDidMount" inner-mount
+                      "componentWillUnount" inner-unmount))]))

 (defn react
   [body]


Don't store :react/key and :react/ref annotations in :statics.

--- src/quiescent.cljs
+++ src/quiescent.cljs
@@ -67,12 +67,16 @@
                  (:componentWillUnmount m)
                  (q/set-prop! -componentWillUnmount (fn [] (om/allow-reads (.apply (:componentWillUnmount m) (js-this) (js-arguments))))))
         react-component (.createClass js/React react-map)
-        q-wrapper (fn [value & static-args]
+        q-wrapper (fn [value & [fsa & rsa :as static-args]]
                     (let [props #js {:value value :statics static-args}]
-                      (when-let [key (get (first static-args) :react/key)]
+                      (when-let [key (get fsa :react/key)]
                         (aset props "key" key))
-                      (when-let [ref (get (first static-args) :react/ref)]
+                      (when-let [ref (get fsa :react/ref)]
                         (aset props "ref" ref))
+                      (when-not (and (undefined? (aget props "key"))
+                                     (undefined? (aget props "ref")))
+                        (aset props "statics"
+                              (into [(dissoc fsa :react/key :react/ref)] rsa)))
                       (react-component props)))]
     (when-let [displayName (.-displayName react-map)]
       (set! (.-displayName q-wrapper) displayName))


this is an outdated definition of check now

--- src/vigil/core.clj
+++ src/vigil/core.clj
@@ -34,8 +34,3 @@

 (defn kill [player]
   (assoc player :alive false))
-
-(defn check [player game]
-  "Look to see if anyone is attacking player's team, and kill them if they are.
-  Returns a vector of killed players."
-  (map kill (attacking-players player game)))


Fix tests for cljs

Fix tests for cljs

--- test/cljc/spec_tools/core_test.cljc
+++ test/cljc/spec_tools/core_test.cljc
@@ -74,10 +74,12 @@
 (deftest reason-test
   (let [expected-problem {:path [] :pred 'pos-int?, :val -1, :via [], :in []}]
     (testing "normal explain-data"
-      (is (= #:clojure.spec{:problems [expected-problem]}
+      (is (= #?(:clj  #:clojure.spec{:problems [expected-problem]}
+                :cljs #:cljs.spec{:problems [expected-problem]})
              (s/explain-data (st/spec pos-int?) -1))))
     (testing "explain-data with reason"
-      (is (= #:clojure.spec{:problems [(assoc expected-problem :reason "positive")]}
+      (is (= #?(:clj  #:clojure.spec{:problems [(assoc expected-problem :reason "positive")]}
+                :cljs #:cljs.spec{:problems [(assoc expected-problem :reason "positive")]})
              (s/explain-data (st/spec pos-int? {:spec/reason "positive"}) -1))))))

 (deftest spec-tools-conform-test


fix test-default-pmap-buffer order should not matter

--- test/com/climate/claypoole_test.clj
+++ test/com/climate/claypoole_test.clj
@@ -965,6 +965,6 @@
                              (range 10)))]
         (Thread/sleep 100)
         ;; As requested, only 3 items have been started
-        (is (= @started (range 3)))
+        (is (= (set @started) (set (range 3))))
         (deliver start true)
         (dorun tasks)))))


fix budgets test error

fix budgets test error

--- test/clj_money/web/budgets_test.clj
+++ test/clj_money/web/budgets_test.clj
@@ -53,7 +53,7 @@
   (let [context (serialization/realize storage-spec budget-context)
         actual (with-authentication (-> context :users first)
                  (-> (budgets/for-display (-> context :budgets first :id))
-                     (update-in [:items] #(map (fn [i] (dissoc i :id)) %))
+                     (update-in [:items] #(map (fn [i] (dissoc i :id :item)) %))
                      (dissoc :id :created-at :updated-at :entity-id)))
         expected {:name "2017"
                   :period :month