add new chat commands

add new chat commands

--- src/clj/game/core.clj
+++ src/clj/game/core.clj
@@ -466,6 +466,10 @@
                   ["Done"] (fn [choice] (resolve-select state side))
                   (assoc args :prompt-type :select :show-discard (:show-discard ability))))))

+(defn corp-trace-prompt [state card trace]
+  (show-prompt state :corp card "Boost trace strength?" :credit
+               #(init-trace state :corp card trace %)))
+
 (defn resolve-ability [state side {:keys [counter-cost advance-counter-cost cost effect msg req once
                                           once-key optional prompt choices end-turn player psi trace
                                           not-distinct priority cancel-effect] :as ability}
@@ -478,8 +482,7 @@
     (when (and psi (or (not (:req psi)) ((:req psi) state side card targets)))
       (psi-game state side card psi))
     (when (and trace (or (not (:req trace)) ((:req trace) state side card targets)))
-      (show-prompt state :corp card "Boost trace strength?" :credit
-                   #(init-trace state :corp card trace %)))
+      (corp-trace-prompt state card trace))
     (when (and (not (get-in @state [once (or once-key cid)]))
                (or (not req) (req state side card targets)))
       (if choices
@@ -1909,6 +1912,18 @@
         "/take-meat"  #(when (= %2 :runner) (damage %1 %2 :meat  (max 0 value)))
         "/take-net"   #(when (= %2 :runner) (damage %1 %2 :net   (max 0 value)))
         "/take-brain" #(when (= %2 :runner) (damage %1 %2 :brain (max 0 value)))
+        "/psi"        #(when (= %2 :corp) (psi-game %1 %2
+                                 {:title "/psi command" :side %2}
+                                 {:equal  {:msg "resolve equal bets effect"}
+                                  :not-equal {:msg "resolve unequal bets effect"}}))
+        "/trace"      #(when (= %2 :corp)
+                             (corp-trace-prompt %1
+                                                {:title "/trace command" :side %2}
+                                                {:base (max 0 value)
+                                                 :msg "resolve successful trace effect"}))
+        "/counter"    #(do (show-select %1 %2 {:title "/counter command"} {:choices {:req nil}}))
+        "/end-run"    #(when (= %2 :corp) (end-run %1 %2))
+        "/jack-out"   #(when (= %2 :runner) (jack-out %1 %2 nil))
         "/discard"    #(move %1 %2 (nth (get-in @%1 [%2 :hand]) num nil) :discard)
         "/deck"       #(move %1 %2 (nth (get-in @%1 [%2 :hand]) num nil) :deck {:front true})
         nil


add fns to add/edit goals in db

add fns to add/edit goals in db

--- src/clj/tictag/db.clj
+++ src/clj/tictag/db.clj
@@ -312,6 +312,37 @@
         (where [:= (:id beeminder-user) :beeminder_id])
         sql/format))))

+(defn add-goal [db user-id goal]
+  (j/execute!
+   (:db db)
+   (-> (insert-into [[:beeminder-goals [:goal :tags :beeminder-id]]
+                     (-> (select (:goal goal) (pr-str (:tags goal)) :id)
+                         (from :beeminder)
+                         (where [:= :beeminder.user-id user-id]))])
+       sql/format))
+  (j/query
+   (:db db)
+   (-> (select :id)
+       (from :beeminder-goals)
+       (where [:= :goal (:goal goal)]
+              [:= :tags (pr-str (:tags goal))]
+              [:= :beeminder-id (-> (select :beeminder.id)
+                                    (from :beeminder)
+                                    (where [:= :beeminder.user-id user-id]))])
+       sql/format)))
+
+(defn update-goal [db user-id goal]
+  (j/execute!
+   (:db db)
+   (-> (update :beeminder-goals)
+       (sset {:goal (:goal goal)
+              :tags (pr-str (:tags goal))})
+       (where [:= (:id goal) :id]
+              [:= :beeminder-id (-> (select :beeminder.id)
+                                    (from :beeminder)
+                                    (join :users [:= :users.id user-id]))])
+       sql/format)))
+
 (defn test-query! [db]
   (try (j/query (:db db) (sql/format (select 1)))
        (catch Exception e nil)))


Use eu for dump links for now

Use eu for dump links for now

--- src/demogorgon/nh.clj
+++ src/demogorgon/nh.clj
@@ -198,10 +198,10 @@
   (let [exts [".txt.html" ".txt"]
         file (first
               (filter #(.exists %)
-                      (map #(File. (str "/srv/un.nethack.nu/users/" nick "/dumps/" nick ".last" %)) exts)))]
+                      (map #(File. (str "/srv/un.nethack.nu/users/" nick "/dumps/eu/" nick ".last" %)) exts)))]
     (if file
       (let [fp (.getCanonicalPath file)]
-        (str "http://un.nethack.nu/user/" nick "/dumps" (.substring fp (.lastIndexOf fp "/"))))
+        (str "http://un.nethack.nu/user/" nick "/dumps/eu" (.substring fp (.lastIndexOf fp "/"))))
       nil)))

 (defn last-dump-hook [irc object match]


fix regression with renaming

fix regression with renaming

--- src/io/perun/atom.clj
+++ src/io/perun/atom.clj
@@ -42,7 +42,7 @@
           ])])))

 (defn generate-atom [tgt-path files options]
-  (let [atom-filepath (str (:target options) "/" (:filename options))
+  (let [atom-filepath (str (:out-dir options) "/" (:filename options))
         atom-string   (generate-atom-str files options)]
     (perun/create-file tgt-path atom-filepath atom-string)
     (perun/report-info "atom" "generated Atom feed and saved to %s" atom-filepath)))


Add main option to read config from an edn file

--- src/less4clj/main.clj
+++ src/less4clj/main.clj
@@ -3,7 +3,8 @@
             [clojure.java.io :as io]
             [clojure.tools.cli :as cli]
             [clojure.string :as str]
-            [clojure.spec.alpha :as s]))
+            [clojure.spec.alpha :as s]
+            [clojure.edn :as edn]))

 (def cli-opts
   [["-h" "--help"]
@@ -11,16 +12,17 @@
    ["-t" "--target-path TARGET" "The path where CSS files are written to"
     :default "target"]
    ["-s" "--source-map" "Enable source-maps for compiled CSS"]
-   ["-c" "--compression" "Enable compression for compiled CSS using simple compression"]
-   ["-i" "--inline-javascript" "Enable inline JavaScript plugin"]
-   ["-v" "--verbosity LEVEL" "Set verbosity level, valid values are 1 and 2."
+   ["-o" "--compression" "Enable compression for compiled CSS using simple compression"]
+   [nil "--inline-javascript" "Enable inline JavaScript plugin"]
+   ["-v" "--verbosity LEVEL" "Set verbosity level, valid values are 1 and 2"
     :parse-fn (fn [s]
                 (Integer/parseInt s))
     :validate [#{1 2} "Must be 1 or 2"]]
    [nil "--source-paths PATHS" "List of LESS source paths, comma separated"
     :default ["src"]
     :parse-fn (fn [x]
-                (str/split x #","))]])
+                (str/split x #","))]
+   ["-c" "--config PATH" "EDN file to read config options from"]])

 (defn help-text [options-summary]
 (str "{less} CSS compiler.
@@ -34,11 +36,18 @@
 If you are seeing SLF4J warnings, check https://github.com/Deraen/less4clj#log-configuration

 Options:
-" options-summary))
+" options-summary "
+
+Config file options are merged over the default options, before CLI options."))

 (defn -main [& args]
   (let [{:keys [options summary errors]} (cli/parse-opts args cli-opts)
-        {:keys [help]} options]
+        {:keys [help]} options
+        config-file (if (:config options)
+                      (edn/read-string (slurp (io/file (:config options)))))
+        options (merge (cli/get-default-options cli-opts)
+                       config-file
+                       options)]
     (cond
       errors (println (str/join "\n" errors))
       help (println (help-text summary))


High macros, for generation validators.

High macros, for generation validators.

--- src/ombs/validate.clj
+++ src/ombs/validate.clj
@@ -7,6 +7,17 @@
 (defn errors-string [tags]
   (reduce str (map #(str "|" % "|") (vld/get-errors tags)))
   )
+
+(defmacro create-rule [tag data]
+  `(vld/rule (first ~data) [~tag (last ~data)])
+  )
+
+;(defmacro create-validator [tag rules]
+;  (vld/clear-errors!)
+;  (map #(create-rule tag %) rules)
+;  (not (vld/errors? tag))
+;  )
+

 (defn registration? [params]
   (vld/clear-errors!)
@@ -31,9 +42,28 @@
   )

 (defn login? [username password]
+  ;(create-validator :login [[ (vld/has-value? username)                       "Username can't be empty" ]
+  ;                          [ (vld/has-value? (:name (db/get-user username))) "User not found"]
+  ;                          [ (= password (:password (db/get-user username))) "Incorrect Login or password"]
+  ;                          ])
+  (vld/clear-errors!)
   (vld/rule (vld/has-value? username) [:login "Username can't be empty"])
   (vld/rule (vld/has-value? (:name (db/get-user username))) [:login "User not found"])
   (vld/rule (= password (:password (db/get-user username))) [:login "Incorrect Login or password"])
   (not (vld/errors? :login))

   )
+
+(defn stake? [eid uid]
+;  (create-validator :pay [[ (vld/has-value? uid) "User not found in database" ]
+;                          [ (vld/has-value? eid) "Event not found in database"] ])
+;
+  (vld/clear-errors!)
+  (create-rule :pay [ (vld/has-value? eid) "Event does not exist!" ])
+  (vld/rule (vld/has-value? uid) [:pay "Incorrect Login or password"])
+  (if-not (vld/errors? :pay)
+    (println "No errors")
+    (println (str "event:" eid " uid:" uid" err: "(vld/get-errors :pay)) )
+    )
+
+  )


add sasuga help

add sasuga help

--- src/panpan/sasuga.clj
+++ src/panpan/sasuga.clj
@@ -12,6 +12,8 @@
   (if-let [i (jb/get SASUGA_KEY)] i 0))

 (defn sasuga-handler
+  "sasuga - さすが俺
+  "
   [{:keys [user] :as arg}]
   (jh/regexp arg
     #"^sasuga"


add a fetch stats helper

add a fetch stats helper

--- src/pc/http/sente.clj
+++ src/pc/http/sente.clj
@@ -512,6 +512,11 @@
                 :or {force false}}]
   ((:send-fn @sente-state) client-id [:frontend/refresh {:force-refresh force}]))

+(defn fetch-stats
+  "Sends a request to the client for statistics, which is handled above in the :frontend/stats handler"
+  [client-id]
+  ((:send-fn @sente-state) client-id [:frontend/stats]))
+
 (defn init []
   (let [{:keys [ch-recv send-fn ajax-post-fn connected-uids
                 ajax-get-or-ws-handshake-fn] :as fns} (sente/make-channel-socket!


Add 2d physics functions to the docstring

--- src/play_clj/core.clj
+++ src/play_clj/core.clj
@@ -274,6 +274,18 @@
       :on-end-contact
       (fn [screen entities]
         (println (:contact screen)) ; the Contact - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/Contact.html
+        entities)
+      ; called between each use of `step!` before the collision is processed
+      :on-pre-solve
+      (fn [screen entities]
+        (println (:contact screen)) ; the Contact - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/Contact.html
+        (println (:impulse screen)) ; the ContactImpulse - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/ContactImpulse.html
+        entities)
+      ; called between each use of `step!` after the collision is processed
+      :on-post-solve
+      (fn [screen entities]
+        (println (:contact screen)) ; the Contact - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/Contact.html
+        (println (:old-manifold screen)) ; the Manifold - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/Manifold.html
         entities))

     ; 3D physics contact (for play-clj.g3d-physics)


Create storage using factory instead of direct html5 local storage constructor

--- src/pot/core/sys.cljs
+++ src/pot/core/sys.cljs
@@ -2,10 +2,9 @@
   (:require-macros [cljs.core.async.macros :refer [go alt!]])
   (:require [cljs.reader :refer [read-string]]
             [cljs.core.async :refer [chan]]
+            [goog.storage.mechanism.mechanismfactory :as storage-factory]
             [pot.core.bl :refer [make-board add-random-cells]]
-            [pot.core.hand :refer [process-msg]])
-  (:import [goog.storage Storage]
-           [goog.storage.mechanism HTML5LocalStorage]))
+            [pot.core.hand :refer [process-msg]]))

 (defn- create-config
   {:pre [(every? number? [height width init win-value])
@@ -35,7 +34,7 @@

 (defn- create-storage
   []
-  (Storage. (HTML5LocalStorage.)))
+  (storage-factory/create))

 (defn- create-runtime
   []
@@ -51,7 +50,7 @@
   (let [storage (:storage system)
         history (-> system :history deref)
         hkey (-> system :config deref :history-store-key)]
-    (.set storage hkey (pr-str history))
+    (.set storage hkey history)
     system))

 (defn- restore-system


:transpose keyword

:transpose keyword

--- src/semitone/core.clj
+++ src/semitone/core.clj
@@ -3,8 +3,6 @@
 ;; TODO:
 ;;   displacement syntax < >
 ;;   MPE support
-;;   :transpose
-;;   :time-signature

 (ns semitone.core)

@@ -62,7 +60,6 @@
                           :notes (rest notes)}))
           (if-let [m (re-matches #"([-=])[-=]*" (name length))]
             (do
-              (pprint m)
               (merge state {:repeat-length (* (:length state) (count (name length)))
                             :key (if (= "=" (get m 1)) (:key state) nil)
                             :notes (rest notes)}))
@@ -241,7 +238,10 @@
                                     (MidiEvent. (ShortMessage.
                                                  (:key-type state)
                                                  (:channel state)
-                                                 (:key state)
+                                                 (+ (:key state)
+                                                    (if (or (= (:key-type state) ShortMessage/NOTE_ON)
+                                                            (= (:key-type state) ShortMessage/POLY_PRESSURE))
+                                                      (:transpose state) 0))
                                                  value)
                                                 (:position state))))
                             (when (and (= (:key-type state) ShortMessage/NOTE_ON) (not (= (:tie state) :begin)))


Remove extra log

Remove extra log


Rollback an earlier bad attempt at schema path.

--- src/sql_file/core.clj
+++ src/sql_file/core.clj
@@ -31,10 +31,10 @@
 (defn- locate-schema-script [ conn basename ]
   (or (some identity
             (map (fn [ prefix ]
-                   (let [ script-path (format "%s/%s" prefix basename)]
+                   (let [ script-path (format "%s%s" prefix basename)]
                      (log/debug "Checking for script:" script-path)
                      (clojure.java.io/resource script-path)))
-                 (conj (get conn :schema-path []) ".")))
+                 (conj (get conn :schema-path []) "")))
       (fail "Cannot find resource script: " basename)))

 (defn- schema-install-script [ conn schema ]


Add more validators to schema.

Add more validators to schema.

--- src/uxbox/schema.cljs
+++ src/uxbox/schema.cljs
@@ -41,7 +41,15 @@
   [v]
   (vector? v))

+
+(v/defvalidator function
+  "Validats if `v` is function."
+  {:default-message-format "%s must be a function."}
+  [v]
+  (fn? v))
+
 (def required v/required)
+(def number v/number)

 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Public Api


More changes to fix useful version

More changes to fix useful version

--- src/cake/core.clj
+++ src/cake/core.clj
@@ -4,7 +4,7 @@
         [cake.project :only [create-project read-project add-global-plugins]]
         [useful.map :only [update into-map merge-in]]
         [useful.utils :only [verify syntax-quote]]
-        [useful.macro :only [defalias]]
+        [useful.ns :only [defalias]]
         [slingshot.core :only [throw+]]
         [clojure.string :only [join]]
         [bake.core :only [force?]])


Move JVM debug only to dev profile

Move JVM debug only to dev profile

--- domain/project.clj
+++ domain/project.clj
@@ -14,7 +14,6 @@
                  [ring-cors "0.1.7"]]
   :plugins [[lein-ring "0.9.7"]]
   :ring {:handler domain.core/app
-         :nrepl {:start? true
-                 :port 3001}}
-  :jvm-opts ["-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"]
+         :nrepl {:start? true :port 3001}}
+  :profiles {:dev {:jvm-opts ["-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"]}}
   :aot [domain.core])


Tracking accept/server/client addr and port.

--- src/cbfg/net.cljs
+++ src/cbfg/net.cljs
@@ -59,10 +59,12 @@
                             (conj results [false result-ch accept-chs]))))
                  (net-clean-up net listens streams results))
                (= ch request-connect-ch) ; Client connect request.
-               (if-let [[to-addr to-port from-addr result-ch] v]
+               (if-let [[accept-addr accept-port from-addr result-ch] v]
                  ; TODO: Need to model connection delay, perhaps with a generic "later".
-                 (if-let [[accept-ch close-accept-ch] (get listens [to-addr to-port])]
-                   (let [end-point-buf-size (get opts :end-point-buf-size 0)
+                 (if-let [[accept-ch close-accept-ch] (get listens [accept-addr accept-port])]
+                   (let [from-port (+ 10000 ts)
+                         to-port (+ 10000 ts)
+                         end-point-buf-size (get opts :end-point-buf-size 0)
                          client-send-ch (achan-buf net end-point-buf-size)
                          client-recv-ch (achan-buf net end-point-buf-size)
                          close-client-recv-ch (achan net)
@@ -75,19 +77,22 @@
                                 (assoc client-send-ch {:send-ch client-send-ch
                                                        :recv-ch server-recv-ch
                                                        :close-recv-ch close-server-recv-ch
-                                                       :server-addr to-addr :server-port to-port
+                                                       :accept-addr accept-addr :accept-port accept-port
+                                                       :server-addr accept-addr :server-port to-port
+                                                       :client-addr from-addr :client-port from-port
                                                        :side :client :msgs []})
                                 (assoc server-send-ch {:send-ch server-send-ch
                                                        :recv-ch client-recv-ch
                                                        :close-recv-ch close-client-recv-ch
-                                                       :server-addr to-addr :server-port to-port
+                                                       :accept-addr accept-addr :accept-port accept-port
+                                                       :server-addr accept-addr :server-port to-port
+                                                       :client-addr from-addr :client-port from-port
                                                        :side :server :msgs []}))
                             (-> results
                                 (conj [false result-ch [client-send-ch client-recv-ch
-                                                        close-client-recv-ch to-addr to-port]])
-                                ; TODO: assign client port.
+                                                        close-client-recv-ch accept-addr to-port]])
                                 (conj [true accept-ch [server-send-ch server-recv-ch
-                                                       close-server-recv-ch from-addr 0]]))))
+                                                       close-server-recv-ch from-addr from-port]]))))
                    (do (aclose net result-ch)
                        (recur (inc ts) listens streams results)))
                  (net-clean-up net listens streams results))


Debug #1717

Debug #1717

--- backend/src/akvo/lumen/auth.clj
+++ backend/src/akvo/lumen/auth.clj
@@ -49,7 +49,7 @@
   Otherwiese grant access. This implies that access is on tenant level."
   [handler]
   (fn [{:keys [jwt-claims] :as request}]
-    (log/warn (:path-info request) "->" (select-keys jwt-claims ["email" "realm_access"]))
+    (when (map? jwt-claims) (log/warn (:path-info request) (select-keys jwt-claims ["email" "realm_access"])))
     (cond
       (public-path? request) (handler request)
       (nil? jwt-claims) not-authenticated


restyle tab

restyle tab

--- frontend/components/build.cljs
+++ frontend/components/build.cljs
@@ -89,12 +89,11 @@
                         :build-running? build-running?
                         :current-container-id current-container-id}
                        {:react-key (:index container)}))
-          [:a.container-selector.add-containers
+          [:a.container-selector.parallelism-tab
             {:role "button"
              :href (build-model/path-for-parallelism build)
              :title "adjust parallelism"}
-            [:span "+"]
-            [:span "-"]]]])))))
+            [:span "+"]]]])))))

 (defn notices [data owner]
   (reify


Remove fn useless in datomic client api

Remove fn useless in datomic client api

--- src/datemo/db.clj
+++ src/datemo/db.clj
@@ -96,12 +96,6 @@
 (defn retract-value [entity-spec attribute value]
   [:db/retract entity-spec attribute value])

-;; (defn add-entity [partition attr-val-map]
-;;   ([attr-val-map]
-;;    (add-entity :db.part/user attr-val-map))
-;;   ([partition attr-val-map]
-;;    (into {:db/id (d/tempid partition)} attr-val-map)))
-
 (defn add-value [entity-spec attribute value]
   [:db/add entity-spec attribute value])


simpler.

simpler.

--- clojure/raindrops/raindrops.clj
+++ clojure/raindrops/raindrops.clj
@@ -17,8 +17,7 @@
 (let [lang {3 "Pling" 5 "Plang" 7 "Plong"}]
   (defn convert
     [n]
-    (let [ns (set (factors n))
-          kws (set (keys lang))]
-      (if-let [found-ns (seq (set/intersection ns kws))]
+    (let [ns (set (factors n))]
+      (if-let [found-ns (seq (set/intersection ns (set (keys lang))))]
         (s/join (map lang found-ns))
         (str n)))))


Add prod task

Add prod task

--- app/build.boot
+++ app/build.boot
@@ -37,6 +37,9 @@
   (task-options! cljs {:optimizations :advanced})
   identity)

+(deftask prod []
+  (comp (production) (build)))
+
 (deftask development []
   (task-options! cljs {:optimizations :none :source-map true}
                  reload {:on-jsload 'app.app/init})


Update cider-nrepl to 0.14-snapshot

Update cider-nrepl to 0.14-snapshot

--- extra/profiles.clj
+++ extra/profiles.clj
@@ -1,5 +1,5 @@
 {:user
- {:plugins [[cider/cider-nrepl "0.13.0-SNAPSHOT"]
+ {:plugins [[cider/cider-nrepl "0.14.0-SNAPSHOT"]
             [lein-pprint "1.1.1"]
             [refactor-nrepl "2.3.0-SNAPSHOT"]
             [venantius/ultra "0.4.1"]]


add French to directory

add French to directory

--- src/babel/directory.cljc
+++ src/babel/directory.cljc
@@ -15,7 +15,8 @@
              (conj model
                    {:generate-fn (fn [spec]
                                    (en/generate spec :model model))})))
-   :la (future babel.latin/model)
+   :fr (future (babel.francais.grammar/medium))
+   :la (future la/model)
    :it (future (let [model (babel.italiano.grammar/small)]
                  (conj model
                        {:generate-fn (fn [spec]


Naming

Naming

--- src/buildviz/handler.clj
+++ src/buildviz/handler.clj
@@ -14,16 +14,16 @@
     (@builds job)
     {}))

-(defn- store-build! [job build buildData]
+(defn- store-build! [job build build-data]
   (let [entry (job-entry job)
-        updatedEntry (assoc entry build buildData)]
-    (swap! builds assoc job updatedEntry)
-    {:body buildData}))
+        updated-entry (assoc entry build build-data)]
+    (swap! builds assoc job updated-entry)
+    {:body build-data}))

 (defn- get-build [job build]
   (if (contains? @builds job)
-    (if-let [buildData ((@builds job) build)]
-      {:body buildData}
+    (if-let [build-data ((@builds job) build)]
+      {:body build-data}
       {:status 404})
     {:status 404}))

@@ -34,14 +34,14 @@
   (if (and (contains? build :end) (contains? build :end))
     (- (build :end) (build :start))))

-(defn- average-runtime-for [summary buildDataEntries]
-  (let [runtimes (filter (complement nil?) (map duration-for buildDataEntries))]
+(defn- average-runtime-for [summary build-data-entries]
+  (let [runtimes (filter (complement nil?) (map duration-for build-data-entries))]
     (if (not (empty? runtimes))
       (assoc summary :averageRuntime (avg runtimes))
       summary)))

-(defn- total-count-for [summary buildDataEntries]
-  (assoc summary :totalCount (count buildDataEntries)))
+(defn- total-count-for [summary build-data-entries]
+  (assoc summary :totalCount (count build-data-entries)))

 (defn- builds-with-outcome [build-data-entries]
   (filter #(contains? % :outcome) build-data-entries))
@@ -57,12 +57,12 @@
     summary))

 (defn- summary-for [job]
-  (let [buildDataEntries (vals (@builds job))]
+  (let [build-data-entries (vals (@builds job))]
     (-> {}
-        (average-runtime-for buildDataEntries)
-        (total-count-for buildDataEntries)
-        (error-count-for buildDataEntries)
-        (flaky-runs-for buildDataEntries))))
+        (average-runtime-for build-data-entries)
+        (total-count-for build-data-entries)
+        (error-count-for build-data-entries)
+        (flaky-runs-for build-data-entries))))

 (defn- get-pipeline []
   (let [jobNames (keys @builds)


move container variables to own key -- wont show in props

--- src/cambo/component.cljc
+++ src/cambo/component.cljc
@@ -210,15 +210,25 @@
              [name {:root root
                     :pathsets pathsets}])))

+(def variables-key "cambo$variables")
+
+(defn get-variables
+  [props]
+  (aget props variables-key))
+
+(defn variables*
+  [this]
+  (get-variables (.-props this)))
+
 (defn variables
   [this]
-  (get-in (props this) [:cambo/prop :variables]))
+  (get (variables* this) :variables))

 (defn set-variables
   ([this variables]
    (set-variables this variables nil))
   ([this variables cb]
-   (let [run (get-in (props this) [:cambo/prop :run])]
+   (let [{:keys [run]} (variables* this)]
      (run variables cb false))))

 (defn force-fetch
@@ -227,7 +237,7 @@
   ([this variables]
    (force-fetch this variables nil))
   ([this variables cb]
-   (let [run (get-in (props this) [:cambo/prop :run])]
+   (let [{:keys [run]} (variables* this)]
      (run variables cb true))))

 #?(:cljs (defn create-container
@@ -238,10 +248,11 @@
                                (.apply js/React.Component this (js-arguments))
                                (set! (.-pending this) nil)
                                (set! (.-mounted this) true)
-                               (set! (.-state this) (->state {:query-data nil
-                                                              :variables nil
-                                                              :cambo/prop {:run (fn [variables cb force?]
-                                                                                (.run-variables this variables cb force?))}}))
+                               (set! (.-state this)
+                                     (->state {:query-data nil
+                                               :variables nil
+                                               :cambo/variables {:run (fn [variables cb force?]
+                                                                        (.run-variables this variables cb force?))}}))
                                this))]
              (set! (.-prototype container)
                    (goog.object/clone js/React.Component.prototype))
@@ -276,7 +287,7 @@
                                            (set-state this (fn [{:keys [cambo/prop]}]
                                                              {:query-data (.get-query-data this (props this) (context this))
                                                               :variables variables
-                                                              :cambo/prop (assoc prop :variables next-variables)}))))
+                                                              :cambo/variables (assoc prop :variables next-variables)}))))
                                        (when cb
                                          (cb ready)))]
                    (if force?
@@ -284,13 +295,13 @@
                      (model/prime model pathsets on-readystate))
                    (set! (.-pending this) {:variables variables})))

-               (initialize [this {:keys [cambo/prop]} props {:keys [route] :as context} vars]
+               (initialize [this {:keys [cambo/variables]} props {:keys [route] :as context} vars]
                  (let [next-vars (prepare-variables vars route)]
                    (.update-fragment-pointers this props next-vars)
                    (.update-subscription this context)
                    {:query-data (.get-query-data this props context)
                     :variables vars
-                    :cambo/prop (assoc prop :variables next-vars)}))
+                    :cambo/variables (assoc variables :variables next-vars)}))

                (update-fragment-pointers [this props vars]
                  (set! (.-fragment-pointers this) (create-fragment-pointers fragments props vars)))
@@ -355,11 +366,11 @@
                          (not= current-context next-context)))))

                (render [this]
-                 (let [{:keys [query-data cambo/prop]} (state this)
+                 (let [{:keys [query-data cambo/variables]} (state this)
                        component-props (merge (props this)
-                                              {:cambo/prop prop}
                                               query-data)]
-                   (js/React.createElement component (->props component-props)))))
+                   (js/React.createElement component (js-obj props-key component-props
+                                                             variables-key variables)))))

              container)))


formatting changes to sync

formatting changes to sync

--- src/chat/server/sync.clj
+++ src/chat/server/sync.clj
@@ -20,8 +20,8 @@
 (let [{:keys [ch-recv send-fn ajax-post-fn ajax-get-or-ws-handshake-fn
               connected-uids]}
       (sente/make-channel-socket! sente-web-server-adapter
-                                  {:user-id-fn (fn [ob]
-                                                 (get-in ob [:session :user-id]))})]
+                                  {:user-id-fn
+                                   (fn [ob] (get-in ob [:session :user-id]))})]
   (def ring-ajax-post                ajax-post-fn)
   (def ring-ajax-get-or-ws-handshake ajax-get-or-ws-handshake-fn)
   (def ch-chsk                       ch-recv)
@@ -54,27 +54,26 @@
     (when ?reply-fn
       (?reply-fn {:umatched-event-as-echoed-from-from-server event}))))

-(defmethod event-msg-handler :chsk/ws-ping
-  [ev-msg]
-  ; Do nothing, just avoid unhandled event message
-  )
+;; Handler helpers

 (defn broadcast-thread
   "broadcasts thread to all subscribed users, except those in ids-to-skip"
   [thread-id ids-to-skip]
-  (let [subscribed-user-ids (db/with-conn
-                              (db/get-users-subscribed-to-thread thread-id))
-        user-ids-to-send-to (-> (difference
-                                  (intersection
-                                    (set subscribed-user-ids)
-                                    (set (:any @connected-uids)))
-                                  (set ids-to-skip)))
-        thread (db/with-conn (db/get-thread thread-id))]
-    (doseq [uid user-ids-to-send-to]
-      (let [user-tags (db/with-conn (db/get-user-visible-tag-ids uid))
-            filtered-thread (update-in thread [:tag-ids] (partial into #{} (filter user-tags)))
-            thread-with-last-opens (db/with-conn (db/thread-add-last-open-at filtered-thread uid))]
-        (chsk-send! uid [:chat/thread thread-with-last-opens])))))
+  (db/with-conn
+    (let [subscribed-user-ids (db/get-users-subscribed-to-thread thread-id)
+          user-ids-to-send-to (-> (difference
+                                    (intersection
+                                      (set subscribed-user-ids)
+                                      (set (:any @connected-uids)))
+                                    (set ids-to-skip)))
+          thread (db/get-thread thread-id)]
+      (doseq [uid user-ids-to-send-to]
+        (let [user-tags (db/get-user-visible-tag-ids uid)
+              filtered-thread (update-in thread [:tag-ids]
+                                         (partial into #{} (filter user-tags)))
+              thread-with-last-opens (db/thread-add-last-open-at
+                                       filtered-thread uid)]
+          (chsk-send! uid [:chat/thread thread-with-last-opens]))))))

 (defn broadcast-user-change
   "Broadcast user info change to clients that can see this user"
@@ -82,8 +81,9 @@
   (let [ids-to-send-to (disj
                          (intersection
                            (set (:any @connected-uids))
-                           (into #{} (map :id)
-                                 (db/with-conn (db/fetch-users-for-user user-id))))
+                           (into
+                             #{} (map :id)
+                             (db/with-conn (db/fetch-users-for-user user-id))))
                          user-id)]
     (doseq [uid ids-to-send-to]
       (chsk-send! uid info))))
@@ -97,16 +97,6 @@
                                (db/with-conn (db/get-users-in-group group-id))))]
     (doseq [uid ids-to-send-to]
       (chsk-send! uid info))))
-
-(defmethod event-msg-handler :chsk/uidport-open
-  [{:as ev-msg :keys [event id ring-req]}]
-  (when-let [user-id (get-in ring-req [:session :user-id])]
-    (broadcast-user-change user-id [:user/connected user-id])))
-
-(defmethod event-msg-handler :chsk/uidport-close
-  [{:as ev-msg :keys [event id ring-req]}]
-  (when-let [user-id (get-in ring-req [:session :user-id])]
-    (broadcast-user-change user-id [:user/disconnected user-id])))

 (defn user-can-message? [user-id ?data]
   (db/with-conn
@@ -126,8 +116,9 @@
             (and
               (or (boolean (= (?data :group-id) (db/tag-group-id tag-id)))
                   (do
-                    (timbre/warnf "User %s attempted to add a tag %s from a different group"
-                                  user-id tag-id)
+                    (timbre/warnf
+                      "User %s attempted to add a tag %s from a different group"
+                      user-id tag-id)
                     false))
               (or (boolean (db/user-in-tag-group? user-id tag-id))
                   (do
@@ -139,12 +130,14 @@
           (fn [mentioned-id]
             (and
               (or (boolean (db/user-in-group? user-id (?data :group-id)))
-                  (do (timbre/warnf "User %s attempted to mention disallowed user %s"
-                                    user-id mentioned-id)
+                  (do (timbre/warnf
+                        "User %s attempted to mention disallowed user %s"
+                        user-id mentioned-id)
                       false))
               (or (boolean (db/user-visible-to-user? user-id mentioned-id))
-                (do (timbre/warnf "User %s attempted to mention disallowed user %s"
-                                  user-id mentioned-id)
+                  (do (timbre/warnf
+                        "User %s attempted to mention disallowed user %s"
+                        user-id mentioned-id)
                     false))))
           (?data :mentioned-user-ids))))))

@@ -170,10 +163,27 @@
           (when (notify-rules/notify? uid rules new-message)
             (if (online? uid)
               (chsk-send! uid [:chat/notify-message new-message])
-              (-> (email/create-message [(db/get-thread (new-message :thread-id))])
+              (-> (email/create-message
+                    [(db/get-thread (new-message :thread-id))])
                   (assoc :subject "Notification from Braid")
                   (->> (email/send-message (db/user-email uid)))))))))))

+;; Handlers
+
+(defmethod event-msg-handler :chsk/ws-ping
+  [ev-msg]
+  ; Do nothing, just avoid unhandled event message
+  )
+
+(defmethod event-msg-handler :chsk/uidport-open
+  [{:as ev-msg :keys [event id ring-req]}]
+  (when-let [user-id (get-in ring-req [:session :user-id])]
+    (broadcast-user-change user-id [:user/connected user-id])))
+
+(defmethod event-msg-handler :chsk/uidport-close
+  [{:as ev-msg :keys [event id ring-req]}]
+  (when-let [user-id (get-in ring-req [:session :user-id])]
+    (broadcast-user-change user-id [:user/disconnected user-id])))

 (defmethod event-msg-handler :chat/new-message
   [{:as ev-msg :keys [event id ?data ring-req ?reply-fn send-fn]}]
@@ -334,12 +344,14 @@
     (if (db/with-conn (db/user-in-group? user-id (?data :group-id)))
       (let [data (assoc ?data :inviter-id user-id)
             invitation (db/with-conn (db/create-invitation! data))]
-        (if-let [invited-user (db/with-conn (db/user-with-email (invitation :invitee-email)))]
+        (if-let [invited-user (db/with-conn
+                                (db/user-with-email (invitation :invitee-email)))]
           (chsk-send! (invited-user :id) [:chat/invitation-received invitation])
           (invites/send-invite invitation)))
       ; TODO: indicate permissions error to user?
-      (timbre/warnf "User %s attempted to invite %s to a group %s they don't have access to"
-                    user-id (?data :invitee-email) (?data :group-id)))))
+      (timbre/warnf
+        "User %s attempted to invite %s to a group %s they don't have access to"
+        user-id (?data :invitee-email) (?data :group-id)))))

 (defmethod event-msg-handler :chat/invitation-accept
   [{:as ev-msg :keys [event id ?data ring-req ?reply-fn send-fn]}]
@@ -373,7 +385,8 @@
         (when (and new-admin-id group-id
                 (db/user-is-group-admin? user-id group-id))
           (db/user-make-group-admin! new-admin-id group-id)
-          (broadcast-group-change group-id [:group/new-admin [group-id new-admin-id]]))))))
+          (broadcast-group-change group-id
+                                  [:group/new-admin [group-id new-admin-id]]))))))

 (defmethod event-msg-handler :chat/set-group-intro
   [{:as ev-msg :keys [event id ?data ring-req ?reply-fn send-fn]}]


CLJS-1022: Concatenate foreign dependencies safely

Join foreign JS dependencies with newlines between them to prevent
last-line comments from commenting out subsequent concatenated code.

--- src/clj/cljs/closure.clj
+++ src/clj/cljs/closure.clj
@@ -931,7 +931,7 @@
                                (:url-min ijs))
                           (:url ijs))]
               (slurp url)))]
-    (apply str (map to-js-str sources))))
+    (str (string/join "\n" (map to-js-str sources)) "\n")))

 (defn add-wrapper [{:keys [output-wrapper] :as opts} js]
   (if output-wrapper


need to compute source map offset when using foreign deps

--- src/clj/cljs/closure.clj
+++ src/clj/cljs/closure.clj
@@ -659,15 +659,17 @@
                             merged)
                           (assoc merged path (get closure-source-map path))))
                       merged)))
-                (spit (io/file name)
-                      (sm/encode merged
-                                 {:preamble-line-count preamble-line-count
-                                  :lines (+ (:lineCount sm-json) preamble-line-count 2)
-                                  :file (:file sm-json)
-                                  :output-dir (util/output-directory opts)
-                                  :source-map-path (:source-map-path opts)
-                                  :source-map (:source-map opts)
-                                  :relpaths relpaths}))))))
+                (spit
+                  (io/file name)
+                  (sm/encode merged
+                    {:preamble-line-count (+ preamble-line-count
+                                             (or (:foreign-deps-line-count opts) 0))
+                     :lines (+ (:lineCount sm-json) preamble-line-count 2)
+                     :file (:file sm-json)
+                     :output-dir (util/output-directory opts)
+                     :source-map-path (:source-map-path opts)
+                     :source-map (:source-map opts)
+                     :relpaths relpaths}))))))
         source)
       (report-failure result))))

@@ -894,12 +896,11 @@
 (defn add-header [opts js]
   (str (make-preamble opts) js))

-(defn add-foreign-deps [opts sources js]
+(defn foreign-deps-str [sources]
   (letfn [(to-js-str [ijs]
-            (util/debug-prn (into {} ijs))
             (let [url (or (:url-min ijs) (:url ijs))]
               (slurp url)))]
-    (apply str (concat (map to-js-str sources) [js]))))
+    (apply str (map to-js-str sources))))

 (defn add-wrapper [{:keys [output-wrapper] :as opts} js]
   (if output-wrapper
@@ -1056,7 +1057,11 @@
                                    [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)]))))
                  optim (:optimizations all-opts)
                  ret (if (and optim (not= optim :none))
-                       (do
+                       (let [fdeps-str (foreign-deps-str
+                                         (filter foreign-source? js-sources))
+                             all-opts (assoc all-opts
+                                        :foreign-deps-line-count
+                                        (- (count (.split #"\r?\n" fdeps-str -1)) 1))]
                          (when-let [fname (:source-map all-opts)]
                            (assert (string? fname)
                              (str ":source-map must name a file when using :whitespace, "
@@ -1069,8 +1074,7 @@
                                (remove foreign-source? js-sources)))
                            (add-wrapper all-opts)
                            (add-source-map-link all-opts)
-                           (add-foreign-deps all-opts
-                             (filter foreign-source? js-sources))
+                           (str fdeps-str)
                            (add-header all-opts)
                            (output-one-file all-opts)))
                        (apply output-unoptimized all-opts js-sources))]


CLJS-1131: cljs.closure/add-dependencies needs to be more aggressively set oriented

--- src/clj/cljs/closure.clj
+++ src/clj/cljs/closure.clj
@@ -523,7 +523,7 @@
           (recur (into (rest required-files) new-req)
                  (into visited new-req)
                  (conj js-deps js)))
-        (remove nil? js-deps)))))
+        (disj js-deps nil)))))

 (comment
   ;; only get cljs deps
@@ -539,11 +539,13 @@
   a new sequence of IJavaScript objects which includes the input list
   plus all dependencies in dependency order."
   [opts & inputs]
-  (let [requires      (mapcat deps/-requires inputs)
-        required-cljs (remove (set inputs) (cljs-dependencies opts requires))
-        required-js   (js-dependencies opts (set (concat (mapcat deps/-requires required-cljs) requires)))
-        provided      (mapcat deps/-provides (concat inputs required-cljs required-js))
-        unprovided    (clojure.set/difference (set requires) (set provided) #{"constants-table"})]
+  (let [inputs        (set inputs)
+        requires      (set (mapcat deps/-requires inputs))
+        required-cljs (clojure.set/difference (cljs-dependencies opts requires) inputs)
+        required-js   (js-dependencies opts
+                        (into (set (mapcat deps/-requires required-cljs)) requires))
+        provided      (set (mapcat deps/-provides (clojure.set/union inputs required-cljs required-js)))
+        unprovided    (clojure.set/difference requires provided #{"constants-table"})]
     (when (seq unprovided)
       (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)}))
     (cons


added prod server defaults

added prod server defaults

--- src/clj/conedit/core.clj
+++ src/clj/conedit/core.clj
@@ -32,10 +32,12 @@
         (root-handler))
       {:port port})))

+(defn -main []
+  (println "Launching!")
+  (start-server {:dev false :port 3000}))
+
 (comment
   (start-server {})

-
-
   *e
   )


Move loaded data to single atom

Move loaded data to single atom

--- src/cljs/gtfve/data.cljs
+++ src/cljs/gtfve/data.cljs
@@ -32,28 +32,29 @@
            (map (partial zipmap headers))
            (map drop-empty)))))

-(go
-  (defonce shapes
-    (->> (<! (GET (str feed "shapes.txt")))
-         (csv/parse)
-         (csv->map)))
+(defonce db (atom {}))

-  (defonce routes
-    (->> (<! (GET (str feed "routes.txt")))
-         (csv/parse)
-         (csv->map)))
-
-  (defonce stops
-    (->> (<! (GET (str feed "stops.txt")))
-         (csv/parse)
-         (csv->map)))
-
-  (defonce stop-times
-    (->> (<! (GET (str feed "stop_times.txt")))
-         (csv/parse)
-         (csv->map)))
-
-  (defonce trips
-    (->> (<! (GET (str feed "trips.txt")))
-         (csv/parse)
-         (csv->map))))
+(defn init! []
+  "Initialise data"
+  (go
+    (let [shapes (->> (<! (GET (str feed "shapes.txt")))
+                      (csv/parse)
+                      (csv->map))
+          routes (->> (<! (GET (str feed "routes.txt")))
+                      (csv/parse)
+                      (csv->map))
+          stops (->> (<! (GET (str feed "stops.txt")))
+                     (csv/parse)
+                     (csv->map))
+          stop-times (->> (<! (GET (str feed "stop_times.txt")))
+                          (csv/parse)
+                          (csv->map))
+          trips (->> (<! (GET (str feed "trips.txt")))
+                     (csv/parse)
+                     (csv->map))]
+      (swap! db assoc
+             :shapes shapes
+             :routes routes
+             :stops stops
+             :stop-times stop-times
+             :trips trips))))


Game over when life runs out or fall down off of screen


update marker location working while in unit view

--- src/cljs/lokate/map.cljs
+++ src/cljs/lokate/map.cljs
@@ -33,7 +33,6 @@

 (defn reset-markers [owner]
   (let [markers (om/get-state owner :markers)]
-    (.log js/console (pr-str markers))
     (dorun
       (map #(.setIcon (:marker %) (reset-ico (:icon %))) markers))))

@@ -70,8 +69,7 @@
     {:marker marker :icon icon :pos pos :id (:id point)}))

 (defn add-markers [data owner units]
-  (let [map (om/get-state owner :map)
-        units (filter (comp not empty? :pos) units)]
+  (let [map (om/get-state owner :map)]
     (om/update-state! owner :markers
       #(into % (for [unit units]
                  (mark-it! data owner map unit))))))
@@ -79,12 +77,18 @@
 (defn add-group [owner places opts]
   (map #({:name (:name %) :group (js/L.MarkerClusterGroup.)})))

+(defn in?
+  "true if seq contains elm"
+  [seq elm]
+  (some #(= elm %) seq))
+
 (defn delete-markers [owner units]
   (let [l-map (om/get-state owner :map)]
     (dorun
-     (map #(.removeLayer l-map (om/get-state owner [:markers % :marker]))
-       (map :id units))))
-  (om/update-state! owner :markers #(apply dissoc % (map first units))))
+      (map #(.removeLayer l-map (om/get-state owner [:markers % :marker]))
+        (map :id units))))
+  (om/update-state! owner :markers
+    #(into [] (filter (comp not (partial in? (map :id units)) :id) %))))

 (defn cancel-action [owner]
   (js/clearTimeout (om/get-state owner :evt-timeout))
@@ -101,12 +105,16 @@

     om/IWillReceiveProps
     (will-receive-props [this {:keys [collections] :as next-props}]
-      (let [next-units (reduce into #{} (map :points collections))
-            current-units (reduce into #{} (map :points (:collections (om/get-props owner))))
+      (let [next-units (set/select (comp not empty? :pos)
+                         (reduce into #{}
+                           (map :points collections)))
+            current-units (set/select (comp not empty? :pos)
+                            (reduce into #{}
+                              (map :points (:collections (om/get-props owner)))))
             to-add (set/difference next-units current-units)
             to-delete (set/difference current-units next-units)]

-        ;(delete-markers owner to-delete)
+        (delete-markers owner to-delete)
         (add-markers data owner to-add)
         (reset-markers owner)
         (when (re-matches #"/collections/(\d+)/points/(\d+)" (:route-name next-props))


Fix #605 by clarifying profile page (#606)

Adds a paragraph clarifying that your profile is just your email address and your password.

--- src/clojars/web/user.clj
+++ src/clojars/web/user.clj
@@ -84,6 +84,7 @@
              (flash flash-msg)
              [:h1 "Profile"]
              (error-list errors)
+             [:p "Your Clojars profile is just your email address and your password. You can change them here, if want."]
              (form-to [:post "/profile"]
                       (label :email "Email")
                       [:input {:type :email :name :email :id


fix regex

fix regex

--- src/codeitlater/core.clj
+++ src/codeitlater/core.clj
@@ -2,15 +2,17 @@
   (:require [clojure.java.io :as io])
   (:gen-class))

-(def testMark "//")
 (def testKeyword "TODO") ;:=using keyword to filter which is which

+;; This regex expression get help from: https://stackoverflow.com/questions/45848999/clojure-regex-delete-whitespace-in-pattern
 (defn make-pattern [commentMark]
-  (re-pattern (str "(?<=" commentMark "+:=)" ".+")))
+  (re-pattern (str commentMark "+:=\\s*" "(.+)")))
+

 (defn read-comments-inline [pattern line]
   (let [result (re-find pattern line)]
-    result))
+    (second result)))
+

 (defn read-comments-in-file [filepath pickcomment] ;pickcomment is curry function
   (with-open [codefile (clojure.java.io/reader filepath)]
@@ -22,6 +24,7 @@
         (list lineNum comment))
       )))

+
 (defn get-all-files
   "return a lazy sequence including all files"
   ([]
@@ -29,6 +32,7 @@
   ([root]
    (map #(.getPath %) (file-seq (io/file root))))
   )
+

 (defn read-files ;;:= TODO: directory format
   "commentMarkFunc is a curry function to give read-comments-in-file
@@ -54,6 +58,7 @@
                                            commentMarkFunc)
                     filepath))))))

+
 (defn -main [& args]
   (println args)
   (println (read-files (partial read-comments-inline (make-pattern ";")) "./src" "clj" ""))


add utilities for partitions and attributes namespaces

--- src/navigator.clj
+++ src/navigator.clj
@@ -18,6 +18,24 @@
 (defn init! [system]
   [(schematode/init-schematode-constraints! (:db-conn system))
    (schematode/load-schema! (:db-conn system) schema/schema)])
+
+;; utitlities
+
+(defn slam
+  "Slams two keywords together into one namespaced keyword"
+  [ns n]
+  (keyword (name ns) (name n)))
+
+(defn prefix-keys
+  "Prefix all keys in a map with prefix"
+  [m prefix]
+  (into {} (for [[k v] m] [(slam prefix k) v])))
+
+(defn get-partition
+  "Get the db partition an entity is in"
+  [entity-type]
+  (or (get-in schema/schema-map [entity-type :part])
+      :db.part/user))

 ;; Get functions


misc cleanup

misc cleanup

--- src/seri/html.clj
+++ src/seri/html.clj
@@ -27,19 +27,6 @@
   (visit [_ w] (w (for [[k v] attrs-map]
                     [" " (name k) (safe \= \") v (safe \")]))))

-(defn- dom-node [tag attrs content]
-  (let [[_ tag id class] (re-matches re-tag (name tag))
-        tag-name (name tag)
-        attrs (TagAttrs.
-               tag
-               (merge (into {} (filter (fn [[k v]] (not (nil? v)))
-                                       {:id id :class class}))
-                      attrs))
-        is-empty (and (maybe-empty-tags tag) (empty? content))
-        sf safe]
-    [(sf \<) tag-name attrs (if is-empty (sf " />") (sf \>))
-     (if-not is-empty [content (sf "</") tag-name (sf \>)])]))
-
 (deftype DomNode [tag attrs content]
   HtmlSerializable
   (visit [_ w]
@@ -49,20 +36,20 @@
                  tag
                  (merge (into {} (filter (fn [[k v]] (not (nil? v)))
                                          {:id id :class class}))
-                        attrs))
+                        attrs))
           is-empty (and (maybe-empty-tags tag) (empty? content))
           open-tag [(safe \<) tag-name attrs
                     (if is-empty (safe " />") (safe \>))]
-          close-tag [(safe "</") tag-name (safe \>)]]
+          close-tag [(safe "</") tag-name (safe \>)]]
       (w [open-tag (if-not is-empty [content close-tag])]) )))

 (defn visit-vector [v w]
   (let [named? (fn [x] (instance? clojure.lang.Named x))]
     (match/match
-     [v]
+     [v]
      [([(tag :when named?) (attrs :when map?) & content] :seq)]
      (w (DomNode. tag attrs content))
-
+
      [([(tag :when named?) & content] :seq)]
      (w (DomNode. tag {} content))

@@ -77,21 +64,18 @@

 ;;; test

-;; (to-html [" match tag: " [:p#asdf "Paragraph&" [1 2 "34" 'bar] " Foo"]
-;;           " match tag: " [:p#fdsf.oiu {:id "bar"} "Paragraph" " Foo"]
-;;           " empty :" [:hr.clear {:asdf 1234}]
-
-;;           "safe: " (apply safe (keys html-esc-map))
-;;           " chars to esc: " (keys html-esc-map)
-;;           (apply str (keys html-esc-map))
-;;           ])
-
-(to-html
-  [" match tag: " [:p "Paragraph&" [1 2 "34" 'bar] " Foo"]
-   " match tag: " [:p {:id "bar" :ti&tle "fo\"o"} "Paragraph" " Foo"]
-   " match tag: " [:input]
-   " match tag: " [:input {:type "text"}]
-   " match tag: " [:form#contact {:action "GET"}
+#_(to-html
+   [" match tag: " [:p "Paragraph&" [1 2 "34" 'bar] " Foo"]
+    " match tag: " [:p {:id "bar" :ti&tle "fo\"o"} "Paragraph" " Foo"]
+    " match tag: " [:input]
+    " match tag: " [:input {:type "text"}]
+    " match tag: " [:form#contact {:action "GET"}
                     [:input {:type "text"
                              :name "first-name"
-                             :value "Tavis & Company"}]]])
+                             :value "Tavis & Company"}]]
+    " empty :" [:hr.clear {:asdf 1234}]
+    " safe: " (apply safe (keys html-esc-map))
+    " chars to esc: " (keys html-esc-map)
+    (apply str (keys html-esc-map))
+    ]
+   )


fixed :document-changed reaction, implemented new timestamp view

--- src/app/cljs/one/sample/view/editor.cljs
+++ src/app/cljs/one/sample/view/editor.cljs
@@ -8,11 +8,13 @@
             [goog.editor.Field         :as editor-iframe]
             [goog.editor.SeamlessField :as editor-div]
             [one.dispatch              :as dispatch]
-            [crate.core                :as crate]))
+            [crate.core                :as crate]
+            [dates                     :as dates]))

-(def CHANGE goog.editor.Field.EventType.DELAYEDCHANGE)
-(def CLICK  goog.events.EventType.CLICK)
-(def BLUR   goog.events.EventType.BLUR)
+(def CHANGE    goog.editor.Field.EventType.DELAYEDCHANGE)
+(def CLICK     goog.events.EventType.CLICK)
+(def BLUR      goog.events.EventType.BLUR)
+(def MOUSEOVER goog.events.EventType.MOUSEOVER)

 (defn- strip-html [s]
   (-> s
@@ -76,30 +78,33 @@

 (defn- add-item-listeners! []
   (doseq [item (nodes ($ "#sidebar-documents > ol > li"))]
-    (let [$bookmark ($ "a.bookmark" item)
-          $delete   ($ "button.delete" item)]
-      (event/listen $bookmark
-                    CLICK
-                    (fn [e] (.preventDefault e)))
-      (event/listen $delete
-                    CLICK
-                    (fn [e]
-                      (.stopPropagation e)
-                      (remove-document (.getAttribute item "docid"))))
-      (event/listen item
-                    CLICK
-                    (fn [e]
-                      (dispatch/fire :document-requested
-                                     (.getAttribute item "docid")))))))
+    (event/listen ($ "a.bookmark" item)
+                  CLICK
+                  (fn [e] (.preventDefault e)))
+    (event/listen ($ "button.delete" item)
+                  CLICK
+                  (fn [e]
+                    (.stopPropagation e)
+                    (remove-document (.getAttribute item "docid"))))
+    (event/listen item
+                  MOUSEOVER
+                  (fn [e]
+                    (set! (.-innerHTML ($ "a.bookmark" item))
+                          (dates/relative (.getAttribute item "docts")))))
+    (event/listen item
+                  CLICK
+                  (fn [e]
+                    (dispatch/fire :document-requested
+                                   (.getAttribute item "docid"))))))

 (defpartial document-list-item [{:keys [id content ts title]}]
-  [:li {:docid id}
+  [:li {:docid id :docts ts}
    [:button.delete]
    [:div.excerpt
     [:span (or title (excerpt content 20))]]
    [:div.document-id
     [:span [:a.bookmark {:href (str \# id)}
-            (.toString (js/Date. ts))]]]])
+            (dates/relative ts)]]]])

 (defn- append-list-items! [target items]
   (doseq [item items]
@@ -114,5 +119,5 @@

 (dispatch/react-to #{:document-changed}
                    (fn [_ d]
-                     (if (not= (:document @state) d)
+                     (if (not= (:document @state) (:id d))
                        (list-documents @docs))))


add replace-patterns post-processor to add infl and agr value-sharing so that conjugation can work

--- src/babel/francais/morphology/verbs.cljc
+++ src/babel/francais/morphology/verbs.cljc
@@ -8,41 +8,46 @@
    #?(:cljs [babel.logjs :as log])
    [dag_unify.core :refer (copy dissoc-paths fail? get-in merge ref? strip-refs unifyc)]))

-(def replace-patterns
+(def replace-patterns-source
   [;; <reflexive past>: e.g. "amusé" => "s'amuser"
-   {:i [#"^([aeéiou].*)é$"   "s'$1er"] ;; :i : inflection
-    :c [#"s'([aeéiou].*)er$" "$1é"]  ;; :c : conjugation
-    :u {:synsem {:infl :past-p
+   {:i [#"^([aeéiou]\S+)é$"    "s'$1er"] ;; :i : how to transform a finite form to infinitive
+    :c [#"^s'([aeéiou]\S+)er$" "$1é"]  ;; :c : how to transform an infinitive into a finite form.
+    :u {:synsem {:infl :past-p ;; :u : what to unify against for these tranformations to occur.
                  :subcat {:1 {:agr {:number :sing
                                     :gender :masc}}}}}}
-   {:i [#"^([aeéiou].*)és$"  "s'$1er"]
-    :c [#"s'([aeéiou].*)er$" "$1és"]
+   {:i [#"^([aeéiou]\S+)és$"   "s'$1er"]
+    :c [#"^s'([aeéiou]\S+)er$" "$1és"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :plur
                                     :gender :masc}}}}}}
-   {:i [#"^([aeéiou].*)ée$"  "s'$1er"]
-    :c [#"s'([aeéiou].*)er$" "$1ée"]
+   {:i [#"^([aeéiou]\S+)ée$"   "s'$1er"]
+    :c [#"^s'([aeéiou]\S+)er$" "$1ée"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :sing
                                     :gender :fem}}}}}}
-   {:i [#"^([aeéiou].*)ées$" "s'$1er"]
-    :c [#"s'([aeéiou].*)er$" "$1ées"]
+   {:i [#"^([aeéiou]\S+)ées$"  "s'$1er"]
+    :c [#"^s'([aeéiou]\S+)er$" "$1ées"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :plur
                                     :gender :fem}}}}}}
-   {:i [#"^([^aeéiou].*)é$" "se $1er"]
+   {:i [#"^([^aeéiou]\S+)é$"   "se $1er"]
+    :c [#"^se (\S+)er$"        "$1é"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :sing
                                     :gender :masc}}}}}}
-   {:i [#"^([^aeéiou].*)és$" "se $1er"]
+
+   {:i [#"^([^aeéiou]\S+)és$"  "se $1er"]
+    :c [#"^se (\S+)er$"        "$1és"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :plur
                                     :gender :masc}}}}}}
-   {:i [#"^([^aeéiou].*)ée$" "se $1er"]
+   {:i [#"^([^aeéiou]\S+)ée$"  "se $1er"]
+    :c [#"^se (\S+)er$"        "$1é"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :sing
                                     :gender :fem}}}}}}
-   {:i [#"^([^aeéiou].*)ées$" "se $1er"]
+   {:i [#"^([^aeéiou]\S+)ées$" "se $1er"]
+    :c [#"^se (\S+)er$"        "$1ées"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :plur
                                     :gender :fem}}}}}}
@@ -50,83 +55,96 @@

    ;; <non-reflexive past>: e.g. "parlé" => "parler"
    ;; singular: could be masculine or feminine
-   {:i [#"^(.*)é$" "$1er"]
+   {:i [#"^(\S+)é$"            "$1er"]
+    :c [#"^(\S+)er$"           "$1é"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :sing}}}}}}
+
    ;; plural: could be masculine or feminine
-   {:i [#"^(.*)és$" "$1er"]
+   {:i [#"^(\S+)és$"           "$1er"]
+    :c [#"^(\S+)er$"           "$1é"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :plur}}}}}}

    ;; singular feminine
-   {:i [#"^(.*)ée$" "$1er"]
+   {:i [#"^(\S+)ée$"           "$1er"]
+    :c [#"^(\S+)er$"           "$1ée"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :sing
                                     :gender :fem}}}}}}
    ;; plural feminine
-   {:i [#"^(.*)ées$" "$1er"]
+   {:i [#"^(\S+)ées$"          "$1er"]
+    :c [#"^(\S+)er$"           "$1ée"]
     :u {:synsem {:infl :past-p
                  :subcat {:1 {:agr {:number :plur
                                     :gender :fem}}}}}}
    ;; </non-reflexive past>

    ;; <reflexive present -er and -ir type verbs>
-   {:i [#"^([aeéiou]\S+)e$" "s'$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :sing
-                                    :person :1st}}}
-                 :infl :present}}}
-
-   {:i [#"^([aeéiou]\S+)es$" "s'$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :sing
-                                    :person :2nd}}}
-                 :infl :present}}}
-
-   {:i [#"^([aeéiou]\S+)e$" "s'$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :sing
-                                    :person :3rd}}}
-                 :infl :present}}}
-
-   {:i [#"^([aeéiou]\S+)ons$" "s'$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :plur
-                                    :person :1st}}}
-                 :infl :present}}}
-
-   {:i [#"^([aeéiou]\S+)ez$" "s'$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :plur
-                                    :person :2nd}}}
-                 :infl :present}}}
-
-   {:i [#"^([aeéiou]\S+)ent$" "s'$1er"]
+   {:i [#"^([aeéiou]\S+)e$"    "s'$1er"]
+    :c [#"^s'(\S+)[ie]r$"      "$1e"]
+    :u {:synsem {:subcat {:1 {:agr {:number :sing
+                                    :person :1st}}}
+                 :infl :present}}}
+
+   {:i [#"^([aeéiou]\S+)es$"   "s'$1er"]
+    :c [#"^s'(\S+)[ie]r$"      "$1es"]
+    :u {:synsem {:subcat {:1 {:agr {:number :sing
+                                    :person :2nd}}}
+                 :infl :present}}}
+
+   {:i [#"^([aeéiou]\S+)e$"    "s'$1er"]
+    :c [#"^s'(\S+)[ie]r$"      "$1e"]
+    :u {:synsem {:subcat {:1 {:agr {:number :sing
+                                    :person :3rd}}}
+                 :infl :present}}}
+
+   {:i [#"^([aeéiou]\S+)ons$"  "s'$1er"]
+    :u {:synsem {:subcat {:1 {:agr {:number :plur
+                                    :person :1st}}}
+                 :infl :present}}}
+
+   {:i [#"^([aeéiou]\S+)ez$"   "s'$1er"]
+    :u {:synsem {:subcat {:1 {:agr {:number :plur
+                                    :person :2nd}}}
+                 :infl :present}}}
+
+   {:i [#"^([aeéiou]\S+)ent$"  "s'$1er"]
     :u {:synsem {:subcat {:1 {:agr {:number :plur
                                     :person :3rd}}}
                  :infl :present}}}

-   {:i [#"^([^aeéiou]\S+)e$" "se $1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :sing
-                                    :person :1st}}}
-                 :infl :present}}}
-
-   {:i [#"^([^aeéiou]\S+)es$" "se $1er"]
+   {:i [#"^([^aeéiou]\S+)e$"   "se $1er"]
+    :u {:synsem {:subcat {:1 {:agr {:number :sing
+                                    :person :1st}}}
+                 :infl :present}}}
+
+   {:i [#"^([^aeéiou]\S+)es$"  "se $1er"]
+    :c [#"^se (\S+)[ie]r$"     "$1es"]
     :u {:synsem {:subcat {:1 {:agr {:number :sing
                                     :person :2nd}}}
                  :infl :present}}}

-   {:i [#"^([^aeéiou]\S+)e$" "se $1er"]
+   {:i [#"^([^aeéiou]\S+)e$"   "se $1er"]
+    :c [#"^se (\S+)[ie]r$"     "$1es"]
     :u {:synsem {:subcat {:1 {:agr {:number :sing
                                     :person :3rd}}}
                  :infl :present}}}

    {:i [#"^([^aeéiou]\S+)ons$" "s'$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :plur
-                                    :person :1st}}}
-                 :infl :present}}}
-
-   {:i [#"^([^aeéiou]\S+)ez$" "s'$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :plur
-                                 :person :2nd}}}
+    :c [#"^se (\S+)[ie]r$"     "$1ons"]
+    :u {:synsem {:subcat {:1 {:agr {:number :plur
+                                    :person :1st}}}
+                 :infl :present}}}
+
+   {:i [#"^([^aeéiou]\S+)ez$"  "s'$1er"]
+    :c [#"^se (\S+)[ie]r$"     "$1ez"]
+    :u {:synsem {:subcat {:1 {:agr {:number :plur
+                                    :person :2nd}}}
               :infl :present}}}

    {:i [#"^([^aeéiou]\S+)ent$" "s'$1er"]
+    :c [#"^se (\S+)[ie]r$"     "$1ent"]
     :u {:synsem {:subcat {:1 {:agr {:number :plur
                                     :person :3rd}}}
                  :infl :present}}}
@@ -135,64 +153,76 @@

    ;; <present non-reflexive>
    ;; <-er verbs>
-   {:i [#"^(\S+)e$" "$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :sing
-                                    :person :1st}}}
-                 :infl :present}}}
-
-   {:i [#"^(\S+)es$" "$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :sing
-                                    :person :2nd}}}
-                 :infl :present}}}
-
-   {:i [#"^(\S+)e$" "$1er"]
+   {:i [#"^(\S+)e$"            "$1er"]
+    :c [#"^(\S+)er$"           "$1e"]
+    :u {:synsem {:subcat {:1 {:agr {:number :sing
+                                    :person :1st}}}
+                 :infl :present}}}
+
+   {:i [#"^(\S+)es$"           "$1er"]
+    :c [#"^(\S+)er$"           "$1es"]
+    :u {:synsem {:subcat {:1 {:agr {:number :sing
+                                    :person :2nd}}}
+                 :infl :present}}}
+
+   {:i [#"^(\S+)e$"            "$1er"]
+    :c [#"^([^ ']+)er$"           "$1e"]
     :u {:synsem {:subcat {:1 {:agr {:number :sing
                                     :person :3rd}}}
                  :infl :present}}}

-   {:i [#"^(\S+)ons$" "$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :plur
-                                    :person :1st}}}
-                 :infl :present}}}
-
-   {:i [#"^(\S+)ez$" "$1er"]
-    :u {:synsem {:subcat {:1 {:agr {:number :plur
-                                    :person :2nd}}}
-                 :infl :present}}}
-
-   {:i [#"^(\S+)ent$" "$1er"]
+   {:i [#"^(\S+)ons$"          "$1er"]
+    :c [#"^(\S+)er$"           "$1ons"]
+    :u {:synsem {:subcat {:1 {:agr {:number :plur
+                                    :person :1st}}}
+                 :infl :present}}}
+
+   {:i [#"^(\S+)ez$"           "$1er"]
+    :c [#"^(\S+)er$"           "$1ez"]
+    :u {:synsem {:subcat {:1 {:agr {:number :plur
+                                    :person :2nd}}}
+                 :infl :present}}}
+
+   {:i [#"^(\S+)ent$"          "$1er"]
+    :c [#"^(\S+)er$"           "$1ent"]
     :u {:synsem {:subcat {:1 {:agr {:number :plur
                                     :person :3rd}}}
                  :infl :present}}}
    ;; </-er verbs>

    ;; <ir verbs>
-   {:i [#"^(\S+)e$" "$1ir"]
-    :u {:synsem {:subcat {:1 {:agr {:number :sing
-                                    :person :1st}}}
-                 :infl :present}}}
-
-   {:i [#"^(\S+)es$" "$1ir"]
-    :u {:synsem {:subcat {:1 {:agr {:number :sing
-                                    :person :2nd}}}
-                 :infl :present}}}
-
-   {:i [#"^(\S+)e$" "$1ir"]
+   {:i [#"^(\S+)e$"            "$1ir"]
+    :c [#"^(\S+)ir$"           "$1e"]
+    :u {:synsem {:subcat {:1 {:agr {:number :sing
+                                    :person :1st}}}
+                 :infl :present}}}
+
+   {:i [#"^(\S+)es$"           "$1ir"]
+    :c [#"^(\S+)ir$"           "$1es"]
+    :u {:synsem {:subcat {:1 {:agr {:number :sing
+                                    :person :2nd}}}
+                 :infl :present}}}
+
+   {:i [#"^(\S+)e$"            "$1ir"]
+    :c [#"^(\S+)ir$"           "$1e"]
     :u {:synsem {:subcat {:1 {:agr {:number :sing
                                     :person :3rd}}}
                  :infl :present}}}

-   {:i [#"^(\S+)ons$" "$1ir"]
-    :u {:synsem {:subcat {:1 {:agr {:number :plur
-                                    :person :1st}}}
-                 :infl :present}}}
-
-   {:i [#"^(\S+)ez$" "$1ir"]
-    :u {:synsem {:subcat {:1 {:agr {:number :plur
-                                    :person :2nd}}}
-                 :infl :present}}}
-
-   {:i [#"^(\S+)ent$" "$1ir"]
+   {:i [#"^(\S+)ons$"          "$1ir"]
+    :c [#"^(\S+)ir$"           "$1ons"]
+    :u {:synsem {:subcat {:1 {:agr {:number :plur
+                                    :person :1st}}}
+                 :infl :present}}}
+
+   {:i [#"^(\S+)ez$"           "$1ir"]
+    :c [#"^(\S+)ir$"           "$1ez"]
+    :u {:synsem {:subcat {:1 {:agr {:number :plur
+                                    :person :2nd}}}
+                 :infl :present}}}
+
+   {:i [#"^(\S+)ent$"          "$1ir"]
+    :c [#"^(\S+)ir$"           "$1ent"]
     :u {:synsem {:subcat {:1 {:agr {:number :plur
                                     :person :3rd}}}
                  :infl :present}}}
@@ -200,6 +230,22 @@
    ;; </present non-reflexive>

    ])
+
+;; this (map..) is needed because a word within a syntactic tree will be conjugated
+;; as a part of :a or :b within the tree, and with its :infl and :agr as keys within
+;; the :francais part of the verb.
+(def replace-patterns
+  (map (fn [pattern]
+         {:i (:i pattern)
+          :c (:c pattern)
+          :u (unifyc (unifyc (:u pattern)
+                             (let [agr (atom :top)
+                                   infl (atom :top)]
+                               {:synsem {:infl infl
+                                         :subcat {:1 {:agr agr}}}
+                                :infl infl
+                                :agr agr})))})
+       replace-patterns-source))

 (defn exception [error-string]
   #?(:clj
@@ -489,11 +535,11 @@
         number-and-person (number-and-person number person)
         ]
     (cond
-
       (and number-and-person
            (get-in word [:present number-and-person]))
       (get-in word [:present number-and-person])

+
      ;;QUI COMINCIANO I VERBI FRANCESI REGOLARI
      (and (= person :1st) (= number :sing) er-type)
      (str stem "e")


analyze regular feminine plural nouns

analyze regular feminine plural nouns

--- src/babel/italiano/morphology/nouns.cljc
+++ src/babel/italiano/morphology/nouns.cljc
@@ -10,14 +10,30 @@
 (def replace-patterns
   [

+   {:p [#"(.*)e$" "$1a"] ;; "donne" -> "donna"
+    :u {:synsem {:cat :noun
+                 :agr {:gender :fem
+                       :number :plur}}}}
+
+   {:p [#"(.*)e$" "$1à"] ;; "citte" -> "città"
+    :u {:synsem {:cat :noun
+                 :agr {:gender :fem
+                       :number :plur}}}}
+
    {:p [#"(.*)i$" "$1io"] ;; "bracci" -> "braccio"
     :u {:synsem {:cat :noun
                  :agr {:number :plur}}}}

-   {:p [#"([^i])i$" "$1e"] ;; "cani" -> "cane"
+   {:p [#"(.*)i$" "$1e"] ;; "cani" -> "cane"
     :u {:synsem {:cat :noun
                  :agr {:gender :masc
                        :number :plur}}}}
+
+   {:p [#"(.*)i$" "$1o"] ;; "compiti" -> "compito"
+    :u {:synsem {:cat :noun
+                 :agr {:number :plur}}}}
+
+

    ])


[core] Fix error due to require of removed lib

Removed the dependency in 44e5b5 but forgot to remove the require

--- src/braid/core/client/state/helpers.cljs
+++ src/braid/core/client/state/helpers.cljs
@@ -2,7 +2,6 @@
   (:require
    [braid.core.client.schema :as schema]
    [braid.lib.misc :as misc]
-   [cljs-utils.core :refer [flip]]
    [clojure.set :as set]))

 (def key-by misc/key-by)
@@ -116,5 +115,5 @@
   (let [group-threads (get-in state [:group-threads group-id])]
     (-> state
         (update-in [:threads] #(apply dissoc % group-threads))
-        (update-in [:group-threads] (flip dissoc group-id))
-        (update-in [:groups] (flip dissoc group-id)))))
+        (update-in [:group-threads] (misc/flip dissoc group-id))
+        (update-in [:groups] (misc/flip dissoc group-id)))))


Wrap user AI functions in an exception handler

--- src/clj/zetawar/players/embedded_ai.cljs
+++ src/clj/zetawar/players/embedded_ai.cljs
@@ -92,6 +92,16 @@
         (game/unit? actor)
         (choose-unit-action db game ctx actor)))))

+(defn wrap-exception-handler [f]
+  (fn [& args]
+    (let [f' (apply f args)]
+      (fn [& args']
+        (try
+          (apply f' args')
+          (catch :default ex
+            (log/error "Error running AI function:" ex)
+            nil))))))
+
 (defmethod handle-event ::players/update-game-state
   [{:as player :keys [conn faction-color fns]} [_ _ game-state]]
   (let [new-conn (d/create-conn db/schema)
@@ -100,10 +110,10 @@
     (let [db @conn
           game (game/game-by-id db game-id)
           {:keys [action-ctx actor-score-fn base-action-score-fn unit-action-score-fn]} fns
-          action (or (binding [*action-ctx* (or action-ctx (constantly nil))
-                               *actor-score-fn* actor-score-fn
-                               *base-action-score-fn* base-action-score-fn
-                               *unit-action-score-fn* unit-action-score-fn]
+          action (or (binding [*action-ctx* (wrap-exception-handler (or action-ctx (constantly nil)))
+                               *actor-score-fn* (wrap-exception-handler actor-score-fn)
+                               *base-action-score-fn* (wrap-exception-handler base-action-score-fn)
+                               *unit-action-score-fn* (wrap-exception-handler unit-action-score-fn)]
                        (choose-action player db game))
                      {:action/type :action.type/end-turn
                       :action/faction-color faction-color})]


stop warnings about shadowing find

stop warnings about shadowing find

--- src/clj_money/models/reconciliations.clj
+++ src/clj_money/models/reconciliations.clj
@@ -1,5 +1,5 @@
 (ns clj-money.models.reconciliations
-  (:refer-clojure :exclude [update])
+  (:refer-clojure :exclude [update find])
   (:require [clojure.spec :as s]
             [clojure.pprint :refer [pprint]]
             [clj-time.coerce :refer [to-long


force heroku rebuild

force heroku rebuild

--- src/cljc/fractalify/workers/schemas.cljc
+++ src/cljc/fractalify/workers/schemas.cljc
@@ -57,4 +57,4 @@
     (+keyword-coercion+ schema)
     (coerce/+string-coercions+ schema)
     (coerce/keyword-enum-matcher schema)
-    (coerce/set-matcher schema)))
+    (coerce/set-matcher schema)))


remove refactored out code

remove refactored out code

--- src/cljs/gointermod/search/handlers.cljs
+++ src/cljs/gointermod/search/handlers.cljs
@@ -42,20 +42,6 @@
     (assoc db :search-term term)
     ))

-(re-frame/register-handler
- ;
- :update-search-results
- (fn [db [_ search-results]]
-   (assoc db :search-results search-results)
-))
-
-(defn aggregate-by-species [search-results]
-  "helper to get the counts of aggregate go terms per organism / go term / ontology branch "
-  (reduce (fn [new-map [_ _ _ _ _ primary-id secondary-id symbol organism & args ]]
-    (update-in new-map [(keyword organism ) (keyword (utils/get-id primary-id secondary-id symbol)) (keyword (last args))] inc))
-      {} search-results)
-)
-
 (defn aggregate-by-orthologue [search-results]
   "helper to get the counts of aggregate go terms per organism / go term / ontology branch "
   (reduce (fn [new-map [_ _ _ _ _ primary-id secondary-id symbol organism & args ]]
@@ -64,17 +50,8 @@
 )

 (re-frame/register-handler
- :aggregate-search-results
- (fn [db [_ search-results]]
-   ;(.log js/console "Aggregate: (aggregate-by-species search-results)" (clj->js (aggregate-by-species search-results)))
-   (assoc db :aggregate-results (aggregate-by-species search-results)))
-)
-
-
-(re-frame/register-handler
  :concat-results
  (fn [db [_ search-results source]]
-;   (.log js/console (clj->js source) "Concat: " (clj->js (:results search-results)))
    (assoc-in db [:multi-mine-results source] search-results)
    (assoc-in db [:multi-mine-aggregate source] (aggregate-by-orthologue (:results search-results))))
 )
@@ -83,18 +60,7 @@
   ;;What do we do when a search button is pressed? This.
   :perform-search
   (fn [db [_ _]]
-    ;query humanmine
-    (go (let
-      [search-results (<! (comms/go-query (:selected-organism db) (:search-term db) :mouse))]
-        ;add results to the db
-      ;  (re-frame/dispatch [:update-search-results search-results])
-        (re-frame/dispatch [:aggregate-search-results (:results search-results)])
-
-          (comms/query-all-selected-organisms (:selected-organism db) (:search-term db))
-))
-    ;experimental multiples query
-;  (go (let [search-results (<! (comms/query-all-selected-organisms (:selected-organism db) (:search-term db)))]
-;        (.log js/console "Booya")
-;  ))
-
-    db))
+    ;asynchronously query all dem mines and add the results to the db
+    (go
+      (comms/query-all-selected-organisms (:selected-organism db) (:search-term db))
+) db))


time/every

time/every

plus make it portable, and with better error correction

--- src/cljx/jamesmacaulay/zelkova/time.cljx
+++ src/cljx/jamesmacaulay/zelkova/time.cljx
@@ -1,14 +1,20 @@
 #+clj
 (ns jamesmacaulay.zelkova.time
-  (:refer-clojure :exclude [second]))
+  (:refer-clojure :exclude [second])
+  (:require [jamesmacaulay.zelkova.signal :as z]
+            [cljs.core.async :as async :refer [>! <! go go-loop]]))

 #+cljs
 (ns jamesmacaulay.zelkova.time
   (:refer-clojure :exclude [second])
   (:require [jamesmacaulay.zelkova.signal :as z]
-            [goog.events :as events]
             [cljs.core.async :as async :refer [>! <!]])
   (:require-macros [cljs.core.async.macros :refer [go go-loop]]))
+
+#+clj
+(defn now [] (System/currentTimeMillis))
+#+cljs
+(defn now [] (.valueOf (js/Date.)))

 (def millisecond 1)
 (def second 1000)
@@ -20,32 +26,38 @@
 (defn in-minutes [n] (/ n minute))
 (defn in-hours [n] (/ n hour))

-#+cljs
-(defn- listen
-  [el type & args]
-  (let [out (apply async/chan 1 args)]
-    (events/listen el type (fn [e] (async/put! out e)))
-    out))
-
-#+cljs
 (defn fps-channel-fn
   [n]
   (fn [graph opts]
     (let [ms-per-frame (/ 1000 n)
           out (async/chan 1 (map (partial z/->Event [::fps n])))]
-      (go-loop [t (js/Date.)]
-        (let [now (js/Date.)
-              diff (- now t)]
-          (<! (async/timeout (- ms-per-frame diff))))
-        (let [now (js/Date.)
-              diff (- now t)]
+      (go-loop [t (now)
+                error 0]
+        (<! (async/timeout (- ms-per-frame error)))
+        (let [new-t (now)
+              diff (- new-t t)]
           (>! out diff)
-          (recur now)))
+          (recur new-t (+ error (- new-t t ms-per-frame)))))
       out)))

-#+cljs
 (defn fps
   [n]
   (assoc (z/input 0 [::fps n])
     :event-sources {[::fps n] (fps-channel-fn n)}))

+(defn every-channel-fn
+  [ms]
+  (fn [graph opts]
+    (let [out (async/chan 1 (map (partial z/->Event [::every ms])))]
+      (go-loop [t (now)
+                error 0]
+        (<! (async/timeout (- ms error)))
+        (let [new-t (now)]
+          (>! out new-t)
+          (recur new-t (+ error (- new-t t ms)))))
+      out)))
+
+(defn every
+  [ms]
+  (assoc (z/input (now) [::every ms])
+    :event-sources {[::every ms] (every-channel-fn ms)}))


index checks

index checks

--- src/clojure/zensols/dataset/elsearch.clj
+++ src/clojure/zensols/dataset/elsearch.clj
@@ -169,6 +169,10 @@
   "Return the total number of documents in the DB."
   []
   (let [{:keys [index-name mapping-type]} (context)]
+    (if-not (exists?)
+      (-> (format "Index does not exist: %s" index-name)
+          (ex-info {:index-name index-name})
+          throw))
     (->> (esd/search (connection) index-name mapping-type :query (q/match-all))
          assert-success
          (esrsp/total-hits))))


support string name for ad-hoc classifiers

--- src/clojure/zensols/model/classifier.clj
+++ src/clojure/zensols/model/classifier.clj
@@ -448,7 +448,9 @@
 (defn classifier-name
   "Return a decent human readable name of a classifier instance."
   [classifier-instance]
-  (second (re-matches #".*\.(.+)" (.getName (.getClass classifier-instance)))))
+  (if (string? classifier-instance)
+    classifier-instance
+    (second (re-matches #".*\.(.+)" (.getName (.getClass classifier-instance))))))

 (defn excel-results
   "Save the results in Excel format."


Bioavailability is hopefully now fully operational.

--- src/clojush/examples/bioavailability.clj
+++ src/clojush/examples/bioavailability.clj
@@ -1,7 +1,7 @@
 ;; bioavailability.clj
 ;;
-;; This problem is a symbolic regression problem with 359 test cases
-;; over 241 variables. Each test case is a candidate drug compound,
+;; This problem is a symbolic regression problem with 359 fitness cases
+;; over 241 variables. Each fitness case is a candidate drug compound,
 ;; with each variable being a bi-dimensional molecule descriptor of
 ;; that compound. The data file data/bioavailability.txt
 ;; contains the data, with the last column being the target variable,
@@ -9,10 +9,10 @@
 ;; range [0.0, 100.0].
 ;;
 ;; The experimental procedure is copied from the paper below. This
-;; procedure uses 70% of the test cases as a training set, and the
+;; procedure uses 70% of the fitness cases as a training set, and the
 ;; remaining 30% as a test set. Each time this file is run, it will
-;; select random training (251 test cases) and test (108 test cases)
-;; sets from the test cases to use throughout the entire run.
+;; select random training (251 fitness cases) and test (108 fitness cases)
+;; sets from the fitness cases to use throughout the entire run.
 ;;
 ;; See this paper for more information about this problem:
 ;; Sara Silva and Leonardo Vanneschi. 2009. Operator equalisation,
@@ -32,7 +32,10 @@
         [clojush.pushstate]
         [clojush.interpreter]
         [clojush.random]
-        [local-file])
+        [local-file]
+        ;[clojush.evaluate] ;;remove later
+        ;[clojush.individual] ;;remove later
+        [clojure.math.numeric-tower])
   (:require [clojure.string :as string]
             [clojure-csv.core :as csv]))

@@ -50,37 +53,41 @@

 ;(map last (read-data))

-;(def test-cases-shuffled
-;  (shuffle (read-data)))

-(defn define-test-cases
+(defn define-fitness-cases
   "Returns a map with two keys: train and test. Train maps to a
-   subset of 251 random test cases (70%), and test maps to the
-   remaining 108 test cases (30%). These sets are different each
+   subset of 251 random fitness cases (70%), and test maps to the
+   remaining 108 fitness cases (30%). These sets are different each
    time this is called."
   []
-  (let [test-cases-shuffled (shuffle (read-data))
+  (let [fitness-cases-shuffled (shuffle (read-data))
         train-num 251]
-    {:train (subvec test-cases-shuffled 0 train-num)
-     :test (subvec test-cases-shuffled train-num)}))
+    {:train (subvec fitness-cases-shuffled 0 train-num)
+     :test (subvec fitness-cases-shuffled train-num)}))

-;(count (:test (define-test-cases)))
+;; Define the fitness cases. Do this once per run, so that train and test
+;; subsets stay the same throughout a run.
+(def bioavailability-fitness-cases (define-fitness-cases))

-;(run-push '(4 5 integer_add yyy6) (make-push-state))
-
-
-;(define-registered (symbol (str "x" (+ 4 2))) (fn [state] (push-item 2999 :integer state)))
-
-;(doseq [a (map #(symbol (str "x" %)) (range 241))]
-;  (define-registered a (fn [state] (push-item 2999 :integer state))))
-
-
-;(sort (map str (vec @registered-instructions)))
+;; Helper functions to get specific train and test cases
+;(defn train-fitness-case
+;  "Returns train fitness case number n for this run."
+;  [n]
+;  (nth (:train bioavailability-fitness-cases) n))
+;
+;(defn test-fitness-case
+;  "Returns test fitness case number n for this run."
+;  [n]
+;  (nth (:test bioavailability-fitness-cases) n))

 ;; I want x0 through x240 to be instructions that, when executed, push
 ;; the float from that column onto the float stack.
+(doseq [[numb symb] (map #(vector % (symbol (str "x" %))) (range 241))]
+  (eval `(define-registered ~symb (fn [state#] (push-item (stack-ref :auxiliary ~numb state#) :float state#)))))
+
+;(sort (map str (vec @registered-instructions)))

 ;; This definition of atom-generators makes it so that choosing a terminal
@@ -88,7 +95,7 @@
 ;; the method used in the paper above.
 (def bioavailability-atom-generators
   (list
-    (fn [] (lrand-nth (list 'integer_div 'integer_mult 'integer_add 'integer_sub)))
+    (fn [] (lrand-nth (list 'float_div 'float_mult 'float_add 'float_sub)))
     (fn [] (lrand-nth (for [n (range 241)]
                         (symbol (str "x" n)))))
     ))
@@ -98,3 +105,48 @@

+(defn bioavailability-error-function
+  [program]
+    (doall
+      (for [fitness-case (:train bioavailability-fitness-cases)]
+        (let [input (butlast fitness-case)
+              output (last fitness-case)
+              state (run-push program
+                              (assoc (make-push-state)
+                                     :auxiliary
+                                     input))
+              top-float (top-item :float state)]
+          (if (number? top-float)
+            (abs (- output top-float))
+            10000.0)))))
+
+;(run-push '(4 5 integer_add x0 x1 x2 float_add x240)
+;          (assoc (make-push-state)
+;                 :auxiliary
+;                 (butlast (first (:train (define-fitness-cases))))))
+;
+;(evaluate-individual (make-individual :program (random-code 100 bioavailability-atom-generators))
+;                     bioavailability-error-function
+;                     (new java.util.Random))
+;
+;(evaluate-individual (make-individual :program '(x2 x19 float_mult))
+;                     bioavailability-error-function
+;                     (new java.util.Random))
+
+(pushgp
+  :error-function bioavailability-error-function
+  :atom-generators bioavailability-atom-generators
+  :max-points 500
+  :evalpush-limit 500
+  :population-size 200
+  :max-generations 50
+  :mutation-probability 0.1
+  :crossover-probability 0.8
+  :simplification-probability 0.05
+  :tournament-size 6
+  :trivial-geography-radius 10
+  :node-selection-method :size-tournament
+  :node-selection-tournament-size 2
+  :report-simplifications 0
+  :final-report-simplifications 1000
+  )


Made not-a-clone-diversifying? compare expressed programs, not genomes.

--- src/clojush/pushgp/genetic_operators.clj
+++ src/clojush/pushgp/genetic_operators.clj
@@ -830,9 +830,13 @@

 (defn not-a-clone-diversifying?
   "Returns true iff genome g passes the diversification test."
-  [g {:keys [parent1-genome parent2-genome]}]
-  (and (not= g parent1-genome)
-       (not= g parent2-genome)))
+  [g {:keys [parent1-genome parent2-genome] :as argmap}]
+  (let [express #(translate-plush-genome-to-push-program {:genome %} argmap)
+        pgm (express g)
+        parent1-pgm (express parent1-genome)
+        parent2-pgm (express parent2-genome)]
+    (and (not= pgm parent1-pgm)
+         (not= pgm parent2-pgm))))

 (defn minimum-genetic-difference-diversifying?
   "Returns true iff genome g passes the diversification test."


Fix lint on connect test

Fix lint on connect test

--- src/com/wsscode/pathom/connect/test.cljc
+++ src/com/wsscode/pathom/connect/test.cljc
@@ -10,7 +10,6 @@
     [com.wsscode.common.combinatorics :as combo]
     [com.wsscode.pathom.connect :as pc]
     [com.wsscode.pathom.core :as p]
-    [com.wsscode.pathom.misc :as p.misc]
     [com.wsscode.pathom.test :as pt])
   #?(:clj
      (:import
@@ -386,7 +385,7 @@
   (assert (s/valid? (s/keys :req [::pc/indexes]) env)
     (s/explain-str (s/keys :req [::pc/indexes]) env))
   (let [data-bank (if data-bank
-                    (do (swap! data-bank assoc ::multi-args (collect-multi-args indexes)))
+                    (swap! data-bank assoc ::multi-args (collect-multi-args indexes))
                     (atom {::multi-args (collect-multi-args indexes)}))]
     (-> (merge {::data-bank data-bank
                 ::report-fn console-print-reporter} env)
@@ -411,22 +410,22 @@
     (apply str (repeat depth "  "))
     "- " more))

-(defmulti console-print-reporter (fn [env event data] event))
+(defmulti console-print-reporter (fn [_env event _data] event))

 (defmethod console-print-reporter ::report-seek
-  [env _ {:keys [::pc/attribute]}]
+  [env _ _]
   (depth-print env "seeking" ::pc/attribute))

 (defmethod console-print-reporter ::report-resolver-start
-  [env _ {:keys [::pc/sym]}]
+  [env _ _]
   (depth-print env "test resolver" ::pc/sym))

 (defmethod console-print-reporter ::report-resolver-discover
-  [env _ {:keys [::pc/sym ::data-bank]}]
+  [env _ {:keys [::data-bank]}]
   (depth-print env "discovered input" ::pc/sym (pr-str data-bank)))

 (defmethod console-print-reporter ::report-resolver-call
-  [env _ {:keys [::pc/sym ::input-arguments]}]
+  [env _ {:keys [::input-arguments]}]
   (depth-print env "call resolver" (pr-str ::pc/sym) (pr-str input-arguments)))

 (defmethod console-print-reporter :default


Fixed test.

Fixed test.

--- project/analyze-data/test/analyze_data/tf_idf/core_test.clj
+++ project/analyze-data/test/analyze_data/tf_idf/core_test.clj
@@ -36,12 +36,11 @@
       "matches the order of all-terms"))

 (deftest tf-idf
-  (is (= ["a" "b" "c"]
-         (first (test-ns/tf-idf [["a" "b"] ["a" "c"] ["b" "a"]])))
-      "first sequence is a sorted sequence of terms")
-  (is (->> (test-ns/tf-idf [["a" "b"] ["a" "c"] ["b" "a"]])
-           (rest)
-           (concat)
-           (map #(= float (type %)))
-           (and))
-      "rest of the sequences contain floats"))
+  (let [term-corpus [["a" "b"] ["a" "c"] ["b" "a"]]
+        result (test-ns/tf-idf term-corpus)]
+    (is (= ["a" "b" "c"]
+           (first result))
+        "first sequence is a sorted sequence of terms")
+    (is (every? #(= java.lang.Double (type %))
+                (->> result (rest) (apply concat)))
+        "rest of the sequences contain doubles")))


Introducing various optimizations

Introducing various optimizations

--- jvm-runner/project.clj
+++ jvm-runner/project.clj
@@ -11,6 +11,7 @@
   :source-paths      ["src/clojure"]
   :java-source-paths ["src/java"]
   :env {:timeout "2000"}
-  :profiles {:uberjar {:aot :all}
-             :env {:timeout "2000"}}
+  :profiles {:uberjar {:aot :all
+                       :omit-source true
+                       :javac-options ["-g:none"]}}
   :main codewars.core)


New version of lein-swank.

New version of lein-swank.

--- lein-swank/project.clj
+++ lein-swank/project.clj
@@ -1,4 +1,4 @@
-(defproject leiningen/lein-swank "1.0.0"
+(defproject leiningen/lein-swank "1.1.0"
   :description "A leiningen plugin to launch a swank server."
-  :dependencies [[swank-clojure "1.1.0-SNAPSHOT"]
+  :dependencies [[swank-clojure "1.1.0"]
                  [org.apache.maven/maven-ant-tasks "2.0.10"]])


Minor name changes.

Minor name changes.

--- src/apio/cli.clj
+++ src/apio/cli.clj
@@ -1,30 +1,31 @@
 (ns apio.cli
   (:import (java.lang Thread))
   (:require [apio.core :as core]
-            [apio.concurrency.threading :as cc-threading]
-            [apio.concurrency.queue :as cc-queue]
-            [apio.concurrency.util :as cc-util])
+            [apio.concurrency.threading :as cthreading]
+            [apio.concurrency.queue :as cqueue]
+            [apio.concurrency.util :as cutil])
   (:gen-class))

 (defn dispatcher
   [queue]
-  (let [pool (cc-threading/fixed-pool (cc-util/max-workers core/*config*))]
+  (let [pool (cthreading/start-pool (cutil/max-workers core/*config*))]
     (loop []
-      (let [task (cc-queue/rcv queue)]
+      (let [task (cqueue/rcv queue)]
         (when (instance? java.util.concurrent.Callable task)
-          (do (cc-threading/spawn pool task) (recur)))))
-    (cc-threading/shutdown-pool pool)))
+          (do (cthreading/spawn pool task) (recur)))))
+    (cthreading/shutdown-pool pool)))

 (defn -main
   [path]
-  (let [queue   (cc-queue/queue (cc-util/max-prefetch core/*config*))
-        thr     (cc-threading/thread #(dispatcher queue))
+  (let [queue   (cqueue/queue (cutil/max-prefetch core/*config*))
+        thr     (cthreading/thread #(dispatcher queue))
         tasks   (map (fn [n]
                        (fn []
                          (println "Thread:" (.getId (Thread/currentThread)) "Processing:" n )
-                         (cc-util/sleep (rand-int 1000)) 2)) (range 10))]
+                         (cutil/sleep (rand-int 1000)) 2)) (range 10))]
     (doseq [t tasks]
-      (cc-queue/snd queue t))
+      (cqueue/snd queue t))

-    (cc-queue/snd queue 1)
+    ;; Send non callable value to shutdown dispatcher.
+    (cqueue/snd queue 1)
     (.join thr)))


Modify interval with regards to slider value

--- src/constraint/core.cljs
+++ src/constraint/core.cljs
@@ -102,9 +102,9 @@
   [state]
   (merge state {:random false}))

-(def interval 10)
+(def interval (atom 10))

-(def ticker (atom interval))
+(def ticker (atom @interval))

 (defn get-legal-moves
   [world-state]
@@ -125,7 +125,7 @@
   (swap! ticker dec)
   (if (neg? @ticker)
     (do
-      (reset! ticker interval)
+      (reset! ticker @interval)
       (-> world-state
           get-random-legal-move-name
           (flip-update-edge world-state)))
@@ -139,7 +139,21 @@
     world-state))

+(defn change-interval
+  []
+  (->> (dommy.core/sel1 :#randomrange)
+       .-value
+       js/parseInt
+       (- 61)
+       (reset! interval)))
+
+(defn listen-on-slider-change
+  []
+  (dommy/listen! (dommy.core/sel1 :#randomrange) :change
+                 change-interval))
+
 (go
+  (listen-on-slider-change)
   (let [read-state (reader/read-string (<! (GET "./state.edn")))]
     (big-bang!
       :initial-state (->> read-state reset-edit reset-random)


log output to server log

log output to server log

fixes #338

--- sidecar/src/figwheel_sidecar/components/figwheel_server.clj
+++ sidecar/src/figwheel_sidecar/components/figwheel_server.clj
@@ -8,7 +8,7 @@
    [clojure.java.io :as io]
    [clojure.java.shell :as shell]
    [clojure.edn :as edn]
-
+   [clojure.stacktrace :as stack]
    [clojure.core.async :refer [go-loop <!! <! timeout]]

    [compojure.route :as route]
@@ -134,10 +134,29 @@
     (with-channel request channel
       (setup-file-change-sender server-state (:params request) channel))))

+(defn log-output-to-figwheel-server-log [handler log-writer]
+  (fn [request]
+    (if log-writer
+      (binding [*out* log-writer
+                *err* log-writer]
+        (try
+          (handler request)
+          (catch Throwable e
+            (let [message (.getMessage e)
+                  trace (with-out-str (stack/print-cause-trace e))]
+                          (println message)
+                          (println trace)
+                          {:status 400
+                           :body (str "
" message "
"
+                                      "
"
+                                      trace
+                                      "
")}))))
+      (handler request))))
+
 (defn server
   "This is the server. It is complected and its OK. Its trying to be a basic devel server and
    also provides the figwheel websocket connection."
-  [{:keys [server-port server-ip http-server-root resolved-ring-handler] :as server-state}]
+  [{:keys [server-port server-ip http-server-root resolved-ring-handler log-writer] :as server-state}]
   (try
     (-> (routes
          (GET "/figwheel-ws/:desired-build-id" {params :params} (reload-handler server-state))
@@ -151,6 +170,7 @@
         (cors/wrap-cors
          :access-control-allow-origin #".*"
          :access-control-allow-methods [:head :options :get :put :post :delete :patch])
+        (log-output-to-figwheel-server-log log-writer)
         (run-server (let [config {:port server-port :worker-name-prefix "figwh-httpkit-"}]
                       (if server-ip
                         (assoc config :ip server-ip)


Better excerpt and tag support.

Better excerpt and tag support.

--- src/dragon/blog/post.clj
+++ src/dragon/blog/post.clj
@@ -5,14 +5,37 @@
             [dragon.util :as util]
             [markdown.core :as markdown]))

+(defn md->html
+  [md]
+  (markdown/md-to-html-string md :inhibit-separator "%%%"))
+
+(defn join-excerpt
+  [words number]
+  (let [excerpt (string/join " " (take number words))]
+    (if (string/ends-with? excerpt ".")
+      (str excerpt "..")
+      (str excerpt "..."))))
+
+(defn convert-body
+  [data]
+  (let [paragraphs (string/split (:body data) #"\n\n")
+        words (-> paragraphs
+                      (first)
+                      (string/split #"\s"))
+        words-100 (take 100 words)
+        excerpt-100 (join-excerpt words-100 100)
+        excerpt-50 (join-excerpt words-100 50)
+        excerpt-25 (join-excerpt words-100 25)]
+    (case (keyword (:content-type data))
+      :md (-> data
+              (update-in [:body] md->html)
+              (assoc :excerpt-100 (md->html excerpt-100)
+                     :excerpt-50 (md->html excerpt-50)
+                     :excerpt-25 (md->html excerpt-25))))))
+
 (defn update-tags
   [data]
   (assoc data :tags (apply sorted-set (string/split (:tags data) #",\s?"))))
-
-(defn convert-body
-  [data]
-  (case (keyword (:content-type data))
-    :md (update-in data [:body] markdown/md-to-html-string)))

 (defn add-file-data
   [data]


s/biatonic/diatonic

s/biatonic/diatonic

--- src/overtone/music/pitch.clj
+++ src/overtone/music/pitch.clj
@@ -171,7 +171,7 @@

 (defn nth-interval
   "Return the count of semitones for the nth degree from the start of the
-  biatonic scale in the specifiec mode (or ionian/major by default).
+  diatonic scale in the specifiec mode (or ionian/major by default).

   i.e. the ionian/major scale has an interval sequence of 2 2 1 2 2 2 1
        therefore the 4th degree is (+ 2 2 1 2) semitones from the start of the


fix spelling mistake

fix spelling mistake

--- src/overtone/sc/dyn_vars.clj
+++ src/overtone/sc/dyn_vars.clj
@@ -47,7 +47,7 @@

 (defmacro without-node-blocking
   "Stops the current thread from being blocked if the node you're
-  attemptign to modify hasn't completed loading. This may result in the
+  attempting to modify hasn't completed loading. This may result in the
   message being ignored by the server."
   [& body]
   `(binding [*block-node-until-ready?* false]


return a vector as the event key rather than a list for neatness

--- src/overtone/studio/midi.clj
+++ src/overtone/studio/midi.clj
@@ -177,7 +177,7 @@
   specific MIDI events."
   []
   (let [next-input (midi-capture-next-control-input true)]
-    (concat (:key next-input) [(:controller next-input)])))
+    (vec (concat (:key next-input) [(:controller next-input)]))))

 (defn midi-mk-control-key-keyword
   [prefix control-key]