util/crawler.clj: get correct redirect URI

--- src/org/altlaw/util/crawler.clj
+++ src/org/altlaw/util/crawler.clj
@@ -106,15 +106,11 @@

 (declare handle-download-request)

-(defn- handle-redirect [result]
+(defn- handle-redirect [result new-location]
   (let [code (:response_status_code result)
-        headers (into {} (:response_headers result))
-        location (get headers "Location")
-        original-request (select-keys result [:request_form_fields
-                                              :request_headers :request_uri])
-        new-request (assoc original-request :request_uri location)]
-    (log/info "HTTP response " code " to " (pr-str original-request))
-    (log/info "Redirecting to " location)
+        new-request {:request_uri new-location}]
+    (log/info "Got HTTP redirect for " (pr-str result))
+    (log/info "Redirecting to " new-location)
     (assoc (handle-download-request new-request) :redirect_from result)))

 (defn- execute-request
@@ -126,9 +122,11 @@
         result (merge (request-map request)
                       (response-map response))
         code (:response_status_code result)]
-    (dl/log-download (:request_uri result))
+    ;; Only log URL on success or NOT FOUND
+    (when (#{200 404} code)
+      (dl/log-download (:request_uri result)))
     (if (#{301 302 303 307} code)
-      (handle-redirect result)
+      (handle-redirect result (str (.getLocationRef response)))
       result)))

 (defn crawl


Fix the circle-build, circle-deploy functions

--- backend/src/circle/backend/project/circle.clj
+++ backend/src/circle/backend/project/circle.clj
@@ -2,8 +2,8 @@
   (:require [circle.backend.build.config :as config]))

 (defn circle-build []
-  (config/build-from-name "Circle" :build))
+  (config/build-from-name "Circle" :job-name :build))

 (defn circle-deploy []
-  (config/build-from-name "Circle" :deploy))
+  (config/build-from-name "Circle" :job-name :deploy))


minor formating changes.

minor formating changes.

--- src/grafter/tabular.clj
+++ src/grafter/tabular.clj
@@ -400,12 +400,7 @@

         expand-rows (fn [row] (map normalise-row colids (repeat ncols row)))]

-    (mapcat expand-rows data-rows)))
-
-
-  )
-
-
+    (mapcat expand-rows data-rows))))

 (comment
   ;; TODO implement inner join, maybe l/r outer joins too


grep clojure.lang.IFn seems to work for usual cases

--- src/grafter/tabular.clj
+++ src/grafter/tabular.clj
@@ -236,14 +236,17 @@
        (filter (fn [[k v]] (col-set k)))
        (map second)))

-(defmethod grep clojure.lang.IFn [dataset f & cols]
+(defmethod grep clojure.lang.IFn
+
+  [dataset f cols]
   (let [data (:rows dataset)
         col-set (into #{} cols)]

-    (make-dataset (column-names dataset)
-                  (->> data
+     (make-dataset cols
+                   (->> data
                        (filter (fn [row]
-                                 (some f (partial cells-from-columns col-set))))))))
+                                 (some f
+                                       (cells-from-columns col-set row))))))))

 (defmethod grep java.lang.String [csv s & cols]
   (apply grep csv #(.contains % s) cols))


comments

comments

Former-commit-id: 2887e1c5027f3aa733f4ab60088e6d962b2b2c98

--- src/status_im/chat/views/response.cljs
+++ src/status_im/chat/views/response.cljs
@@ -38,24 +38,36 @@
 (defn enough-dy [gesture]
   (> (Math/abs (.-dy gesture)) 10))

+(defn on-move [response-height kb-height orientation]
+  (fn [_ gesture]
+    (when (enough-dy gesture)
+      (let [w        (react/get-dimensions "window")
+            ;; depending on orientation use height or width of screen
+            prop     (if (= :portrait @orientation)
+                       :height
+                       :width)
+            ;; subtract keyboard height to get "real height" of screen
+            ;; then subtract gesture position to get suggestions height
+            ;; todo maybe it is better to use margin-top instead height
+            ;; it is not obvious
+            to-value (- (prop w) @kb-height (.-moveY gesture))]
+        (println to-value )
+        (anim/start
+          (anim/spring response-height {:toValue to-value}))))))
+
+(defn on-release [response-height]
+  (fn [_ gesture]
+    (when (enough-dy gesture)
+      (dispatch [:fix-response-height
+                 (.-vy gesture)
+                 ;; todo access to "private" property
+                 ;; better to find another way...
+                 (.-_value response-height)]))))
+
 (defn pan-responder [response-height kb-height orientation]
   (drag/create-pan-responder
-    {:on-move    (fn [_ gesture]
-                   (when (enough-dy gesture)
-                     (let [w        (react/get-dimensions "window")
-                           prop     (if (= :portrait @orientation)
-                                      :height
-                                      :width)
-                           to-value (- (prop w) @kb-height (.-moveY gesture))]
-                       (anim/start
-                         (anim/spring response-height {:toValue to-value})))))
-     :on-release (fn [_ gesture]
-                   (when (enough-dy gesture)
-                     (dispatch [:fix-response-height
-                                (.-vy gesture)
-                                ;; todo access to "private" property
-                                ;; better to find another way...
-                                (.-_value response-height)])))}))
+    {:on-move    (on-move response-height kb-height orientation)
+     :on-release (on-release response-height)}))

 (defn request-info [response-height]
   (let [orientation   (subscribe [:get :orientation])


fixes #7363

fixes #7363

Signed-off-by: Julien Eluard <c768e13f1ba4c3ebed2909a1fc1e4b50bb021a2f@gmail.com>

--- src/status_im/extensions/registry.cljs
+++ src/status_im/extensions/registry.cljs
@@ -107,35 +107,64 @@
         new-extensions (assoc-in extensions [extension-key :active?] active?)
         hook-fn        (if active?
                          hook-in
-                         unhook)]
-    (fx/merge cofx
+                         unhook)
+        lifecycle-event (if active?
+                          (get-in extensions [extension-key :lifecycle :on-activation])
+                          (get-in extensions [extension-key :lifecycle :on-deactivation]))]
+    (fx/merge cofx
+              ;; on-deactivate called before
+              (when (and (not active?) lifecycle-event) (lifecycle-event))
               (accounts.update/account-update {:extensions new-extensions} {:success-event nil})
-              (update-hooks hook-fn extension-key))))
+              (update-hooks hook-fn extension-key)
+              ;; on-activation called after
+              (when (and active? lifecycle-event) (lifecycle-event)))))

 (fx/defn install
   [{:keys [db] :as cofx} url {:keys [hooks] :as extension-data} modal?]
   (let [{:account/keys    [account]} db
-        extension      {:id      url
-                        :name    (or (get-in extension-data [:meta :name]) "Unnamed")
-                        :url     url
+        on-installation  (get-in extension-data [:lifecycle :on-installation])
+        on-activation    (get-in extension-data [:lifecycle :on-activation])
+        ephemeral?       (get-in extension-data [:lifecycle :ephemeral])
+        extension      {:id         url
+                        :name       (or (get-in extension-data [:meta :name]) "Unnamed")
+                        :url        url
+                        :lifecycle  {:on-installation on-installation
+                                     :on-deinstallation (get-in extension-data [:lifecycle :on-deinstallation])
+                                     :on-activation on-activation
+                                     :on-deactivation (get-in extension-data [:lifecycle :on-deactivation])
+                                     :ephemeral? ephemeral?}
                         :active? true}
         new-extensions (assoc (:extensions account) url extension)]
-    (fx/merge cofx
-              {:utils/show-popup {:title      (i18n/label :t/success)
-                                  :content    (i18n/label :t/extension-installed)
-                                  :on-dismiss #(re-frame/dispatch (if modal?
-                                                                    [:navigate-back]
-                                                                    [:navigate-to-clean :my-profile]))}}
-              (when hooks (accounts.update/account-update {:extensions new-extensions} {}))
-              (when hooks (add-to-registry url extension-data true)))))
+    (if ephemeral?
+      (fx/merge cofx
+                {:dispatch (if modal?
+                             [:navigate-back]
+                             [:navigate-to-clean :my-profile])}
+                (when on-activation (on-activation)))
+      (fx/merge cofx
+                {:utils/show-popup {:title      (i18n/label :t/success)
+                                    :content    (i18n/label :t/extension-installed)
+                                    :on-dismiss #(re-frame/dispatch (if modal?
+                                                                      [:navigate-back]
+                                                                      [:navigate-to-clean :my-profile]))}}
+                (when hooks (accounts.update/account-update {:extensions new-extensions} {}))
+                (when hooks (add-to-registry url extension-data true))
+                (when on-installation (on-installation))
+                (when on-activation (on-activation))))))

 (fx/defn uninstall
   [{:keys [db] :as cofx} extension-key]
   (let [{:account/keys [account]} db
+        extension     (get-in cofx [:db :account/account :extensions extension-key])
+        active?       (get extension :active?)
+        on-deinstallation  (get-in extension [:lifecycle :on-deinstallation])
+        on-deactivation    (get-in extension [:lifecycle :on-deactivation])
         new-extensions (dissoc (:extensions account) extension-key)]
     (fx/merge cofx
               {:utils/show-popup {:title     (i18n/label :t/success)
                                   :content   (i18n/label :t/extension-uninstalled)}}
+              (when (and active? on-deactivation) (on-deactivation))
+              (when on-deinstallation (on-deinstallation))
               (remove-from-registry extension-key)
               (accounts.update/account-update {:extensions new-extensions} {}))))


Minor fix on error handling.

Minor fix on error handling.

--- src/potok/core.cljs
+++ src/potok/core.cljs
@@ -85,16 +85,11 @@
   (update [func state]
     (func state)))

-(def ^:dynamic *on-error* nil)
-
 (defn- default-error-handler
   [error]
-  (if (fn? *on-error*)
-    (*on-error* error)
-    (do
-      (js/console.error "Using default error handler, consider using your own!")
-      (js/console.error error)
-      (throw error))))
+  (js/console.error "Using default error handler, consider using your own!")
+  (js/console.error error)
+  (throw error))

 (enable-console-print!)


[Fix #39] only consider those ns-s for find symbol

which are on the classpath

related to #32 too

--- src/refactor_nrepl/analyzer.clj
+++ src/refactor_nrepl/analyzer.clj
@@ -2,10 +2,12 @@
   (:refer-clojure :exclude [macroexpand-1])
   (:require [clojure.tools.analyzer :as ana]
             [clojure.tools.analyzer.jvm :as aj]
+            [clojure.tools.analyzer.jvm.utils :as ajutils]
             [clojure.tools.analyzer.passes :refer [schedule]]
             [clojure.tools.analyzer.passes.jvm.validate :refer [validate]]
             [clojure.tools.namespace.parse :refer [read-ns-decl]]
-            [clojure.tools.analyzer.passes.emit-form :as emit-form])
+            [clojure.tools.analyzer.passes.emit-form :as emit-form]
+            [clojure.java.io :as io])
   (:import java.io.PushbackReader))

 ;;; The structure here is {ns {content-hash ast}}
@@ -46,17 +48,22 @@
   (swap! ast-cache update-in [ns] merge {(hash file-content) ast})
   ast)

+(defn- ns-on-cp? [ns]
+  (io/resource (ajutils/ns->relpath ns)))
+
 (defn- build-ast
   [ns aliases]
-  (binding [ana/macroexpand-1 noop-macroexpand-1]
-    (assoc-in (aj/analyze-ns ns) [0 :alias-info] aliases)))
+  (when (ns-on-cp? ns)
+    (binding [ana/macroexpand-1 noop-macroexpand-1]
+      (assoc-in (aj/analyze-ns ns) [0 :alias-info] aliases))))

 (defn- cachable-ast [file-content]
   (let [[ns aliases] (parse-ns file-content)]
     (when ns
       (if-let [cached-ast (get-ast-from-cache ns file-content)]
         cached-ast
-        (update-ast-cache file-content ns (build-ast ns aliases))))))
+        (when-let [new-ast (build-ast ns aliases)]
+          (update-ast-cache file-content ns new-ast))))))

 (defn ns-ast
   "Build AST for a namespace"


Minor fixes on schema ns.

Minor fixes on schema ns.

--- src/continuo/postgresql/schema.clj
+++ src/continuo/postgresql/schema.clj
@@ -17,10 +17,6 @@
             [cuerdas.core :as str]
             [continuo.postgresql.attributes :as attrs]
             [continuo.postgresql.types :as types]))
-
-(comment
-  (ct/run-schema! db [[:db/add :user/username {:unique true :type :text}]
-                      [:db/drop :user/name]]))

 (defmulti -execute-schema
   "A polymorphic abstraction for build appropiate
@@ -51,12 +47,13 @@

 (defn run-schema
   [context schema]
-  (let [ops (map (partial compile-op context) schema)
-        conn (.-connection context)]
+  (let [conn (.-connection context)]
     (ct/atomic conn
       (let [txid (tx/get-next-txid conn)]
         (run! #(apply -execute-schema conn %) schema)
-        (-execute-transact conn txid schema))
+        (-execute-transact conn txid schema)))))

+;; (comment
+;;   (ct/run-schema! db [[:db/add :user/username {:unique true :type :text}]
+;;                       [:db/drop :user/name]]))

-      (run! (fn [op] (-execute op conn)) ops))))


:sparkles: Navigate to home when click on main icon at dashboard

--- frontend/src/uxbox/main/ui/dashboard.cljs
+++ frontend/src/uxbox/main/ui/dashboard.cljs
@@ -15,6 +15,7 @@
    [uxbox.common.exceptions :as ex]
    [uxbox.common.uuid :as uuid]
    [uxbox.common.spec :as us]
+   [uxbox.main.store :as st]
    [uxbox.main.refs :as refs]
    [uxbox.main.ui.dashboard.sidebar :refer [sidebar]]
    [uxbox.main.ui.dashboard.search :refer [search-page]]
@@ -22,6 +23,7 @@
    [uxbox.main.ui.dashboard.recent-files :refer [recent-files-page]]
    [uxbox.main.ui.dashboard.library :refer [library-page]]
    [uxbox.main.ui.dashboard.profile :refer [profile-section]]
+   [uxbox.util.router :as rt]
    [uxbox.util.i18n :as i18n :refer [t]]))

 (defn ^boolean uuid-str?
@@ -62,11 +64,13 @@
   (let [profile (mf/deref refs/profile)
         page (get-in route [:data :name])
         {:keys [search-term team-id project-id library-id library-section] :as params}
-        (parse-params route profile)]
+          (parse-params route profile)]
     [:*
      [:& global-notifications {:profile profile}]
      [:section.dashboard-layout
-      [:div.main-logo i/logo-icon]
+      [:div.main-logo
+        [:a {:on-click #(st/emit! (rt/nav :dashboard-team {:team-id team-id}))}
+         i/logo-icon]]
       [:& profile-section {:profile profile}]
       [:& sidebar {:team-id team-id
                    :project-id project-id


safe-aget example

safe-aget example

--- src/purnam_crafty_game/core.cljs
+++ src/purnam_crafty_game/core.cljs
@@ -1,6 +1,9 @@
 (ns purnam-crafty-game.core
   (:require [goog.object :as o])
-  (:use-macros [purnam.core :only [obj arr ! def.n def*n def* do*n]]))
+  (:use-macros [purnam.core :only [obj arr ! def.n def*n def* do*n]]
+               [purnam.common :only [set-safe-aget]]))
+
+(set-safe-aget false)

  (def* G
   {:grid {:width 24
@@ -236,3 +239,5 @@

 (def START (fn [] (js/Crafty.scene "Game")))
+
+(set-safe-aget true)


Add code for chapter06 till 6.5 section

Add code for chapter06 till 6.5 section

--- clojure-lang/src/clojure_lang/chapter06.clj
+++ clojure-lang/src/clojure_lang/chapter06.clj
@@ -1,5 +1,6 @@
 (ns clojure-lang.chapter06
   (:require [taoensso.carmine :as car]
+            [cheshire.core :as cc]
             [clj-time.coerce :as ctc]
             [clj-time.core :as ct]
             [clojure.string :refer [starts-with?]]
@@ -60,7 +61,7 @@
     (wcar* (car/watch lock-key)
            (if (= identifier (car/get lock-key))
              (car/del lock-key)
-             (car/unwatch lock-key)))))
+             (car/unwatch)))))

 ;; Listing 6.9: The purchase_item_with_lock() function

@@ -86,7 +87,6 @@
     (when lock-identifier
       (release-lock "market" lock-identifier))))

-
 ;; Listing 6.11: The acquire_lock_with_timeout() function

 (defn acquire-lock-with-timeout
@@ -98,8 +98,210 @@
                acquire-timeout)]
     (loop []
       (cond
-        (< end (ctc/to-long (ct/now))) false
-        (= 1 (wcar* (car/setnx (str "lock:" lockname) identifier))) (do (wcar* (car/expire (str "lock:" lockname) lock-timeout))
-                                                                        identifier)
-        :else (do (Thread/sleep 100)
-                  (recur))))))
+        (< end (ctc/to-long (ct/now)))
+        false
+
+        (= 1 (wcar* (car/setnx (str "lock:" lockname) identifier)))
+        (do (wcar* (car/expire (str "lock:" lockname) lock-timeout))
+            identifier)
+
+        :else
+        (do (Thread/sleep 100)
+            (recur))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 6.3 Counting Semaphores ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Listing 6.12: The acquire_semaphore() function
+
+(defn acquire-semaphore
+  [semname limit & {:keys [timeout]
+                    :or {timeout 10}}]
+  (let [identifier (str (java.util.UUID/randomUUID))
+        now (ctc/to-long (ct/now))
+        result (wcar* (car/zremrangebyscore semname Double/NEGATIVE_INFINITY (- now timeout))
+                      (car/zadd semname now identifier)
+                      (car/zrank semname identifier) limit)]
+    (if (< (last result) limit)
+      identifier
+      (do
+        (wcar* (car/zrem semname identifier))
+        nil))))
+
+;; Lisiting 6.13: The release_semaphore() function
+
+(defn release-semaphore
+  [semname identifier]
+  (wcar* (car/zrem semname identifier)))
+
+;; Listing 6.14: The acquire_fair_semaphore() function
+
+(defn acquire-fair-semaphore
+  [semname limit & {:keys [timeout]
+                    :or {timeout 10}}]
+  (let [identifier (str (java.util.UUID/randomUUID))
+        czset (str semname ":owner")
+        ctr (str semname ":counter")
+        now (ctc/to-long (ct/now))
+        counter-result (wcar* (car/zremrangebyscore semname Double/NEGATIVE_INFINITY (- now timeout))
+                              (car/zinterstore czset 2 czset semname "weights" 1 0)
+                              (car/incr ctr))
+        counter (last counter-result)
+        acquire-sem-result (wcar* (car/zadd semname now identifier)
+                                  (car/zadd czset counter identifier)
+                                  (car/zrank czset identifier))]
+    (if (< (last acquire-sem-result) limit)
+      identifier
+      (do
+        (wcar* (car/zrem semname identifier)
+               (car/zrem czset identifier))
+        nil))))
+
+;; Listing 6.15: The release_fair_semaphore() function
+
+(defn release-fair-semaphore
+  [semname identifier]
+  (-> (wcar* (car/zrem semname identifier)
+             (car/zrem (str semname ":owner") identifier))
+      first
+      (= 1)))
+
+;; Listing 6.16: The refresh_fair_semaphore() function
+
+(defn refresh-fair-semaphore
+  [semname identifier]
+  (if (wcar* (car/zadd semname (ctc/to-long (ct/now)) identifier))
+    (do
+      (release-fair-semaphore semname identifier)
+      false)
+    true))
+
+;; Listing 6.17: The acquire_semaphore_with_lock() function
+
+(defn acquire-semaphore-with-lock
+  [semname limit & {:keys [timeout]
+                    :or {timeout 10}}]
+  (let [identifier (acquire-lock semname :acquire-timeout 10)]
+    (if identifier
+      (try
+        (acquire-fair-semaphore semname limit :timemout timeout)
+        (finally
+          (release-lock semname identifier))))))
+
+;;;;;;;;;;;;;;;;;;;;;
+;; 6.4 Task Queues ;;
+;;;;;;;;;;;;;;;;;;;;;
+
+;; Listing 6.18: The send_sold_email_via_queue() function
+
+(defn send-sold-email-via-queue
+  [seller item price buyer]
+  (let [data {:seller_id seller
+              :item_id item
+              :price price
+              :buyer_id buyer
+              :time (ctc/to-long (ct/now))}]
+    (wcar* (car/rpush "queue:email" (cc/generate-string data)))))
+
+;; Listing 6.19: The process_sold_email_queue() function
+
+(defn process-sold-email-queue
+  []
+  (let [packed (wcar* (car/blpop "queue:email" 30))]
+    (if packed
+      (println "Sending email with this content:"
+               (cc/parse-string (last packed) true))
+      (println "No item in queue."))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;
+;; 6.5 Pull Messaging ;;
+;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Listing 6.25: The send_message() function
+
+(defn send-message
+  [chat-id sender message]
+  (let [identifier (acquire-lock (str "chat:" chat-id))]
+    (if (not identifier)
+      (throw (Exception. "Couldn't get the lock"))
+      (do
+        (try
+          (let [mid (wcar* (car/incr (str ":ds:" chat-id)))
+                ts (ctc/to-long (ct/now))
+                data (-> {:id mid :ts ts :sender sender :message message}
+                         cc/generate-string)]
+            (wcar* (car/zadd (str "msgs:" chat-id) mid data)))
+          (finally
+            (release-lock (str "chat:" chat-id) identifier)))
+        chat-id))))
+
+;; Listing 6.24: The create_chat() function
+
+(defn create-chat
+  [sender recipients message & {:keys [chat-id]}]
+  (let [chat-id (or chat-id
+                    (wcar* (car/incr "ids:chat")))
+        create-chat! (fn [recipient-id]
+                       (car/zadd (str "chat:" chat-id)
+                                 0
+                                 recipient-id)
+                       (car/zadd (str "seen:" recipient-id)
+                                 0
+                                 chat-id))]
+    (wcar* (doseq [recipient recipients]
+             (create-chat! recipient)))
+    (send-message chat-id sender message)))
+
+;; Listing 6.26: The fecth_pending_messages() function
+
+(defn fetch-pending-messages
+  [recipient]
+  (let [seen (wcar* (car/zrange (str "seen:" recipient) 0 -1 "withscores"))
+        partitioned-seen (partition 2 seen)
+        fetch-msgs (fn [[chat-id seen-id]]
+                     (wcar* (car/zrangebyscore (str "msgs:" chat-id)
+                                               (inc (Integer/parseInt seen-id))
+                                               Double/POSITIVE_INFINITY)))
+        chat-info (->> partitioned-seen
+                       (mapv fetch-msgs)
+                       (zipmap partitioned-seen))]
+    (reduce-kv (fn [m [chat-id seen-id] v]
+                 (when v
+                   (let [parsed-messages (map #(cc/parse-string % true) v)
+                         seen-id (:id (last parsed-messages))
+                         _ (wcar* (car/zadd (str "chat:" chat-id) seen-id recipient))
+                         min-id (wcar* (car/zrange (str "chat:" chat-id)
+                                                   0
+                                                   0
+                                                   "withscores"))]
+                     (wcar* (car/zadd (str "seen:" recipient) seen-id chat-id))
+                     (when min-id
+                       (wcar* (car/zremrangebyscore (str "msgs:" chat-id) 0 (last min-id))))
+                     (assoc m chat-id parsed-messages))))
+               {}
+               chat-info)))
+
+
+;; Listing 6.27: The join_chat() function
+
+(defn join-chat
+  [chat-id user]
+  (let [message-id (-> (wcar* (car/get (str "ids:" chat-id)))
+                       Integer/parseInt)]
+    (wcar* (car/zadd (str "chat:" chat-id) message-id user)
+           (car/zadd (str "seen:" user) message-id chat-id))))
+
+
+;; Listing 6.28: The leave_chat() function
+
+(defn leave-chat
+  [chat-id user]
+  (wcar* (car/zrem (str "chat:" chat-id) user)
+         (car/zrem (str "seen:" user) chat-id)
+         (if (not (car/zcard (str "chat:" chat-id)))
+           (do
+             (car/del (str "msgs:" chat-id))
+             (car/del (str "ids:" chat-id)))
+           (let [oldest (car/zrange (str "chat:" chat-id) 0 0 "withscores")]
+             (car/zremrangebyscore (str "chat:" chat-id) 0 oldest)))))


Catch failures when updating resolve-missing cache

After adding core.async as a dependency and attempting to hotload it I
got a confusing ClassNotfoundException.  There's nothing we can do
about this situation, but we shouldn't freak the user out either when
the consequences are so trivial: resolve-missing, and
cljr-add-missing-libspec, won't work until the user has manually
loaded one of the namespaces in the new dependency.

--- src/refactor_nrepl/artifacts.clj
+++ src/refactor_nrepl/artifacts.clj
@@ -95,10 +95,16 @@
   "Once the deps are available on cp we still have to load them and
   reset slamhound's cache to make resolve-missing work."
   [coords repos]
-  (let [dep (first (alembic/resolve-dependencies alembic/the-still coords repos nil))
-        jarfile (JarFile. (:jar dep))]
-    (dorun (map require (find/find-namespaces-in-jarfile jarfile)))
-    (slamhound/reset)))
+  (try
+    (let [dep (first (alembic/resolve-dependencies alembic/the-still coords repos nil))
+          jarfile (JarFile. (:jar dep))]
+      (dorun (map require (find/find-namespaces-in-jarfile jarfile)))
+      (slamhound/reset))
+    (catch ClassNotFoundException _
+      ;; I've seen this happen after adding core.async as a dependency.
+      ;; A failure here isn't a big deal, it only means that resolve-missing
+      ;; isn't going to work until the namespace has been loaded manually.
+      )))

 (defn- ensure-quality-coordinates [coordinates]
   (let [coords (->> coordinates read-string (take 2) vec)]


Printing Invoice PDF whena PDF has been requested

--- src/time_tracker/invoices/handlers.clj
+++ src/time_tracker/invoices/handlers.clj
@@ -95,6 +95,21 @@
   [request connection]
   (res/response (invoices-db/retrieve-all connection)))

+(defn- invoice-record->invoice
+  "Converts the invoice saved in DB into a format used for PDF reports"
+  [invoice-in-db]
+  {:from-date (:from_date invoice-in-db)
+   :address (:address invoice-in-db)
+   :to-date (:to_date invoice-in-db)
+   :utc-offset (:utc_offset invoice-in-db)
+   :tax-amounts (read-string (:tax_amounts invoice-in-db))
+   :client (:client invoice-in-db)
+   :subtotal (:subtotal invoice-in-db)
+   :currency (:currency invoice-in-db)
+   :notes (:notes invoice-in-db)
+   :amount-due (:amount_due invoice-in-db)
+   :items (read-string (:items invoice-in-db))})
+
 ;; Endpoint for retrieving a single invoice
 ;; GET /api/invoices/<id>/
 (defn retrieve
@@ -104,8 +119,11 @@
                       connection
                       invoice-id)]
       (do
-        (log/debug {:event "invoice-received" :data invoice})
-        (res/response invoice))
+        (log/debug {:event "invoice-received" :data invoice})
+        (let [content-type (:content-type request)]
+          (if (= "application/pdf" content-type)
+            (print-invoice (invoice-record->invoice invoice))
+            (res/response invoice))))
       web-util/error-not-found)))

 ;; Endpoint for updating a single invoice.


Remove attachment header from invoice response

--- src/time_tracker/invoices/handlers.clj
+++ src/time_tracker/invoices/handlers.clj
@@ -15,6 +15,4 @@
         projects   (util/normalize-entities (projects-db/retrieve-all connection))
         timers     (util/normalize-entities (timers-db/retrieve-all connection))
         csv-string (invoices-core/generate-csv users projects timers)]
-    ;; TODO: add current date in the invoice filename
-    (-> (res/response csv-string)
-        (res/header "Content-Disposition" "attachment; filename=\"invoice.csv\""))))
+    (res/response csv-string)))


chore: Pin Clojure to 1.10.1 (same as in boot.properties)

--- build.boot
+++ build.boot
@@ -2,6 +2,7 @@
   :source-paths #{"test"}
   :resource-paths #{"src"}
   :dependencies '[[boot/core "2.8.2" :scope "provided"]
+                  [org.clojure/clojure "1.10.1"]
                   [adzerk/boot-test "1.2.0" :scope "test"]
                   [adzerk/bootlaces "0.2.0" :scope "test"]])


Get boot pom task to work

Get boot pom task to work

--- build.boot
+++ build.boot
@@ -1,7 +1,8 @@
 (def project 'radicalzephyr/webfx)
 (def version "0.1.0-SNAPSHOT")

-(set-env! :resource-paths #{"src"})
+(set-env! :resource-paths #{"src"}
+          :dependencies [])

 (task-options!
  pom {:project     project


Set jar file name

Set jar file name

--- build.boot
+++ build.boot
@@ -24,7 +24,8 @@
                      nightcode.lein}}
   jar {:main 'nightcode.core
        :manifest {"Description" "An IDE for Clojure and ClojureScript"
-                  "Url" "https://github.com/oakes/Nightcode"}})
+                  "Url" "https://github.com/oakes/Nightcode"}
+       :file "project.jar"})

 (deftask run []
   (comp


Rename the boot autotest command to boot test-auto

--- build.boot
+++ build.boot
@@ -80,7 +80,7 @@
   (merge-env! :source-paths #{"src/test"})
   identity)

-(deftask autotest
+(deftask test-auto
   []
   (comp
     (testing)


make triangle red, move coordinates

make triangle red, move coordinates

--- src/gamma_hello_triangle/core.cljs
+++ src/gamma_hello_triangle/core.cljs
@@ -8,7 +8,7 @@

 (def vertex-shader {(g/gl-position) (g/vec4 vertex-position 0 1)})

-(def fragment-shader {(g/gl-frag-color) (g/vec4 1 0.5 0.5 1)})
+(def fragment-shader {(g/gl-frag-color) (g/vec4 1 0 0 1)})

 (def hello-triangle
   (p/program
@@ -20,7 +20,7 @@
         vs  (.createShader gl ggl/VERTEX_SHADER)
         fs  (.createShader gl ggl/FRAGMENT_SHADER)
         pgm (.createProgram gl)
-        xs  (js/Float32Array. #js [0 0.9 0.5 0.4 -1 1])
+        xs  (js/Float32Array. #js [-0.5 -0.5 0.5 -0.5 0 0])
         buf (.createBuffer gl)]
     (doto gl
       (.shaderSource vs (-> hello-triangle :vertex-shader :glsl))


Revert "Load symbols in the values as well"

5f1eaba4a818ca4c8c08109892638e3fd99583e3

--- src/search/core.clj
+++ src/search/core.clj
@@ -69,7 +69,6 @@
    :graph (fnk [graphs :- [utils/Graph]] (apply g/graph graphs))
    :values-graph (fnk [values :- {s/Keyword s/Any}]
                   (->> values
-                    utils/eval-load-ns
                     (plumbing.core/map-vals utils/v->fnk)
                     g/graph))
    :wrappers (fnk [wrapper-forms :- [Wrapper]] (map utils/eval-load-ns wrapper-forms))


remove spurious println

remove spurious println

Signed-off-by: Chris Granger <5654cde260ae99bc4d30054171b8bcd902efd19a@gmail.com>

--- src/lt/objs/deploy.cljs
+++ src/lt/objs/deploy.cljs
@@ -120,7 +120,6 @@
 (defn check-version [& [notify?]]
   (fetch/xhr (version-url) {}
              (fn [data]
-               (println (is-newer? (:version version) data))
                (when (re-seq version-regex data)
                  (if (and (not= data "")
                           (not= data (:version version))


Fixes error internal to CM when line number is not specified

--- src/lt/objs/editor.cljs
+++ src/lt/objs/editor.cljs
@@ -202,7 +202,7 @@
   ed)

 (defn move-cursor [ed pos]
-  (.setCursor (->cm-ed ed) (clj->js pos)))
+  (.setCursor (->cm-ed ed) (clj->js (or pos 0))))

 (defn scroll-to [ed x y]
   (.scrollTo (->cm-ed ed) x y))


Lots of tree diagrams!

Lots of tree diagrams!

--- src/merkle_db/index.clj
+++ src/merkle_db/index.clj
@@ -190,12 +190,62 @@
 ;; - The "parent" is the current top level node we're recursing on.
 ;; - The "children" are the direct descendants of the parent node.
 ;; - The "candidates" are changed but unstored descendant nodes.
-
+;;
+;; The batch-update algorithm has the following goals:
+;; - **Log scaling:** visit `O(log_b(n))` nodes for `n` nodes updated.
+;; - **Minimize garbage:** avoid storing nodes which are not part of the final
+;;   tree.
+;; - **Deduplication:** reuse existing stored nodes where possible.
+;;
+;; In the following diagrams, these symbols are used:
+;; - `O` - unchanged index node
+;; - `*` - candidate index node
+;; - `+` - persisted index node
+;; - `#` - unchanged partition leaf
+;; - `@` - persisted partition leaf
+;; - `U` - underflowing partition leaf
+;; - `x` - node which has had all children deleted

 ;; ### Divide Changes
 ;;
 ;; In the first **downward** phase, changes at the parent are grouped by the
 ;; child which links to the subtree containing the referenced keys.
+;;
+;;     >        .[O].         [????|?????]
+;;             /     \
+;;            /       \
+;;           O        .O.
+;;          / \      / | \
+;;         O   O    O  O  O
+;;        /|\  |\  / \/|\/ \
+;;       # # # # # # ##### #
+;;
+;;              ..O..              |?????]
+;;             /     \
+;;            /       \
+;;     >    [O]       .O.     [??|???]
+;;          / \      / | \
+;;         O   O    O  O  O
+;;        /|\  |\  / \/|\/ \
+;;       # # # # # # ##### #
+;;
+;;              ..O..              |?????]
+;;             /     \
+;;            /       \
+;;           O        .O.        |???]
+;;          / \      / | \
+;;     >  [O]  O    O  O  O   [?|?]
+;;        /|\  |\  / \/|\/ \
+;;       # # # # # # ##### #
+;;
+;;              ..O..              |?????]
+;;             /     \
+;;            /       \
+;;           O        .O.        |???]
+;;          / \      / | \
+;;         O   O    O  O  O     |?]
+;;        /|\  |\  / \/|\/ \
+;;     >[#]# # # # # ##### #  [?]

 ,,,

@@ -207,10 +257,59 @@
 ;; resulting node would be empty. The resulting candidate node may have any
 ;; number of children, meaning it can either underflow, be valid, or overflow,
 ;; depending on the changes applied.
-
-,,,
-
-
+;;
+;;              ..O..
+;;             /     \
+;;            /       \
+;;           O        .O.
+;;          / \      / | \
+;;         O   O    O  O  O
+;;        /|\  |\  /|\/|\/ \
+;;     > x x[U]# # ####### #
+;;
+;; When a node has only a single child, it is 'pulled' up the tree recursively
+;; so it can be passed down the next branch for merging into the next branch.
+;;
+;;              ..O..
+;;             /     \
+;;            /       \
+;;           O        .O.
+;;          / \      / | \
+;;     >  [U]  O    O  O  O
+;;             |\  /|\/|\/ \
+;;             # # ####### #
+;;
+;;              ..O..
+;;             /     \
+;;            /       \
+;;     >    [O](U)    .O.
+;;           |       / | \
+;;           O      O  O  O
+;;          / \    /|\/|\/ \
+;;         #   #   ####### #
+;;
+;; When a subtree is being passed to the next branch, and the current node's
+;; height is one more than the subtree root, insert it as a child of the
+;; current node:
+;;
+;;              ..O..
+;;             /     \
+;;            /       \
+;;           O        .O.
+;;           |       / | \
+;;     >    [*]     O  O  O
+;;          /|\    /|\/|\/ \
+;;        (U)# #   ####### #
+;;
+;;              ..O..
+;;             /     \
+;;            /       \
+;;           O        .O.
+;;           |       / | \
+;;           *      O  O  O
+;;          /|\    /|\/|\/ \
+;;     >   U x[U]  ####### #
+;;
 ;; ### Redistribute Children
 ;;
 ;; In the **distribution** phase, any candidate children which have over or
@@ -223,121 +322,169 @@
 ;; of valid nodes. Otherwise, resolve the last link before the run and add it
 ;; to the pool to redistribute. Use the link after if the run includes the
 ;; first child.
-
-; TODO: what to do in the exceptional case that there is only a single child left?
-
-; Seems like it needs a 'zipper' phase to re-merge. Say a series of changes has
-; left us in this state with a tree with b=4. O is an unchanged node, * is a
-; candidate:
-; >      *
-;       / \
-;      /   \
-;     *     *
-;    /     / \
-;   *     *   O
-;   |    /|\  |\
-;   *   O * O O O
-
-; Now we've propagated up to the root, and we see that the left child is
-; underflowing, so merge it into the right child:
-;        *
-;        |
-;        |
-; >    .-*-.
-;     /  |  \
-;    *   *   O
-;    |  /|\  |\
-;    * O * O O O
-
-; The check continues recursively, and the new merged node checks its children.
-; Again, the left node needs to be merged in:
-;        *
-;        |
-;        |
-;        *.
-;       /  \
-; >   .*.   O
-;    // \\  |\
-;   * O * O O O
-
-; The final node is valid with 4 children, so we can start serializing the
-; children:
-;        *
-;        |
-;        |
-;        *.
-;       /  \
-;     .*.   O
-;    // \\  |\
-; > + O + O O O
-
-;        *
-;        |
-;        |
-;        *.
-;       /  \
-; >   .+.   O
-;    // \\  |\
-;   + O + O O O
-
-;        *
-;        |
-;        |
-; >      +.
-;       /  \
-;     .+.   O
-;    // \\  |\
-;   + O + O O O
-
-; If the upward recursion reaches a branch with a single child, we're on a path
-; up to the root, so return the subtree directly, decreasing the height of the
-; tree.
-; >      *
-;        |
-;        |
-;        +.
-;       /  \
-;     .+.   O
-;    // \\  |\
-;   + O + O O O
-
-; Resulting balanced tree:
-;        +.
-;       /  \
-;     .+.   O
-;    // \\  |\
-;   + O + O O O
-
-,,,
-
-
-;; ### Flush Candidates
-;;
-;; After the merge, the first child should have all fully valid candidates.
-;; Serialize the candidates and replace them with links. Update the child
-;; node's height and record count as needed. Flushing proceeds to each child
-;; node in turn until all candidates have been serialized and are valid nodes,
-;; completing that layer of the tree.
-
-;; ### Update Parent
-;;
-;; If the number of children is higher than the limit, the node has
-;; _overflowed_, and is split to return a sequence of valid nodes which are at
-;; least half full. Otherwise, a sequence containing a single updated node
-;; candidate is returned. If every child was deleted, an empty sequence is
-;; returned.
-;;
-;; Propagate the changes up to the top of the index tree, creating new layers
-;; as necessary to accommodate child splits.
-;;
-;; ...
-
-
-;; References
+;;
+;; Two underflowing partitions need to be merged, but still form an
+;; underflowing partition:
+;;
+;;              ..O..
+;;             /     \
+;;            /       \
+;;           O        .O.
+;;           |       / | \
+;;           *      O  O  O
+;;           |     /|\/|\/ \
+;;     >  [->U<-]  ####### #
+;;
+;;              ..O..
+;;             /     \
+;;            /       \
+;;           O        .O.
+;;           |       / | \
+;;     >    [U]     O  O  O
+;;                 /|\/|\/ \
+;;                 ####### #
+;;
+;;              ..O..
+;;             /     \
+;;            /       \
+;;     >    [U]       .O.
+;;                   / | \
+;;                  O  O  O
+;;                 /|\/|\/ \
+;;                 ####### #
+;;
+;;     >       [O](U)
+;;              |
+;;            ..O..
+;;           /  |  \
+;;          O   O   O
+;;         /|\ /|\ / \
+;;         ### ### # #
+;;
+;;              O
+;;              |
+;;     >      .[O]. (U)
+;;           /  |  \
+;;          O   O   O
+;;         /|\ /|\ / \
+;;         ### ### # #
+;;
+;;              O
+;;              |
+;;            ..O..
+;;           /  |  \
+;;     >   [O]  O   O
+;;        // \\/|\ / \
+;;        U# U@### # #
+;;
+;; Again, redistribute the records in the underflowing partitions.
+;;
+;;              O
+;;              |
+;;            ..O..
+;;           /  |  \
+;;          O   O   O
+;;         /|\ /|\ / \
+;;     >  [@@@]### # #
+;;
+;;              O
+;;              |
+;;            ..O..
+;;           /  |  \
+;;     >   [*]  O   O
+;;         /|\ /|\ / \
+;;         @@@ ### # #
+;;
+;;              O
+;;              |
+;;     >      .[*].
+;;           /  |  \
+;;          *   O   O
+;;         /|\ /|\ / \
+;;         @@@ ### # #
+;;
+;; Fast forward, deleting a subtree...
+;;
+;;              O
+;;              |
+;;            ..*..
+;;           /  |  \
+;;     >    *  [x]  O
+;;         /|\ /|\ / \
+;;     >   @@@[xxx]# #
+;;
+;; Insert nodes into right subtree, resulting in splitting multiple partitions.
+;;
+;;              O
+;;              |
+;;            ..*..
+;;           /     \
+;;          *       *
+;;         /|\    //|\\
+;;     >  @ @ @  [#@@@@]
+;;
+;; Back to distribution phase, fix overflow by splitting the node:
+;;              O
+;;              |
+;;            ..*..
+;;           /  |  \
+;;     >    *  [*   *]
+;;         /|\ / \ /|\
+;;         @@@ # @ @@@
+;;
+;;              *
+;;              |
+;;     >      .[*].
+;;           /  |  \
+;;          *   *   *
+;;         /|\ / \ /|\
+;;         @@@ # @ @@@
+;;
+;;     >       [*]
+;;              |
+;;            ..*..
+;;           /  |  \
+;;          *   *   *
+;;         /|\ / \ /|\
+;;         @@@ # @ @@@
+;;
+;; Now that we're back at the root, we can serialize out the resulting tree.
+;;
+;;              *
+;;              |
+;;     >      .[+].
+;;           /  |  \
+;;          +   +   +
+;;         /|\ / \ /|\
+;;         @@@ # @ @@@
+;;
+;; If the upward recursion reaches a branch with a single child, we're on a path
+;; up to the root, so return the subtree directly, decreasing the height of the
+;; tree.
+;;
+;;     >       [*]
+;;              |
+;;            ..+..
+;;           /  |  \
+;;          +   +   +
+;;         /|\ / \ /|\
+;;         @@@ # @ @@@
+;;
+;; Resulting balanced tree:
+;;
+;;            ..+..
+;;           /  |  \
+;;          +   +   +
+;;         /|\ / \ /|\
+;;         @@@ # @ @@@
+
+
+;; ### References
 ;;
 ;; https://pdfs.semanticscholar.org/85eb/4cf8dfd51708418881e2b5356d6778645a1a.pdf
 ;; Insight: instead of flushing all the updates, select a related subgroup that
 ;; minimizes repeated changes to the same node path.
+

 (defn build-index
   "Given a sequence of partitions, builds an index tree with the given


condition handling on triggered abilities

--- src/node/game/core.cljs
+++ src/node/game/core.cljs
@@ -179,9 +179,10 @@
 (defn trigger-event
   ([state side event] (trigger-event state side event nil))
   ([state side event target]
-     (doseq [e (get-in @state [:events event])]
+     (doseq [{:keys [ability] :as e} (get-in @state [:events event])]
        (when-let [card (get-card state (:card e))]
-         (resolve-ability state (to-keyword (:side card)) (:ability e) card [target])))))
+         (when (or (not (:req ability)) ((:req ability) state side card [target]))
+           (resolve-ability state (to-keyword (:side card)) ability card [target]))))))

 (defn add-prop [state side card key n]
   (update! state side (update-in card [key] #(+ % n)))


[ref] Use partial func for selector content.

--- src/ombs/view/event.clj
+++ src/ombs/view/event.clj
@@ -7,11 +7,16 @@
     [ombs.validate :refer [errors-string]]
     ))

-
-
 (def parts-row-sel [:#parts-row])
 (h/defsnippet parts-snip "../resources/public/parts.html" parts-row-sel [parts]
   [:.parts] (h/set-attr :value (str parts)))
+
+(defn pay-action [name date match]
+  (if (and
+        (core/participated? (sess/get :username) name date)
+        (core/is-active? name date) ) ; have debt
+    ((h/remove-attr :disabled "")  match)
+    ((h/set-attr :disabled "")     match)) )

 (def event-sel [:.event])
 (h/defsnippet event-elem "../resources/public/event.html" event-sel [{:keys [name price date author status parts]}]
@@ -32,13 +37,7 @@
                  ((h/remove-attr :disabled "")  match)
                  ((h/set-attr :disabled "")     match)))

-  [:.action.pay] (fn [match]
-                   (if (and
-                         (core/participated? (sess/get :username) name date)
-                         (not= (core/debt (sess/get :username) name date) 0.0)) ; have debt
-                     ((h/remove-attr :disabled "")  match)
-                     ((h/set-attr :disabled "")     match)))
-
+  [:.action.pay] (partial pay-action name date)
   [:.action.start] (fn [match]
                      ;(println (str "author: " author " name "name " date " date " initial? " (core/is-initial? name date) ))
                      (if (and (= author (sess/get :username))


new impl-free function

new impl-free function

--- src/logic/cnf.clj
+++ src/logic/cnf.clj
@@ -1,4 +1,32 @@
 (ns logic.util)
+
+; new try
+; TODO impl expands to macro (if (not A) true B) - should that be?
+
+(defn impl-free [formula]
+    (if (literal? formula)
+      formula
+
+		  (let [op (first formula)
+		        name-op (name op)]
+	      (if (or
+	            (= name-op "and")
+	            (= name-op "or")
+	            (= name-op "not"))
+	        (concat
+	          (list op)
+	            (for [elem (rest formula)]
+	              (impl-free elem)
+	            ))
+	          (let [expform (macroexpand-1 formula)]
+	            (concat
+	              (list (first expform))
+	              (for [elem (rest expform)]
+	                (impl-free elem)
+	              )))))))
+
+
+; old try

 (declare only-and-or-not)


Planck-side support for #245

Planck-side support for #245

--- planck-cljs/src/planck/repl.cljs
+++ planck-cljs/src/planck/repl.cljs
@@ -60,6 +60,10 @@
 (defonce ^:private st (cljs/empty-state))

 (defonce ^:private current-ns (atom 'cljs.user))
+
+(defn- current-alias-map
+  []
+  (get-in @st [:cljs.analyzer/namespaces @current-ns :requires]))

 (defn- all-ns
   "Returns a sequence of all namespaces."
@@ -150,7 +154,8 @@
   (binding [ana/*cljs-ns* @current-ns
             env/*compiler* st
             r/*data-readers* tags/*cljs-data-readers*
-            r/resolve-symbol ana/resolve-symbol]
+            r/resolve-symbol ana/resolve-symbol
+            r/*alias-map* (current-alias-map)]
     (let [reader (rt/string-push-back-reader source)]
       [(r/read {:read-cond :allow :features #{:cljs}} reader) (apply str (read-chars reader))])))

@@ -389,7 +394,8 @@
     (binding [ana/*cljs-ns* @current-ns
               env/*compiler* st
               r/*data-readers* tags/*cljs-data-readers*
-              r/resolve-symbol ana/resolve-symbol]
+              r/resolve-symbol ana/resolve-symbol
+              r/*alias-map* (current-alias-map)]
       (try
         (r/read {:eof (js-obj) :read-cond :allow :features #{:cljs}} rdr)
         (nil? (rt/peek-char rdr))


Remove TODO about &rest syntax for cmds

Remove TODO about &rest syntax for cmds

On second thought, I don't think this is that critical.
It might make sense to have it, but commands aren't
as general as functions, and I can't immediately
think of a need for it.

It could also be implemented as an argtype if someone
really wanted it (but really didn't want to contribute
to rainboots core, for whatever reason)

--- src/rainboots/parse.clj
+++ src/rainboots/parse.clj
@@ -32,7 +32,6 @@
   arguments as appropriate with any annotated
   argtypes. See (defargtype)"
   [cmd-fn cli raw-args]
-  ;; TODO support [cli &rest] input, for example
   (let [arg-types (:arg-types (meta cmd-fn))]
     (if (empty? arg-types)
       ;; easy case


More work on layouts explanation

More work on layouts explanation

--- src/re_demo/layout.cljs
+++ src/re_demo/layout.cljs
@@ -12,7 +12,8 @@
                 components are not themselves visible."]
                [:p "Instead, their purpose is to arrange other components."]
                [title :level :level2 :label "Two Boxes"]
-               [:p "The two key components are h-box and v-box, which lay out their children horizontally and vertically respectively."]
+               [:p "The two key components are " [:span.bold "h-box"] " and " [:span.bold "v-box"] ", which lay out
+               their children horizontally and vertically respectively."]
                [:p "They are mutually composable, and can be arbitrarily nested to create complex arrangements."]
                [:p "Here's example code showing them nesting..."]]
                [:pre
@@ -44,59 +45,52 @@
    :children [[title :level :level2 :label "Flexbox"]
               [paragraphs
                [:p
-                "Re-com's layout model is a thin layer over "
-                [hyperlink-href
-                 :label "CSS Flexbox"
-                 :href "https://css-tricks.com/snippets/css/a-guide-to-flexbox"
-                 :target "_blank"]
-                "."]
+                "Re-com's layout model is a thin layer over " [:span.bold "CSS Flexbox"] "."]
                [:p "While Re-com's components work in their own right, to understand them fully and use them
-               powerfully, you need to have a strong understanding of Flexbox. Do the tutorials soon."]
-               [:p "Flexbox works via the interplay between container (parent) attributes and item (child)
-               attributes. Intermediate DOM nodes almost always play the role of both parent and child."]
-               [:p.bold "Inserting block-level elements (divs?) into this tree
-               can break this interplay and can cause a variety of layout issues."]
-               [:p "As a result, we have found Flexbox use to be quite viral. Once you start using it, you seem to
-               have to use it everywhere. The use of non-dispay-flex elements causes problems.
-               So, we recommend you go 100% all-in on the re-com h-box/v-box layout method.
-               If you do, everything should \"just work\". But, if instead you mix block-level and flex, you
-               may spend alot of time playing layout whack-a-mole."]
-               [:p.bold "Just to be clear: when using re-com, don't create [:divs].
-               Always use an h-box or a v-box, or one of the widget components, like label."]
+               powerfully, you " [:span.bold "will"] " need to have a strong understanding of Flexbox. Do these tutorials very soon:"]
+               [:ul
+                [:li
+                 [hyperlink-href
+                  :label "CSS-Tricks guide to flexbox"
+                  :href "https://css-tricks.com/snippets/css/a-guide-to-flexbox"
+                  :target "_blank"]]
+                [:li
+                 [hyperlink-href
+                  :label "The Ultimate Flexbox Cheat Sheet"
+                  :href "http://www.sketchingwithcss.com/samplechapter/cheatsheet.html"
+                  :target "_blank"]]]
+
+               [:p "Flexbox works via the interplay of " [:span.bold "container"] " (parent) styles and " [:span.bold "item"] " (child)
+               styles. In any interesting layout, intermediate DOM elements play the role of both container and item."]
+               [:p "But, if block-level elements (divs?) are inserted, they
+               can break the flex style interplay up and down the DOM hierarchy, and cause a variety of layout issues."]
+               [:p [:span.bold "As a result, we have found Flexbox use to be quite viral."] " Once you start using it, you
+               have to use it everywhere - right up and down the DOM tree."]
+               [:p "So, we recommend you go 100% all-in on using h-box and v-box. If you do, everything should \"just work\"."]
+               [:p [:span.bold "Never use a naked [:div]"] " or you may spend alot of time playing layout whack-a-mole (unless
+                you adroitly apply the correct flexbox styles to that :div, which is ardulous and error prone.)"]
+
                ;[gap :size "10px"]
                [title :level :level2 :label "Non-Flex Leaf"]
                [:p "When we first put bootstrap buttons into re-com, they were streched.
-               It turned out their block display didn't play well with flex containers."]
-               [:p "So we created the box component which allows you to wrap
+               It turned out their block display didn't play well with flex containers like h-box."]
+               [:p "So we created the " [:span.bold "[box ...]"] "  component which allows you to wrap
                non-flex leaf widgets for use in flex containers."]
                ]]])

-(defn flex-property
+
+(defn size-table
   []
   (let [col1 "50px"
         col2 "130px"
         col3 "500px"]
     [v-box
-     :children [[title :level :level2 :label "Understanding Flexbox"]
+     :children [[title :level :level2 :label ":size"]
                 [paragraphs
-                 [:p "In the Flexbox world, there are two parties in any layout - " [:span.bold "containers"] " and their child " [:span.bold "items"] "."]
-                 [:p "There are many good tutorials giving an overview of Flexbox. We've found that the key to getting a Flexbox layout right is understanding the way that child flex styles interact."]
-                 [:p "We use :size in place of :flex."]
-                 ]
-                [title :level :level2 :label "The :size Parameter"]
-                [paragraphs
-                 [:p "Any flex style resolves to a triple of sub-values:"]
-                 [:ul
-                  [:li [:span [:span.bold "grow"]   " - Integer ratio (used with other siblings) which determines how an item grows in size if there is extra container space to distribute. 0 for no growing."]]
-                  [:li [:span [:span.bold "shrink"] " - Integer ratio (used with other siblings) which determines how an item shrinks in size if container space is reduced. 0 for no shrinking."]]
-                  [:li [:span [:span.bold "basis"]  " - The default size of an item before any necessary growing or shrinking. Can be any values like 60%, 100px, auto"]]]
-                 [:li [:span [:span.bold "Note:"]  " A basis value of \"auto\" will cause the initial size to be calculated to take up as much space as possible, in conjunction with it's siblings :flex settings."]]
-                 [:p "Even a single flex style value such as auto is transformed into the triple " [:span.bold "1 1 auto"] "."]
-                 [:p "Determines the value for the 'flex' attribute (which has grow, shrink and basis), based on the :size parameter."]
-                 [:p.bold "IMPORTANT: The term 'size' means width of the item in the case of flex-direction 'row' OR height of the item in the case of flex-direction 'column'."]
-                 [:p "Flex property explanation:"]
-                 ]
-                [:p "Supported values:"]
+                 [:p "v-box and h-box have a parameter called :size. This mirrors the flex sytle. In the Flexbox world, there are two parties in any layout - " [:span.bold "containers"] " and their child " [:span.bold "items"] "."]
+                 [:p "While the tutorials walk you through the menagerie of flexbox related styles, we've found that one style more than any other is
+                 critical to getting what you want: the " [:span.bold "flex"] " style of the children."]
+                 [:p "When we're having a bad day with layouts, 90% of the time it is because we didn't get the flex style of a child right."]]
                 [v-box
                  :class "rc-div-table"
                  :align-self :start
@@ -146,15 +140,45 @@
                              :class "rc-div-table-row"
                              :children [[label :width col1 :label "g s b"]
                                         [label :width col2 :label "grow shrink basis"]
-                                        [:span {:style {:width col3}} "If none of the above common valaues above meet your needs, this gives you precise control."]]]
-
-                            ]]
-                [gap :size "20px"]
-                [paragraphs
-                 [:p "If number of words is not 1 or 3, an exception is thrown."]
-                 [:p "Reference: http://www.w3.org/TR/css3-flexbox/#flexibility"]
-                 [:p "Diagram:   http://www.w3.org/TR/css3-flexbox/#flex-container"]
-                 [:p "Regex101 testing: ^(initial|auto|none)|(\\d+)(px|%|em)|(\\d+)\\w(\\d+)\\w(.*) - remove double backslashes"]]]]))
+                                        [:span {:style {:width col3}} "If none of the above common valaues above meet your needs, this gives you precise control."]]]]]]]))
+
+
+(defn the-key-style
+  []
+  [v-box
+   :children [[title :level :level2 :label "The Key Style"]
+              [paragraphs
+               [:p "In the Flexbox world, there are two parties in any layout - " [:span.bold "containers"] " and their child " [:span.bold "items"] "."]
+               [:p "While the tutorials walk you through the menagerie of flexbox related styles, we've found that one style more than any other is
+                 critical to getting what you want: the " [:span.bold "flex"] " style of the children."]
+               [:p "When we're having a bad day with layouts, 90% of the time it is because we didn't get the flex style of a child right."]]
+              [title :level :level2 :label "flex=??"]
+              [paragraphs
+               [:p "The tutorials tell you that the " [:span.bold "flex"] " style can be single values like " [:span.bold "none"] "  and " [:span.bold "auto"] ".
+                 But realise that every single time, flex resolves to a triple of sub-values:"]
+               [:ul
+                [:li [:span [:span.bold "grow"]   " - Integer which determines how an item grows in size if there is extra container space to distribute. 0 for no growing."]]
+                [:li [:span [:span.bold "shrink"] " - Integer which determines how an item shrinks in size if container space is reduced. 0 for no shrinking."]]
+                [:li [:span [:span.bold "basis"]  " - The default size of an item before any necessary growing or shrinking. Can be
+                  values like 60% or 100px. It can also be " [:span.bold "auto"] " which means to use the natural size of the item (or its further children)"]]]
+
+               [:p "So even if you provide a single value like " [:span.bold "flex=\"none\""]  ",
+                 what you are really providing is " [:span.bold "flex=\"0 0 auto\""] ". It always resolves to a triple. If you get this point, you are half way there."]
+
+               [:p "Confusion reigns a bit because you can do this " [:span.bold "flex=\"auto\""]  ",
+                 which resolves to " [:span.bold "flex=\"1 1 auto\""] ".  Eh?  My \"auto\" begates another \"auto\". Breeding like rabits."]
+               [:p "Determines the value for the 'flex' attribute (which has grow, shrink and basis), based on the :size parameter."]
+               [:p.bold "IMPORTANT: The term 'size' means width of the item in the case of flex-direction 'row' OR height of the item in the case of flex-direction 'column'."]
+               [:p "Flex property explanation:"]
+               ]
+              [:p "Supported values:"]
+
+              [gap :size "20px"]
+              [paragraphs
+               [:p "If number of words is not 1 or 3, an exception is thrown."]
+               [:p "Reference: http://www.w3.org/TR/css3-flexbox/#flexibility"]
+               [:p "Diagram:   http://www.w3.org/TR/css3-flexbox/#flex-container"]
+                 [:p "Regex101 testing: ^(initial|auto|none)|(\\d+)(px|%|em)|(\\d+)\\w(\\d+)\\w(.*) - remove double backslashes"]]]])

@@ -168,7 +192,11 @@
                :children [[left-column]
                           [right-column]]]
               [line]
-              [flex-property]]])
+              [h-box
+               :gap      "100px"
+               :children [[the-key-style]
+                          [size-table]]]
+              ]])

 ;; core holds a reference to panel, so need one level of indirection to get figwheel updates
 (defn panel


factor of 10 improvement copying an array to a nio buffer. (#16)

--- compute/src/think/compute/datatype.clj
+++ compute/src/think/compute/datatype.clj
@@ -137,11 +137,10 @@
   [item item-offset dest dest-offset elem-count]
   `(let [~item-offset (long ~item-offset)
          ~dest-offset (long ~dest-offset)
-         ~elem-count (long ~elem-count)]
-     (loop [~'idx 0]
-       (when (< ~'idx ~elem-count)
-         (.put ~dest (+ ~'idx ~dest-offset) (aget ~item (+ ~'idx ~item-offset)))
-         (recur (inc ~'idx))))
+         ~elem-count (long ~elem-count)
+         dest# (.duplicate ~dest)]
+     (.position dest# ~dest-offset)
+     (.put dest# ~item ~item-offset ~elem-count)
      ~dest))

 (defmacro set-array-constant-impl


pretty print timestamp in table view

pretty print timestamp in table view

--- app/views/viz/table.cljs
+++ app/views/viz/table.cljs
@@ -39,7 +39,7 @@
        [:td (:y d)]
        [:td (:z d)]
        [:td (:pressure d)]
-       [:td (:timestamp d)]
+       [:td (.format (js/moment (:timestamp d)) "HH:mm:ss:SSS")]
        [:td (if (:is_new d)
               (/ (- (.getTime (js/Date.)) (:timestamp d)) 1000)
               "N/A")]]))))


pascal row refacto

pascal row refacto

--- src/sandbox/fractal.clj
+++ src/sandbox/fractal.clj
@@ -4,27 +4,8 @@
            (java.io File)))

 (defn pascal-row-step [yield pascal-row]
-;;=> pascal-row is the one above the row we're computing
-  {:pre [(> (get  pascal-row 0) 0)]}  ;=> We can only start from [1]!
-  (let [cnt-elts (count pascal-row)
-        half-row (subvec pascal-row 0 (inc (double (/ cnt-elts 2))))
-;;=> We compute half the above row
-        padded-half-row (into [0] half-row)
-;;=> and add a 0 to the beginning, as we'll use it in computation
-        half-step (vec (map (comp (partial apply yield) vec)
-                            (partition 2 1 padded-half-row)))
-;;=> we compute the first half, summing the above element
-;;      and the element at the left of the above one.
-        other-half-step (vec (if (even? cnt-elts)
-                               (-> half-step
-                                   butlast
-                                   reverse)
-                               (-> half-step
-                                   reverse)))]
-;;=> the mirror of the half we computed. If count elements is
-;; even, we omit the last element from half-step.
-    (into half-step other-half-step)))
-;;=> we return half to which we append the mirror copy.
+  {:pre [(> (get  pascal-row 0) 0)]}
+  (vec (map #(apply yield %) (partition 2 1 (concat [0] pascal-row [0])))))

 (defn pascal-rows [yield row-number]
   (loop [nb 0


Fix domain/main/apps back to domain/main/app-db

--- src/trustroots/domain/main.cljs
+++ src/trustroots/domain/main.cljs
@@ -27,7 +27,7 @@
           }))

 ;; initial state of app-db
-(def apps
+(def app-db
   (merge auth/app-db
          {
           :services {:toaster nil}


Update composite Leiningen profiles

Update composite Leiningen profiles

This renames the :awesome profile to :terminal, which makes more sense
now that we also have an :emacs profile. It also removes the :refactor
profile from the :emacs profile, because refactor-nrepl should be
injected automatically and it can/does cause problems when you're not
in a project.

--- profiles.clj
+++ profiles.clj
@@ -157,21 +157,18 @@

  ;;; Composite profiles

- ;; This profile includes useful profiles for a REPL in the terminal.
- :awesome [:cljs
-           :humane
-           :inject
-           :alembic
-           :pretty
-           :refactor
-           :refresh]
+ ;; This profile is for use at the terminal.
+ :terminal [:cljs
+            :humane
+            :inject
+            :alembic
+            :pretty
+            :refresh]

- ;; This profile is for use with Emacs.
- :emacs [:cider
-         :cljs
+ ;; This profile is for use from Emacs.
+ :emacs [:cljs
          :inject
-         :alembic
-         :refactor]
+         :alembic]

  ;; When running tests.
  :test [:humane]


remove warnings for using size and width in setup - these should be allowed. If they have inconsistent behaviour, this is a bug.

--- src/quil/core.clj
+++ src/quil/core.clj
@@ -1709,8 +1709,7 @@
     :added "1.0"}
   height
   "Height of the display window. The value of height is zero until
-  size is called.  Should not be called during setup because result may be incosistent:
-  https://github.com/quil/quil/issues/50"
+  size is called."
   []
   (.getHeight (current-applet)))

@@ -4110,8 +4109,7 @@
     :added "1.0"}
   width
   "Width of the display window. The value of width is zero until size is
-  called. Should not be called during setup because result may be incosistent:
-  https://github.com/quil/quil/issues/50"
+  called."
   []
   (.getWidth (current-applet)))


fix registration bug

fix registration bug

--- src/room/core.clj
+++ src/room/core.clj
@@ -73,6 +73,7 @@
         (javascript-tag
          (str "messages = " (json/write-str messages :value-fn timestamp-to-string)
               ";\nuser = " (json/write-str user)
+              ";\nsession = " (json/write-str (:session req))
               ";\ntopics = " (json/write-str (get-topics {:param 42}
                                                          {:row-fn (fn [row]
                                                                     (update-in row [:users] #(seq (.getArray %))))}))
@@ -84,6 +85,7 @@
     (html5
      [:div
       [:a {:href "/login"} "login"]
+      [:br]
       [:a {:href "/register"} "register"]]
      [:div {:id "app"}])))

@@ -145,7 +147,9 @@
                               :password password
                               :name username})
          session (-> (:session request)
-                     (assoc :email email))]
+                     (assoc :email email)
+                     (assoc :identity (:id user))
+                     (assoc :name (:name user)))]
      (-> (redirect (get-in request [:query-params :next] "/"))
          (assoc :session (assoc session :identity (:id user) :email (:email user)))))))


fix for postgres test.

fix for postgres test.

--- test/metabase/driver/postgres_test.clj
+++ test/metabase/driver/postgres_test.clj
@@ -59,8 +59,8 @@
     [#uuid "84ed434e-80b4-41cf-9c88-e334427104ae"]]])

 ;; Check that we can load a Postgres Database with a :UUIDField
-(expect {:cols    [{:description nil, :base_type :IntegerField, :name "id", :display_name "Id", :special_type :id, :target nil, :extra_info {}}
-                   {:description nil, :base_type :UUIDField, :name "user_id", :display_name "User Id", :special_type :category, :target nil, :extra_info {}}],
+(expect {:cols    [{:description nil, :base_type :IntegerField, :name "id", :display_name "Id", :preview_display true, :special_type :id, :target nil, :extra_info {}}
+                   {:description nil, :base_type :UUIDField, :name "user_id", :display_name "User Id", :preview_display true, :special_type :category, :target nil, :extra_info {}}],
          :columns ["id" "user_id"],
          :rows    [[1 #uuid "4f01dcfd-13f7-430c-8e6f-e505c0851027"]
                    [2 #uuid "4652b2e7-d940-4d55-a971-7e484566663e"]


Child components are ignored generating morphs

--- test-cljs/frontend/test_gencard.cljs
+++ test-cljs/frontend/test_gencard.cljs
@@ -3,6 +3,7 @@
             [clojure.spec :as s :include-macros true]
             [clojure.test.check.generators :as gen]
             [goog.object :as gobject]
+            [om.core :as om]
             [om.next :as om-next :refer-macros [defui]])
   (:require-macros
    [devcards.core :as dc :refer [defcard deftest]]
@@ -15,7 +16,8 @@
   (let [props (gobject/get element "props")
         children (->> (gobject/get props "children")
                       js/React.Children.toArray
-                      (filter js/React.isValidElement))]
+                      (filter js/React.isValidElement)
+                      (filter #(string? (gobject/get % "type"))))]
     (into {:type (gobject/get element "type")
            :children (map signature children)}
           (map (juxt identity (partial gobject/get props))
@@ -66,19 +68,38 @@

 (s/def ::description (s/and string? seq))

+
+(defn demo-child-component-om-prev [{:keys [description]} owner]
+  (reify
+    om/IRender
+    (render [_]
+      (html [:div description]))))
+
+(defui DemoChildComponentOmNext
+  Object
+  (render [this]
+    (let [{:keys [description]} (om-next/props this)]
+      (html [:div description]))))
+
+(def demo-child-component-om-next (om-next/factory DemoChildComponentOmNext))
+
 (defn demo-component [props]
   (html
    [:div {:class (name (::type props))}
     [:.description
      (when (< 2 (count (::description props))) {:class "long"})
-     (::description props)]]))
+     (::description props)

-(defui DemoComponentOm
+     ;; Child components should be ignored.
+     (om/build demo-child-component-om-prev {:description (::description props)})
+     (demo-child-component-om-next {:description (::description props)})]]))
+
+(defui DemoComponentOmNext
   Object
   (render [this]
     (demo-component (om-next/props this))))

-(def demo-component-om (om-next/factory DemoComponentOm))
+(def demo-component-om-next (om-next/factory DemoComponentOmNext))

 (deftest morph-data-test
   (testing "Generates one set of data for each morph of a functional component"
@@ -91,7 +112,7 @@

   (testing "Generates one set of data for each morph of an Om component"
     (dotimes [_ 10]
-      (let [data (morph-data demo-component-om ::data-for-component)]
+      (let [data (morph-data demo-component-om-next ::data-for-component)]
         (is (not (nil? data)))
         (is (every? (partial s/valid? ::data-for-component) data))
         (is (= 4 (count data)))


disabled newrelic test until we can figure out how

to get it a license key for circleci

--- test/onyx/metrics/new_relic_test.clj
+++ test/onyx/metrics/new_relic_test.clj
@@ -21,8 +21,8 @@

 (def failure? (atom false))

-(deftest metrics-test
-  (doseq [sender [:onyx.metrics.newrelic/newrelic-sender]]
+#_(deftest metrics-test
+  (doseq [sender [:onyx.metrics.newrelic/newrelic-sender]]

     (def in-chan (chan (inc n-messages)))

@@ -40,10 +40,10 @@
     (def out-calls
       {:lifecycle/before-task-start inject-out-ch})

-    (let [events-atom (atom [])]
-      (with-redefs [onyx.metrics.newrelic/warn-failure (fn [a b]
+    (let [events-atom (atom [])]
+      (with-redefs [onyx.metrics.newrelic/warn-failure (fn [a b]
                                                          (println "Failed request " a b)
-                                                         (reset! failure? true))]
+                                                         (reset! failure? true))]
         (let [id (java.util.UUID/randomUUID)
               env-config {:zookeeper/address "127.0.0.1:2188"
                           :zookeeper/server? true
@@ -103,11 +103,11 @@
                   _ (close! in-chan)
                   start-time (System/currentTimeMillis)
                   job (onyx.api/submit-job peer-config
-                                         {:catalog catalog
-                                          :metadata {:name "test-workflow"}
-                                          :workflow workflow
-                                          :lifecycles lifecycles
-                                          :task-scheduler :onyx.task-scheduler/balanced})
+                                           {:catalog catalog
+                                            :metadata {:name "test-workflow"}
+                                            :workflow workflow
+                                            :lifecycles lifecycles
+                                            :task-scheduler :onyx.task-scheduler/balanced})
                   results (take-segments! out-chan)
                   _ (onyx.api/await-job-completion peer-config (:job-id job))]
               (is (not @failure?)))))))))


fix server

fix server

--- src/ci3/controller.clj
+++ src/ci3/controller.clj
@@ -66,6 +66,8 @@
     (reset! stop false)
     (watch-resource :repositories process-repository)))

+(defn watch []
+  (watch-resource :build process-build))

 (comment


Move watch-page-changes func on top of history ns.

--- src/uxbox/main/data/history.cljs
+++ src/uxbox/main/data/history.cljs
@@ -21,75 +21,10 @@
                                      replace-by-id
                                      index-by)]))

-;; --- Pinned Page History Fetched
-
-(declare update-history-index)
-
-(defrecord PinnedPageHistoryFetched [history]
-  rs/UpdateEvent
-  (-apply-update [_ state]
-    (-> state
-        (assoc-in [:workspace :history :pinned-items] (mapv :version history))
-        (update-history-index history true))))
-
-;; --- Fetch Pinned Page History
-
-(defrecord FetchPinnedPageHistory [id]
-  rs/WatchEvent
-  (-apply-watch [_ state s]
-    (letfn [(on-success [{history :payload}]
-              (->PinnedPageHistoryFetched (into [] history)))]
-      (let [params {:page id :pinned true}]
-        (->> (rp/req :fetch/page-history params)
-             (rx/map on-success))))))
-
-(defn fetch-pinned-page-history
-  [id]
-  (->FetchPinnedPageHistory id))
-
-;; --- Page History Fetched
-
-(defrecord PageHistoryFetched [history append?]
-  rs/UpdateEvent
-  (-apply-update [_ state]
-    (letfn [(update-counters [state items]
-              (-> (assoc state :min-version (apply min items))
-                  (assoc :max-version (apply max items))))
-
-            (update-lists [state items]
-              (if append?
-                (update state :items #(reduce conj %1 items))
-                (assoc state :items items)))]
-
-      (let [items (mapv :version history)
-            hstate (-> (get-in state [:workspace :history] {})
-                       (update-counters items)
-                       (update-lists items))]
-        (-> state
-            (assoc-in [:workspace :history] hstate)
-            (update-history-index history append?))))))
-
-;; --- Fetch Page History
-
-(defrecord FetchPageHistory [id since max]
-  rs/WatchEvent
-  (-apply-watch [_ state s]
-    (letfn [(on-success [{history :payload}]
-              (let [history (into [] history)]
-                (->PageHistoryFetched history (not (nil? since)))))]
-      (let [params (merge
-                    {:page id :max (or max 15)}
-                    (when since {:since since}))]
-        (->> (rp/req :fetch/page-history params)
-             (rx/map on-success))))))
-
-(defn fetch-page-history
-  ([id]
-   (fetch-page-history id nil))
-  ([id params]
-   (map->FetchPageHistory (assoc params :id id))))
-
 ;; --- Watch Page Changes
+
+(declare fetch-page-history)
+(declare fetch-pinned-page-history)

 (defn watch-page-changes
   "A function that starts watching for `IPageUpdate`
@@ -106,6 +41,74 @@
       (rx/map (comp :id :page) $)
       (rx/on-value $ on-value))))

+;; --- Pinned Page History Fetched
+
+(declare update-history-index)
+
+(defrecord PinnedPageHistoryFetched [history]
+  rs/UpdateEvent
+  (-apply-update [_ state]
+    (-> state
+        (assoc-in [:workspace :history :pinned-items] (mapv :version history))
+        (update-history-index history true))))
+
+;; --- Fetch Pinned Page History
+
+(defrecord FetchPinnedPageHistory [id]
+  rs/WatchEvent
+  (-apply-watch [_ state s]
+    (letfn [(on-success [{history :payload}]
+              (->PinnedPageHistoryFetched (into [] history)))]
+      (let [params {:page id :pinned true}]
+        (->> (rp/req :fetch/page-history params)
+             (rx/map on-success))))))
+
+(defn fetch-pinned-page-history
+  [id]
+  (->FetchPinnedPageHistory id))
+
+;; --- Page History Fetched
+
+(defrecord PageHistoryFetched [history append?]
+  rs/UpdateEvent
+  (-apply-update [_ state]
+    (letfn [(update-counters [state items]
+              (-> (assoc state :min-version (apply min items))
+                  (assoc :max-version (apply max items))))
+
+            (update-lists [state items]
+              (if append?
+                (update state :items #(reduce conj %1 items))
+                (assoc state :items items)))]
+
+      (let [items (mapv :version history)
+            hstate (-> (get-in state [:workspace :history] {})
+                       (update-counters items)
+                       (update-lists items))]
+        (-> state
+            (assoc-in [:workspace :history] hstate)
+            (update-history-index history append?))))))
+
+;; --- Fetch Page History
+
+(defrecord FetchPageHistory [id since max]
+  rs/WatchEvent
+  (-apply-watch [_ state s]
+    (letfn [(on-success [{history :payload}]
+              (let [history (into [] history)]
+                (->PageHistoryFetched history (not (nil? since)))))]
+      (let [params (merge
+                    {:page id :max (or max 15)}
+                    (when since {:since since}))]
+        (->> (rp/req :fetch/page-history params)
+             (rx/map on-success))))))
+
+(defn fetch-page-history
+  ([id]
+   (fetch-page-history id nil))
+  ([id params]
+   (map->FetchPageHistory (assoc params :id id))))
+
 ;; --- Select Page History

 (defrecord SelectPageHistory [version]


Minor improvements on grid render code.

Minor improvements on grid render code.

--- src/uxbox/ui/workspace/grid.cljs
+++ src/uxbox/ui/workspace/grid.cljs
@@ -14,7 +14,6 @@

 ;; --- Grid (Component)

-(declare ticks-range)
 (declare vertical-line)
 (declare horizontal-line)

@@ -24,8 +23,14 @@
         color (:grid/color options "#cccccc")
         width wb/viewport-width
         height wb/viewport-height
-        x-ticks (ticks-range width (:grid/x-axis options 10))
-        y-ticks (ticks-range height (:grid/y-axis options 10))
+        x-ticks (range (- 0 wb/canvas-start-x)
+                       (- width wb/canvas-start-x)
+                       (:grid/x-axis options 10))
+
+        y-ticks (range (- 0 wb/canvas-start-x)
+                       (- height wb/canvas-start-x)
+                       (:grid/y-axis options 10))
+
         path (as-> [] $
                (reduce (partial vertical-line height) $ x-ticks)
                (reduce (partial horizontal-line width) $ y-ticks))]
@@ -50,9 +55,3 @@
   [height acc value]
   (let [pos (+ value wb/canvas-start-y)]
     (conj acc (str/format "M %s %s L %s %s" pos 0 pos height))))
-
-(defn- ticks-range
-  [size step]
-  (range (- 0 wb/canvas-start-y)
-         (- size wb/canvas-start-y)
-         step))


When indexing, leave the newest non-snapshot in the index.

Fixes #121.

--- src/clojars/search.clj
+++ src/clojars/search.clj
@@ -38,14 +38,23 @@
               :at (.lastModified pom-file)
               :_content content)]
     (binding [clucy/*analyzer* analyzer]
-      (clucy/search-and-delete index (format "artifact-id:%s AND group-id:%s"
-                                             (:artifact-id pom)
-                                             (:group-id pom)))
-      (clucy/add index (with-meta doc field-settings)))))
+      (let [[old] (clucy/search index (format "artifact-id:%s AND group-id:%s"
+                                              (:artifact-id pom)
+                                              (:group-id pom)) 1)]
+        (if old
+          (when (and (< (Long. (:at old)) (:at doc))
+                     (not (re-find #"-SNAPSHOT$" (:version doc))))
+            (clucy/search-and-delete index (format "artifact-id:%s AND group-id:%s"
+                                                   (:artifact-id doc)
+                                                   (:group-id doc)))
+            (clucy/add index (with-meta doc field-settings)))
+          (clucy/add index (with-meta doc field-settings)))))))

 (defn index-repo [root]
   (let [indexed (atom 0)]
     (with-open [index (clucy/disk-index (config :index-path))]
+      ;; searching with an empty index creates an exception
+      (clucy/add index {:dummy true})
       (doseq [file (file-seq (io/file root))
               :when (.endsWith (str file) ".pom")]
         (swap! indexed inc)
@@ -54,7 +63,8 @@
         (try
           (index-pom index file)
           (catch Exception e
-            (println "Failed to index" file " - " (.getMessage e))))))))
+              (println "Failed to index" file " - " (.getMessage e)))))
+      (clucy/search-and-delete index "dummy:true"))))

 (defn search [query]
   (if (empty? query)


expand try/catch block in caching code

expand try/catch block in caching code

a failed cache write is never critical and shouldn't break
the rest of the compilation. invalid specs in the registry
were causing cryptic errors.

--- src/main/shadow/build/compiler.clj
+++ src/main/shadow/build/compiler.clj
@@ -689,75 +689,73 @@
         nil)

     :else
-    (let [cache-file
-          (get-cache-file-for-rc state rc)
-
-          cache-compiler-options
-          (reduce
-            (fn [cache-options option-path]
-              (assoc cache-options option-path (get-in state option-path)))
-            {}
-            cache-affecting-options)
-
-          ns-str
-          (str ns)
-
-          spec-filter-fn
-          (fn [v]
-            (or (= ns-str (namespace v))
-                (= ns (-> v meta :fdef-ns))))
-
-          ns-specs
-          (reduce-kv
-            (fn [m k v]
-              (if-not (spec-filter-fn k)
-                m
-                (assoc m k v)))
-            {}
-            ;; this is {spec-kw|sym raw-spec-form}
-            @cljs-spec/registry-ref)
-
-          ;; this is a #{fqn-var-sym ...}
-          ns-speced-vars
-          (->> (cljs-spec/speced-vars)
-               (filter spec-filter-fn)
-               (into []))
-
-          ana-data
-          (get-in @env/*compiler* [::ana/namespaces ns])
-
-          cache-data
-          {:output output
-           :cache-keys (make-cache-key-map state rc)
-           :clj-info (macros/gather-clj-info state rc)
-           :analyzer ana-data
-           :ns ns
-           :ns-specs ns-specs
-           :ns-speced-vars ns-speced-vars
-           :compiler-options cache-compiler-options}]
-
-      ;; FIXME: the write can still happen before flush
-      ;; should maybe safe this data somewhere instead and actually flush in flush
-      (try
-        (util/with-logged-time
-          [state {:type :cache-write
-                  :resource-id resource-id
-                  :resource-name resource-name
-                  :ns ns}]
-
+    (try
+      (util/with-logged-time
+        [state {:type :cache-write
+                :resource-id resource-id
+                :resource-name resource-name
+                :ns ns}]
+
+        (let [cache-file
+              (get-cache-file-for-rc state rc)
+
+              cache-compiler-options
+              (reduce
+                (fn [cache-options option-path]
+                  (assoc cache-options option-path (get-in state option-path)))
+                {}
+                cache-affecting-options)
+
+              ns-str
+              (str ns)
+
+              spec-filter-fn
+              (fn [v]
+                (or (= ns-str (namespace v))
+                    (= ns (-> v meta :fdef-ns))))
+
+              ns-specs
+              (reduce-kv
+                (fn [m k v]
+                  (if-not (spec-filter-fn k)
+                    m
+                    (assoc m k v)))
+                {}
+                ;; this is {spec-kw|sym raw-spec-form}
+                @cljs-spec/registry-ref)
+
+              ;; this is a #{fqn-var-sym ...}
+              ns-spec-vars
+              (->> (cljs-spec/speced-vars)
+                   (filter spec-filter-fn)
+                   (into []))
+
+              ana-data
+              (get-in @env/*compiler* [::ana/namespaces ns])
+
+              cache-data
+              {:output output
+               :cache-keys (make-cache-key-map state rc)
+               :clj-info (macros/gather-clj-info state rc)
+               :analyzer ana-data
+               :ns ns
+               :ns-specs ns-specs
+               :ns-spec-vars ns-spec-vars
+               :compiler-options cache-compiler-options}]
+
+          ;; FIXME: the write can still happen before flush
+          ;; should maybe safe this data somewhere instead and actually flush in flush
           ;; FIXME: is this thread-safe?
           (io/make-parents cache-file)
-
-          (cache/write-file cache-file cache-data))
-
-        (catch Exception e
-          (util/warn state {:type :cache-error
-                            :action :write
-                            :ns ns
-                            :id resource-id
-                            :error e})
-          nil)
-        ))))
+          (cache/write-file cache-file cache-data)))
+
+      (catch Exception e
+        (util/warn state {:type :cache-error
+                          :action :write
+                          :ns ns
+                          :id resource-id
+                          :error e})
+        nil))))

 (defn maybe-compile-cljs
   "take current state and cljs resource to compile


Use into rather than concat in conj super

--- src/onyx/windowing/aggregation.clj
+++ src/onyx/windowing/aggregation.clj
@@ -52,7 +52,7 @@
     {:n n :sum sum :average (/ sum n)}))

 (defn conj-super-aggregation [state-1 state-2 window]
-  (concat state-1 state-2))
+  (into state-1 state-2))

 (defn sum-super-aggregation [state-1 state-2 window]
   (+ state-1 state-2))


whitespace

whitespace

--- src/open_company/api/companies.clj
+++ src/open_company/api/companies.clj
@@ -78,8 +78,8 @@
     [true {}])) ; it will fail later on :exists?

 (defn processable-post-req? [conn {:keys [user data]}]
-  (let [company (company/->company data user (find-slug conn data))
-        invalid? (schema/check common-res/Company company)
+  (let [company     (company/->company data user (find-slug conn data))
+        invalid?    (schema/check common-res/Company company)
         slug-taken? (not (company/slug-available? conn (:slug company)))]
     (cond
       invalid? [false {:reason invalid?}]


Fix coords

Fix coords


Egoeraren gorputza itzuli

Egoeraren gorputza itzuli


[#242] Project definition clean up

[#242] Project definition clean up

- nrepl dependency is not needed as part for the main dependencies
- Use exclusion of javamail library to avoid "version ranges" warning in `lein`

--- api/project.clj
+++ api/project.clj
@@ -15,7 +15,7 @@
                  [ring/ring-json "0.4.0"]
                  [ring-jetty-component "0.3.1"]
                  [commons-fileupload "1.3.1"]
-                 [org.akvo.flow/akvo-flow "20220120-233458.89643d80" :classifier "classes"]
+                 [org.akvo.flow/akvo-flow "20220120-233458.89643d80" :classifier "classes" :exclusions [org.simplejavamail/simple-java-mail]]
                  [org.akvo/commons "0.4.5" :exclusions [org.clojure/tools.nrepl]]
                  [raven-clj "1.5.0"]
                  [javax.jdo/jdo2-api "2.3-eb"]
@@ -33,7 +33,6 @@
                  [com.google.cloud.sql/postgres-socket-factory "1.0.16"]
                  [clj-http "2.3.0"]
                  [cheshire "5.7.0"]
-                 [nrepl/nrepl "0.6.0"]
                  [com.taoensso/timbre "4.10.0"]
                  [com.fzakaria/slf4j-timbre "0.3.8" :exclusions [org.slf4j/slf4j-jdk14]]
                  [org.slf4j/slf4j-api "1.7.14"]


test fix

test fix


Refactors change-document for consistency

--- src/wordsmith/core.cljs
+++ src/wordsmith/core.cljs
@@ -52,12 +52,8 @@
 ;; Left menu

 (defn update-current [event app]
-  (let [title (.. event -target -textContent)
-        input (p/get-document title)]
-    (om/update! app :input input)
-    (om/update! app :title title)
-    (om/update! app :last-title title)
-    (om/update! app :last-input input)))
+  (let [title (.. event -target -textContent)]
+    (put! (:channel @app) [:change title])))

 (defn delete-click [title app]
   (put! (:channel @app) [:remove title]))
@@ -90,6 +86,13 @@

 ;; The main app

+(defn change-document [app title]
+  (let [input (p/get-document title)]
+    (om/update! app :input input)
+    (om/update! app :title title)
+    (om/update! app :last-title title)
+    (om/update! app :last-input input)))
+
 (defn save-document [app]
   (om/update! app :last-input (:input @app))
   (om/update! app :last-title (:title @app))
@@ -108,7 +111,8 @@
   (case command
     :save   (save-document app)
     :rename (rename-document app params)
-    :remove (remove-document app params)))
+    :remove (remove-document app params)
+    :change (change-document app params)))

 (defn listen [el type app]
   (events/listen el type


Update ring-core dependencies

Update ring-core dependencies

--- ring-core/project.clj
+++ ring-core/project.clj
@@ -5,15 +5,15 @@
   :license {:name "The MIT License"
             :url "http://opensource.org/licenses/MIT"}
   :dependencies [[org.clojure/clojure "1.7.0"]
-                 [ring/ring-codec "1.1.1"]
+                 [ring/ring-codec "1.1.2"]
                  [commons-io "2.6"]
-                 [commons-fileupload "1.3.3"]
+                 [commons-fileupload "1.4"]
                  [crypto-random "1.2.0"]
                  [crypto-equality "1.0.0"]]
   :aliases {"test-all" ["with-profile" "default:+1.8:+1.9:+1.10" "test"]}
   :profiles
   {:provided {:dependencies [[javax.servlet/servlet-api "2.5"]]}
-   :dev  {:dependencies [[clj-time "0.14.3"]
+   :dev  {:dependencies [[clj-time "0.15.2"]
                         [javax.servlet/servlet-api "2.5"]]}
    :1.8  {:dependencies [[org.clojure/clojure "1.8.0"]]}
    :1.9  {:dependencies [[org.clojure/clojure "1.9.0"]]}


new scene tests

new scene tests

--- test/clojure/yaw/scene_test.clj
+++ test/clojure/yaw/scene_test.clj
@@ -1,33 +1,59 @@
 (ns clojure.yaw.scene-test
-  (:require [clojure.yaw.scene :as sut]
+  (:require [yaw.world :as w]
+            [yaw.scene :as sut]
             [clojure.test :as t]))

 ;; As it is really awkward to test for openGL rendering,
 ;; these are not formal tests.
 ;; Going to have to "play test"

-;; Empty scene
-(sut/display-scene [:scene])
+;; ;; Empty scene
+;; (sut/display-scene [:scene])

-;; Red skybox
-(sut/display-scene [:scene {:skybox {:color :red :scale [10 10 10]}}])
+;; ;; Red skybox
+;; (sut/display-scene [:scene {:skybox {:color :red :scale [10 10 10]}}])

-;; Lone white box at (0,0,-5)
-(sut/display-scene
- [:scene
-  [:item :test/box {:mesh :mesh/box :pos [0 0 -5] :mat :white}]])
+;; ;; Lone white box at (0,0,-5)
+;; (sut/display-scene
+;;  [:scene
+;;   [:item :test/box {:mesh :mesh/box :pos [0 0 -5] :mat :white}]])

-;; Small cone at the center, cube to the upper-right, with a sun from the left
-(sut/display-scene
- [:scene
-  [:ambient {:color :white :i 0.1}]
-  [:sun {:color :yellow :i 1 :dir [1 0 0]}]
-  [:item :test/cone {:mesh :mesh/cone :mat :white :pos [0 0 -5] :rot [-90 0 0]}]
-  [:item :test/box {:mesh :mesh/box :pos [1 1 -7] :rot [20 20 20] :mat :blue}]])
+;; ;; Small cone at the center, cube to the upper-right, with a sun from the left
+;; (sut/display-scene
+;;  [:scene
+;;   [:ambient {:color :white :i 0.1}]
+;;   [:sun {:color :yellow :i 1 :dir [1 0 0]}]
+;;   [:item :test/cone {:mesh :mesh/cone :mat :white :pos [0 0 -5] :rot [-90 0 0]}]
+;;   [:item :test/box {:mesh :mesh/box :pos [1 1 -7] :rot [20 20 20] :mat :blue}]])

-;; Two cameras, second one active, focusing on a cube
-(sut/display-scene
- [:scene {:camera :test/cam2}
-  [:item :test/focus {:mesh :mesh/box :pos [0 0 0] :mat :white}]
-  [:camera :test/cam1 {:fov 90 :pos [-2 2 2] :target :test/focus}]
-  [:camera :test/cam2 {:pos [2 2 2] :target :test/focus}]])
+;; ;; Two cameras, second one active, focusing on a cube
+;; (sut/display-scene
+;;  [:scene {:camera :test/cam2}
+;;   [:item :test/focus {:mesh :mesh/box :pos [0 0 0] :mat :white}]
+;;   [:camera :test/cam1 {:fov 90 :pos [-2 2 2] :target :test/focus}]
+;;   [:camera :test/cam2 {:pos [2 2 2] :target :test/focus}]])
+
+(defn -lone-box
+  []
+  (let [univ (w/start-universe!)]
+    (sut/display-diff
+     univ
+     (sut/diff sut/empty-item-map
+               (sut/item-map [:scene [:item :test/box {:mesh :mesh/box :pos [0 0 -5] :mat :white}]])))))
+
+(defn -rotating-cube
+  []
+  (let [univ (w/start-universe!)]
+    (for [y (range 90)]
+      (do
+        (sut/display-diff
+         univ
+         (sut/diff (:data @univ)
+                   (sut/item-map [:scene [:item :test/box
+                                          {:mesh :mesh/box :pos [0 0 -5]
+                                           :rot [0 y 0]}]])))
+        (Thread/sleep 30)))))
+
+(-lone-box)
+
+(-rotating-cube)


Added some missing functions. Minor changes

create-input/output, load-bytes, load-shape,

--- rosado/processing.clj
+++ rosado/processing.clj
@@ -210,14 +210,22 @@

 (defn create-image [w h format] (.createImage *applet* (int w) (int h) (int format)))

-;; $$createInput
-;; $$createInputRaw
-;; $$createOutput
+(defn create-input [filename]
+	(PApplet/createInput (java.io.File. filename)))
+
+(defn create-input-raw
+	"Call openStream() without automatic gzip decompression."
+	[filename]
+	(.createInputRaw *applet* filename))
+
+(defn create-output [filename]
+	(PApplet/createOutput (java.io.File. filename)))

 (defn create-path [filename] (PApplet/createPath filename))

-;; $$createReader
-;; $$createWriter
+(defn create-reader [filename] (.createReader *applet* filename))
+
+(defn create-writer [filename] (.createWriter *applet* filename))

 (defn cursor
   ([] (.cursor *applet*))
@@ -386,8 +394,10 @@
   ([x1 y1 z1 x2 y2 z2] (.line *applet* (float x1) (float y1) (float z1) (float x2) (float y2) (float z2))))

 ;; $$link
-;; $$loadBytes
-;; $$loadFont
+
+(defn load-bytes [filename] (PApplet/loadBytes filename))
+
+(defn load-font [filename] (.loadFont *applet* filename))

 (defn load-image [filename] (.loadImage *applet* filename))

@@ -395,7 +405,16 @@

 (defn load-pixels [] (.loadPixels *applet*))

-;; $$loadStrings
+(defn load-shape
+	"Load a geometry from a file as a PShape."
+	[filename]
+	(.loadShape *applet* filename))
+
+(defn load-strings
+	"Load data from a file and shove it into a String array."
+	[filename]
+	(.loadStrings *applet* filename))
+
 ;; $$log

 (defn start-loop [] (.loop *applet*))
@@ -461,7 +480,10 @@

 (defn no-loop [] (.noLoop *applet*))

-(defn norm [val start stop] (PApplet/norm (float val) (float start) (float stop)))
+(defn norm
+	"Normalize a value to exist between 0 and 1 (inclusive)."
+	[val start stop]
+	(PApplet/norm (float val) (float start) (float stop)))

 (defn normal [nx ny nz] (.normal *applet* (float nx) (float ny) (float nz)))

@@ -550,11 +572,14 @@
 ;; $$registerPre
 ;; $$registerSize
 ;; $$registerSize
-;; $$requestImage
+
+(defn request-image
+	([filename] (.requestImage *applet* filename))
+	([filename extension] (.requestImage *applet* filename extension)))

 (defn reset-matrix [] (.resetMatrix *applet*))

-;; $$reverse
+(defn reverse-array [arr] (PApplet/reverse arr))

 (defn rotate
   ([angle] (.rotate *applet* (float angle)))


removed unnecessary print

removed unnecessary print

--- test/frontend/uploads-test.cljs
+++ test/frontend/uploads-test.cljs
@@ -110,5 +110,5 @@
                   :room-uploads-order new-uploads-order
                   :upload-slots upload-slots}
        changed-state (uploads/handle-state-change old-state new-state)]
-    (println changed-state)
     (is (:stopped? ((:uploads changed-state) upload-id-2)))))
+


Reverse stacktraces in string format

Reverse stacktraces in string format

--- src/riemann/jvm_profiler/stack.clj
+++ src/riemann/jvm_profiler/stack.clj
@@ -64,6 +64,7 @@
   "Formats a stacktrace as a nice string."
   [trace]
   (->> trace
+       reverse
        (map (fn [^StackTraceElement frame]
               (str (.getClassName frame) " "
                    (.getMethodName frame) " ("


New version of cider.

New version of cider.

--- rc/profiles.clj
+++ rc/profiles.clj
@@ -10,7 +10,7 @@
  ;; and connect Cider to it, you need to get the middleware plugged
  ;; in.  The options are to include it in project.clj, or have it
  ;; more prominent in profiles.clj.  This seems like a better option.
- :user {:plugins [[cider/cider-nrepl "0.15.0-snapshot"]
+ :user {:plugins [[cider/cider-nrepl "0.15.1"]
                   [jonase/eastwood "0.2.3"]]}}


Fix some typos

Fix some typos

--- src/status_im/translations/it.cljs
+++ src/status_im/translations/it.cljs
@@ -62,19 +62,19 @@
    :profile                               "Profilo"
    :report-user                           "SEGNALA UTENTE"
    :message                               "Messaggio"
-   :username                              "Username"
+   :username                              "Nome dell'utente"
    :not-specified                         "Non specificato"
    :public-key                            "Chiave pubblica"
    :phone-number                          "Numero di telefono"
    :email                                 "Email"
    :profile-no-status                     "Nessuno stato"
-   :add-to-contacts                       "Aggiunto ai contatti"
+   :add-to-contacts                       "Aggiungere ai contatti"
    :error-incorrect-name                  "Seleziona un altro nome"
    :error-incorrect-email                 "e-mail non valida"

    ;;make_photo
    :image-source-title                    "Immagine del profilo"
-   :image-source-make-photo               "Acquisisci"
+   :image-source-make-photo               "Catturare"
    :image-source-gallery                  "Scegli dalla galleria"
    :image-source-cancel                   "Elimina"

@@ -99,7 +99,7 @@
    :new-group-chat                        "Nuova chat di gruppo"

    ;discover
-   :discover                             "Scopri"
+   :discover                              "Scopri"
    :none                                  "Nessuno"
    :search-tags                           "Digita qui le tue etichette di ricerca"
    :popular-tags                          "Tag popolari"
@@ -112,7 +112,7 @@
    ;contacts
    :contacts                              "Contatti"
    :new-contact                           "Nuovo contatto"
-   :show-all                              "MOSTRA TUTTI"
+   :show-all                              "Mostra tutti"
    :contacts-group-dapps                  "ÐApps"
    :contacts-group-people                 "Persone"
    :contacts-group-new-chat               "Comincia una nuova chat"


Temporarily disabled redirection of benchmark results.

--- src/validation_benchmark/bench.clj
+++ src/validation_benchmark/bench.clj
@@ -27,8 +27,9 @@

 (defn quick [f]
-  (stdout->tmpfile "criterium"
-                   (summarize (criterium/quick-benchmark* f nil))))
+  ;; (stdout->tmpfile "criterium"
+  ;;                  (summarize (criterium/quick-benchmark* f nil)))
+  (summarize (criterium/quick-benchmark* f nil)))

 (defn real [f]


(defn analyze) returns a (possibly-empty) list

--- test/babel/test/it.cljc
+++ test/babel/test/it.cljc
@@ -12,14 +12,14 @@
             [dag_unify.core :refer [get-in strip-refs]]))

 (deftest analyze-1
-  (let [singular (analyze "compito")
-        plural  (analyze "compiti")]
+  (let [singular (analyze "compito" (:lookup medium))
+        plural  (analyze "compiti" (:lookup medium))]
     (is (not (empty? singular)))
     (is (not (empty? plural)))))

 (deftest analyze-2
-  (let [singular (analyze "difficile")
-        plural  (analyze "difficili")]
+  (let [singular (analyze "difficile" (:lookup medium))
+        plural  (analyze "difficili" (:lookup medium))]
     (is (not (empty? singular)))
     (is (not (empty? plural)))))