Make geom/size as shape transformation instead simple calculation.

--- frontend/src/uxbox/main/geom.cljs
+++ frontend/src/uxbox/main/geom.cljs
@@ -12,6 +12,7 @@

 ;; --- Relative Movement

+;; TODO: revisit, maybe dead code
 (declare move-rect)
 (declare move-path)
 (declare move-circle)
@@ -118,31 +119,47 @@

 (declare size-rect)
 (declare size-circle)
+(declare size-path)

 (defn size
   "Calculate the size of the shape."
   [shape]
   (case (:type shape)
+    :group (assoc shape :width 100 :height 100)
     :circle (size-circle shape)
     :text (size-rect shape)
     :rect (size-rect shape)
     :icon (size-rect shape)
     :image (size-rect shape)
-    :path (size-rect shape)))
+    :path (size-path shape)))
+
+(defn- size-path
+  [{:keys [points x1 y1 x2 y2] :as shape}]
+  (if (and x1 y1 x2 y2)
+    (assoc shape
+           :width (- x2 x1)
+           :height (- y2 y1))
+    (let [minx (apply min (map :x points))
+          miny (apply min (map :y points))
+          maxx (apply max (map :x points))
+          maxy (apply max (map :y points))]
+      (assoc shape
+             :width (- maxx minx)
+             :height (- maxy miny)))))

 (defn- size-rect
   "A specialized function for calculate size
   for rect-like shapes."
   [{:keys [x1 y1 x2 y2] :as shape}]
-  {:width (- x2 x1)
-   :height (- y2 y1)})
+  (merge shape {:width (- x2 x1)
+                :height (- y2 y1)}))

 (defn- size-circle
   "A specialized function for calculate size
   for circle shape."
-  [{:keys [rx ry]}]
-  {:width (* rx 2)
-   :height (* ry 2)})
+  [{:keys [rx ry] :as shape}]
+  (merge shape {:width (* rx 2)
+                :height (* ry 2)}))

 ;; --- Vertex Access


comment out process-shot-spec until refactor can be determined

--- tests/battlebots/game/bot/decisions/shoot_spec.clj
+++ tests/battlebots/game/bot/decisions/shoot_spec.clj
@@ -83,7 +83,7 @@
          ((#'shoot/reward-shooter "1" o 50) {:players test-players}))
       "When a player strikes an open space, they will recieve no additional energy"))

-(deftest process-shot-spec
+#_(deftest process-shot-spec
   (is (= {:game-state {:dirty-arena (au/update-cell
                                      test-arena
                                      [0 1]


Locks: Simplify `have-lock?` impl. (no Lua necessary)

--- src/taoensso/carmine/locks.clj
+++ src/taoensso/carmine/locks.clj
@@ -68,17 +68,8 @@
      (release-lock "my-lock" uuid)
      (release-lock "my-lock" uuid)]))

-(defn have-lock?
-  [lock-name owner-uuid]
-  (-> (car/lua-script
-       "if redis.call('get', _:lkey) == _:uuid then
-         return 1
-       else
-         return 0
-       end"
-       {:lkey (lkey lock-name)}
-       {:uuid owner-uuid})
-      car/parse-bool wcar))
+(defn have-lock? [lock-name owner-uuid]
+  (= (wcar (car/get (lkey lock-name))) owner-uuid))

 (comment
   (when-let [uuid (acquire-lock "my-lock" 2000 500)]


remove println

remove println

--- src/turbo_eureka/ios/core.cljs
+++ src/turbo_eureka/ios/core.cljs
@@ -78,10 +78,8 @@
 (defn create-pan-responder [drag-model]
   (.create PanResponder
      #js{:onPanResponderStart #(swap! drag-model merge {:dragging? true})
-         :onPanResponderEnd #(do (println "end dragging")
-                                 (swap! drag-model merge {:dragging? false
-                                                          :current-item nil})
-                                 (println @drag-model))
+         :onPanResponderEnd #(swap! drag-model merge {:dragging? false
+                                                      :current-item nil})
          :onStartShouldSetPanResponder (constantly true)
          :onPanResponderMove
             (animated-event [nil {:dx (.-x (:pan @drag-model)) :dy (.-y (:pan @drag-model))}])


better behavior for homer footer

better behavior for homer footer

--- frontend/components/landing-b.cljs
+++ frontend/components/landing-b.cljs
@@ -140,6 +140,8 @@
                   "About Us"
                   (common/ico :chevron-down)]
                  [:div.home-footer
+                  [:div.home-footer-logo
+                   (common/ico :logo)]
                   [:nav.home-footer-about
                    [:h5 "CircleCI"]
                    [:a "Tour"]
@@ -162,6 +164,4 @@
                    [:a "Email"]
                    [:a "Support Chat"]
                    [:a "Phone"]
-                   [:a "San Francisco, CA"]]
-                  [:div.home-footer-logo
-                   (common/ico :logo)]]]]])))))
+                   [:a "San Francisco, CA"]]]]]])))))


returning 404 when there it no guid

returning 404 when there it no guid

--- src/blackbox/src/leela/blackbox/network/zmqserver.clj
+++ src/blackbox/src/leela/blackbox/network/zmqserver.clj
@@ -26,7 +26,9 @@
   ["done"])

 (defn msg-name [u t k n g]
-  ["name" u t k n (str g)])
+  (if-not g
+    (msg-fail 404)
+    ["name" u t k n (str g)]))

 (defn msg-link [links]
   (cons "link" (map str links)))


using fn

using fn

--- src/celestial/jobs.clj
+++ src/celestial/jobs.clj
@@ -26,7 +26,9 @@
 (defn job-exec [f {:keys [identity args] :as spec}]
   "Executes a job function tries to lock identity first (if used)"
   (if identity
-    (with-lock identity #(apply f args) {:expiry half-hour :wait-time minute})
+    (with-lock identity
+      (fn [] (apply f args) :success)
+     {:expiry half-hour :wait-time minute})
     (do (apply f args) :success)))

 (def jobs


include group avatar when loading

include group avatar when loading

--- src/chat/server/db.clj
+++ src/chat/server/db.clj
@@ -120,13 +120,15 @@
    {:extension/_group [:extension/id :extension/type]}])

 (defn- db->group [e]
-  {:id (:group/id e)
-   :name (:group/name e)
-   :admins (into #{} (map :user/id) (:group/admins e))
-   :intro (-> e (get :group/settings "{}") edn/read-string :intro)
-   :extensions (map (fn [x] {:id (:extension/id x)
-                             :type (:extension/type x)})
-                    (:extension/_group e))})
+  (let [settings (-> e (get :group/settings "{}") edn/read-string)]
+    {:id (:group/id e)
+     :name (:group/name e)
+     :admins (into #{} (map :user/id) (:group/admins e))
+     :intro (settings :intro)
+     :avatar (settings :avatar)
+     :extensions (map (fn [x] {:id (:extension/id x)
+                               :type (:extension/type x)})
+                      (:extension/_group e))}))

 (defmacro with-conn
   "Execute the body with *conn* dynamically bound to a new connection."


Fix docstring typo

Fix docstring typo

--- src/clj/reply/main.clj
+++ src/clj/reply/main.clj
@@ -60,7 +60,7 @@
       (eval-modes.nrepl/main options))))

 (defn launch-standalone
-  "Launches the streamed (non-nREPL) version of REPL-y, with options already
+  "Launches the standalone (non-nREPL) version of REPL-y, with options already
   parsed out"
   [options]
   (with-launching-context


event/on-raw should accept selector strings.

--- src/cljs/c2/event.cljs
+++ src/cljs/c2/event.cljs
@@ -11,7 +11,7 @@
   (.listen goog.events js/window goog.events.EventType.LOAD f))

 (defn on-raw [node event-type f]
-  (gevents/listen node (name event-type) f))
+  (gevents/listen (dom/select node) (name event-type) f))

 (defn on
   ([node event-type f] (on node "*" event-type f))


cleanup

cleanup

--- test/babel/test/core_test.cljc
+++ test/babel/test/core_test.cljc
@@ -1,19 +0,0 @@
-(ns babel.test.core_test
-  (:require #?(:clj [clojure.test :refer [deftest is]])
-            #?(:cljs [cljs.test :refer-macros [deftest is]])
-            [dag_unify.core :refer [get-in unify unifyc]]
-            [babel.pos :refer [noun]])
-  (:refer-clojure :exclude [get-in]))
-
-(deftest eugene-test
-  (is (= 1 1)))
-
-(deftest eugene-test2
-  (is (= 2 2)))
-
-(deftest eugene-test3
-  (is (= 3 3)))
-
-(deftest eugene-test4
-  (is (= 4 4)))
-


REmove finished TODOS.

REmove finished TODOS.

--- test/clj_spotify/core_test.clj
+++ test/clj_spotify/core_test.clj
@@ -6,11 +6,6 @@
             [clj-http.client :as client]
             [clojure.data.codec.base64 :as b64]
             ))
-
-;TODO - Create fixtures for json responses.
-;TODO - Use spotifys client credentials flow to get token to perform tests.
-;TODO - Encrypt client_id,client_secret to use with travis.
-;TODO - Remove followers key from artist data as it changes over time.

 ;TODO - Make the below look nice! Separate maybe.


make tools custom tooltips work

make tools custom tooltips work

--- frontend/components/landing_b.cljs
+++ frontend/components/landing_b.cljs
@@ -278,14 +278,16 @@
                     [:div.supported-tools {:class (when (= selected-toolset toolset) "active")}
                      [:div.supported-tools-upper
                       (for [tool (:upper template)]
-                       [:div.supported-tool {:class (when (contains? tools tool) "hover")}
+                       [:div.supported-tool {:class (when (contains? tools tool) "hover")
+                                             :alt (name tool)}
                         [:figure
                          [:svg {:class (str "tool-logo-" (name tool))
                                :x "0" :y "0" :view-box "0 0 100 100"}
                           [:path {:d (get tools-logos tool)}]]]])]
                      [:div.supported-tools-lower
                       (for [tool (:lower template)]
-                       [:div.supported-tool {:class (when (contains? tools tool) "hover")}
+                       [:div.supported-tool {:class (when (contains? tools tool) "hover")
+                                             :alt (name tool)}
                         [:figure
                          [:svg {:class (str "tool-logo-" (name tool))
                                :x "0" :y "0" :view-box "0 0 100 100"}


remove unused controls event

remove unused controls event

--- frontend/controllers/controls.cljs
+++ frontend/controllers/controls.cljs
@@ -22,11 +22,6 @@
 (defmethod control-event :show-all-branches-toggled
   [target message project-id state]
   (update-in state [:settings :projects project-id :show-all-branches] not))
-
-(defmethod control-event :build-inspected
-  [target message [project-id build-num] state]
-  (assoc-in state [:inspected-project] {:project project-id
-                                        :build-num build-num}))

 (defmethod control-event :state-restored
   [target message path state]


Add lein-kibit

Add lein-kibit

--- clojure/lein.symlink/profiles.clj
+++ clojure/lein.symlink/profiles.clj
@@ -3,7 +3,8 @@
         :plugins [[lein-difftest "1.3.8"]
                   [lein-marginalia "0.7.1"]
                   [lein-pprint "1.1.1"]
-                  [lein-swank "1.4.4"]]
+                  [lein-swank "1.4.4"]
+                  [lein-kibit "0.0.8"]]
         :aliases {"slamhound" ["run" "-m" "slam.hound"]}
         :source-paths ["/Users/jcf/.lein/dev"]
         :search-page-size 30}}


lein: Add lein-plz

lein: Add lein-plz

--- clojure/lein.symlink/profiles.clj
+++ clojure/lein.symlink/profiles.clj
@@ -10,6 +10,7 @@
                   [lein-try "0.4.3"]
                   [cider/cider-nrepl "0.8.2"]
                   [lein-ancient "0.6.7"]
+                  [lein-plz "0.4.0-SNAPSHOT" :exclusions [[rewrite-clj] [ancient-clj]]]
                   [lein-pprint "1.1.1"]]}
  :lib-dev {
            :gsub-version/files #{"README.md"}


Removes TODO

Removes TODO

--- src/expound/alpha.cljc
+++ src/expound/alpha.cljc
@@ -242,7 +242,6 @@
    (show-spec-name spec-name (printer/indent (*value-str-fn* spec-name val path (problems/value-in val path))))
    (expected-str _type spec-name val path problems opts)))

-;; TODO - clean up unused params
 (defmethod expected-str :problem/not-in-set [_type _spec-name _val _path problems _opts]
   (let [combined-set (apply set/union (map :pred problems))]
     (printer/format


Compojure: Clamp query count between 1 and 500 to comply with test spec

--- compojure/hello/src/hello/handler.clj
+++ compojure/hello/src/hello/handler.clj
@@ -82,12 +82,22 @@
      queries ; Number of queries to run
      (repeatedly get-world-raw)))))

+(defn get-query-count [queries]
+  "Parse provided string value of query count, clamping values to between 1 and 500."
+  (let [q (try (Integer/parseInt queries)
+               (catch Exception e 1))] ; default to 1 on parse failure
+    (if (> q 500)
+      500 ; clamp to 500 max
+      (if (< q 1)
+        1 ; clamp to 1 min
+        q)))) ; otherwise use provided value
+
 ; Define route handlers
 (defroutes app-routes
   (GET "/" [] "Hello, World!")
   (GET "/json" [] (response {:message "Hello, World!"}))
-  (GET "/db/:queries" [queries] (response (run-queries (Integer/parseInt queries))))
-  (GET "/dbraw/:queries" [queries] (response (run-queries-raw (Integer/parseInt queries))))
+  (GET "/db/:queries" [queries] (response (run-queries (get-query-count queries))))
+  (GET "/dbraw/:queries" [queries] (response (run-queries-raw (get-query-count queries))))
   (route/not-found "Not Found"))

 ; Format responses as JSON


Layout manager #41

Layout manager #41

--- src/flatgui/layout.clj
+++ src/flatgui/layout.clj
@@ -8,10 +8,10 @@

 (ns ^{:doc    "Utilities for arranging container component layout."
       :author "Denys Lebediev"}
-flatgui.layout
+  flatgui.layout
     (:require [flatgui.base :as fg]
       [flatgui.awt :as awt]
-      [flatgui.util.matrix :as m] [flatgui.util.matrix :as m])
+      [flatgui.util.matrix :as m])
   (:import (java.util.regex Pattern)))

@@ -98,9 +98,22 @@
 ;  ([cfg stcher bgner sfn ])
 ;  ([cfg] (map-direction cfg \- \< m/x)))

+(declare flattenmap)
+
+(defn flatten-mapper [e]
+  (if (coll? e) (flattenmap e) [e]))
+
+(defn flattenmap [coll]
+  (mapcat flatten-mapper coll))
+
+(defn flagnestedvec->coordmap [flags]
+  (into {} (map (fn [flg] [(:element flg) flg]) (flattenmap flags))))
+
 (fg/defaccessorfn map-direction [component cfg stcher bgner sfn]
   (let [stretches? (fn [element] (true? (and (:flags element) (.contains (:flags element) (str stcher)))))
-        flags (map #(assoc % :stch-weight (count (filter (fn [f] (= f stcher)) (:flags %)))) (cfg->flags cfg))
+        flags (map
+                #(assoc % :stch-weight (count (filter (fn [f] (= f stcher)) (:flags %))))
+                (flattenmap (cfg->flags cfg)))
         total-stch-weight (reduce + (map #(:stch-weight %) flags))
         dir (mapv #(assoc %
                          :min (get-child-minimum-size component (:element %))
@@ -124,13 +137,26 @@
       ;; TODO
       nil)))

-;(fg/defevolverfn :coord-map
-; (if-let [layout (map cmd->smile (get-property [:this] :layout))]
-;   (let [
-;         ;No need in this, should contain rel coords ;cs (get-property [:this] :clip-size)
-;         ;w (m/x cs)
-;         ;h (m/y cs)
-;         ])))
+(fg/defevolverfn :coord-map
+ (if-let [layout (map cmd->smile (get-property [:this] :layout))]
+   (flagnestedvec->coordmap (map-direction component layout \- \< m/x))))
+
+(fg/defevolverfn :position-matrix
+  (if-let [coord-map (get-property [] :coord-map)]
+    (if-let [coord ((:id component) coord-map)]
+      (let [ps (get-property [] :clip-size)]
+        (m/translation (* (:x coord) (m/x ps)) (* (:y coord (m/y ps)))))
+      old-position-matrix)
+    old-position-matrix))
+
+(fg/defevolverfn :clip-size
+  (if-let [coord-map (get-property [] :coord-map)]
+    (if-let [coord ((:id component) coord-map)]
+      (let [ps (get-property [] :clip-size)]
+        (m/defpoint (* (:w coord) (m/x ps)) (* (:h coord (m/y ps)))))
+      old-clip-size)
+    old-clip-size))
+

 ;;; 1. After map-direction is used for elements of each row, each row will
 ;;; receive it's own set of smile flags. Then map-direction will be used
@@ -148,10 +174,3 @@
 ;;;          - either equally if nothing else specified
 ;;;          - or according to specified weights (may be specified with digits or
 ;;;            with smile lengths like :--- or :|||)
-
-
-
-;(fg/defevolverfn :position-matrix)
-;
-;(fg/defevolverfn :clip-size)
-


CMR-4467 - Added support for trace logging.

--- common-lib/src/cmr/common/log.clj
+++ common-lib/src/cmr/common/log.clj
@@ -62,6 +62,11 @@
 ;; think they're more efficient if they are macros when there is a lot of
 ;; logging.

+(defmacro trace
+  "Logs a message at the trace level."
+  [& body]
+  `(t/trace ~@body))
+
 (defmacro debug
   "Logs a message at the debug level."
   [& body]


adding license

adding license

--- src/freenas/remote.clj
+++ src/freenas/remote.clj
@@ -1,14 +1,24 @@
+(comment
+   Celestial, Copyright 2012 Ronen Narkis, narkisr.com
+   Licensed under the Apache License,
+   Version 2.0  (the "License") you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.)
+
 (ns freenas.remote
   (:require
     [celestial.common :refer (import-logging)]
     [cheshire.core :refer :all]
     [celestial.model :refer (hypervisor)]
     [clojure.core.strint :refer (<<)]
-    [org.httpkit.client :as client])
- )
+    [org.httpkit.client :as client]))

-(defn freenas [ks]
-  )
+(defn freenas [ks])
+
 (defn root
    []
    (<< "https://~(hypervisor :freenas :host)/api/v1.0/"))


tracks deleted on client

tracks deleted on client

--- src/frontend/core.cljs
+++ src/frontend/core.cljs
@@ -284,3 +284,12 @@
     (swap! app-state assoc :music-files {})
     (swap! app-state assoc :current-track-id nil)
     (swap! app-state assoc :current-sound-id nil)))
+
+(.on socket "delete-track"
+  (fn [track-id]
+    (println track-id)
+    (let [new-music-files (dissoc (:music-files @app-state) track-id)
+          new-music-info  (vec (remove #(= track-id (.-id %1))
+                                       (:music-info @app-state)))]
+      (swap! app-state assoc :music-files new-music-files)
+      (swap! app-state assoc :music-info new-music-info))))


More thorough test for assoc/update record

--- test/useful/datatypes_test.clj
+++ test/useful/datatypes_test.clj
@@ -1,13 +1,13 @@
 (ns useful.datatypes-test
   (:use clojure.test useful.datatypes))

-(defrecord Test [a b])
+(defrecord Test [a b c])
 (record-accessors Test)

 (deftest test-record
-  (let [init (Test. 1 2)
-        second (Test. 1 5)]
-    (is (= init (make-record Test :b 2 :a 1)))
-    (is (= second (assoc-record init :b 5)))
-    (is (= second (update-record init (+ b 3))))
+  (let [init (Test. 1 2 3)
+        second (Test. 1 5 4)]
+    (is (= init (make-record Test :b 2 :a 1 :c 3)))
+    (is (= second (assoc-record init :b 5 :c 4)))
+    (is (= second (update-record init (+ b 3) (inc c))))
     (is (= (:a init) (a init)))))


exception-handling portability

exception-handling portability

--- src/babel/espanol/morphology.cljc
+++ src/babel/espanol/morphology.cljc
@@ -11,8 +11,14 @@

 (declare get-string)

+(defn exception [error-string]
+  #?(:clj
+     (throw (Exception. error-string)))
+  #?(:cljs
+     (throw (js/Error. error-string))))
+
 (defn fo [input]
-  (try
+;  (try
     (cond

       (= input :fail)
@@ -43,11 +49,12 @@

       true
       "")
-    (catch Exception e
-      (do
-        (log/trace (str "ignoring exception: '" e "' and returning canonical form instead."))
-        (show-as-tree (get-in input [:espanol]) :espanol)))))
-
+  ;; TODO: commented out until I figure out exception-catching in Javascript
+;    (catch Exception e
+;      (do
+;        (log/trace (str "ignoring exception: '" e "' and returning canonical form instead."))
+;        (show-as-tree (get-in input [:espanol]) :espanol)))))
+)
 ;; TODO: this is an overly huge method that needs to be rewritten to be easier to understand and maintain.
 (defn get-string-1 [word & [ {usted :usted
                               tú :tu
@@ -194,7 +201,7 @@
       (get-in word [:espanol])

       true
-      (throw (Exception. (str "get-string-1: don't know what to do with input argument: " word)))))))
+      (exception (str "get-string-1: don't know what to do with input argument: " word))))))

 (defn get-string [a & [ b ]]
   (cond (and (nil? b)


fixed concurrency issues

fixed concurrency issues

--- src/rester/core.clj
+++ src/rester/core.clj
@@ -177,25 +177,27 @@
         test-agents (vec (map agent tests))
         exec-test (fn[test]
                     (if (:done test) test
-                        (let [deps (map #(nth test-agents %) (:deps test))
-                              test (if (every? (comp :done deref) deps)
-                                     (assoc
-                                      (if (every? (comp :success deref) deps)
-                                        (try (exec-test-case test opts)
-                                             (catch Exception e
-                                               (log/error e "failed executing test")
-                                               (assoc test :error (str (or (.getMessage e) e)))))
-                                        (assoc test :skipped "dependents failed"))
-                                      :done true)
-                                     test)]
-                          (if (:done test)
-                            (swap! count-down dec))
-                          test)))]
-
-    (add-watch count-down :key (fn [k r o n] (if (zero? n) (deliver p (map deref test-agents)))))
+                        (let [deps (map #(nth test-agents %) (:deps test))]
+                          (if (every? (comp :done deref) deps)
+                            (assoc
+                             (if (every? (comp :success deref) deps)
+                               (try (exec-test-case test opts)
+                                    (catch Exception e
+                                      (log/error e "failed executing test")
+                                      (assoc test :error (str (or (.getMessage e) e)))))
+                               (assoc test :skipped "dependents failed"))
+                             :done true)
+                            test))))]
+
+    (add-watch count-down :key (fn [k r o n]
+                                 (when (zero? n)
+                                   (log/info "execution complete!")
+                                   (deliver p (map deref test-agents)))))
     (doseq [a test-agents i (:deps @a)]
       (add-watch (nth test-agents i) [(:id @a) i] (fn [k r o n] (send-off a exec-test))))
     (doseq [a test-agents]
+      (add-watch a [(:id @a)] (fn [k r o n]
+                                (if (and (:done n) (not (:done o))) (swap! count-down dec))))
       (send-off a exec-test))
     @p))

@@ -213,7 +215,8 @@
          (map (fn [suite]
                 (merge suite
                        (reduce (fn[s t]
-                                 (-> s (update (some #(if (t %) %) (keys s)) inc)
+                                 (-> s (update (or (some #(if (t %) %) (keys s))
+                                                   (log/warn "unexpected:" t) :error) inc)
                                      (update :total inc)))
                                result-keys (:tests suite)))))
          (#(assoc (reduce add-results %) :suites %)))))


move flatten out of add-comps, and into add-bolt-at

--- src/babel/generate.cljc
+++ src/babel/generate.cljc
@@ -89,30 +89,30 @@
           truncate? (if (= truncate? false) false true)]
       (if (empty? comp-paths)
         [bolt] ;; done: we've added all the comps to the bolt, so just return the bolt as a singleton vector.
-        (flatten
-         (mapfn #(add-comps % model
-                            (rest comp-paths)
-                            (rest bolts-at-paths)
-                            depth max-depth top-bolt truncate? take-n)
-                (add-bolts-to-path
-                 (first comp-paths) (first bolts-at-paths)
-                 bolt top-bolt model depth max-depth truncate? take-n)))))))
+        (mapfn #(add-comps % model
+                           (rest comp-paths)
+                           (rest bolts-at-paths)
+                           depth max-depth top-bolt truncate? take-n)
+               (add-bolts-to-path
+                (first comp-paths) (first bolts-at-paths)
+                bolt top-bolt model depth max-depth truncate? take-n))))))

 (defn add-bolt-at [top-bolt bolt path bolt-at model depth max-depth truncate? & [take-n]]
   (mapfn #(do-defaults % model)
          (let [comp-paths (find-comp-paths bolt-at)]
            (mapfn #(assoc-in bolt path %)
                   (lazy-seq
-                   (add-comps bolt-at
-                              model
-                              comp-paths
-                              (mapfn #(comp-path-to-bolts bolt-at % model (+ 1 depth) max-depth)
-                                     comp-paths)
-                              (+ 1 depth)
-                              max-depth
-                              top-bolt
-                              truncate?
-                              take-n))))))
+                   (flatten
+                    (add-comps bolt-at
+                               model
+                               comp-paths
+                               (mapfn #(comp-path-to-bolts bolt-at % model (+ 1 depth) max-depth)
+                                      comp-paths)
+                               (+ 1 depth)
+                               max-depth
+                               top-bolt
+                               truncate?
+                               take-n)))))))
 (defn generate
   "Return one (by default) or _n_ (using :take _n_) expressions matching spec _spec_ given the model _model_."
   [spec language-model


more (log/debug)ging

more (log/debug)ging

--- src/babel/generate.cljc
+++ src/babel/generate.cljc
@@ -74,10 +74,7 @@
                     & {:keys [max-total-depth truncate-children]
                        :or {max-total-depth max-total-depth
                             truncate-children true}}]
-  (log/trace (str "generate-all: generating from spec with cat "
-                  (get-in spec [:synsem :cat])))
-  (log/debug (str "generate-all:"
-                  (strip-refs (get-in spec [:synsem :cat])) "^" total-depth))
+  (log/debug (str "generate-all: spec:" (strip-refs spec)))

   (let [language-model (if (future? language-model) @language-model language-model)
         total-depth (if total-depth total-depth 0)]
@@ -314,6 +311,8 @@
         ;; total-depth, on the other hand, is the depth all the way to the top of the entire
         ;; expression, which might involve several parent lightning bolts.
         parents (shuffle (candidate-parents grammar spec))]
+    (log/debug (str "lightning-bolt: candidate-parents:" (string/join "," (map :rule parents))))
+    (log/debug (str "lightning-bolt:  for spec: " (strip-refs spec)))
     (let [lexical ;; 1. generate list of all phrases where the head child of each parent is a lexeme.
           (mapfn (fn [parent]
                          (if (= false (get-in parent [:head :phrasal] false))
@@ -354,6 +353,7 @@
   (log/trace (str "candidate-parents: spec: " (strip-refs spec)))
   (filter #(not (fail? %))
           (mapfn (fn [rule]
+                   (log/debug (str "candidate-parents: rule: " (:rule rule)))
                    (if (and (not (fail? (unifyc (get-in rule [:synsem :cat] :top)
                                                 (get-in spec [:synsem :cat] :top))))
                             (not (fail? (unifyc (get-in rule [:synsem :infl] :top)
@@ -362,8 +362,18 @@
                                                 (get-in spec [:synsem :sem :tense] :top))))
                             (not (fail? (unifyc (get-in rule [:synsem :modified] :top)
                                                 (get-in spec [:synsem :modified] :top)))))
-                     (unifyc spec rule)
-                     :fail))
+                     (let [result
+                           (unifyc spec rule)]
+                       (if (fail? result)
+                         (do
+                           (log/debug (str " rule: " (:rule rule) " failed (2)"))
+                           :fail)
+                         (do
+                           (log/debug (str " rule: " (:rule rule) " did not fail."))
+                           result)))
+                     (do
+                       (log/debug (str " rule: " (:rule rule) " failed (1)"))
+                       :fail)))
                  rules)))

 (defn lazy-shuffle [seq]


working on getting depth limits to work

working on getting depth limits to work

--- src/babel/generate.cljc
+++ src/babel/generate.cljc
@@ -11,7 +11,7 @@

 ;; during generation, will not decend deeper than this when creating a tree:
 ;; TODO: should also be possible to override per-language.
-(def ^:const max-total-depth 6)
+(def ^:const max-total-depth 2)

 ;; TODO support setting max-generated-complements to :unlimited
 (def ^:const max-generated-complements 20000)
@@ -52,42 +52,39 @@
                   (or (:lexicon (:generate model)) (:lexicon model))))))
     []))

-(declare comp-path-to-bolts)
+(declare comp-path-to-complements)
 (declare create-mapping)
-
-(declare nugents)
-
-;;(def foo (map #(println (fo %)) (take 10000 (nugents med spec))))

 (declare add-bolt-at)
 (declare do-assoc)

-(defn filter-by [bolt-and-comps]
-  (not (some empty? (vals bolt-and-comps))))
-
 (declare bolts-with-comps)

-(defn nugents [model spec & [max-depth]]
-  (log/debug (str "nugents:" (strip-refs spec)))
-  (let [max-depth (or max-depth babel.generate/max-total-depth)
-        bolts-and-comps (filter #(not (some empty? (vals %)))
-                                (bolts-with-comps spec model 0 max-depth))
-        all-of-them
-        (mapcat (fn [each-bolt-and-comps]
-                  (let [trellis (apply combo/cartesian-product (vals each-bolt-and-comps))]
-                    (map (fn [each-path-through-trellis]
-                           (zipmap (keys each-bolt-and-comps)
-                                   each-path-through-trellis))
-                         trellis)))
-                bolts-and-comps)]
-    (filter not-fail?
-            (map (fn [bolt-and-comps]
-                   (log/debug (str "bolt-and-comps: " ((:morph-ps model) (get bolt-and-comps []))))
-                   (do-defaults
-                    (do-assocs (get bolt-and-comps [])
-                               (dissoc bolt-and-comps []))
-                    model))
-                 all-of-them))))
+(defn nugents [model spec & [depth max-depth]]
+  (let [depth (or depth 0)
+        max-depth (or max-depth max-total-depth)]
+    (log/debug (str "nugents:" depth "/" max-depth ":         " (strip-refs spec)))
+    (let [bolts-and-comps (filter #(not (some empty? (vals %)))
+                                  (bolts-with-comps spec model depth max-depth))
+          all-of-them
+          (mapcat (fn [each-bolt-and-comps]
+                    (let [trellis (apply combo/cartesian-product (vals each-bolt-and-comps))]
+                      (map (fn [each-path-through-trellis]
+                             (zipmap (keys each-bolt-and-comps)
+                                     each-path-through-trellis))
+                           trellis)))
+                  bolts-and-comps)]
+      (filter not-fail?
+              (map (fn [bolt-and-comps]
+                     (log/debug (str "doing defaults on: " depth "/" max-depth ":" ((:morph-ps model) (get bolt-and-comps []))))
+                     (doall (map (fn [k]
+                                   (log/debug (str "doing defaults with path:" k " => " (count (get bolt-and-comps k)))))
+                                 (keys bolt-and-comps)))
+                     (do-defaults
+                      (do-assocs (get bolt-and-comps [])
+                                 (dissoc bolt-and-comps []))
+                      model))
+                   all-of-them)))))

 ;; TODO: use recur or reduce
 (defn do-assocs [accum paths-to-vals-at-path]
@@ -101,7 +98,7 @@
                      [path])
        (dissoc paths-to-vals-at-path path)))))

-(declare comp-path-to-bolts)
+(declare comp-path-to-complements)

 (defn bolts-with-comps
   "return a lazy sequence of maps. Each member in this lazy sequence
@@ -110,27 +107,29 @@
   sequence of possible complements at the end of each such path. In
   addition, there is an key [] whose value is the bolt itself."
   [spec model depth max-depth]
+  (log/debug  (str "bolts-with-comps:" depth "/" max-depth ":" (strip-refs spec)))
   (map (fn [lb]
          (merge
           (let [comp-paths (find-comp-paths lb)]
             (zipmap comp-paths
                     (map (fn [path]
-                           (filter not-fail? (comp-path-to-bolts lb path model depth max-depth)))
+                           (filter not-fail? (comp-path-to-complements lb path model depth max-depth)))
                          comp-paths)))
           {[]
            (filter not-fail? [lb])}))
        (lightning-bolts model spec depth max-depth)))

-(defn comp-path-to-bolts
+(defn comp-path-to-complements
   "return a lazy sequence of bolts for all possible complements that can be added to the end of the _path_ within _bolt_."
   [bolt path model depth max-depth]
+  (log/debug (str "comp-path-to-complements:" depth "/" max-depth ":" ((:morph-ps model) bolt) "@" path))
   (let [spec (get-in bolt path)
         lexemes (shufflefn (get-lexemes model spec))
         bolts-at (if (< depth max-depth)
                    (nugents
                     model
                     (get-in bolt path)
-                    depth max-depth))
+                    (+ 1 depth) max-depth))
         lexemes-before-phrases
         (lexemes-before-phrases depth max-depth)]
     (if lexemes-before-phrases
@@ -172,7 +171,7 @@
                    (flatten
                     (add-comps bolt-at model
                                comp-paths
-                               (mapfn #(comp-path-to-bolts bolt-at % model (+ 1 depth) max-depth)
+                               (mapfn #(comp-path-to-complements bolt-at % model (+ 1 depth) max-depth)
                                       comp-paths)
                                (+ 1 depth) max-depth top-bolt truncate? take-n)))))))
 (defn generate


adding a (pmap) here seems to improve time-to-first-expression

--- src/babel/generate.cljc
+++ src/babel/generate.cljc
@@ -55,14 +55,13 @@

      ;; for each such map in each bolt, find all possible combinations of complements, taking one complement per path.
      ;; the result is a trellis for each bolt, and a path through this trellis is one complement for each complement position.
-     (map (fn [each-bolt-and-comps]
-            (let [paths-to-comps (keys each-bolt-and-comps)
-                  vals (vals each-bolt-and-comps)]
-              (map (fn [each-path-through-trellis]
-                     (zipmap paths-to-comps
-                             each-path-through-trellis))
-                   (apply combo/cartesian-product vals)))))
-
+     (pmap (fn [each-bolt-and-comps]
+             (let [paths-to-comps (keys each-bolt-and-comps)
+                   vals (vals each-bolt-and-comps)]
+               (map (fn [each-path-through-trellis]
+                      (zipmap paths-to-comps each-path-through-trellis))
+                    (apply combo/cartesian-product vals)))))
+
      (mapcat (fn [bolt-group]
                (->> bolt-group
                     (map (fn [bolt-and-comps]


adding an example of using ionicons to the reagent template.  thanks @jamesnyika #66

--- resources/leiningen/new/expo/reagent/core.cljs
+++ resources/leiningen/new/expo/reagent/core.cljs
@@ -7,6 +7,9 @@

 (def ReactNative (js/require "react-native"))
 (def expo (js/require "expo"))
+(def AtExpo (js/require "@expo/vector-icons"))
+(def ionicons (.-Ionicons AtExpo))
+(def ic (r/adapt-react-class ionicons))

 (def text (r/adapt-react-class (.-Text ReactNative)))
 (def view (r/adapt-react-class (.-View ReactNative)))
@@ -25,6 +28,7 @@
                :style {:width 200
                        :height 200}}]
        [text {:style {:font-size 30 :font-weight "100" :margin-bottom 20 :text-align "center"}} @greeting]
+       [ic {:name "ios-arrow-down" :size 60 :color "green"}]
        [touchable-highlight {:style {:background-color "#999" :padding 10 :border-radius 5}
                              :on-press #(alert "HELLO!")}
         [text {:style {:color "white" :text-align "center" :font-weight "bold"}} "press me"]]])))


Updat about page to be consistent with quil template.

--- src/clj/quil_site/views/about.clj
+++ src/clj/quil_site/views/about.clj
@@ -65,7 +65,7 @@
             [:li "Create project: " [:code "lein new quil-cljs my-sketch"]]
             [:li [:code "cd my-sketch"]]
             [:li "Build the project: "[:code "lein cljsbuild once"]]
-            [:li "Open " [:code "web/index.html"]]]]]
+            [:li "Open " [:code "index.html"]]]]]

          [:p "Check out tutorials for using Quil in "
           (link-to "https://github.com/quil/quil/wiki/Dynamic-Workflow-%28for-LightTable%29" "Light Table")


try to fix sort error

try to fix sort error

force list comprehension up front to see if
that resolves the intermittent test failures
due to accounts not being sorted

--- src/clj_money/models/accounts.clj
+++ src/clj_money/models/accounts.clj
@@ -145,7 +145,8 @@
   (let [children (->> all-accounts
                       (filter #(= (:id account) (:parent-id %)))
                       (map #(append-children % all-accounts))
-                      (sort-by :name))]
+                      (sort-by :name)
+                      vec)]
     (assoc account :children children)))

 (defn select-nested-by-entity-id


Sign up page, begin activities edit, and general clean up

--- src/cljs/elevent_client/core.cljs
+++ src/cljs/elevent_client/core.cljs
@@ -19,6 +19,8 @@
       [garden.core :refer [css]]
       [secretary.core :as secretary :refer-macros [defroute]]
       [validateur.validation :refer [format-of
+                                     inclusion-of
+                                     numericality-of
                                      presence-of
                                      valid?
                                      validation-set]])
@@ -46,7 +48,7 @@
 ;; Location
 ;; =============================================================================

-(def location (atom ""))
+(defonce location (atom ""))

 ;; Session
@@ -291,8 +293,8 @@
 ;; =============================================================================

 (defn sign-in! [form]
-  (let [{:keys [email password]} form
-        auth-string (b64/encodeString (str email ":" password))]
+  (let [{:keys [Email Password]} form
+        auth-string (b64/encodeString (str Email ":" Password))]
     (GET (str api-url "/token")
          {:format          :json
           :response-format :json
@@ -300,16 +302,20 @@
           :headers         {:Authorization (str "Basic " auth-string)}
           :handler         (fn [response]
                              (swap! session assoc :token (:Token response))
-                             (swap! session assoc-in [:user :Email] email)
+                             (swap! session assoc-in [:user :Email] Email)
                              (refresh!)
                              (reset! messages {})
                              (add-message! :positive "Sign in succeeded")
-                             (js/location.replace (events-route)))})))
+                             (js/location.replace (events-route)))
+          :error-handler   (add-message! :negative "Sign in failed")})))

 (defn sign-out! []
   (swap! session dissoc :token :user)
   (refresh!)
   (set! js/location (home-route)))
+
+(defn sign-up! [form]
+  (users-endpoint :create form #(sign-in! form)))

 (add-watch users-db
            :find-user
@@ -557,7 +563,7 @@
         message])]))

-;; Views
+;; Pages
 ;; =============================================================================

 (defn home-page []
@@ -577,62 +583,97 @@
      "attend. Elevent Solutions provides the best possible experience for "
      "event organizers, attendees, and everyone in between."]]])

-; TODO: abstract events and events-explore into single component
-(defn events-page []
-  (let [leave-event
-        (fn [attendee-id]
-          ; TODO: this doesn't delete schedules
-          (attendees-endpoint :delete (d/entity @attendees-db attendee-id) nil))]
-    [:div.sixteen.wide.column
-     [:div.ui.segment
-      [:div
-       [:div.ui.vertical.segment
-        [:div.ui.two.column.grid
-         [:div.column
-          [:h1.ui.header "Your Events"]]
-         [:div.right.aligned.column
-          [:a.ui.tiny.labeled.icon.button {:href (event-add-route)}
-           [:i.plus.icon]
-           "Add event"]]]]
-       [:div.ui.vertical.segment
-        [:div.ui.divided.items
-         (for [event (map (fn [[event-id attendee-id]]
-                            (merge
-                              (into {} (d/entity @events-db event-id))
-                              {:AttendeeId attendee-id}))
-                          (d/q '[:find ?event-id ?attendee-id
-                                 :in $ ?user-id
-                                 :where
-                                 [?attendee-id :UserId  ?user-id]
-                                 [?attendee-id :EventId ?event-id]]
-                               @attendees-db
-                               (get-in @session [:user :UserId])))]
-           [:div.item
-            [:div.content
-             [:a.header {:href (event-route event)}
-              (:Name event)]
-             [:div.meta
-              [:strong "Date:"]
-              (let [start (from-string (:StartDate event))
-                    end   (from-string (:EndDate   event))]
-                (str (unparse datetime-formatter start)
-                     (when (after? end start)
-                       (str " to " (unparse datetime-formatter end)))))]
-             [:div.meta
-              [:strong "Venue:"]
-              (:Venue event)]
-             [:div.description
-              (:Description event)]
-             [:div.extra
-              [:a.ui.right.floated.small.button {:href (event-schedule-route event)}
-               "Your activities"
-               [:i.right.chevron.icon]]
-              [:a.ui.right.floated.small.button
-               {:on-click #(leave-event (:AttendeeId event))}
-               [:i.red.remove.icon]
-               "Leave event"]]]])]]]
-      [:div.ui.dimmer {:class (when (empty? @events) "active")}
-       [:div.ui.loader]]]]))
+(defn sign-in-page []
+  (let [form (atom {})
+        validator (validation-set (format-of :Email :format #"@")
+                                  (presence-of :Password))]
+    (fn []
+      (let [{:keys [Email Password]} @form
+            errors                   (validator @form)]
+        [:div.eight.wide.centered.column
+         [:div.ui.segment
+          [:h1.ui.dividing.header "Sign In"]
+          [:form.ui.form {:on-submit (fn [e]
+                                       (.preventDefault e)
+                                       (when (empty? errors)
+                                         (sign-in! @form)))}
+           [:div.two.fields
+            [:div.required.field {:class (when (and Email (:Email errors))
+                                           "error")}
+             [:label "Email"]
+             [:div.ui.icon.input
+              [input-atom :email
+               (r/wrap Email swap! form assoc :Email)]
+              [:i.mail.icon]]]
+            [:div.required.field {:class (when (and Password (:Password errors))
+                                           "error")}
+             [:label "Password"]
+             [:div.ui.icon.input
+              [input-atom :password
+               (r/wrap Password swap! form assoc :Password)]
+              [:i.lock.icon]]]]
+           [:button.ui.primary.button {:type :submit
+                                       :class (when (seq errors) "disabled")}
+            "Sign in"]]]]))))
+
+(defn sign-up-page []
+  (let [form (atom {})]
+    (fn []
+      (let [{:keys [Email Password PasswordConfirm FirstName LastName]} @form
+            validator (validation-set (format-of :Email :format #"@")
+                                      (presence-of :Password)
+                                      (inclusion-of :PasswordConfirm
+                                                    :in #{(:Password @form)})
+                                      (presence-of :FirstName)
+                                      (presence-of :LastName))
+            errors                   (validator @form)]
+        [:div.eight.wide.centered.column
+         [:div.ui.segment
+          [:h1.ui.dividing.header "Sign Up"]
+          [:form.ui.form {:on-submit (fn [e]
+                                       (.preventDefault e)
+                                       (when (empty? errors)
+                                         (sign-up! @form)))}
+           [:div.required.field {:class (when (and Email (:Email errors))
+                                          "error")}
+            [:label "Email"]
+            [:div.ui.icon.input
+             [input-atom :email
+              (r/wrap Email swap! form assoc :Email)]
+             [:i.mail.icon]]]
+           [:div.two.fields
+            [:div.required.field {:class (when (and Password (:Password errors))
+                                           "error")}
+             [:label "Password"]
+             [:div.ui.icon.input
+              [input-atom :password
+               (r/wrap Password swap! form assoc :Password)]
+              [:i.lock.icon]]]
+            [:div.required.field {:class (when (and PasswordConfirm
+                                                    (:PasswordConfirm errors))
+                                           "error")}
+             [:label "Confirm Password"]
+             [:div.ui.input
+              [input-atom :password
+               (r/wrap PasswordConfirm swap! form assoc :PasswordConfirm)]]]]
+           [:div.two.fields
+            [:div.required.field {:class (when (and FirstName
+                                                    (:FirstName errors))
+                                           "error")}
+             [:label "First Name"]
+             [:div.ui.input
+              [input-atom :text
+               (r/wrap FirstName swap! form assoc :FirstName)]]]
+            [:div.required.field {:class (when (and LastName
+                                                    (:LastName errors))
+                                           "error")}
+             [:label "Last Name"]
+             [:div.ui.input
+              [input-atom :text
+               (r/wrap LastName swap! form assoc :LastName)]]]]
+           [:button.ui.primary.button {:type :submit
+                                       :class (when (seq errors) "disabled")}
+            "Sign up"]]]]))))

 (defn events-explore-page []
   (let [attending-events (d/q '[:find [?event-id ...]
@@ -689,206 +730,116 @@
       [:div.ui.dimmer {:class (when-not @events "active")}
        [:div.ui.loader]]]]))

-(defn event-edit-page []
-  (let [form (atom {})
-        validator (validation-set (presence-of :Name)
-                                  (presence-of :Organization)
-                                  (presence-of :Venue)
-                                  (presence-of :StartDate)
-                                  (presence-of :EndDate))
-        clone-id (atom 0)]
-    (add-watch clone-id :clone
-               (fn [_ _ _ id]
-                 (when-not (zero? (int id))
-                   (reset! form (dissoc (->> id
-                                             int
-                                             (d/entity @events-db)
-                                             seq
-                                             (into {}))
-                                        :EventId)))))
-    (fn []
-      (let [{:keys [Name OrganizationId Venue StartDate EndDate Description]}
-            @form
-
-            errors
-            (validator @form)
-
-            clonable-events
-            (cons ["None" 0]
-                  (d/q '[:find ?name ?id
-                         :where [?id :Name ?name]]
-                       @events-db))
-
-            associated-organizations
-            (cons ["None" 0]
-                  (d/q '[:find ?name ?id
-                         :where [?id :Name ?name]]
-                       @organizations-db))
-
-            create-event
-            (fn [form]
-              (when (empty? errors)
-                (events-endpoint :create
-                                 form
-                                 #(set! js/location (events-explore-route)))))]
-        [:div.sixteen.wide.column
-         [:div.ui.segment
-          [:form.ui.form
-           [:div.ui.vertical.segment
-            [:h2.ui.dividing.header "Add an Event"]
-            [:p (prn-str @form)]
-            [:div.two.fields
-             [:div.required.field {:class (when (and Name (:Name errors))
-                                            "error")}
-              [:label "Name"]
-              [input-atom :text (r/wrap Name swap! form assoc :Name)]]
-             [:div.field
-              [:label "Clone From"]
-              [input-atom :select clonable-events clone-id]]]
-            [:div.two.fields
-             [:div.field
-              [:label "Organization"]
-              [input-atom :select associated-organizations
-               (r/wrap OrganizationId swap! form assoc :OrganizationId)]]
-             [:div.required.field {:class (when (and Venue (:Venue errors))
-                                            "error")}
-              [:div.field
-               [:label "Venue"]
-               [input-atom :text (r/wrap Venue swap! form assoc :Venue)]]]]
-            [:div.two.fields
-             [:div.field
-              [:label "Start Date"]
-              [input-atom :datetime-local
-               (r/wrap StartDate swap! form assoc :StartDate)
-               #(or % (unparse (:date-hour-minute formatters) (now)))
-               #(unparse (:date-hour-minute formatters) (from-string %))]]
-             [:div.field
-              [:label "End Date"]
-              [input-atom :datetime-local
-               (r/wrap EndDate swap! form assoc :EndDate)
-               #(or % (unparse (:date-hour-minute formatters) (now)))
-               #(unparse (:date-hour-minute formatters) (from-string %))]]]
-            [:div.field
-             [:label "Description"]
-             [input-atom :textarea
-              (r/wrap Description swap! form assoc :Description)]]
-            [:button.ui.primary.button {:class (when (seq errors) "disabled")
-                                        :type :submit
-                                        :on-click #(create-event @form)}
-             "Add"]]]]]))))
-
-(defn event-activity-edit-page [event-id]
-  (let [form (atom {:EventId event-id})
-        validator (validation-set (presence-of :Name)
-                                  (presence-of :StartTime)
-                                  (presence-of :EndTime)
-                                  (format-of   :EnrollmentCap :format #"[0-9_]"
-                                                              :allow-nil true
-                                                              :allow-blank true
-                                                              :message "Please enter a number"))
-        reset-form!
-        (fn []
-          (reset! form (atom {:EventId event-id})))]
-    (fn [event-id]
-      (let [{:keys [Name Location EnrollmentCap StartTime EndTime Description]}
-            @form
-
-            errors
-            (validator @form)
-
-            event
-            (into {} (seq (d/entity @events-db event-id)))
-
-            activities
-            (map #(d/entity @activities-db %)
-                 (d/q '[:find [?e ...]
-                        :in $ ?event-id
-                        :where
-                        [?e :EventId ?event-id]]
-                      @activities-db
-                      event-id))
-
-            create-activity
-            (fn [form]
-              (when (empty? errors)
-                (activities-endpoint :create
-                                     (let [start-time (:StartTime form)
-                                           end-time   (:EndTime form)]
-                                       (assoc form
-                                         :StartTime (unparse (:date-hour-minute-second formatters)
-                                                             (from-string start-time))
-                                         :EndTime (unparse (:date-hour-minute-second formatters)
-                                                           (from-string end-time))))
-                                     #(reset-form!))))]
-        (when (seq event)
-          [:div.sixteen.wide.column
-           [:div.ui.segment
-            [:div.ui.vertical.segment
-             [:h2.ui.header
-              (:Name event)]]
-            [:div.ui.vertical.segment
-             [:h2.ui.header
-              "Add activity"]
-             [:form.ui.form
-              [:div.one.field
-               [:div.required.field {:class (when (and Name (:Name errors))
-                                              "error")}
-                [:label "Name"]
-                [input-atom :text (r/wrap Name swap! form assoc :Name)]]]
-              [:div.two.fields
-               [:div.field
-                [:label "Location"]
-                [input-atom :text (r/wrap Location swap! form assoc :Location)]]
-               [:div.field {:class (when (and EnrollmentCap (:EnrollmentCap errors))
-                                     "error")}
-                [:label "Enrollment Cap"]
-                [input-atom :text (r/wrap EnrollmentCap swap! form assoc :EnrollmentCap)]]]
-              [:div.two.fields =
-               [:div.required.field {:class (when (and StartTime (:StartTime errors))
-                                              "error")}
-                [:label "Start Time"]
-                [input-atom :datetime-local
-                 (r/wrap StartTime swap! form assoc :StartTime)
-                 #(or % (unparse (:date-hour-minute formatters) (now))) ; todo: date inputs clear on input
-                 #(unparse (:date-hour-minute formatters) (from-string %))]]
-               [:div.required.field {:class (when (and EndTime (:EndTime errors))
-                                              "error")}
-                [:label "End Time"]
-                [input-atom :datetime-local
-                 (r/wrap EndTime swap! form assoc :EndTime)
-                 #(or % (unparse (:date-hour-minute formatters) (now)))
-                 #(unparse (:date-hour-minute formatters) (from-string %))]]]
-              [:div.field
-               [:label "Description"]
-               [input-atom :textarea
-                (r/wrap Description swap! form assoc :Description)]]
-              [:button.ui.primary.button {:class (when (seq errors) "disabled")
-                                          :type :submit
-                                          :on-click #(create-activity @form)}
-               "Add"]]]
-            [:div.ui.vertical.segment
-             [:h2.ui.header
-              "Activities"]
-             [:table.ui.table
-              [:thead
-               [:tr
-                [:th "Start Time"]
-                [:th "End Time"]
-                [:th "Activity"]
-                [:th "Location"]]]
-              [:tbody
-               (for [activity activities]
-                 ^{:key (:ActivityId activity)}
-                 [:tr
-                  [:td (let [start (from-string (:StartTime activity))]
-                         (unparse datetime-formatter start))]
-                  [:td (let [end   (from-string (:EndTime   activity))]
-                         (unparse datetime-formatter end))]
-                  [:td (:Name activity)]
-                  [:td (:Location activity)]])]]]]])))))
+; TODO: abstract events and events-explore into single component
+(defn events-page []
+  (let [leave-event
+        (fn [attendee-id]
+          ; TODO: this doesn't delete schedules
+          (attendees-endpoint :delete
+                              (d/entity @attendees-db attendee-id)
+                              nil))]
+    #_[:div.sixteen.wide.column
+     [:div.ui.segment
+      [:div
+       [:div.ui.vertical.segment
+        [:div.ui.two.column.grid
+         [:div.column
+          [:h1.ui.header "Events You've Created"]]
+         [:div.right.aligned.column
+          [:a.ui.tiny.labeled.icon.button {:href (event-add-route)}
+           [:i.plus.icon]
+           "Add event"]]]]
+       [:div.ui.vertical.segment
+        [:div.ui.divided.items
+         (for [event (map (fn [[event-id attendee-id]]
+                            (assoc
+                              (into {} (d/entity @events-db event-id))
+                              :AttendeeId attendee-id))
+                          (d/q '[:find ?event-id ?attendee-id
+                                 :in $ ?user-id
+                                 :where
+                                 [?attendee-id :UserId  ?user-id]
+                                 [?attendee-id :EventId ?event-id]]
+                               @attendees-db
+                               (get-in @session [:user :UserId])))]
+           [:div.item
+            [:div.content
+             [:a.header {:href (event-route event)}
+              (:Name event)]
+             [:div.meta
+              [:strong "Date:"]
+              (let [start (from-string (:StartDate event))
+                    end   (from-string (:EndDate   event))]
+                (str (unparse datetime-formatter start)
+                     (when (after? end start)
+                       (str " to " (unparse datetime-formatter end)))))]
+             [:div.meta
+              [:strong "Venue:"]
+              (:Venue event)]
+             [:div.description
+              (:Description event)]
+             [:div.extra
+              [:a.ui.right.floated.small.button {:href (event-schedule-route event)}
+               "Your activities"
+               [:i.right.chevron.icon]]
+              [:a.ui.right.floated.small.button
+               {:on-click #(leave-event (:AttendeeId event))}
+               [:i.red.remove.icon]
+               "Leave event"]]]])]]]
+      [:div.ui.dimmer {:class (when (empty? @events) "active")}
+       [:div.ui.loader]]]
+     [:div.ui.segment
+      [:div
+       [:div.ui.vertical.segment
+        [:div.ui.two.column.grid
+         [:div.column
+          [:h1.ui.header "Your Events"]]
+         [:div.right.aligned.column
+          [:a.ui.tiny.labeled.icon.button {:href (event-add-route)}
+           [:i.plus.icon]
+           "Add event"]]]]
+       [:div.ui.vertical.segment
+        [:div.ui.divided.items
+         (for [event (map (fn [[event-id attendee-id]]
+                            (merge
+                              (into {} (d/entity @events-db event-id))
+                              {:AttendeeId attendee-id}))
+                          (d/q '[:find ?event-id ?attendee-id
+                                 :in $ ?user-id
+                                 :where
+                                 [?attendee-id :UserId  ?user-id]
+                                 [?attendee-id :EventId ?event-id]]
+                               @attendees-db
+                               (get-in @session [:user :UserId])))]
+           [:div.item
+            [:div.content
+             [:a.header {:href (event-route event)}
+              (:Name event)]
+             [:div.meta
+              [:strong "Date:"]
+              (let [start (from-string (:StartDate event))
+                    end   (from-string (:EndDate   event))]
+                (str (unparse datetime-formatter start)
+                     (when (after? end start)
+                       (str " to " (unparse datetime-formatter end)))))]
+             [:div.meta
+              [:strong "Venue:"]
+              (:Venue event)]
+             [:div.description
+              (:Description event)]
+             [:div.extra
+              [:a.ui.right.floated.small.button {:href (event-schedule-route event)}
+               "Your activities"
+               [:i.right.chevron.icon]]
+              [:a.ui.right.floated.small.button
+               {:on-click #(leave-event (:AttendeeId event))}
+               [:i.red.remove.icon]
+               "Leave event"]]]])]]]
+      [:div.ui.dimmer {:class (when (empty? @events) "active")}
+       [:div.ui.loader]]]]))

 (defn event-page [event-id]
-  (let [event (into {} (seq (d/entity @events-db event-id)))
+  (let [event (into {} (d/entity @events-db event-id))

         activities (map #(d/entity @activities-db %)
                         (d/q '[:find [?e ...]
@@ -1000,6 +951,210 @@
               [:i.edit.icon]
               "Edit"]]]]]]]])))

+(defn event-edit-page []
+  (let [form (atom {})
+        validator (validation-set (presence-of :Name)
+                                  (presence-of :Organization)
+                                  (presence-of :Venue)
+                                  (presence-of :StartDate)
+                                  (presence-of :EndDate))
+        clone-id (atom 0)]
+    (add-watch clone-id :clone
+               (fn [_ _ _ id]
+                 (when-not (zero? (int id))
+                   (reset! form (dissoc (->> id
+                                             int
+                                             (d/entity @events-db)
+                                             seq
+                                             (into {}))
+                                        :EventId)))))
+    (fn []
+      (let [{:keys [Name OrganizationId Venue StartDate EndDate Description]}
+            @form
+
+            errors
+            (validator @form)
+
+            clonable-events
+            (cons ["None" 0]
+                  (d/q '[:find ?name ?id
+                         :where [?id :Name ?name]]
+                       @events-db))
+
+            associated-organizations
+            (cons ["None" 0]
+                  (d/q '[:find ?name ?id
+                         :where [?id :Name ?name]]
+                       @organizations-db))
+
+            create-event
+            (fn [form]
+              (when (empty? errors)
+                (events-endpoint :create
+                                 form
+                                 #(set! js/location (events-explore-route)))))]
+        [:div.sixteen.wide.column
+         [:div.ui.segment
+          [:form.ui.form
+           [:div.ui.vertical.segment
+            [:h2.ui.dividing.header "Add an Event"]
+            [:p (prn-str @form)]
+            [:div.two.fields
+             [:div.required.field {:class (when (and Name (:Name errors))
+                                            "error")}
+              [:label "Name"]
+              [input-atom :text (r/wrap Name swap! form assoc :Name)]]
+             [:div.field
+              [:label "Clone From"]
+              [input-atom :select clonable-events clone-id]]]
+            [:div.two.fields
+             [:div.field
+              [:label "Organization"]
+              [input-atom :select associated-organizations
+               (r/wrap OrganizationId swap! form assoc :OrganizationId)]]
+             [:div.required.field {:class (when (and Venue (:Venue errors))
+                                            "error")}
+              [:div.field
+               [:label "Venue"]
+               [input-atom :text (r/wrap Venue swap! form assoc :Venue)]]]]
+            [:div.two.fields
+             [:div.field
+              [:label "Start Date"]
+              [input-atom :datetime-local
+               (r/wrap StartDate swap! form assoc :StartDate)
+               #(or % (unparse (:date-hour-minute formatters) (now)))
+               #(unparse (:date-hour-minute formatters) (from-string %))]]
+             [:div.field
+              [:label "End Date"]
+              [input-atom :datetime-local
+               (r/wrap EndDate swap! form assoc :EndDate)
+               #(or % (unparse (:date-hour-minute formatters) (now)))
+               #(unparse (:date-hour-minute formatters) (from-string %))]]]
+            [:div.field
+             [:label "Description"]
+             [input-atom :textarea
+              (r/wrap Description swap! form assoc :Description)]]
+            [:button.ui.primary.button {:class (when (seq errors) "disabled")
+                                        :type :submit
+                                        :on-click #(create-event @form)}
+             "Add"]]]]]))))
+
+(defn event-activity-edit-page [event-id & [activity-id]]
+  (let [form (atom {:EventId event-id})
+        validator (validation-set (presence-of     :Name)
+                                  (presence-of     :StartTime)
+                                  (presence-of     :EndTime)
+                                  (numericality-of :EnrollmentCap))
+        reset-form!
+        (fn []
+          (reset! form (atom {:EventId event-id})))]
+    (when activity-id
+      (if-let [activity (seq (d/entity @activities-db activity-id))]
+        (reset! form (into {} activity))
+        (add-watch activities-db
+                   :activity-edit
+                   (fn [_ _ _ _]
+                     (reset! form (into {} (d/entity @activities-db
+                                                     activity-id)))
+                     (remove-watch activities-db :activity-edit)))))
+    (fn [event-id]
+      (let [{:keys [Name Location EnrollmentCap StartTime EndTime Description]}
+            @form
+
+            errors
+            (validator @form)
+
+            event
+            (into {} (d/entity @events-db event-id))
+
+            activities
+            (doall (map #(d/entity @activities-db %)
+                        (d/q '[:find [?e ...]
+                               :in $ ?event-id
+                               :where
+                               [?e :EventId ?event-id]]
+                             @activities-db
+                             event-id)))
+
+            create-activity
+            (fn [form]
+              (when (empty? errors)
+                (activities-endpoint :create
+                                     (let [start-time (:StartTime form)
+                                           end-time   (:EndTime form)]
+                                       (assoc form
+                                         :StartTime (unparse (:date-hour-minute-second formatters)
+                                                             (from-string start-time))
+                                         :EndTime (unparse (:date-hour-minute-second formatters)
+                                                           (from-string end-time))))
+                                     #(reset-form!))))]
+        (when (seq event)
+          [:div.sixteen.wide.column
+           [:div.ui.segment
+            [:div.ui.vertical.segment
+             [:h2.ui.header
+              (:Name event)]]
+            [:div.ui.vertical.segment
+             [:h2.ui.header
+              "Add activity"]
+             [:form.ui.form
+              [:div.one.field
+               [:div.required.field {:class (when (and Name (:Name errors))
+                                              "error")}
+                [:label "Name"]
+                [input-atom :text (r/wrap Name swap! form assoc :Name)]]]
+              [:div.two.fields
+               [:div.field
+                [:label "Location"]
+                [input-atom :text (r/wrap Location swap! form assoc :Location)]]
+               [:div.field {:class (when (and EnrollmentCap (:EnrollmentCap errors))
+                                     "error")}
+                [:label "Enrollment Cap"]
+                [input-atom :text (r/wrap EnrollmentCap swap! form assoc :EnrollmentCap)]]]
+              [:div.two.fields =
+               [:div.required.field {:class (when (and StartTime (:StartTime errors))
+                                              "error")}
+                [:label "Start Time"]
+                [input-atom :datetime-local
+                 (r/wrap StartTime swap! form assoc :StartTime)
+                 #(or % (unparse (:date-hour-minute formatters) (now))) ; todo: date inputs clear on input
+                 #(unparse (:date-hour-minute formatters) (from-string %))]]
+               [:div.required.field {:class (when (and EndTime (:EndTime errors))
+                                              "error")}
+                [:label "End Time"]
+                [input-atom :datetime-local
+                 (r/wrap EndTime swap! form assoc :EndTime)
+                 #(or % (unparse (:date-hour-minute formatters) (now)))
+                 #(unparse (:date-hour-minute formatters) (from-string %))]]]
+              [:div.field
+               [:label "Description"]
+               [input-atom :textarea
+                (r/wrap Description swap! form assoc :Description)]]
+              [:button.ui.primary.button {:class (when (seq errors) "disabled")
+                                          :type :submit
+                                          :on-click #(create-activity @form)}
+               "Add"]]]
+            [:div.ui.vertical.segment
+             [:h2.ui.header
+              "Activities"]
+             [:table.ui.table
+              [:thead
+               [:tr
+                [:th "Start Time"]
+                [:th "End Time"]
+                [:th "Activity"]
+                [:th "Location"]]]
+              [:tbody
+               (for [activity activities]
+                 ^{:key (:ActivityId activity)}
+                 [:tr
+                  [:td (let [start (from-string (:StartTime activity))]
+                         (unparse datetime-formatter start))]
+                  [:td (let [end   (from-string (:EndTime   activity))]
+                         (unparse datetime-formatter end))]
+                  [:td (:Name activity)]
+                  [:td (:Location activity)]])]]]]])))))
+
 (defn event-attendee-page [event-id attendee-id]
   (let [check-in-or-out
         (fn [op url init callback]
@@ -1018,22 +1173,21 @@

         button-loading? (atom false)]
     (fn [event-id attendee-id]
-      (let [event (into {} (seq (d/entity @events-db event-id)))
+      (let [event (into {} (d/entity @events-db event-id))

             attendee
-            (atom (into {} (seq
-                             (first (map (fn [[user-id attendee-id]]
-                                           (merge (into {} (d/entity @users-db
-                                                                     user-id))
-                                                  (into {} (d/entity @attendees-db
-                                                                     attendee-id))))
-                                         (d/q '[:find ?e ?a
-                                                :in $ ?attendee-id
-                                                :where
-                                                [?a :AttendeeId ?attendee-id]
-                                                [?a :UserId ?e]]
-                                              @attendees-db
-                                              attendee-id))))))
+            (atom (into {} (first (map (fn [[user-id attendee-id]]
+                                         (merge (into {} (d/entity @users-db
+                                                                   user-id))
+                                                (into {} (d/entity @attendees-db
+                                                                   attendee-id))))
+                                       (d/q '[:find ?e ?a
+                                              :in $ ?attendee-id
+                                              :where
+                                              [?a :AttendeeId ?attendee-id]
+                                              [?a :UserId ?e]]
+                                            @attendees-db
+                                            attendee-id)))))

             attendee-activities
             (d/q '[:find ?schedule-id ?activity-id
@@ -1198,7 +1352,7 @@
             (validator @form)

             event
-            (into {} (seq (d/entity @events-db event-id)))
+            (into {} (d/entity @events-db event-id))

             register
             (fn [form]
@@ -1250,7 +1404,7 @@
                "Register"]]]]])))))

 (defn event-schedule-page [event-id]
-  (let [event (into {} (seq (d/entity @events-db event-id)))
+  (let [event (into {} (d/entity @events-db event-id))

         scheduled-activities
         (d/q '[:find ?schedule-id ?activity-id
@@ -1576,36 +1730,6 @@
   (create-class {:reagent-render statistics-page
                  :component-did-mount #(statistics-page-did-mount event-id)}))

-(defn sign-in-page []
-  (let [form (atom {})
-        validator (validation-set (format-of :email :format #"@")
-                                  (presence-of :password))]
-    (fn []
-      (let [{:keys [email password]} @form
-            errors                   (validator @form)]
-        [:div.eight.wide.centered.column
-         [:div.ui.segment
-          [:h1.ui.dividing.header "Sign In"]
-          [:form.ui.form {:on-submit (fn [e]
-                                       (.preventDefault e)
-                                       (when (valid? validator @form)
-                                         (sign-in! @form)))}
-           [:div.two.fields
-            [:div.required.field {:class (when (and email (:email errors))
-                                           "error")}
-             [:label "Email"]
-             [:div.ui.icon.input
-              [input-atom :email
-               (r/wrap email swap! form assoc :email)]
-              [:i.mail.icon]]]
-            [:div.required.field
-             [:label "Password"]
-             [:div.ui.icon.input
-              [input-atom :password
-               (r/wrap password swap! form assoc :password)]
-              [:i.lock.icon]]]]
-           [:button.ui.primary.button {:type :submit} "Sign in"]]]]))))
-

 ;; Frame
 ;; =============================================================================


add form helpers

add form helpers

--- src/bones/editable.cljs
+++ src/bones/editable.cljs
@@ -78,9 +78,14 @@
           things (local-get-item prefix cmdspace)]

       (local-set-item prefix cmdspace
-                      (if (some #{"new" "update"} #{action})
+                      (condp = (name action)
+                        "new"
                         (assoc things id thing)
+                        "update"
+                        (update-in things [id :inputs] merge args)
+                        "delete"
                         (dissoc things id)))
+      ;; (go (<! (a/timeout 10)))
       (dispatch [:response/command
                  ;; args in the response means update the editable thing's inputs
                  {:args (:inputs thing)
@@ -93,16 +98,88 @@
           things (local-get-item prefix form-type)]
       (dispatch [:response/query {:results things} 200 tap]))))

-(comment
-
-  (local-get-item "bones" "todos")
-  (str "bones" (name "todos"))
-  (str "bones" "-" (name :todos))
-
-
-
-
-  )
+
+;; forms
+(defn detect-controls [{:keys [enter escape]}]
+  (fn [keypress]
+    (case (.-which keypress)
+      13 (enter)
+      ;; chrome won't fire 27, so use on-blur instead
+      27 (escape)
+      nil)))
+
+(defn field [form-type identifier attr html-attrs]
+  (let [path [:editable form-type identifier :inputs attr]
+        value (subscribe path)
+        input-type (or (:input-type html-attrs) :input)
+        value-attr (or (:value-attr html-attrs) :value)
+        opts (dissoc html-attrs :value-attr :input-type)]
+    (fn []
+      [input-type (merge {:on-change #(dispatch-sync (conj path (-> % .-target .-value)))
+                          value-attr @value}
+                         opts)])))
+
+(defn checkbox [form-type identifier attr & {:as html-attrs}]
+  (field form-type identifier attr (merge {:type "checkbox"
+                                           :value-attr :checked}
+                                          html-attrs)))
+
+(defn input [form-type identifier attr & {:as html-attrs}]
+  (field form-type identifier attr html-attrs))
+
+(defn calculate-command [form-type action]
+  (keyword (name form-type) (name action)))
+
+(defn save-fn
+  ([[form-type id]]
+   ;; call it, save all the inputs. this way it can be used without parens, which
+   ;; is kind of neat
+   ((apply save-fn [form-type id] {} {})))
+  ([[form-type id] args]
+   ;; in case you don't want to pass opts
+   (save-fn [form-type id] args {}))
+  ([[form-type id] args opts]
+   ;; opts can be options like :solo "don't merge values from :inputs"
+   (fn []
+     (let [new-args (if (fn? args) (args) args)
+           action (if (= :new id) :new :update)
+           ;; args are attributes/inputs of the thing
+           more-args (merge (if (= :new id) nil {:id id}) new-args)
+           ;; opts are logistical things like how to update the form from the
+           ;; response
+           ;; we can't send :id equal to :new because :id is probably an
+           ;; attribute that will be generated
+           more-opts (merge (if (= :new id) {:identifier :new} ) opts)
+           ;; convention for commands here e.g.: :todos/update
+           calculated-command (calculate-command form-type action)]
+       (dispatch [:request/command calculated-command more-args more-opts])))))
+
+(defn form
+  "returns function as closures around subscriptions to a single 'editable' thing in the
+  db. The thing has attributes, whose current value is accessed by calling `inputs' e.g., with arguments. No arguments will return all the attributes"
+  [form-type identifier]
+  (let [inputs-atom (subscribe [:editable form-type identifier :inputs])
+        state-atom (subscribe [:editable form-type identifier :state])
+        errors-atom (subscribe [:editable form-type identifier :errors])
+        defaults-atom (subscribe [:editable form-type identifier :defaults])
+        inputs (fn [& args] (get-in @inputs-atom args))
+        state (fn [& args] (get-in @state-atom args))
+        errors (fn [& args] (get-in @errors-atom args))
+        defaults (fn [& args] (get-in @defaults-atom args))]
+    {:inputs inputs
+     :state  state
+     :errors errors
+     :defaults defaults
+
+     :save (partial save-fn [form-type identifier])
+     :delete #(dispatch [:request/command (calculate-command form-type :delete) {:id identifier}])
+     :reset  #(dispatch (e/editable-reset :todos identifier (state :reset)))
+     :edit   #(dispatch [:editable
+                         [:editable :todos identifier :state :reset (inputs)]
+                         [:editable :todos identifier :state :editing true]])
+     }))
+
+
 ;; helpers
 (defn editable-reset [etype identifier inputs]
   [:editable
@@ -227,9 +304,12 @@
                          (:identifier opts)
                          ;; must be an :identifier like :new or just an id
                          partial-args)
+          solo (:solo opts)
           ;; form-type and identifier are needed by the response handler to
           ;; update the thing in the db
-          more-tap (merge tap {:form-type form-type :identifier identifier})
+          more-tap (merge tap
+                          {:form-type form-type :identifier identifier}
+                          {:solo solo})
           ;; this is the specification for the editable thing:
           {:keys [spec outgoing-spec]
            :or {outgoing-spec spec}
@@ -240,7 +320,9 @@
           ;; partial-args will needlessly overwrite the :id in :inputs, but oh well
           ;; partial-args should overwrite what is in the db, and update the db
           ;; in the response handler
-          inputs-defaults (merge defaults inputs (if (map? partial-args) partial-args))
+          ;; don't merge inputs from the if :solo - only send the given
+          ;; attributes in the event vector
+          inputs-defaults (merge defaults (if solo {} inputs) (if (map? partial-args) partial-args))
           ]
       ;; it seems weird but a request with empty args is possible
       ;; both outgoing-spec and inputs can be nil


change line formatting

change line formatting

--- src/cake/tasks/test.clj
+++ src/cake/tasks/test.clj
@@ -114,7 +114,7 @@
 (defn report-ns
   "generate a summary for the namespace with `results`"
   [ns results]
-  (print (ansi/style (apply str (repeat 40 "-")) :white))
+  (println (ansi/style (apply str (repeat 40 " ")) :underline))
   (println (ansi/style (str"\ncake test " ns) :cyan))
   (doseq [test-result (doall (:tests results))]
     (report-test ns test-result))
@@ -157,14 +157,15 @@
                                                                     (find-namespaces-in-dir (java.io.File. test-path))))
                                                          (merge (test-opts) (apply hash-map opts)))]
                              (report-ns ns (bake-invoke run-ns-tests ns tests))))]
-               (println (ansi/style (apply str (repeat 50 " ")) :underline))
-               (println (format "\nRan %d tests in %d namespaces, containing %d assertions, in %.2f seconds."
-                                test-count
-                                ns-count
-                                assertion-count
-                                (/ (- (System/currentTimeMillis)
-                                      start)
-                                   1000.0)))
+               (let [summary (format "\nRan %d tests in %d namespaces, containing %d assertions, in %.2f seconds."
+                                     test-count
+                                     ns-count
+                                     assertion-count
+                                     (/ (- (System/currentTimeMillis)
+                                           start)
+                                        1000.0))]
+                 (println (ansi/style (apply str (repeat (- (count summary) 2) " ")) :underline))
+                 (println summary))
                (println (ansi/style (format "%d OK, %d failures, %d errors." pass-count fail-count error-count)
                                     (if (= 0 (+ fail-count error-count)) :green :red)))))))


added new line

added new line

--- src/clabango/parser.clj
+++ src/clabango/parser.clj
@@ -337,4 +337,4 @@
 (defn render-file [filename context]
   (-> filename
       load-template
-      (render context)))+      (render context)))


Clean unneeded core away

Clean unneeded core away

--- src/clj/mysema/core.clj
+++ src/clj/mysema/core.clj
@@ -1,110 +0,0 @@
-(ns mysema.core
-  (:import (java.util ArrayList)))
-
-
-(defn lookback-map
-  "Map trandsducer which calls f with previous value as second argument"
-  [f]
-  (fn [rf]
-    (let [pv (volatile! nil)]
-      (fn
-        ([] (rf))
-        ([result] (rf result))
-        ([result input]
-         (let [v (f input @pv)]
-           (vreset! pv v)
-           (rf result v)))))))
-
-;; Not so usefule
-(defn lookback-map-with-rf
-  "Map trandsducer which calls f with previous value as second argument and gives
-  function access to reducing function"
-  [f]
-  (fn [rf]
-    (let [pv (volatile! nil)]
-      (fn
-        ([] (rf))
-        ([result] (rf result))
-        ([result input]
-         (let [v (f input @pv rf result)]
-           (vreset! pv v)
-           (rf result v)))))))
-
-(defn map-with-rf
-  "Map trandsducer which calls f with previous value as second argument"
-  [f]
-  (fn [rf]
-      (fn
-        ([] (rf))
-        ([result] (rf result))
-        ([result input]
-           (rf result (f input rf result))))))
-
-
-;
-;(extend-protocol clojure.core.protocols.CollReduce
-;  java.io.BufferedReader
-;  (coll-reduce
-;    ([rdr f]
-;     (let [line (.readLine rdr)]
-;       (if line
-;         (loop [ret line]
-;           (if line
-;             (let [ret (f)])))
-;         (f)
-;         )
-;
-;       )
-;     (loop [line (.readLine rdr)]
-;       (if line
-;         (let [ret (f )]))))
-;    ([rdr f val]
-;    (loop []))
-;
-
-  ;
-  ;  (let [s (.s str-seq)]
-  ;    (loop [i (.i str-seq)
-  ;           val val]
-  ;      (if (< i (.length s))
-  ;        (let [ret (f val (.charAt s i))]
-  ;          (if (reduced? ret)
-  ;            @ret
-  ;            (recur (inc i) ret)))
-  ;        val))))
-  ;)
-
-  ;(when-let [line (.readLine rdr)]
-  ;  (cons line (lazy-seq (line-seq rdr)))))
-
-(defn lookback-partition-by
-  "Returns a stateful transducer which partitions the collection
-  when predicate returns truthy value.
-  Predicate function is called with two values, current item
-  and previous item."
-  [pred]
-  (fn [rf]
-    (let [a (ArrayList.)
-          pi (volatile! nil)]
-      (fn
-        ([] (rf))
-        ([result]
-         (let [result (if (.isEmpty a)
-                        result
-                        (let [v (vec (.toArray a))]
-                          (.clear a)
-                          (unreduced (rf result v))))]
-           (rf result)))
-        ([result input]
-         (let [pitem @pi]
-           (vreset! pi input)
-           (if-not (pred input pitem)
-             (do
-               (.add a input)
-               result)
-             (let [v (vec (.toArray a))]
-               (.clear a)
-               (let [ret (rf result v)]
-                 (when-not (reduced? ret)
-                   (.add a input))
-                 ret)))))))))


Fix (random-3d) to produce uniformly distributed vectors. Issue #200.

See comment in http://nbeloglazov.com/2017/04/09/random-vector-generation.html and http://mathworld.wolfram.com/SpherePointPicking.html.

--- src/cljc/quil/core.cljc
+++ src/cljc/quil/core.cljc
@@ -2671,32 +2671,6 @@

 (defn
   ^{:requires-bindings true
-    :processing-name "random2d()"
-    :category "Math"
-    :subcategory "Random"
-    :added "2.6"}
-  random-2d
-  "Returns a new 2D unit vector in a random direction" []
-  (let [theta (.random (ap/current-applet) TWO-PI)]
-    [(Math/cos theta) (Math/sin theta)]))
-
-(defn
-  ^{:requires-bindings true
-    :processing-name "random3d()"
-    :category "Math"
-    :subcategory "Random"
-    :added "2.6"}
-  random-3d
-  "Returns a new 3D unit vector in a random direction" []
-  (let [theta (.random (ap/current-applet) TWO-PI)
-        phi   (.random (ap/current-applet) (- HALF-PI) HALF-PI)
-        vx    (* (Math/cos theta) (Math/sin phi))
-        vy    (* (Math/sin theta) (Math/sin phi))
-        vz    (Math/cos phi)]
-    [vx vy vz]))
-
-(defn
-  ^{:requires-bindings true
     :processing-name "noise()"
     :category "Math"
     :subcategory "Random"
@@ -3216,6 +3190,36 @@
   including) 10.2."
   ([max] (.random (ap/current-applet) (float max)))
   ([min max] (.random (ap/current-applet) (float min) (float max))))
+
+(defn
+  ^{:requires-bindings true
+    :processing-name "random2d()"
+    :category "Math"
+    :subcategory "Random"
+    :added "2.6.1"}
+  random-2d
+  "Returns a new 2D unit vector in a random direction"
+  []
+  (let [theta (random TWO-PI)]
+    [(Math/cos theta) (Math/sin theta)]))
+
+(defn
+  ^{:requires-bindings true
+    :processing-name "random3d()"
+    :category "Math"
+    :subcategory "Random"
+    :added "2.6.1"}
+  random-3d
+  "Returns a new 3D unit vector in a random direction"
+  []
+  ; Algorithm: http://mathworld.wolfram.com/SpherePointPicking.html
+  (let [theta (random TWO-PI)
+        u (random -1 1)
+        xy (Math/sqrt (- 1 (* u u)))
+        vx (* xy (Math/cos theta))
+        vy (* xy (Math/sin theta))
+        vz u]
+    [vx vy vz]))

 (defn
   ^{:requires-bindings true


Support nil as argument of fill and stroke functions.

--- src/cljc/quil/core.cljc
+++ src/cljc/quil/core.cljc
@@ -1545,6 +1545,8 @@
   [color]
   (swap! (internal-state) assoc :current-fill color))

+(declare no-fill)
+
 (defn
   ^{:requires-bindings true
     :processing-name "fill()"
@@ -1553,15 +1555,16 @@
     :added "1.0"}
   fill
   "Sets the color used to fill shapes. For example, if you run fill(204, 102, 0),
-  all subsequent shapes will be filled with orange.  This function casts all input as a float"
+  all subsequent shapes will be filled with orange.  This function casts all input as a float.
+  If nil is passed it removes any fill color; equivalent to (no-fill)."
   ([gray]
     ; provided color might be passed from (current-fill) in which case it's a vector.
     ; In that case just call fill again applying vector to the (fill).
-   (if (vector? gray)
-     (apply fill gray)
-     (do
-       (.fill (current-graphics) (float gray))
-       (save-current-fill [gray]))))
+   (cond (vector? gray) (apply fill gray)
+         (nil? gray) (no-fill)
+         true (do
+                (.fill (current-graphics) (float gray))
+                (save-current-fill [gray]))))

   ([gray alpha]
    (.fill (current-graphics) (float gray) (float alpha))
@@ -3883,15 +3886,16 @@
   "Sets the color used to draw lines and borders around shapes. This
   color is either specified in terms of the RGB or HSB color depending
   on the current color-mode (the default color space is RGB, with
-  each value in the range from 0 to 255)."
+  each value in the range from 0 to 255).
+  If nil is passed it removes any fill color; equivalent to (no-stroke)."
   ([gray]
     ; provided color might be passed from (current-fill) in which case it's a vector.
     ; In that case just call fill again applying vector to the (fill).
-   (if (vector? gray)
-     (apply stroke gray)
-     (do
-       (.stroke (current-graphics) (float gray))
-       (save-current-stroke [gray]))))
+   (cond (vector? gray) (apply stroke gray)
+         (nil? gray) (no-stroke)
+         true (do
+                (.stroke (current-graphics) (float gray))
+                (save-current-stroke [gray]))))
   ([gray alpha]
    (.stroke (current-graphics) (float gray) (float alpha))
    (save-current-stroke [gray alpha]))
@@ -4432,8 +4436,7 @@
   `(let [fill# ~fill
          previous-fill# (quil.core/current-fill)]

-     (cond (not fill#) (quil.core/no-fill)
-           (sequential? fill#) (apply quil.core/fill fill#)
+     (cond (sequential? fill#) (apply quil.core/fill fill#)
            true (quil.core/fill fill#))

      ;;return the value from body, not from the if after it.
@@ -4462,8 +4465,7 @@
   `(let [stroke# ~stroke
          previous-stroke# (quil.core/current-stroke)]

-     (cond (not stroke#) (quil.core/no-stroke)
-           (sequential? stroke#) (apply quil.core/stroke stroke#)
+     (cond (sequential? stroke#) (apply quil.core/stroke stroke#)
            true (quil.core/stroke stroke#))

      ;;return the value from body, not from the if after it.


change documentation to explain the input type

--- src/cljc/quil/core.cljc
+++ src/cljc/quil/core.cljc
@@ -1545,7 +1545,8 @@
     :subcategory "Setting"
     :added "1.0"}
   fill
-  "Sets the color used to fill shapes."
+  "Sets the color used to fill shapes. For example, if you run fill(204, 102, 0),
+  all subsequent shapes will be filled with orange.  This function casts all input as a float"
   ([gray]
    (.fill (current-graphics) (float gray))
    #?(:cljs (clear-no-fill-cljs (current-graphics))))


Updated leiningen project to also use clojure 1.4.

--- clojure-functional/project.clj
+++ clojure-functional/project.clj
@@ -1,7 +1,7 @@
 (defproject fp "1.0.0-SNAPSHOT"
   :description "FIXME: write description"
-  :dependencies [[org.clojure/clojure "1.3.0"]
+  :dependencies [[org.clojure/clojure "1.4.0"]
                  [no.knowit/common "0.1-SNAPSHOT"]
                  [joda-time/joda-time "2.0"]]
   :source-path "src/main/clojure"
-  :test-path "src/test/clojure")+  :test-path "src/test/clojure")


CLJS-534: use ternary satisfies? in chunked-seq?

This restores support for custom chunked seq types (benefiting
core.rrb-vector) while preserving good performance of comprehensions
in the non-chunked case.

--- src/cljs/cljs/core.cljs
+++ src/cljs/cljs/core.cljs
@@ -1111,9 +1111,7 @@
 (declare ChunkedCons ChunkedSeq)

 (defn ^boolean chunked-seq?
-  [x]
-  (or (instance? ChunkedCons x)
-      (instance? ChunkedSeq x)))
+  [x] (satisfies? IChunkedSeq x false))

 ;;;;;;;;;;;;;;;;;;;; js primitives ;;;;;;;;;;;;
 (defn js-obj


* src/cljs/cljs/core.cljs: CLJS-add JS toString methods for all the IPrintable types. Fixes behavior of str on CLJS IPrintable types.

--- src/cljs/cljs/core.cljs
+++ src/cljs/cljs/core.cljs
@@ -324,9 +324,13 @@
            (recur (f val (-nth cicoll n)) (inc n))
            val))))

-(declare hash-coll cons)
+(declare hash-coll cons pr-str)

 (deftype IndexedSeq [a i]
+  Object
+  (toString [this]
+    (pr-str this))
+
   ISeqable
   (-seq [this] this)
   ISeq
@@ -1121,6 +1125,10 @@

 ;;;;;;;;;;;;;;;; cons ;;;;;;;;;;;;;;;;
 (deftype List [meta first rest count]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (List. meta first rest count))

@@ -1155,6 +1163,10 @@
   (-count [coll] count))

 (deftype EmptyList [meta]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (EmptyList. meta))

@@ -1199,6 +1211,10 @@
   (reduce conj () (reverse items)))

 (deftype Cons [meta first rest]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (Cons. meta first rest))

@@ -1291,6 +1307,10 @@
         (.-x lazy-seq)))))

 (deftype LazySeq [meta realized x]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (LazySeq. meta realized x))

@@ -1937,6 +1957,10 @@
 ;;; DEPRECATED
 ;;; in favor of PersistentVector
 (deftype Vector [meta array]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (Vector. meta array))

@@ -2094,6 +2118,10 @@
              ret))))

 (deftype PersistentVector [meta cnt shift root tail]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (PersistentVector. meta cnt shift root tail))

@@ -2206,6 +2234,10 @@
 (defn vector [& args] (vec args))

 (deftype Subvec [meta v start end]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (Subvec. meta v start end))

@@ -2291,6 +2323,10 @@
 ;;; PersistentQueue ;;;

 (deftype PersistentQueueSeq [meta front rear]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (PersistentQueueSeq. meta front rear))

@@ -2323,6 +2359,10 @@
   (-seq [coll] coll))

 (deftype PersistentQueue [meta count front rear]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (PersistentQueue. meta count front rear))

@@ -2421,6 +2461,10 @@
      :else 0)))

 (deftype ObjMap [meta keys strobj]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (ObjMap. meta keys strobj))

@@ -2500,6 +2544,10 @@
 ; collisions. A bucket is an array of alternating keys (not their hashes) and
 ; vals.
 (deftype HashMap [meta count hashobj]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (HashMap. meta count hashobj))

@@ -2657,6 +2705,10 @@
 ;;; Set

 (deftype Set [meta hash-map]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [coll meta] (Set. meta hash-map))

@@ -2817,6 +2869,10 @@
        (cons (first s) (take-while pred (rest s)))))))

 (deftype Range [meta start end step]
+  Object
+  (toString [this]
+    (pr-str this))
+
   IWithMeta
   (-with-meta [rng meta] (Range. meta start end step))


Scroll to top for DevDocs FTS results

Scroll to top for DevDocs FTS results

--- src/cljs/zest/core.cljs
+++ src/cljs/zest/core.cljs
@@ -227,7 +227,10 @@
              entry-path (.-path (.-contents entry))]
          (if (= "__FTS__" entry-path)
            (fts-results (.search (get-search-index) @query)
-                        #(async-set-html % (fn [])))
+                        #(async-set-html
+                          %
+                          (fn [] (set! (.-scrollTop
+                                   (.getElementById js/document "right")) 0))))
            (activate-item (.-docset entry) (.-contents entry)))))

      render-item


Fixed typo in module server.clj

Fixed typo in module server.clj

--- src/clouseau/server.clj
+++ src/clouseau/server.clj
@@ -286,7 +286,7 @@
     (str
         "[Package]\n" package
         "\n\n[CCS Description]\n" ccs-description
-        "\n\n"))
+        "\n\n"
         (apply str
             (for [p package-descriptions]
                 (str "[" (key p) "]\n"


Fix namespace name.

Fix namespace name.

--- src/clu/server/core.clj
+++ src/clu/server/core.clj
@@ -1 +1 @@
-(ns clu.core)
+(ns clu.server.core)


:bug: Fix edit rectangle radius

:bug: Fix edit rectangle radius

--- common/uxbox/common/pages.cljc
+++ common/uxbox/common/pages.cljc
@@ -233,7 +233,9 @@
   [{:type :rect
     :name "Rect"
     :fill-color default-color
-    :stroke-alignment :center}
+    :stroke-alignment :center
+    :rx 0
+    :ry 0}
    {:type :image}
    {:type :icon}
    {:type :circle


Use either DATABASE_URL or localhost db connection.

--- src/webdev/core.clj
+++ src/webdev/core.clj
@@ -13,7 +13,9 @@
             [compojure.core :refer [defroutes ANY GET POST PUT DELETE]]
             [compojure.route :refer [not-found]]))

-(def db "jdbc:postgresql://localhost/webdev")
+(def db (or
+          (System/getenv "DATABASE_URL")
+          "jdbc:postgresql://localhost/webdev"))

 (defn greet [req]
   {:status 200 :body "Hello, world! Now with reloading!" :headers {}})