+anno(N),avere[relation](V),cittadino(Adj),dopo(Adv),neonato(N),ostana(N),ventotto(Det)

--- src/babel/italiano/lexicon.cljc
+++ src/babel/italiano/lexicon.cljc
@@ -270,6 +270,9 @@
 ;              :note "andare-pp"}))))

+   "anno"  {:synsem {:cat :noun
+                     :sem {:pred :year}}}
+
    "annunciare"  {:synsem {:cat :verb
                            :sem {:pred :announce}}}

@@ -330,7 +333,9 @@
                                             :1plur "abbiamo"
                                             :2plur "avete"
                                             :3plur "hanno"}}}]
-     [(unify ;; 1. "avere": to possess something buyable
+
+     ;; 1. "avere": to possess something buyable
+     [(unify
        transitive
        avere-common
        {:note "avere (possess)"
@@ -340,8 +345,18 @@
                        :subj {:human true}
                        :obj {:buyable true}}}})

-
-      ;; 2. avere: unspecified object
+      ;; 2. "avere": to be in a relation with: e.g. "I have two children"
+      (unify
+       transitive
+       avere-common
+       {:note "avere (relation)"
+        :synsem {:sem {:pred :have
+                       :activity false
+                       :discrete false
+                       :subj {:human :top}
+                       :obj {:human :top}}}})
+
+      ;; 3. avere: unspecified object
       (unify
        avere-common
        verb-subjective
@@ -352,7 +367,7 @@
                        :discrete false
                        :subj {:human true}}}})

-      ;; 3. "avere" that takes a transitive verb: e.g. "io l'ho vista (i saw her)"
+      ;; 4. "avere" that takes a transitive verb: e.g. "io l'ho vista (i saw her)"
       (let [agr-of-obj-of-main-verb (atom :top)]
         (unify
          verb-aux
@@ -366,7 +381,7 @@
                                             :pronoun true}}
                                :essere false}}}}))

-      ;; 4. "avere" that takes an intransitive verb or a transitive verb within a VP
+      ;; 5. "avere" that takes an intransitive verb or a transitive verb within a VP
       ;;    with the object (e.g. "io ho dormito (i slept)" or "io ho [mangiato la pizza] (i ate the pizza)"
       ;; "avere": auxiliary-verb: takes 2 args:
       ;; 1. subject that is the same as the subject of 2.
@@ -694,27 +709,30 @@
                                 :discrete false
                                 :pred :cercare
                                 :subj {:animate true}}}}
-      "città"
-      (unify agreement-noun
-             common-noun
-             countable-noun
-             feminine-noun
-             {:synsem {:sem {:artifact true
-                             :buyable false  ;; can't buy a city (unless you're a billionaire like Mike Bloomberg)
-                             :city true
-                             :place true
-                             :pred :città
-                             }
-                       :subcat {:1 {:cat :det
-                                    :def :def}}}})
-
-      "colpire" (let [common {:italiano {:boot-stem1 "colpisc"}}]
-                  [(merge common
-                          {:synsem {:cat :verb
-                                    :sem {:pred :hit}}})
-                   (merge common
-                          {:synsem {:cat :verb
-                                    :sem {:pred :strike}}})])
+   "città"
+   (unify agreement-noun
+          common-noun
+          countable-noun
+          feminine-noun
+          {:synsem {:sem {:artifact true
+                          :buyable false  ;; can't buy a city (unless you're a billionaire like Mike Bloomberg)
+                          :city true
+                          :place true
+                          :pred :città
+                          }
+                    :subcat {:1 {:cat :det
+                                 :def :def}}}})
+
+   "cittadino" {:synsem {:cat :adjective
+                         :sem {:pred :local}}}
+
+   "colpire" (let [common {:italiano {:boot-stem1 "colpisc"}}]
+               [(merge common
+                       {:synsem {:cat :verb
+                                 :sem {:pred :hit}}})
+                (merge common
+                       {:synsem {:cat :verb
+                                 :sem {:pred :strike}}})])
    "cominciare"
    [{:synsem {:cat :verb
               :essere false
@@ -985,6 +1003,12 @@
               {:synsem {:sem {:human true
                               :pred :donna
                               :child false}}})
+      "dopo"
+      (unify sentential-adverb
+              {:synsem {:cat :sent-modifier
+                        :sem {:pred :after}
+                        :subcat {:1 {:cat :verb
+                                     :subcat '()}}}})
       "dopodomani"
       (unify sentential-adverb
               {:synsem {:cat :sent-modifier
@@ -1571,9 +1595,9 @@
     {:synsem {:cat :noun
               :pronoun true
               :case :nom
-             :agr {:gender :masc
-                   :person :1st
-                   :number :plur}
+              :agr {:gender :masc
+                    :person :1st
+                    :number :plur}
               :sem {:human true
                     :pred :luisa-and-i}
               :subcat '()}}]
@@ -1643,6 +1667,12 @@
                       :essere true
                       :sem {:pred :be-born}}
              :italiano {:passato "nato"}}
+
+   "neonato" {:synsem {:cat :noun
+                       :agr {:gender :masc}
+                       :sem {:pred :newborn
+                             :human true
+                             :adult false}}}

    ;; non-comparative
    ;; TODO: add comparative
@@ -1676,6 +1706,11 @@

    "osservare" {:synsem {:cat :verb
                          :sem {:pred :observe}}}
+
+   "ostana" {:synsem {:cat :noun
+                      :propernoun true
+                      :sem {:pred :ostana
+                            :city true}}}

    "ottenere" {:synsem {:cat :verb
                         :sem {:pred :obtain}}
@@ -2088,6 +2123,8 @@
                                   :3plur "vengono"}
                         :future-stem "verr"}}

+   "ventotto" {:synsem {:cat :det}}
+
    "vestirsi" (let [subject-semantics (atom {:human true})
                     subject-agr (atom :top)]
                 {:synsem {:cat :verb


Use edn reader to correctly interpret strings as ints

--- src/clj/comic_reader/server.clj
+++ src/clj/comic_reader/server.clj
@@ -9,7 +9,15 @@
             [compojure.route :as route]
             [hiccup.page :as hp]
             [environ.core :refer [env]]
-            [ring.adapter.jetty :refer [run-jetty]]))
+            [ring.adapter.jetty :refer [run-jetty]]
+            [clojure.tools.reader.edn :as edn]))
+
+(defn unknown-val [tag val]
+  {:unknown-tag tag
+   :value val})
+
+(defn safe-read-string [s]
+  (edn/read-string {:default unknown-val} s))

 (defn edn-response [data & [status]]
   {:status (or status 200)
@@ -48,10 +56,10 @@
                        :as request}]
   (println (:uri request))
   (let [chapter-map (get-comic-chapter site comic
-                                       (int chapter))
+                                       (safe-read-string chapter))
         page-list (get-following-pages site comic
                                        chapter-map
-                                       (int page))]
+                                       (safe-read-string page))]
     (if-let [comic-urls nil]
       (edn-response)
       (edn-response (gen-error-data request) 404))))


change Progenitor to use card targeting

change Progenitor to use card targeting

--- src/clj/game/cards-programs.clj
+++ src/clj/game/cards-programs.clj
@@ -406,10 +406,9 @@
    {:abilities [{:label "Install a virus program on Progenitor"
                  :cost [:click 1] :req (req (empty? (:hosted card)))
                  :prompt "Choose a Virus program to install on Progenitor"
-                 :choices (req (filter #(and (= (:type %) "Program")
-                                             (has? % :subtype "Virus")
-                                             (<= (:cost %) (:credit runner)))
-                                       (:hand runner)))
+                 :choices :choices {:req #(and (= (:type %) "Program")
+                                               (has? % :subtype "Virus")
+                                               (= (:zone %) [:hand]))}
                  :msg (msg "host " (:title target))
                  :effect (effect (gain :memory (:memoryunits target))
                                  (runner-install target {:host-card card}))}


Add a go-to fn for setting the page

Add a go-to fn for setting the page

--- src/cljs/comic_reader/main.cljs
+++ src/cljs/comic_reader/main.cljs
@@ -21,6 +21,9 @@

 (defroute "*" {:as _}
   (rf/dispatch [:unknown]))
+
+(defn go-to [page]
+  (history/set-token page))

 ;; Actual re-frame code


Make the filter regex case-insensitive

Make the filter regex case-insensitive

This is about half the work of #11.  The rest of it is simply putting a
nice UI on the filtering feature.

--- src/cljs/comic_reader/main.cljs
+++ src/cljs/comic_reader/main.cljs
@@ -54,7 +54,7 @@

 (defn filter-comics [cl-filter comic-list]
   (if cl-filter
-    (let [filter-re (re-pattern cl-filter)]
+    (let [filter-re (re-pattern (str "(?i)" cl-filter))]
       (filter (fn [{:keys [name]}]
                 (re-find filter-re name))
               comic-list))


Give beeps a duration.

Give beeps a duration.

--- src/cljs/leipzig_live/core.cljs
+++ src/cljs/leipzig_live/core.cljs
@@ -9,8 +9,7 @@
 ;; Model
 (defonce state
   (atom {:music nil
-         :text ""
-         :playing nil}))
+         :text ""}))

 ;; -------------------------
 ;; Behaviour
@@ -31,27 +30,22 @@

 (defonce context (js/window.AudioContext.))
 (defn beep [freq]
+  (let [start (.-currentTime context)
+        stop (inc start)]
     (doto (.createOscillator context)
       (.connect (.-destination context))
       (-> .-frequency .-value (set! freq))
       (-> .-type (set! "square"))
-      (.start 0)))
-
-(defn kill [oscillator]
-  (.stop oscillator 0))
+      (.start start)
+      (.stop stop))))

 (defn handle [expr-str]
   (swap! state assoc-in [:text] expr-str)
   (when-let [value (evaluate expr-str)]
     (swap! state assoc-in [:music] value)))

-(defn toggle []
-  (if (:playing @state)
-    (let [oscillator (:playing @state)]
-      (kill oscillator)
-      (swap! state assoc-in [:playing] nil))
-    (let [oscillator (beep (:music @state))]
-      (swap! state assoc-in [:playing] oscillator))))
+(defn play []
+  (beep (:music @state)))

 ;; -------------------------
 ;; Views
@@ -61,7 +55,7 @@
    [:div [:input {:type "text"
                   :value (:text @state)
                   :on-change #(-> % .-target .-value handle)}]
-    [:button {:on-click toggle} "Start/stop"]]
+    [:button {:on-click play} "Start/stop"]]
    [:div
     (:music @state)]])


Add :strs destructig (thanks to @dialelo suggestion)

--- src/cljs/pfrt_js/templates.cljs
+++ src/cljs/pfrt_js/templates.cljs
@@ -11,16 +11,13 @@

 (defn host-entry
   [host data]
-  (let [downloaded      (get data "in")
-        uploaded        (get data "out")
-        download-speed  (get data "speed-in")
-        upload-speed    (get data "speed-out")]
+  (let [{:strs [in out speed-in speed-out]} data]
     (html [:tr {:id (hostname->domid host)}
-           [:td (:name data host)]
-           [:td (js/filesize downloaded)]
-           [:td (js/filesize uploaded)]
-           [:td (str (js/filesize download-speed) "/s")]
-           [:td (str (js/filesize upload-speed) "/s")]])))
+           [:td host]
+           [:td (js/filesize in)]
+           [:td (js/filesize out)]
+           [:td (str (js/filesize speed-in) "/s")]
+           [:td (str (js/filesize speed-out) "/s")]])))

 (defn stats-layout
   []


reload!

reload!

--- src/cljs/secret_shame/core.cljs
+++ src/cljs/secret_shame/core.cljs
@@ -46,7 +46,7 @@
     [:div.panel-heading.clearfix.widget.widget-card
      [:h3.panel-title.pull-left
       (if-not @editing?
-        "Widget Card Title: The Quantum Thief"
+        "Widget Title: The Quantum Thief"
         "@Editing?g: The Quantum Thief")]

      [editing-button id]]


Accidentally broke all the tools

Accidentally broke all the tools

--- src/clojure/staircase/tools.clj
+++ src/clojure/staircase/tools.clj
@@ -20,17 +20,13 @@

 (defn strip-by-cap
   [conf]
-  (let [caps (set (:capabilities conf))
-        conf (if (not (caps "next"))
-               (dissoc conf :headingTemplateURI :headingControllerURI)
-               conf)
-        conf (if (not (caps "provider"))
-               (dissoc conf :providerURI)
-               conf)
-        conf (if (or (not (caps "initial")) (= "native" (:type conf)))
-               (dissoc conf :templateURI :controllerURI)
-               conf)]
-    conf))
+  (let [has-cap (set (:capabilities conf))
+        strippers [
+                  [#(not (has-cap "next")) [:headingTemplateURI :headingControllerURI]]
+                  [#(not (has-cap "provider")) [:providerURI]]
+                  [#(and (not (has-cap "initial")) (not (= "native" (:type conf)))) [:templateURI :controllerURI]]]
+        reduce-f (fn [c [test-f to-strip]] (if (test-f) (apply dissoc c to-strip) c))]
+    (reduce reduce-f conf strippers)))

 (defn get-tool-conf*
   [tool-name]
@@ -44,8 +40,8 @@
         (fix-uris #(replace-first % "." (str "/" (name tool-name))))
         (strip-by-cap))))

-;; Don't do io on every access.
-(def get-tool-conf (memoize get-tool-conf*))
+;; Don't do io on every access. - but for now in development we should.
+(def get-tool-conf get-tool-conf*) ;;(memoize get-tool-conf*))

 (defn has-capability
   [capability tool-conf]


not my best work

not my best work

--- 2016/day-9/src/day_9/core.clj
+++ 2016/day-9/src/day_9/core.clj
@@ -63,74 +63,45 @@
     :res
     (apply str)))

-(defn conj-c-2
-  ([c] (conj-c-2 c 1))
-  ([c i]
-   #(apply conj % (repeat i c))))
+(defn subsec
+  ([s start] (drop start s))
+  ([s start end]
+   (->>
+     s
+     (drop start)
+     (take (- end start)))))

-(defn expand
-  [{rep-cnt :rep-cnt char-cnt :char-cnt}]
-  #(concat
-     (flatten
-       (repeat (dec rep-cnt) (take char-cnt %))) %))
+(defn parse-instr
+  [s]
+  (when s
+    (let [[char-cnt rep-cnt] (clojure.string/split s #"x")
+          char-cnt-i (Integer. (subs char-cnt 1))
+          rep-cnt-i   (Integer.  (subs rep-cnt 0 (dec (count rep-cnt))))]
+      {:char-cnt char-cnt-i :rep-cnt rep-cnt-i})))

-(defn decomp-2-char
-  [{:keys [res cnt act s rep-cnt char-cnt] :as state}]
-  (let [[c & rst] s]
-    (condp = act
-      :bgn-parse (condp = c
-                   \x (->
-                        state
-                        (assoc :act :nxt-parse)
-                        (update-in [:s] rest))
-                   (->
-                     state
-                     (update-in [:char-cnt] (conj-c-2 c))
-                     (update-in [:s] rest)))
-      :nxt-parse (condp = c
-                   \) (->
-                        state
-                        (assoc :act :end-parse)
-                        (update-in [:s] rest))
-                   (->
-                     state
-                     (update-in [:rep-cnt] (conj-c-2 c))
-                     (update-in [:s] rest)))
-      :end-parse (let [rep-cnt-i (->>
-                                   rep-cnt
-                                   reverse
-                                   (apply str)
-                                   Integer.)
-                       char-cnt-i (->>
-                                    char-cnt
-                                    reverse
-                                    (apply str)
-                                    Integer.)
-                       instr {:rep-cnt rep-cnt-i
-                              :char-cnt char-cnt-i}]
-                   (->
-                     state
-                     (update-in [:s] (expand instr))
-                     (assoc :act nil)
-                     (assoc :rep-cnt nil)
-                     (assoc :char-cnt nil)))
-      (condp = c
-        \( (->
-             state
-             (assoc :act :bgn-parse)
-             (update-in [:s] rest))
-        (->
-          state
-          (update-in [:cnt] inc)
-          (update-in [:s] rest))))))
+(defn next-s
+  [s]
+  (let [[match rst] (clojure.string/split s #"\(\d+x\d+\)" 2)]
+    {:cnt (count match) :next-s rst}))
+
+(defn find-instr
+  [s]
+  (let [instr-s (re-find #"\(\d+x\d+\)" s)
+        instr (parse-instr instr-s)]
+    (if (nil? instr)
+      {:cnt (count s) :done true}
+      (merge instr (next-s s)))))

 (defn decompress-2
   [s]
-  (->>
-    (iterate decomp-2-char {:s s :cnt 1})
-    (take-while #(not (empty? (% :s))))
-    last
-    :cnt))
+  (let [{:keys [cnt done char-cnt rep-cnt next-s]} (find-instr s)]
+    ;(println cnt done char-cnt rep-cnt next-s)
+    (if done
+      cnt
+      (+
+       cnt
+       (* rep-cnt (decompress-2 (subs next-s 0 char-cnt)))
+       (decompress-2 (subs next-s char-cnt))))))

 (defn -main
   "I don't do a whole lot ... yet."
@@ -140,6 +111,5 @@
     slurp
     (remove #(re-matches #"\s+" (str %)))
     (apply str)
-    decompress
-    count
+    decompress-2
     println))


Track clicks of "Add Project" in empty state

Right now the "Add Project" link in the top-right of the projects page
is tracked as a `add-project-clicked` event. This modifies that event to
include an `is-empty-state` property with a value of `false`. A new
`add-project-clicked` event is added with an `is-empty-state` property
with a `true` value for clicks on the link in the empty state.

--- src-cljs/frontend/components/pages/projects.cljs
+++ src-cljs/frontend/components/pages/projects.cljs
@@ -71,18 +71,29 @@
                                             (when bitbucket-enabled? "or Bitbucket ")
                                             "organization (or username) to view your projects.")}))))

-(defn- no-projects-available [org]
-  (component
-    (empty-state/empty-state {:icon (html [:i.material-icons "book"])
-                              :heading (html
-                                        [:span
-                                         (empty-state/important (:name org))
-                                         " has no projects building on CircleCI"])
-                              :subheading "Let's fix that by adding a new project."
-                              :action (button/link
-                                       {:href (routes/v1-add-projects)
-                                        :primary? true}
-                                       "Add Project")})))
+(defn- add-project-button [{:keys [empty-state?]} owner]
+  (reify
+    om/IRender
+    (render [_]
+      (button/link
+        {:href (routes/v1-add-projects)
+         :primary? true
+         :on-click #((om/get-shared owner :track-event)
+                     {:event-type :add-project-clicked
+                      :properties {:is-empty-state empty-state?}})}
+        "Add Project"))))
+
+(defn- no-projects-available [{:keys [org]} owner]
+  (reify
+    om/IRender
+    (render [_]
+      (empty-state/empty-state {:icon (html [:i.material-icons "book"])
+                                :heading (html
+                                           [:span
+                                            (empty-state/important (:name org))
+                                            " has no projects building on CircleCI"])
+                                :subheading "Let's fix that by adding a new project."
+                                :action (om/build add-project-button {:empty-state? true})}))))

 (defn- organization-ident
   "Builds an Om Next-like ident for an organization."
@@ -138,7 +149,7 @@
                             (if-let [projects-with-followers
                                      (seq (filter #(< 0 (count (:followers %))) projects))]
                               (table projects-with-followers (:plan selected-org))
-                              (no-projects-available selected-org))
+                              (om/build no-projects-available {:org selected-org}))
                             (html [:div.loading-spinner common/spinner])))
              (no-org-selected available-orgs (vcs/bitbucket-enabled? user)))]])))))

@@ -149,10 +160,4 @@
       (om/build main-template/template
                 {:app app
                  :main-content (om/build main-content app)
-                 :header-actions (button/link
-                                  {:href (routes/v1-add-projects)
-                                   :primary? true
-                                   :on-click #((om/get-shared owner :track-event)
-                                               {:event-type :add-project-clicked
-                                                :properties {:view :projects}})}
-                                  "Add Project")}))))
+                 :header-actions (om/build add-project-button {:empty-state? false})}))))


Start sending a message after initial slot selection

--- exercise3/src/de/haw/vs/business_logic/station.clj
+++ exercise3/src/de/haw/vs/business_logic/station.clj
@@ -25,6 +25,10 @@
     (stat/status-detail :station :error "No slot assigned")
     (stat/status-detail :station :ok @slot-atom)))

+(defn send-message [state-atom config-params connector]
+  ;TODO
+  )
+
 (defrecord Station [config app-status connector]
   c/Lifecycle
   (start [self]
@@ -35,6 +39,7 @@
                             :utc-offset    (:utc-offset config-params)})
           new-self (assoc self :slot state-atom)]
       (select-free-slot! state-atom (:frame-size config-params) (:slots-count config-params) connector)
+      (send-message state-atom config-params connector)
       (appstat/register-status-fun app-status #(status state-atom))
       new-self))


More work on chapter 3

More work on chapter 3

--- clojure-programming/src/clojure_programming/chp03.clj
+++ clojure-programming/src/clojure_programming/chp03.clj
@@ -934,20 +934,243 @@
 ;; FIXME: complete

 [[:section {:title "Concise Collection Access --  page: 111"}]]
+"
+in collections most types of keys are also functions
+"
+
+(fact "using 'get'..."
+  (get [:a :b :c] 2)
+  => :c
+  (get {:a 5 :b 6} :b)
+  => 6
+  (get {:a 5 :b 6} :c 7)
+  => 7
+  (get #{1 2 3} 3)
+  >= 3 )
+
+(fact  "... is equivalent to using the collection as a function:"
+  ([:a :b :c] 2)
+  => :c
+  ({:a 5 :b 6} :b)
+  => 6
+  ({:a 5 :b 6} :c 7)
+  => 7
+  (#{1 2 3} 3)
+  => 3 )

 [[:subsection {:title "Idiomatic Usage --  page: 112"}]]
+"when should keyword functions be use:"
+
+
+(facts "avoiding null pointer exceptions"
+  (defn get-foo
+    [map]
+    (:foo map))
+  (fact "using keyword avoids the exception"
+    (get-foo nil)
+    => nil)
+
+  (defn get-bar
+    [map]
+    (map :bar))
+  (fact
+    (get-bar nil)
+    => (throws NullPointerException)) )
+"
+also not all collections can act as functions
+
+but if the keys are not keywords or symbols then the collection or
+'get' or 'nth' must be used
+"
+

 [[:subsection {:title "Collections and Keys and Higher-Order Functions --  page: 113"}]]

+"
+since keywords and symbols are functions they may be passed into
+higher order functions:
+"
+
+(fact "a keyword passed as a function to 'map'"
+  (map :name [{:age 21 :name "David"}
+              {:gender :f :name "Suzanne"}
+              {:name "Sara" :location "NYC"}])
+  => '("David" "Suzanne" "Sara"))
+
+(facts "'some' searches for the first values in  a sequnce that
+         logically returns true"
+  (fact
+    (some #{1 3 7} [0  2 4 5 6])
+    => nil )
+  (fact
+    (some #{1 3 7} [0 2 3 4 5 6])) )
+
+(fact
+  (filter :age [{:age 21 :name "David"}
+                {:gender :f :name "Suzanne"}
+                {:name "Sara" :location "NYC"}])
+  => '({:age 21, :name "David"}))
+
+(fact "can 'comp' keywords with other functions:"
+  (filter (comp (partial <= 25) :age)
+          [{:age 21 :name "David"}
+           {:gender :f :name "Suzanne" :age 20}
+           {:name "Sara" :location "NYC" :age 34}])
+  => '({:age 34, :name "Sara", :location "NYC"}))
+
+(facts "beware of 'nil'"
+  (fact "ok"
+    (remove #{5 7} (cons false (range 10)))
+    => '(false 0 1 2 3 4 6 8 9))
+  (fact "not quite:"
+    (remove #{5 7 false} (cons false (range 10)))
+    => '(false 0 1 2 3 4 6 8 9))  ;; false is still there
+  (fact "use partial contains"
+    (remove (partial contains? #{5 7 false}) (cons false (range 10)))
+    => '(0 1 2 3 4 6 8 9)) )
+
+
 [[:section {:title "Data Structure Types --  page: 114"}]]
-
+"
+besides abstractions, we may treat the Clojure data types as their
+concrete representations.
+"
 [[:subsection {:title "Lists --  page: 114"}]]
+"
+The original type in Lisp but mustly used to represent function calls
+in Clojure.
+They are singly linked lists so they are most efficiently accesd at
+their 'head' using 'conj' 'pop', or 'rest' They don't have efficient random access. 'nth' runs in linear time. 'get' is not supported for lists. Lists are their own sequences
+"
+(fact "list literal:"
+  '(1 2 3)
+  => '(1 2 3))
+
+(fact "list literals in lists a also not evaluated:"
+  '(1 2 (+ 1 2))
+  => '(1 2 (+ 1 2)))
+
+(fact "for evaluating sublists"
+  (list 1 2 (+ 1 2))
+  => '(1 2 3))

 [[:subsection {:title "Vectors --  page: 115"}]]
+"
+Vectors are sequenctial data structures that support random lookup
+Vectors partiscipate in associative, indexed and stack abstractions
+"
+(fact "vectors can be created with 'vector' and 'vec'"
+  (vector 1 2 3)  ; 'vector' is the analog of 'list'
+  => '[1 2 3]
+  (vec (range 5)) ; 'vec' expects a sequectial arguement to construct
+  => [0 1 2 3 4]) ; to construct a vector.
+
+(fact "'vector?' is used to test whether a value is a vector "
+  (vector? '(1 2 3))
+  => false
+  (vector? [1 2 3])
+  => true )
+
+"
+Vectors as tuples - a common use case.
+"
+
+(defn euclidian-division
+  "return a euclidian tuple"
+  [x y]
+  [(quot x y) (rem x y)])
+
+(fact "returning multiple values from a function"
+  (euclidian-division 42 8)
+  => [5 2])
+
+(fact "simpler:"
+  ((juxt quot rem) 42 8)
+  => [5 2])
+
+(fact "using destructuring to unpack return values "
+  (let [[q r] (euclidian-division 53 7)]
+    (str "53/7 = " q " * 7 + " r))
+  => "53/7 = 7 * 7 + 4")
+"
+Use sparingly in api's:
+  tuples are hard to document the use of each index.
+  tuples are inflexible, you have to provide values for all the slots
+  and tuples are hard to extend.
+
+maps are more flexible, but vectors as tuples make sence in some
+ domains, such a points in graphics
+"
+(def point-3d [42 26 -7])
+
+(def travel-legs [["LYS" "FRA"] ["FRA" "PHL"] ["PHL" "RDU"]])
+

 [[:subsection {:title "Sets --  page: 117"}]]

+(fact "literal set notation"
+  #{1 2 3} => #{1 2 3})
+
+;(fact
+;  #{1 2 3 3}
+;  => '(throws IllegalArgumentException "Duplicate key: 3"))
+
+(fact "the 'hash-set' function creates unsorted sets"
+  (hash-set :a :b :c :d)
+  => #{:a :b :c :d})
+
+(fact "create a set from values in a collection"
+  (set [1 6 1 8 3 7 7])
+  => (contains #{1 3 6 7 8}))
+
+"
+'set' works for anything seqable giving very useful idioms, where
+sets are functions
+"
+
+(fact "remove vowels and restring"
+  (apply str (remove (set "aeiouy") "vowels are useless"))x
+  => "vwls r slss" )
+
+;; test that every is a number
+(defn numeric? [s] (every? (set "0123456789") s))
+
+(fact
+  (numeric? "123")
+  => true
+  (numeric? "42b")
+  => false)
+
 [[:subsection {:title "Maps --  page: 117"}]]
+
+(fact "the man literal"
+  {:a 5 :b 6} => {:a 5 :b 6}
+;  {:a 5 :a 5}
+;  => '(throws IllegalArgumentException)
+  )
+
+(fact "creating maps"
+  (hash-map :a 5 :b 6)
+  => {:a 5 , :b 6}
+  (apply hash-map [:a 5 :b 6])
+  => {:a 5, :b 6})
+
+(fact "keys and vals"
+  (keys m)
+  => '(:c :b :a)
+  (vals m)
+  => '(3 2 1))
+
+(fact "'keys' and 'map' are just shortcut for:"
+  (map key m)
+  => '(:c :b :a)
+  (map val m)
+  => '(3 2 1))
+
+
+
+
+

 [[:section {:title "Immutability and Persistence --  page: 122"}]]


:bug: Fixed problem with selection

:bug: Fixed problem with selection

--- frontend/src/uxbox/main/ui/workspace/shapes/common.cljs
+++ frontend/src/uxbox/main/ui/workspace/shapes/common.cljs
@@ -36,7 +36,7 @@
         (= type :frame)
         (when selected?
           (dom/stop-propagation event)
-          (st/emit! (dw/start-move-selected)))))
+          (st/emit! (dw/start-move-selected)))

         :else
         (do
@@ -45,7 +45,7 @@
             (when-not (or (empty? selected) (kbd/shift? event))
               (st/emit! dw/deselect-all))
             (st/emit! (dw/select-shape id)))
-          (st/emit! (dw/start-move-selected)))))
+          (st/emit! (dw/start-move-selected)))))))

 (defn on-context-menu
   [event shape]


Fix #41 Honor `:print`, `:print-no-newline` and `:flush`

--- Clojure/src/ambly/repl/jsc.clj
+++ Clojure/src/ambly/repl/jsc.clj
@@ -28,17 +28,17 @@
 (defn name-endpoint-map->choice-list [name-endpoint-map]
   (map vector (iterate inc 1) name-endpoint-map))

-(defn print-discovered-devices [name-endpoint-map]
+(defn print-discovered-devices [name-endpoint-map opts]
   (if (empty? name-endpoint-map)
-    (println "(No devices)")
+    ((:print opts) "(No devices)")
     (doseq [[choice-number [bonjour-name _]] (name-endpoint-map->choice-list name-endpoint-map)]
-      (println (str "[" choice-number "] " (bonjour-name->display-name bonjour-name))))))
+      ((:print opts) (str "[" choice-number "] " (bonjour-name->display-name bonjour-name))))))

 (defn discover-and-choose-device
   "Looks for Ambly WebDAV devices advertised via Bonjour and presents
   a simple command-line UI letting user pick one, unless
   choose-first-discovered? is set to true in which case the UI is bypassed"
-  [choose-first-discovered?]
+  [choose-first-discovered? opts]
   (let [reg-type "_http._tcp.local."
         name-endpoint-map (atom (sorted-map))
         mdns-service (JmDNS/create)
@@ -63,16 +63,16 @@
         (when (empty? @name-endpoint-map)
           (Thread/sleep 100)
           (when (= 20 count)
-            (println "\nSearching for devices ..."))
+            ((:print opts) "\nSearching for devices ..."))
           (recur (inc count))))
       (Thread/sleep 500)                                    ;; Sleep a little more to catch stragglers
       (loop [current-name-endpoint-map @name-endpoint-map]
-        (println)
-        (print-discovered-devices current-name-endpoint-map)
+        ((:print opts))
+        (print-discovered-devices current-name-endpoint-map opts)
         (when-not choose-first-discovered?
-          (println "\n[R] Refresh\n")
-          (print "Choice: ")
-          (flush))
+          ((:print opts) "\n[R] Refresh\n")
+          ((:print-no-newline opts) "Choice: ")
+          ((:flush opts)))
         (let [choice (if choose-first-discovered? "1" (read-line))]
           (if (= "r" (.toLowerCase choice))
             (recur @name-endpoint-map)
@@ -100,7 +100,7 @@
   (.write out (int 0)) ;; terminator
   (.flush out))

-(defn read-messages [^BufferedReader in response-promise]
+(defn read-messages [^BufferedReader in response-promise opts]
   (loop [sb (StringBuilder.) c (.read in)]
     (cond
       (= c -1) (do
@@ -108,8 +108,8 @@
                    (deliver resp-promise :eof))
                  :eof)
       (= c 1) (do
-                (print (str sb))
-                (flush)
+                ((:print-no-newline opts) (str sb))
+                ((:flush opts))
                 (recur (StringBuilder.) (.read in)))
       (= c 0) (do
                 (deliver @response-promise (str sb))
@@ -120,11 +120,11 @@

 (defn start-reading-messages
   "Starts a thread reading inbound messages."
-  [repl-env]
+  [repl-env opts]
   (.start
         (Thread.
           #(try
-            (let [rv (read-messages (:in @(:socket repl-env)) (:response-promise repl-env))]
+            (let [rv (read-messages (:in @(:socket repl-env)) (:response-promise repl-env) opts)]
               (when (= :eof rv)
                 (close-socket @(:socket repl-env))))
             (catch IOException e
@@ -195,7 +195,7 @@
 (defn setup
   [repl-env opts]
   (let [_ (set-logging-level "javax.jmdns" java.util.logging.Level/OFF)
-        [bonjour-name endpoint] (discover-and-choose-device (:choose-first-discovered repl-env))
+        [bonjour-name endpoint] (discover-and-choose-device (:choose-first-discovered repl-env) opts)
         endpoint-address (.getHostAddress (:address endpoint))
         endpoint-port (:port endpoint)
         webdav-mount-point (str "/Volumes/Ambly-" endpoint-address)
@@ -203,13 +203,13 @@
         _ (.mkdirs output-dir)
         env (ana/empty-env)
         core (io/resource "cljs/core.cljs")]
-    (println "\nConnecting to" (bonjour-name->display-name bonjour-name) "...\n")
+    ((:print opts) "\nConnecting to" (bonjour-name->display-name bonjour-name) "...\n")
     (reset! (:webdav-mount-point repl-env) webdav-mount-point)
     (shell/sh "mount_webdav" (str "http://" endpoint-address ":" endpoint-port) webdav-mount-point)
     (reset! (:socket repl-env)
       (socket endpoint-address (:port repl-env)))
     ;; Start dedicated thread to read messages from socket
-    (start-reading-messages repl-env)
+    (start-reading-messages repl-env opts)
     ;; compile cljs.core & its dependencies, goog/base.js must be available
     ;; for bootstrap to load, use new closure/compile as it can handle
     ;; resources in JARs
@@ -272,7 +272,7 @@
   (-print-stacktrace [_ stacktrace _ build-options]
     (doseq [{:keys [function file url line column]}
             (repl/mapped-stacktrace stacktrace build-options)]
-      (println "\t" (str function " (" (str (or url file)) ":" line ":" column ")"))))
+      ((:print repl/*repl-opts*) "\t" (str function " (" (str (or url file)) ":" line ":" column ")"))))
   repl/IJavaScriptEnv
   (-setup [repl-env opts]
     (setup repl-env opts))


Hide facebook share button

Hide facebook share button

--- src/name_bazaar/ui/components/share_buttons.cljs
+++ src/name_bazaar/ui/components/share_buttons.cljs
@@ -5,11 +5,6 @@
 (defn share-buttons [{:keys [url title]}]
   [:div.share-buttons
    [:div.title "Share On:"]
-   [:a {:on-click (fn []
-                    (js-invoke js/FB "ui" (clj->js {:method "feed"
-                                                    :link url
-                                                    :caption title})))}
-    [:i.icon.fb]]
    [:a {:target "_blank"
         :href (str "https://twitter.com/intent/tweet?text=" title "&url=" url)}
     [:i.icon.twitter]]])


CMR-3733: Added limit to the keyword wildcard searches.

--- search-app/src/cmr/search/data/query_to_elastic.clj
+++ search-app/src/cmr/search/data/query_to_elastic.clj
@@ -184,21 +184,50 @@
   [query]
   (keywords-extractor/extract-keywords query))

+(defn- ^:pure get-max-kw-number-allowed
+  "Returns the max number of keyword string with wildcards allowed, given the max length of
+   the keyword string with wildcards"
+  [length]
+  (cond
+    (> length 241) 0
+    (and (> length 121) (<= length 241)) 10
+    (and (> length 61) (<= length 121)) 16
+    (and (> length 41) (<= length 61)) 22
+    (and (> length 21) (<= length 41)) 26
+    (and (> length 7) (<= length 21)) 36
+    (and (> length 5) (<= length 7)) 66
+    (= length 5) 83
+    (and (> length 0) (<= length 4)) 118))
+
+(defn- ^:pure validate-keyword-wildcards
+  "Validates if the number of keyword strings with wildcards exceeds the max number allowed
+   for the max length of the keyword strings."
+  [keywords]
+  (when-let [kw-with-wild-cards (get (group-by #(.contains % "*") keywords) true)]
+    (let [max-kw-length (apply max (map count kw-with-wild-cards))
+          kw-number (count kw-with-wild-cards)
+          max-kw-number-allowed (get-max-kw-number-allowed max-kw-length)
+          msg (str "Max number of keyword strings with wildcard allowed is: " max-kw-number-allowed
+                    " given the max length of the keyword strings being: " max-kw-length)]
+      (when (> kw-number max-kw-number-allowed)
+        (errors/throw-service-errors :bad-request (vector msg))))))
+
 (defmethod q2e/query->elastic :collection
   [query]
   (let [boosts (:boosts query)
         {:keys [concept-type condition]} (query-expense/order-conditions query)
         core-query (q2e/condition->elastic condition concept-type)]
     (if-let [keywords (keywords-in-query query)]
-      ;; Forces score to be returned even if not sorting by score.
-      {:track_scores true
-       ;; function_score query allows us to compute a custom relevance score for each document
-       ;; matched by the primary query. The final document relevance is given by multiplying
-       ;; a boosting term for each matching filter in a set of filters.
-       :query {:function_score {:score_mode :multiply
-                                :functions (k2e/keywords->boosted-elastic-filters keywords boosts)
-                                :query {:filtered {:query (eq/match-all)
-                                                   :filter core-query}}}}}
+      (let [_ (validate-keyword-wildcards keywords)]
+        ;; Forces score to be returned even if not sorting by score.
+        {:track_scores true
+         ;; function_score query allows us to compute a custom relevance score for each document
+         ;; matched by the primary query. The final document relevance is given by multiplying
+         ;; a boosting term for each matching filter in a set of filters.
+         :query {:function_score {:score_mode :multiply
+                                  :functions (k2e/keywords->boosted-elastic-filters keywords boosts)
+                                  :query {:filtered {:query (eq/match-all)
+                                                     :filter core-query}}}}})
       (if boosts
         (errors/throw-service-errors :bad-request ["Relevance boosting is only supported for keyword queries"])
         {:query {:filtered {:query (eq/match-all)


:bug: Fixed problem with selrect

:bug: Fixed problem with selrect

--- frontend/src/app/main/ui/workspace/shapes/text.cljs
+++ frontend/src/app/main/ui/workspace/shapes/text.cljs
@@ -250,6 +250,12 @@
     (map? content) (clj->js [content])
     :else (initial-text "")))

+(defn- content-size
+  [node]
+  (let [current (count (:text node))
+        children-count (->> node :children (map content-size) (reduce +))]
+    (+ current children-count)))
+
 (mf/defc text-shape-edit
   {::mf/wrap [mf/memo]}
   [{:keys [shape read-only?] :or {read-only? false} :as props}]
@@ -261,6 +267,8 @@
         selecting-ref (mf/use-ref)
         measure-ref (mf/use-ref)

+        content-var (mf/use-var content)
+
         on-close
         (fn []
           (when (not read-only?)
@@ -270,6 +278,7 @@
         (fn [event]
           (dom/prevent-default event)
           (dom/stop-propagation event)
+
           (let [sidebar (dom/get-element "settings-bar")
                 cpicker (dom/get-element-by-class "colorpicker-tooltip")
                 self    (mf/ref-val self-ref)
@@ -280,7 +289,10 @@
                           (and cpicker (.contains cpicker target)))
               (if selecting?
                 (mf/set-ref-val! selecting-ref false)
-                (on-close)))))
+                (on-close))))
+
+          (when (= 0 (content-size @content-var))
+            (st/emit! (dw/delete-shapes [id]))))

         on-mouse-down
         (fn [event]
@@ -322,7 +334,8 @@
              (let [content (js->clj val :keywordize-keys true)
                    content (first content)]
                (st/emit! (dw/update-shape id {:content content}))
-               (reset! state val)))))
+               (reset! state val)
+               (reset! content-var content)))))

         loaded-fonts (mf/use-var 0)
         on-load-font #(swap! loaded-fonts inc)]
@@ -332,7 +345,8 @@
     (mf/use-effect
      (mf/deps content)
      (fn []
-       (reset! state (parse-content content))))
+       (reset! state (parse-content content))
+       (reset! content-var content)))

     ;; Checks the size of the wrapper to update if it were necesary
     (mf/use-effect
@@ -352,12 +366,15 @@
                  (st/emit! (dwt/update-overflow-text id true))

                  (and (= :fixed (:grow-type shape)) (:overflow-text shape) (<= height (:height shape)))
-                 (st/emit! (dwt/update-overflow-text id false)))
-
-               (if (#{:auto-width :auto-height} grow-type)
-                 (st/emit! (dw/update-shape id (if (= grow-type :auto-width)
-                                                 {:width width :height height}
-                                                 {:height height}))))))))))
+                 (st/emit! (dwt/update-overflow-text id false))
+
+                 (= grow-type :auto-width)
+                 (st/emit! (dw/update-dimensions [id] :width width)
+                           (dw/update-dimensions [id] :height height))
+
+                 (= grow-type :auto-height)
+                 (st/emit! (dw/update-dimensions [id] :height height))
+                 )))))))

     [:foreignObject {:ref self-ref
                      :transform (geom/transform-matrix shape)


core skeleton

core skeleton

--- desafio4/clojure/src/huffman/core.clj
+++ desafio4/clojure/src/huffman/core.clj
@@ -1,7 +1,21 @@
 (ns huffman.core
   (:gen-class))

+(defn usage [] (println "Uso: huffman [-c|-d] archivo_entrada archivo_salida"))
+
+(defn compress [input output])
+
+(defn decompress [input output])
+
+(defn process [[cmd input-file output-file]]
+  (case cmd
+    "-c" (compress input-file output-file)
+    "-d" (decompress input-file output-file)
+    (usage)))
+
 (defn -main
-  "I don't do a whole lot ... yet."
+  "Huffman encoding decoding"
   [& args]
-  (println "Hello, World!"))
+  (if (or (empty? args) (not= 3 (count args)))
+    (usage)
+    (process args)))


Update build script to use one.repmax.config

--- src/app/clj/script/build.clj
+++ src/app/clj/script/build.clj
@@ -3,7 +3,7 @@
   the project."
   (:require [clojure.java.io :as io]
             [one.tools :as tools]
-            [one.sample.config :as config]))
+            [one.repmax.config :as config]))

 (defn -main
   "Compile ClojureScript sources and output them as well as all static


show exception in log/error message

show exception in log/error message

--- src/babel/espanol/writer.clj
+++ src/babel/espanol/writer.clj
@@ -211,7 +211,7 @@
                                                   (do
                                                     (log/error (str "Stopping further generation: unexpectedly "
                                                                     "couldn't generate any expression for spec:"
-                                                                    spec))
+                                                                    spec "; exception:" e))
                                                     (throw e)))))
                                             ))
                                         [:sing :plur]))))


Update handler.clj

Update handler.clj

--- luminus/hello/src/hello/handler.clj
+++ luminus/hello/src/hello/handler.clj
@@ -4,7 +4,7 @@
   (:require [noir.util.middleware :as middleware]
             [compojure.route :as route]
             [taoensso.timbre :as timbre]
-            [com.postspectacular.rotor :as rotor]))
+            [taoensso.timbre.appenders.rotor :as rotor]))

 (defroutes app-routes
   (route/resources "/")
@@ -25,11 +25,11 @@
      :enabled? true
      :async? false ; should be always false for rotor
      :max-message-per-msecs nil
-     :fn rotor/append})
+     :fn rotor/appender-fn})

   (timbre/set-config!
     [:shared-appender-config :rotor]
-    {:path "hello.log" :max-size 10000 :backlog 10})
+    {:path "{{sanitized}}.log" :max-size (* 512 1024) :backlog 10})

   (timbre/info "hello started successfully"))


Document missing :onyx/params in information model

--- src/onyx/information_model.cljc
+++ src/onyx/information_model.cljc
@@ -74,6 +74,11 @@
              :type :keyword
              :choices :any
              :restrictions ["Must resolve to a function on the classpath at runtime."]
+             :optional? true}
+
+            :onyx/params
+            {:doc "A vector of keys to obtain from the task map, and inject into the parameters of the function defined in :onyx/fn."
+             :type :vector
              :optional? true}

             :onyx/medium
@@ -293,12 +298,12 @@
             :aggregation/fn {:doc "Fn (state, window, segment) to generate a serializable state machine update."
                              :type :function
                              :optional? false}
+            :aggregation/apply-state-update {:doc "Fn (state, entry) to apply state machine update entry to a state."
+                                             :type :function
+                                             :optional? false}
             :aggregation/super-aggregation-fn {:doc "Fn (state-1, state-2, window) to combine two states in the case of two windows being merged, e.g. session windows."
                                                :type :function
-                                               :optional? true}
-            :aggregation/apply-state-update {:doc "Fn (state, entry) to apply state machine update entry to a state."
-                                             :type :function
-                                             :optional? false}}}
+                                               :optional? true}}}
    :trigger-entry
    {:summary "Triggers are a feature that interact with Windows. Windows capture and bucket data over time. Triggers let you release the captured data over a variety stimuli."
     :link nil
@@ -721,7 +726,6 @@
              :default :rocksdb
              :choices [:rocksdb]}

-
             :onyx.rocksdb.filter/base-dir
             {:doc "Temporary directory to persist uniqueness filtering data."
              :optional? true
@@ -734,8 +738,9 @@
              :optional? true
              :type :integer
              :default 10}
+
             :onyx.rocksdb.filter/compression
-            {:doc "Whether to use compression in rocksdb filter. Recommended this is turned off unless your keys are large."
+            {:doc "Whether to use compression in rocksdb filter. It is recommended that `:none` is used unless your uniqueness keys are large."
              :optional? true
              :type :string
              :choices [:bzip2 :lz4 :lz4hc :none :snappy :zlib]
@@ -842,11 +847,3 @@
                                             rows))))
                           model))
              :style :github-markdown))))
-
-(comment
-  (def a
-    (spit "a.txt" (with-out-str
-                    (gen-markdown (:model (:peer-config model))
-                                  [[:doc "Doc"]
-                                   [:optional? "Optional?"]]
-                                  800)))))


"Team Members" -> "Team"

"Team Members" -> "Team"

--- src-cljs/frontend/components/pages/projects.cljs
+++ src-cljs/frontend/components/pages/projects.cljs
@@ -18,8 +18,8 @@
                  :columns [{:header "Project"
                             :cell-fn #(vcs-url/repo-name (:vcs_url %))}

-                           {:header "Team Members"
                             :type :right
+                           {:header "Team"
                             :cell-fn #(count (:followers %))}

                            {:header "Settings"


Only show Device API User Metadata when editing

Users can only create User Metadata via the API.

The users can create a map of metadata for their own bookkeeping when
creating devices. This shouldn't be something edited via the UI.

--- src-cljs/kixi/hecuba/tabs/hierarchy/devices.cljs
+++ src-cljs/kixi/hecuba/tabs/hierarchy/devices.cljs
@@ -203,6 +203,7 @@
               (static-text cursor :device_id "Device ID")
               (text-input-control cursor owner [:device] :name "Parent Device Name")
               (location-input cursor owner)
+              (static-text cursor :metadata "API User Metadata")
               (bs/checkbox cursor owner :device :privacy "Private")
               [:h3 "Sensors"]
               (let [sensors (remove #(:synthetic %) (:readings cursor))]
@@ -258,7 +259,6 @@
               "Cancel"]]]
            (bs/text-input-control cursor owner :device :description "Description" true)
            (location-input cursor owner)
-           (bs/text-input-control cursor owner :device :metadata "Metadata")
            (bs/text-input-control cursor owner :device :name "Name")
            (bs/checkbox cursor owner :device :privacy "Private")]]])))))


move to using SearcherTaxonomyManager, which removes some lock contention on name->smanager->taxo

--- src/bzzz/index_directory.clj
+++ src/bzzz/index_directory.clj
@@ -15,6 +15,7 @@
            (redis.clients.jedis JedisPool)
            (org.apache.lucene.analysis Analyzer)
            (org.apache.lucene.facet.taxonomy.directory DirectoryTaxonomyWriter DirectoryTaxonomyReader)
+           (org.apache.lucene.facet.taxonomy SearcherTaxonomyManager SearcherTaxonomyManager$SearcherAndTaxonomy)
            (org.apache.lucene.index IndexWriter IndexReader IndexWriterConfig DirectoryReader)
            (org.apache.lucene.search Query ScoreDoc SearcherManager IndexSearcher)
            (org.apache.lucene.store NIOFSDirectory Directory NoSuchDirectoryException)))
@@ -22,8 +23,8 @@
 (declare use-searcher)
 (declare use-writer)
 (declare bootstrap-indexes)
-(declare get-smanager-taxo)
-(declare try-close-manager-taxo)
+(declare get-manager)
+(declare try-close-manager)
 (declare try-create-prefix)
 (declare read-alias-file)
 (declare refresh-searcher-locked)
@@ -32,7 +33,7 @@
 (def alias* (atom {}))
 (def redis* (atom {}))
 (def identifier* (atom default-identifier))
-(def name->smanager-taxo* (atom {}))
+(def name->manager* (atom {}))
 (def unacceptable-name-pattern (re-pattern "[^a-zA-Z_0-9-:]"))
 (def shard-suffix "-shard-")
 (def shard-suffix-sre (str ".*" shard-suffix "\\d+"))
@@ -124,51 +125,34 @@
 (defn new-taxo-writer ^DirectoryTaxonomyWriter [name]
   (DirectoryTaxonomyWriter. (new-index-directory (taxo-dir-prefix name) (str "__" name "_taxo__"))))

-(defn new-taxo-reader ^DirectoryTaxonomyReader [name]
-  (DirectoryTaxonomyReader. (new-index-directory (taxo-dir-prefix name) (str "__" name "_taxo__"))))
-
-(defn try-new-taxo-reader ^DirectoryTaxonomyReader [name]
-  (try
-    (new-taxo-reader name)
-    (catch Exception e
-      (if (instance? NoSuchDirectoryException e)
-        (do
-          (log/warn (ex-str e))
-          (let [writer ^DirectoryTaxonomyWriter (new-taxo-writer name)]
-            (.commit writer)
-            (.close writer))
-          (new-taxo-reader name))))))
-
-(defn new-index-reader ^IndexReader [name]
-  (with-open [writer (new-index-writer name nil)]
-    (DirectoryReader/open ^IndexWriter writer false)))
-
-(defn new-searcher-manager ^SearcherManager [name]
-  (SearcherManager. (new-index-directory (root-identifier-path) name) nil))
+(defn new-searcher-manager ^SearcherTaxonomyManager [name]
+  (SearcherTaxonomyManager. (new-index-directory (root-identifier-path) name)
+                            (new-index-directory (taxo-dir-prefix name) (str "__" name "_taxo__"))
+                            nil))

 (defn sharded [index x-shard]
   (let [shard (int-or-parse x-shard)]
     (str (as-str index) shard-suffix (str shard))))

 (defn reset-search-managers []
-  (doseq [[name [manager taxo]] @name->smanager-taxo*]
+  (doseq [[name manager] @name->manager*]
     (log/info "\tclosing: " name " " manager)
-    (try-close-manager-taxo manager taxo))
-  (reset! name->smanager-taxo* {}))
+    (try-close-manager manager))
+  (reset! name->manager* {}))

 (defn use-searcher [index refresh callback]
-  (let [[^SearcherManager manager taxo] (get-smanager-taxo index refresh)
-        searcher ^IndexSearcher (.acquire manager)
+  (let [^SearcherTaxonomyManager manager (get-manager index refresh)
+        pair ^SearcherTaxonomyManager$SearcherAndTaxonomy (.acquire manager)
         t0 (time-ms)]
     (try
-      (callback searcher taxo)
+      (callback (.searcher pair) (.taxonomyReader pair))
       (catch Throwable e
         (do
           (stat/update-error index "use-searcher")
           (throw e)))
       (finally
         (do
-          (.release manager searcher)
+          (.release manager pair)
           (stat/update-took-count index "use-searcher" (time-took t0)))))))

 (defn log-close-err [^Directory dir ^Throwable e]
@@ -199,14 +183,9 @@
           (log-close-err (.getDirectory taxo) e)
           (DirectoryTaxonomyWriter/unlock (.getDirectory taxo)))))))

-(defn try-close-manager-taxo [^SearcherManager manager ^DirectoryTaxonomyReader taxo]
-  (try
-    (do
-      (try
-        (.close taxo)
-        (catch Exception e
-          (log/warn (as-str e))))
-      (.close manager))
+(defn try-close-manager [^SearcherTaxonomyManager manager]
+  (try
+    (.close manager)
     (catch Exception e
       (log/warn (as-str e)))))

@@ -247,63 +226,58 @@
   (try
     (doseq [dir (sub-directories)]
       (try
-        (get-smanager-taxo (.getName ^File dir) false)
+        (get-manager (.getName ^File dir) false)
         (catch Exception e
           (log/warn (ex-str e)))))
     (catch Exception e
       (log/warn (ex-str e)))))

-(defn get-smanager-taxo [index refresh]
-  (locking name->smanager-taxo*
-    (when (nil? (@name->smanager-taxo* index))
-      (do
-        (swap! name->smanager-taxo* assoc index [(new-searcher-manager index)
-                                                 (try-new-taxo-reader index)])
+(defn get-manager [index refresh]
+  (when (nil? (@name->manager* index))
+    (locking name->manager*
+      (when (nil? (@name->manager* index))
         ;; to avoid a race in case multiple threads
-        ;; decide to use the index in the very beginning
+        ;; decide to use the index (including creating it) in the very beginning
         ;; before the statistic keys are ready, so we just create them under the lock here
         ;; not doing the same for writers, because of the exclusive write lock there
-        (stat/get-statistics index)))
-    (let [[manager taxo] (@name->smanager-taxo* index)]
-      (when refresh
-        (refresh-searcher-locked index manager taxo))
-      [manager taxo])))
-
-(defn refresh-searcher-locked [index ^SearcherManager manager ^DirectoryTaxonomyReader taxo]
-  (log/debug "refreshing: " index " " manager " " taxo)
+        (use-writer index nil 0 (fn [^IndexWriter writer ^DirectoryTaxonomyWriter taxo]))
+        (swap! name->manager* assoc index (new-searcher-manager index))
+        (stat/get-statistics index))))
+  (let [manager (@name->manager* index)]
+    (when refresh
+      (refresh-searcher-locked index manager))
+    manager))
+
+(defn refresh-searcher-locked [index ^SearcherTaxonomyManager manager]
+  (log/debug "refreshing: " index " " manager)
   (let [t0 (time-ms)]
     (try
       (do
         (.maybeRefresh manager)
-        (if-let [changed (DirectoryTaxonomyReader/openIfChanged taxo)]
-          (do
-            (.close taxo)
-            (swap! name->smanager-taxo* assoc-in [index 1] changed)))
         (stat/update-took-count index "refresh-search-managers" (time-took t0)))
       (catch Throwable e
         (do
           (log/info (str index " refresh exception, closing it. Exception: " (ex-str e)))
-          (try-close-manager-taxo manager taxo)
-          (swap! name->smanager-taxo* dissoc index))))))
+          (try-close-manager manager)
+          (swap! name->manager* dissoc index))))))

 (defn refresh-search-managers []
   (let [t0 (time-ms)]
     (bootstrap-indexes)
-    (locking name->smanager-taxo*
-      (doseq [[index [^SearcherManager manager
-                      ^DirectoryTaxonomyReader taxo]] @name->smanager-taxo*]
-        (refresh-searcher-locked index manager taxo)))
+    (locking name->manager*
+      (doseq [[index ^SearcherTaxonomyManager manager] @name->manager*]
+        (refresh-searcher-locked index manager)))
     (stat/update-took-count stat/total "refresh-search-managers" (time-took t0))
     (log/debug "refreshing took" (time-took t0))))

 (defn shutdown []
-  (locking name->smanager-taxo*
-    (log/info "executing shutdown hook, current mapping: " @name->smanager-taxo*)
+  (locking name->manager*
+    (log/info "executing shutdown hook, current mapping: " @name->manager*)
     (reset-search-managers)
-    (log/info "mapping after cleanup: " @name->smanager-taxo*)))
+    (log/info "mapping after cleanup: " @name->manager*)))

 (defn index-stat []
-  (into {} (for [[name [manager taxo]] @name->smanager-taxo*]
+  (into {} (for [[name manager] @name->manager*]
              [name (use-searcher name
                                  false
                                  (fn [^IndexSearcher searcher ^DirectoryTaxonomyReader taxo-reader]


Added explanation of how to use schemas in schemas.cljs

--- src/chessground/schemas.cljs
+++ src/chessground/schemas.cljs
@@ -1,6 +1,29 @@
 (ns chessground.schemas
   (:require [schema.core :as s :refer [Any Str Bool]])
   (:require-macros [schema.macros :as sm :refer [defschema]]))
+
+(comment
+  ;; The main benefit of schemas is that they offer code as documentation. They
+  ;; are not a replacement for a type system, but can nevertheless be used for
+  ;; input and output validation similar to how one would use :pre and :post
+  ;; conditions. Schemas should never get in the users way, and thus in order to
+  ;; use the validation capabilities you need to explicitly declare so by:
+  ;;   - Using schema.core/validate or schema.core/check, e.g.
+
+  (s/validate Square "a1") ; => "a1"
+  (s/validate Square "i4") ; => Throws
+
+  (s/check ChessPiece {:role "knight" :color "white"}) ; => nil
+  (s/check ChessPiece {:role "janitor" :colr "white"})
+  ; => {:role (not (#{"bishop" "rook" "queen" "pawn" "king" "knight"} "janitor"))
+  ;     :color missing-required-key, :colr disallowed-key}
+
+  ;;   - Enabling namespace wide validation as a part of running tests with
+  (cemerick.cljs.test/use-fixtures :once schema-test/validate-schemas)
+
+  ;;   - Or by forcing validation for key functions:
+  (sm/defn ^:always-validate make :- Str [fen :- Str] ...)
+  )

 (defschema AnyMap {Any Any})


[#3091] Fix tests

[#3091] Fix tests

--- backend/test/akvo/lumen/lib/aggregation_test.clj
+++ backend/test/akvo/lumen/lib/aggregation_test.clj
@@ -189,9 +189,9 @@

       (let [[tag query-result] (query {:bucketColumn "c2"})]
         (is (= query-result
-               {:series [{:key "B", :label "B", :data [{:value 5} {:value 3}]}],
-                :common
-                {:data [{:key "b1", :label "b1"} {:key "b2", :label "b2"}],
+               {:series [{:key "B", :label "B", :data (vec (reverse [{:value 5} {:value 3}]))}],
+                :common
+                {:data (vec (reverse [{:key "b1", :label "b1"} {:key "b2", :label "b2"}])),
                  :metadata {:type "text"}}}))))))

 (deftest ^:functional bar-tests
@@ -399,11 +399,11 @@
                {:series
                 [{:key nil,
                   :label nil,
-                  :data [{:value 2} {:value 1} {:value 1}],
+                  :data (vec (reverse [{:value 2} {:value 1} {:value 1}])),
                   :metadata {:type nil}}],
                 :common
                 {:metadata {:sampled false},
-                 :data [{:label "c" :key "c"} {:label "b" :key "b"} {:label "a" :key "a"}]}}))))
+                 :data (vec (reverse [{:label "c" :key "c"} {:label "b" :key "b"} {:label "a" :key "a"}]))}}))))
     (testing "Metric queries"
       (let [[tag query-result] (query {:bucketColumn "c1"
                                        :metricColumn "c2"
@@ -412,8 +412,8 @@
                {:series
                 [{:key "B",
                   :label "B",
-                  :data [{:value 4M} {:value 2M} {:value 1M}],
+                  :data (vec (reverse [{:value 4M} {:value 2M} {:value 1M}])),
                   :metadata {:type "number"}}],
                 :common
                 {:metadata {:sampled false},
-                 :data [{:label "c" :key "c"} {:label "b" :key "b"} {:label "a" :key "a"}]}}))))))
+                 :data (vec (reverse [{:label "c" :key "c"} {:label "b" :key "b"} {:label "a" :key "a"}]))}}))))))


Change the dirac switch to -D (uppercase)

--- resources/leiningen/new/lambone/common/build.boot
+++ resources/leiningen/new/lambone/common/build.boot
@@ -239,7 +239,7 @@
   - port 5088 for the frontend (call adzerk.boot-cljs-repl/start-repl and then
   point your browser to http://localhost:3000)."
   [f flavor VAL kw   "The flavor (backend or frontend)"
-   d dirac      bool "Enable the Dirac repl instead of the standard one."]
+   D dirac      bool "Enable the Dirac repl instead of the standard one."]
   (boot.util/info "Starting interactive dev...\n")
   (let [<% if any backend %>dev-backend #(boot/dev-backend (boot/options [:backend :dev]))<% endif %><% if any frontend %>
         dev-frontend #(boot/dev-frontend (boot/options [:frontend :dev]) dirac)<% endif %>]
@@ -288,7 +288,7 @@
   If no -t|--type is specified the task will assume :prod."
   [f flavor VAL kw   "The flavor"
    t type   VAL kw   "The build type, either prod or dev"
-   d dirac      bool "Enable the Dirac repl instead of the standard one."]
+   D dirac      bool "Enable the Dirac repl instead of the standard one."]
   (let [type (or type :prod)
         flavor (or flavor (keyword (get (env/env) "BOOT_DEFAULT_FLAVOR")))]
     (assert flavor "Cannot list dependencies without a flavor. Either specify it with -f/--flavor or set BOOT_DEFAULT_FLAVOR")


get user info in background

get user info in background

--- src/pc/auth.clj
+++ src/pc/auth.clj
@@ -45,7 +45,7 @@
                                   :google-account/sub (:sub user-info)
                                   :cust/uuid (or session-uuid (UUID/randomUUID))})]
           (ping-chat-with-new-user (:email user-info))
-          (update-user-from-sub user)
+          (future (update-user-from-sub user))
           user)
         (catch Exception e
           (if (pcd/unique-conflict? e)


extra column for the mobile version

extra column for the mobile version

--- src/clj/just_married/css.clj
+++ src/clj/just_married/css.clj
@@ -46,7 +46,7 @@
                                         :grid-template-rows "auto auto auto"})

    :mobile (merge common-grid-options {:grid-template-columns "auto"
-                                       :grid-template-rows "auto auto auto auto"})})
+                                       :grid-template-rows "auto auto auto auto auto"})})

 (def ^:private navbar-grid-config
   {:desktop {:background-color (:dark-red COLOR-PALLETTE)
@@ -233,6 +233,8 @@

               [:.navbar__container (:mobile navbar-grid-config)]
               [:.container (:mobile grid-config)]
+              ;; should I simply change the layout
+              ;; entirely moving with everything on the same column??
               [:.timeline {:grid-column 1
                            :grid-row 3}])]])


reshape

reshape

--- src/clj/just_married/css.clj
+++ src/clj/just_married/css.clj
@@ -1,6 +1,8 @@
 (ns just-married.css
   (:require [garden.def :refer [defstyles defcssfn]]
             [garden.core :refer [css]]))
+
+(def ^:private map-width "500px")

 (def COLOR-PALLETTE
   {:amaranth "#E52B50"
@@ -44,7 +46,7 @@
 (def main-page
   [[:.container {:display "grid"
                  :grid-gap "5px"
-                 :grid-template-columns "300px auto"
+                 :grid-template-columns (str "550px" " auto")
                  :grid-template-rows "auto auto"}]

    [:.countdown {:grid-column 1
@@ -98,8 +100,8 @@
              :color (:deep-blue COLOR-PALLETTE)}]

    ;; find us settings
-   [:#map {:width "500px"
-           :height "500px"
+   [:#map {:width map-width
+           :height map-width
            :padding "15px"
            :box-shadow "10px 10px 20px #999"
            :border-radius "10px"}]


set avatar_url to 250, reset needed

set avatar_url to 250, reset needed

--- src/clj/lobos/migrations.clj
+++ src/clj/lobos/migrations.clj
@@ -13,7 +13,7 @@
 			(integer 		:id :primary-key :auto-inc )
 			(varchar 		:name 100 )
 			(varchar 		:instructions 1000)
-			(varchar 		:avatar_url 100)
+			(varchar 		:avatar_url 250)
 			(timestamp 	:created (default (now))))))
 	(down [] (drop (table :recipes ))))


Read firebase, working district toggles, fix React warnings

--- src/cljs/geoforms/forms.cljs
+++ src/cljs/geoforms/forms.cljs
@@ -1,103 +1,30 @@
 (ns geoforms.forms
-  (:require [json-html.core :refer [edn->hiccup]]
-            [reagent.core :as reagent :refer [atom]]
-            [reagent-forms.core :refer [bind-fields init-field value-of]]))
-
-
-;; Example taken from https://github.com/reagent-project/reagent-forms/tree/master/forms-example
-
-
-;;; DATA
-
-;; should be in firebase
-
-(defonce app-cms
-  (atom
-   {:title "Support local ideas!"
-    :subtitle ""
-    :instructions-district "1. Choose your district"
-    :instructions-vote     "2. Check ideas you want to support!"
-    :instructions-add      "3. Add your won ideas"
-    :instructions-sign     "4. Sign your choices"}))
-
-(def app-ideas
-  (atom
-   [{:id "i-1"
-     :timestamp "2013-08-10 11:20:22"
-     :districts ["Brooklyn"]
-     :idea "New skatepark"
-     :desc "Would be really nice to have a new skatepark"
-     :links ["http://lapresse.ca/article-2"]
-     :supporters [""]
-     :category "Other..."}
-    {:id "i-2"
-     :timestamp "2013-08-10 11:20:24"
-     :districts ["Manhattan"]
-     :idea "More police to prevent steeling"
-     :desc ""
-     :links ["http://lapresse.ca/article-1"]
-     :supporters [""]
-     :category "Security"}
-    {:id "i-3"
-     :timestamp "2013-08-10 11:20:26"
-     :districts ["Manhattan"]
-     :idea "Create a park near fifth avenue"
-     :desc "Would be awesome to have a park on broadway near fith avenue."
-     :links ["http://lapresse.ca/article-56"]
-     :supporters ["email@email.com" "email2@email.com" "john@hotmail.com"]
-     :category "Green"}]))
-
-(def app-users
-  (atom
-   [{:created "2013-08-10 11:20:22"
-     :fullname "Leon Talbot"
-     :email "email@email.com"
-     :zip-code "G21 2C5" ;; this is canadian zip code.
-     :age "32"
-     :annual-revenue ""
-     :subscribe-idea-alerts true
-     :subscribe-volonteer-idea-alerts false
-     :subscribe-district-alerts true
-     :comments ""}
-    {:created "2013-08-10 11:20:22"
-     :fullname "John Talbot"
-     :email "john@hotmail.com"
-     :zip-code "G21 2C3"
-     :age "33"
-     :annual-revenue ""
-     :subscribe-idea-alerts true
-     :subscribe-volonteer-idea-alerts false
-     :subscribe-district-alerts true
-     :comments ""}
-    {:created "2013-08-10 11:20:22"
-     :fullname "Marc Talbot"
-     :email "email2@email.com"
-     :zip-code "G21 2C2"
-     :age "34"
-     :annual-revenue ""
-     :subscribe-idea-alerts true
-     :subscribe-volonteer-idea-alerts false
-     :subscribe-district-alerts true
-     :comments "better UX please. Thanks."}]))
-
-(def app-cats
-  (atom
-   ["Shops" "Security" "Green" "Other..."]))
-
-(def app-districts
-  (atom ["Manhattan" "Brooklyn" "Queens" "Other districts"]))
-
-
-;;; STATE
+  (:require [reagent.core :as reagent :refer [atom]]
+            [reagent-forms.core :refer [bind-fields init-field value-of]]
+            [geoforms.db :as db]))
+
+;;; state

 (def app-state
   (atom
-   {:selected-districts @app-districts
+   {:selected-districts nil
     :added-ideas nil
     :added-user  nil}))

-
-;;; FNS
+(defn toggle-district! [d]
+  (swap! app-state update :selected-districts
+         #(if (% d) (disj % d) (conj % d))))
+
+(defn selected-district? [d]
+  (if-let [selected (:selected-districts @app-state)]
+    (contains? selected d)
+    (do (swap! app-state assoc :selected-districts (set @db/districts))
+        true)))
+
+(defn district-ideas [d]
+  (filter (comp #(some #{d} %) :districts) @db/ideas))
+
+;;; view helpers

 (defn row [label input]
   [:div.row
@@ -114,7 +41,7 @@
   [:div
    [:p label]
    (radio "Yes" name true)
-   (radio "No" name false)])
+   (radio "No"  name false)])

 (defn input [label type id]
   (row label [:input.form-control {:field type :id id}]))
@@ -124,49 +51,87 @@
    [:div.col
     [:input.form-control {:field type :id id :placeholder label}]]])

-(defn friend-source [text]
-  (filter
-   #(-> % (.toLowerCase %) (.indexOf text) (> -1))
-   ["Alice" "Alan" "Bob" "Beth" "Jim" "Jane" "Kim" "Rob" "Zoe"]))
-
-
 ;;; COMPONENTS
+
+(defn district-toggle [d]
+  ^{:key d}
+  [:button.btn.btn-default
+   {:active   (selected-district? d)
+    :on-click #(toggle-district! d)}
+   [(if (selected-district? d) :b :span)
+    d]])

 (defn select-districts-component []
   [:div.btn-group {:field :multi-select :id :every.position}
-   (for [d @app-districts]
-     [:button.btn.btn-default {:key (keyword d)} d])])
+   (for [d @db/districts]
+     ^{:key d}
+     [district-toggle d])])

 (defn select-cat-component []
     [:div.form-group
      [:select.form-control {:field :list :id :idea.category}
       [:option
-       {:value "" :disabled "disabled" :selected "selected"}
-       "Pick a category..."]
-      (for [c @app-cats]
-        [:option {:key (keyword c)} c])]])
+       {:value "" :disabled true}
+       "Pick a category..."]]])

 (defn list-idea-blocks-component []
-  nil)
+  (let [districts @db/districts]
+    [:div
+     (doall
+      (for [d districts
+            :when (selected-district? d)
+            :let [ideas (district-ideas d)]]
+        ^{:key d}
+        [:div
+         [:p [:strong d " - " (count ideas) " idea(s)"]]
+         (for [{:keys [id idea]} ideas]
+           ^{:key id}
+           [:div.checkbox
+            [:label
+             [:input {:type :checkbox :id id}]
+             idea]])
+         ;; should we put just name instead of id?
+         (input-placeholder (str "add an idea for " d) :text :idea.idea)
+         [:br]]))]))

 (defn add-idea-component []
-    [:div
-     (input-placeholder "Add your idea" :text :idea.idea) ;; should we put just name instead of id ?
-     [select-cat-component]
-     [:p
-      [:textarea.form-control
-       {:rows "4" :placeholder "If necessary, add a description"}]]
-     (input-placeholder "Add a reference URL" :text :idea.url.1)
-     (input-placeholder "Add a reference URL" :text :idea.url.2)
-     (input-placeholder "Add a reference URL" :text :idea.url.3)
-     [:button.btn.btn-default
-      #_{:on-click #(handle-add-idea)}
-      "Add"]])
+  [:div
+   ;; should we put just name instead of id ?
+   (input-placeholder "Add your idea" :text :idea.idea)
+   [select-cat-component]
+   [:p
+    [:textarea.form-control
+     {:rows "4" :placeholder "If necessary, add a description"}]]
+   (input-placeholder "Add a reference URL" :text :idea.url.1)
+   (input-placeholder "Add a reference URL" :text :idea.url.2)
+   (input-placeholder "Add a reference URL" :text :idea.url.3)
+   [:button.btn.btn-default
+    #_{:on-click #(handle-add-idea)}
+    "Add"]])

 (defn signature-component []
   nil)

-;;; TEMPLATE
+
+;;; templates
+
+;; could just generate from the break points
+(def age-groups
+  [:17-
+   :18-24
+   :25-29
+   :30-34
+   :35-39
+   :40-44
+   :45-49
+   :50-54
+   :55-59
+   :60-64
+   :65-69
+   :70-74
+   :75-79
+   :80-84
+   :85+])

 (def form-template
   [:div
@@ -174,22 +139,7 @@
    [select-districts-component]

    [:h3 "2. Check ideas you want to support!"]
-   #_[list-idea-blocks-component]
-
-   [:div
-    (for [d (:selected-districts @app-state)
-          :let [selected-ideas
-                (filter (fn [i] (some #(= % d) (:districts i)))
-                        @app-ideas)]]
-      [:div
-       [:p [:strong d " - " (count selected-ideas) " idea(s)"]]
-       (for [i selected-ideas]
-         [:div.checkbox
-          [:label
-           [:input.form-control {:field :checkbox :id (:id i)}]
-           (:idea i)]])
-       (input-placeholder (str "add an idea for " d) :text :idea.idea) ;; should we put just name instead of id ?
-       [:br]])]
+   [list-idea-blocks-component]

    [:hr]
    [:p [:em "When 'add an idea' input field is clicked:"]]
@@ -199,47 +149,34 @@
    #_[signature-component]

    [:div
-   (input "first name" :text :person.first-name)
-   [:div.row
-    [:div.col-md-2]
-    [:div.col-md-5
-     [:div.alert.alert-danger
-      {:field :alert :id :errors.first-name}]]]
-
-   (input "last name" :text :person.last-name)
-   [:div.row
-    [:div.col-md-2]
-    [:div.col-md-5
-     [:div.alert.alert-success
-      {:field :alert :id :person.last-name :event empty?}
-      "last name is empty!"]]]
-
-   [:div.form-group
-    [:label "age"]
-    [:select.form-control {:field :list :id :person.age}
-     [:option {:key :17-} "17-"]
-     [:option {:key :18-24} "18-24"]
-     [:option {:key :25-29} "25-29"]
-     [:option {:key :30-34} "30-34"]
-     [:option {:key :35-39} "35-39"]
-     [:option {:key :40-44} "40-44"]
-     [:option {:key :45-49} "45-49"]
-     [:option {:key :50-54} "50-54"]
-     [:option {:key :55-59} "55-59"]
-     [:option {:key :60-64} "60-64"]
-     [:option {:key :65-69} "65-69"]
-     [:option {:key :70-74} "70-74"]
-     [:option {:key :75-79} "75-79"]
-     [:option {:key :80-84} "80-84"]
-     [:option {:key :85+} "85+"]]]
-
-   (input "email" :email :person.email)
-   [:div.row
-    [:div.col-md-2]
-    [:div.col-md-5
-     [:div.alert.alert-danger
-      {:field :alert :id :errors.email :event empty?}
-      "email is empty!"]]]
+    (input "first name" :text :person.first-name)
+    [:div.row
+     [:div.col-md-2]
+     [:div.col-md-5
+      [:div.alert.alert-danger
+       {:field :alert :id :errors.first-name}]]]
+
+    (input "last name" :text :person.last-name)
+    [:div.row
+     [:div.col-md-2]
+     [:div.col-md-5
+      [:div.alert.alert-success
+       {:field :alert :id :person.last-name :event empty?}
+       "last name is empty!"]]]
+
+    [:div.form-group
+     [:label "age"]
+     [:select.form-control {:field :list :id :person.age}
+      (for [a age-groups]
+        [:option {:value a :key a} (name a)])]]
+
+    (input "email" :email :person.email)
+    [:div.row
+     [:div.col-md-2]
+     [:div.col-md-5
+      [:div.alert.alert-danger
+       {:field :alert :id :errors.email :event empty?}
+       "email is empty!"]]]

     (radios-yes-no
      "I want to get noticed about my supported ideas progress."
@@ -253,54 +190,23 @@
      "I want to get noticed about major updates in my supported district(s)."
      :subscribe-district-alerts)

-   (row
-    "comments"
-    [:textarea.form-control
-     {:field :textarea :id :comments}])]
-   [:hr]
-   [:h4 "This is not useful anymore (from reagent-forms example)"]
-   (input "kg" :numeric :weight-kg)
-   (input "lb" :numeric :weight-lb)
-
-   [:hr]
-
-   (row "Best friend"
-        [:div {:field           :typeahead
-               :id              :ta
-               :data-source     friend-source
-               :input-class     "form-control"
-               :list-class      "typeahead-list"
-               :item-class      "typeahead-item"
-               :highlight-class "highlighted"}])
-   [:br]
-
-   (row "isn't data binding lovely?"
-        [:input {:field :checkbox :id :databinding.lovely}])
-   [:label
-    {:field :label
-     :preamble "The level of awesome: "
-     :placeholder "N/A"
-     :id :awesomeness}]
-
-   [:input {:field :range :min 1 :max 10 :id :awesomeness}]])
+    (row
+     "comments"
+     [:textarea.form-control
+      {:field :textarea :id :comments}])]])

 ;;; PAGE

+(defn validate! [doc]
+  (if (empty? (get-in @doc [:person :first-name]))
+    (swap! doc assoc-in [:errors :first-name] "first name is empty")))
+
+(defn submit! [doc]
+  )
+
 (defn page []
-  (let [doc (atom {:person {:first-name "John"
-                            :age 35
-                            :email "foo@bar.baz"}
-                   :weight 100
-                   :height 200
-                   :bmi 0.5
-                   :comments "some interesting comments\non this subject"
-                   :radioselection :b
-                   :position [:left :right]
-                   :pick-one :bar
-                   :unique {:position :middle}
-                   :pick-a-few [:bar :baz]
-                   :many {:options :bar}})]
+  (let [doc (atom {})]
     (fn []
       [:div
        [:div.page-header [:h1 "Sample Form"]]
@@ -308,23 +214,10 @@
        [bind-fields
         form-template
         doc
-        (fn [[id] value {:keys [weight-lb weight-kg] :as document}]
-           (cond
-            (= id :weight-lb)
-            (assoc document :weight-kg (/ value 2.2046))
-            (= id :weight-kg)
-            (assoc document :weight-lb (* value 2.2046))
-            :else nil))
-        (fn [[id] value {:keys [height weight] :as document}]
-          (when (and (some #{id} [:height :weight]) weight height)
-            (assoc document :bmi (/ weight (* height height)))))]
+        (fn [document])
+        (fn [document])]

        [:button.btn.btn-default
-         {:on-click
-          #(if (empty? (get-in @doc [:person :first-name]))
-             (swap! doc assoc-in [:errors :first-name]"first name is empty"))}
-         "Submit"]
-
-       [:hr]
-       [:h1 "Document State"]
-       [edn->hiccup @doc]])))
+        {:on-click #(and (validate! doc)
+                         (submit! doc))}
+        "Submit"]])))


datastore only missing nested entities

datastore only missing nested entities

--- src/datasplash/datastore.clj
+++ src/datasplash/datastore.clj
@@ -1,19 +1,18 @@
 (ns datasplash.datastore
   (:require [datasplash.core :refer :all])
   (:import
-   [com.google.datastore.v1.client DatastoreHelper ]
-   [com.google.datastore.v1 Entity]
-   ;; [com.google.cloud.datastore Datastore DatastoreOptions Entity Key KeyFactory]
-   [com.google.cloud.dataflow.sdk.io.datastore DatastoreIO ]
+   [com.google.datastore.v1.client DatastoreHelper]
+   [com.google.cloud.dataflow.sdk.io.datastore DatastoreIO]
    [com.google.cloud.dataflow.sdk Pipeline]
    [com.google.cloud.dataflow.sdk.values PBegin PCollection]
-   [com.google.datastore.v1 Entity$Builder Key$Builder Value$Builder])
+   [com.google.datastore.v1 Entity Value Entity$Builder Key$Builder Value$Builder Value$ValueTypeCase]
+   [com.google.protobuf ByteString])
   (:gen-class))

 (defn write-datastore-raw
   "Write a pcoll of already generated datastore entity in datastore"
   [{:keys [project-id] :as options} pcoll]
-  (let [opts (assoc options :label :write-datastore)
+  (let [opts (assoc options :label :write-datastore-raw)
         ptrans (-> (DatastoreIO/v1)
                    (.write)
                    (.withProjectId project-id))]
@@ -30,30 +29,55 @@
                    (cond-> query (.withQuery query)))]
     (apply-transform pcoll ptrans named-schema opts)))

+(declare value->clj)
+
+(def type-mapping {(Value$ValueTypeCase/valueOf "INTEGER_VALUE") (fn [^Value v] (.getIntegerValue v))
+                   (Value$ValueTypeCase/valueOf "DOUBLE_VALUE") (fn [^Value v] (.getDoubleValue v))
+                   (Value$ValueTypeCase/valueOf "STRING_VALUE") (fn [^Value v] (.getStringValue v))
+                   (Value$ValueTypeCase/valueOf "BOOLEAN_VALUE") (fn [^Value v] (.getBooleanValue v))
+                   (Value$ValueTypeCase/valueOf "BLOB_VALUE") (fn [^Value v] (.toByteArray (.getBlobValue v)))
+                   (Value$ValueTypeCase/valueOf "ARRAY_VALUE") (fn [^Value v] (mapv value->clj (.getValuesList (.getArrayValue v))))
+                   (Value$ValueTypeCase/valueOf "NULL_VALUE") (constantly nil)})
+
+(defn value->clj
+  [v]
+  (let [t (.getValueTypeCase v)
+        tx (type-mapping t)]
+    (if tx
+      (tx v)
+      (throw (ex-info (format "Datastore type not supported: %s" t) {:value v})))))
+
+(defn entity->clj
+  [e]
+  (reduce (fn [acc kv]
+            (let [value (value->clj (.getValue kv))]
+              (assoc acc (keyword (.getKey kv)) value)))
+          {} (.getProperties e)))
+
 (defprotocol IValDS
-  (make-ds-value-builder [options v]))
+  (make-ds-value-builder [v options]))

 (declare make-ds-value)

 (extend-protocol IValDS
   String
-  (make-ds-value-builder [_ ^String v] (DatastoreHelper/makeValue v))
+  (make-ds-value-builder [^String v _] (DatastoreHelper/makeValue v))
+  clojure.lang.Keyword
+  (make-ds-value-builder [v _] (DatastoreHelper/makeValue (name v)))
   java.util.Date
-  (make-ds-value-builder [_ ^java.util.Date v] (DatastoreHelper/makeValue v))
-  Long
-  (make-ds-value-builder [_ ^Long v] (DatastoreHelper/makeValue v))
-  java.util.List
-  (make-ds-value-builder [options ^Iterable v] (mapv #(make-ds-value options %) v))
+  (make-ds-value-builder [^java.util.Date v _] (DatastoreHelper/makeValue v))
+  clojure.lang.PersistentList
+  (make-ds-value-builder [v options] (DatastoreHelper/makeValue ^Iterable (mapv #(make-ds-value % options) v)))
+  clojure.lang.PersistentHashSet
+  (make-ds-value-builder [v options] (DatastoreHelper/makeValue ^Iterable (mapv #(make-ds-value % options) v)))
+  clojure.lang.PersistentTreeSet
+  (make-ds-value-builder [v options] (DatastoreHelper/makeValue ^Iterable (mapv #(make-ds-value % options) v)))
+  clojure.lang.PersistentVector
+  (make-ds-value-builder [v options] (DatastoreHelper/makeValue ^Iterable (mapv #(make-ds-value % options) v)))
   Object
-  (make-ds-value-builder [_ v] (DatastoreHelper/makeValue v)))
+  (make-ds-value-builder [v _] (DatastoreHelper/makeValue v)))

-(defn make-ds-value
-  [options v]
-  ;; stop building here when print bug id fixed in gcloud and handle excluded afterwards
-  (.build ^Value$Builder (make-ds-value-builder options v)))
-
-(defn make-ds-entity-builder
-  "Generate a datastore entity. Take as parameters the string key of the entity, a map containing the different fields and an options map defining the datastore namespace and kind"
+(defn- make-ds-entity-builder
   [raw-key raw-values {:keys [ds-namespace ds-kind exclude-from-index] :as options}]
   (let [^Key$Builder key-builder (DatastoreHelper/makeKey (into-array [ds-kind raw-key]))
         excluded-set (into #{} (map name exclude-from-index))
@@ -63,13 +87,19 @@
     (doseq [[v-key v-val] raw-values]
       (.put (.getMutableProperties entity-builder)
             (if (keyword? v-key) (name v-key) v-key)
-            (let [^Value$Builder val-builder (make-ds-value-builder options v-val)]
+            (let [^Value$Builder val-builder (make-ds-value-builder v-val options)]
               (-> val-builder
                   (cond-> (excluded-set (name v-key)) (.setExcludeFromIndexes true))
                   (.build)))))
     entity-builder))

+(defn- make-ds-value
+  [v options]
+  ;; stop building here when print bug id fixed in gcloud and handle excluded afterwards
+  (.build ^Value$Builder (make-ds-value-builder v options)))
+
 (defn make-ds-entity
+  "Builds a Datastore Entity with the given key, value is a map corresponding to the desired entity, and options contains namespace, kind and an optional set of keys that shoud not be indexed (irrespective of nesting). For now repeated fields are supported but not nested entity"
   [raw-key raw-values {:keys [ds-namespace ds-kind exclude-from-index] :as options}]
   (-> ^Entity$Builder (make-ds-entity-builder raw-key raw-values options)
       (.build)))


CMR-5691: Allow EDSC search with JSON requests through CORS.

--- common-app-lib/src/cmr/common_app/api/routes.clj
+++ common-app-lib/src/cmr/common_app/api/routes.clj
@@ -77,7 +77,7 @@
    :headers {CONTENT_TYPE_HEADER "text/plain; charset=utf-8"
              CORS_ORIGIN_HEADER "*"
              CORS_METHODS_HEADER "POST, GET, OPTIONS"
-             CORS_CUSTOM_ALLOWED_HEADER "Echo-Token, Client-Id, CMR-Request-Id, CMR-Scroll-Id"
+             CORS_CUSTOM_ALLOWED_HEADER "Echo-Token, Accept, Content-Type, Client-Id, CMR-Request-Id, CMR-Scroll-Id"
              ;; the value in seconds for how long the response to the preflight request can be cached
              ;; set to 30 days
              CORS_MAX_AGE_HEADER "2592000"}})


Make sure hitting raw delete url does nothing

--- test/clj_crud/accounts_test.clj
+++ test/clj_crud/accounts_test.clj
@@ -124,7 +124,13 @@

 (deftest delete-profile-flow
-  ;; todo peridot check on raw post to delete
+  (let [response (-> (p/session (tc/reuse-handler))
+                     (p/header "Accept" "text/html")
+                     (p/request "/profile/user1/delete"
+                                :request-method :post)
+                     :response)]
+    (is (= 303 (:status response)))
+    (is (= "/login" (get-in response [:headers "Location"]))))

   (-> (session (tc/reuse-handler))
       (visit "/profile/user1/edit")


add find-commodities

add find-commodities

--- test/clj_money/test_helpers.clj
+++ test/clj_money/test_helpers.clj
@@ -121,6 +121,10 @@
   [context symbol]
   (find-in-context context :commodities :symbol symbol))

+(defn find-commodities
+  [context & symbols]
+  (map #(find-commodity context %) symbols))
+
 (defn context-errors
   [context]
   (reduce (fn [result [category models]]


Found StackOverflow that's been so much trouble

This thing has been plaguing me for days. I don't understand why it
happens, but at least now I can see what triggers the underlying
problem (I'm trying to call assoc on an agent somewhere...yet another
reason to just eliminate the thing completely). And the exception isn't
escaping unhandled.

--- test/frereth_cp/server_test.clj
+++ test/frereth_cp/server_test.clj
@@ -405,7 +405,14 @@
                     cleaned (if (map? client-state)
                               (dissoc client-state ::log/state)
                               {::agent-state client-state})]
-                (println "Stopping client agent" cleaned))
+                (try
+                  (println "Stopping client agent" cleaned)
+                  ;; Here's the StackOverflowError I've been chasing around for so long.
+                  (catch StackOverflowError ex
+                    (is not ex)
+                    (println "Failed to print the cleaned-up client agent state:"
+                             (log/exception-details ex))
+                    (println "The state object that caused problems is a" (class client-state)))))
               (if-let [problem (agent-error client-agent)]
                 (println "Uh-oh. Don't try to stop client-agent. It's in a failed state:\n"
                          problem)


Call constructor of ConstraintMapping.

Call constructor of ConstraintMapping.

Last refactoring removed call to constructor of ConstraintMapping, and
broke a build.

--- test/http/async/client/test.clj
+++ test/http/async/client/test.clj
@@ -134,7 +134,7 @@
      (let [srv (Server. port)
            loginSrv (HashLoginService. "MyRealm" "test-resources/realm.properties")
            constraint (Constraint.)
-           mapping (ConstraintMapping)
+           mapping (ConstraintMapping.)
            security (ConstraintSecurityHandler.)]

        (.addBean srv loginSrv)


fix shelf test

fix shelf test

--- test/shelver/goodreads_test.clj
+++ test/shelver/goodreads_test.clj
@@ -26,4 +26,4 @@
            (is (some #(= [:id ["138241012"]] ((juxt :tag :content) %)) (xml-seq (:body resp))))))
         (testing "page 2"
           (let [resp (gr/shelves goodreads-client 2)]
-            (is (some #(= [:id ["138241012"]] ((juxt :tag :content) %)) (xml-seq (:body resp))))))))))
+            (is (nil? (some #(= [:id ["138241012"]] ((juxt :tag :content) %)) (xml-seq (:body resp)))))))))))


Add count and concat test

Add count and concat test

--- test/simple_check/core_test.clj
+++ test/simple_check/core_test.clj
@@ -71,6 +71,21 @@
                   (let [result (sc/quick-check 100 exception-thrower [gen/int])]
                     [(:result result) (get-in result [:shrunk :smallest])])))))

+;; Count and concat work as expected
+;; ---------------------------------------------------------------------------
+
+(defn concat-counts-correct
+  [a b]
+  (= (count (concat a b))
+     (+ (count a) (count b))))
+
+(deftest count-and-concat
+  (testing "For all vectors A and B:
+           length(A + B) == length(A) + length(B)"
+           (is (:result
+                 (let [v (gen/vector gen/int)]
+                   (sc/quick-check 1000 concat-counts-correct [v v]))))))
+
 ;; Tests are deterministic
 ;; ---------------------------------------------------------------------------


Pretty-print records

Pretty-print records

--- ClojureScript/replete/src/replete/pprint.cljs
+++ ClojureScript/replete/src/replete/pprint.cljs
@@ -1,6 +1,7 @@
 (ns replete.pprint
   (:refer-clojure :exclude [lift-ns])
-  (:require [fipp.visit :refer [visit visit*]]
+  (:require [clojure.string :as string]
+            [fipp.visit :refer [visit visit*]]
             [fipp.engine :refer (pprint-document)]
             [goog.object :as gobj]))

@@ -129,7 +130,9 @@
     [:text (pr-str x)])

   (visit-record [this x]
-    [:text (pr-str x)]))
+    (pretty-coll this (str "#" (string/replace (pr-str (type x)) #"/" ".") "{") x [:span "," :line] "}"
+      (fn [printer [k v]]
+        [:span (visit printer k) " " (visit printer v)]))))

 (defn pprint
   ([x] (pprint x {}))


Line numbering reader

Line numbering reader

--- src/aero/core.clj
+++ src/aero/core.clj
@@ -155,7 +155,12 @@
   ([source given-opts]
    (let [opts (merge default-opts given-opts {:source source})
          tag-fn (partial reader opts)
-         config (with-open [pr (-> source io/reader java.io.PushbackReader.)]
-                  (edn/read {:eof nil :default tag-fn} pr))]
+         config (with-open [pr (-> source io/reader clojure.lang.LineNumberingPushbackReader.)]
+                  (try
+                    (edn/read {:eof nil :default tag-fn} pr)
+                    (catch Exception e
+                      (let [line (.getLineNumber pr)]
+                        (throw (ex-info (format "Config error on line %s" line) {:line line}  e)))
+                      )))]
      (-> config (get-in-ref) (realize-deferreds))))
   ([source] (read-config source {})))


add *File* to defile

add *File* to defile

--- src/cake/core.clj
+++ src/cake/core.clj
@@ -86,6 +86,8 @@
         actions (vec (map #(list 'fn [] %) actions))]
     `(swap! tasks update '~name update-task '~deps '~doc ~actions)))

+(declare *File*)
+
 (defmacro defile
   "Define a file task. Uses the same syntax as deftask, however the task name
    is a string representing the name of the file to be generated by the body.
@@ -99,12 +101,13 @@
                       [#{} body])
         [files tasks] (for [pred [string? symbol?]]
                         (set (filter pred deps)))]
-    `(deftask ~name ~files
-       (let [f# (file ~name)]
-         (when (or (not (.exists f#))
-                   (seq (filter #(< (.lastModified f#) (.lastModified (file %))) ~files))
-                   (nil? (seq ~deps)))
-           ~@body)))))
+    `(deftask ~name ~tasks
+       (binding [*File* (file ~name)]
+         (let [f# (file ~name)]
+           (when (or (not (.exists f#))
+                     (seq (filter #(< (.lastModified f#) (.lastModified (file %))) ~files))
+                     (nil? (seq ~deps)))
+             ~@body))))))

 (defmacro undeftask [& names]
   `(swap! tasks dissoc ~@(map #(list 'quote %) names)))


CMR-2571: Removing test code.

CMR-2571: Removing test code.

--- search-app/src/cmr/search/data/elastic_search_index.clj
+++ search-app/src/cmr/search/data/elastic_search_index.clj
@@ -27,11 +27,6 @@
   [context]
   (let [fetched-index-set (index-set/get-index-set context index-set-id)]
     (get-in fetched-index-set [:index-set :concepts])))
-
-(def context
-  {:system (get-in user/system [:apps :search])})
-
-(fetch-concept-type-index-names context)

 (def index-cache-name
   "The name of the cache for caching index names. It will contain a map of concept type to a map of


Sanity check Twitter

Sanity check Twitter

--- src/clojush/problems/software/benchmarks_v2/twitter.clj
+++ src/clojush/problems/software/benchmarks_v2/twitter.clj
@@ -11,21 +11,20 @@

 ; Atom generators
 (def twitter-atom-generators
-  (concat (list
-            0
-            140
-            "Too many characters"
-            "You didn't type anything"
-            "Your tweet has "
-            " characters"
-            ;;; end constants
-            (tag-instruction-erc [:integer :boolean :exec] 1000)
-            (tagged-instruction-erc 1000)
-            ;;; end tag ERCs
-            'in1
-            ;;; end input instructions
-            )
-          (registered-for-stacks [:integer :boolean :exec :string :char])))
+  (make-proportional-atom-generators
+   (concat
+    (registered-for-stacks [:integer :boolean :exec :string :char])
+    (list (tag-instruction-erc [:integer :boolean :exec :string :char] 1000) ; tags
+          (tagged-instruction-erc 1000)))
+   (list 'in1) ; inputs
+   (list 0
+         140
+         "Too many characters"
+         "You didn't type anything"
+         "Your tweet has "
+         " characters") ; constants
+   {:proportion-inputs 0.15
+    :proportion-constants 0.05}))

 ;; Define test cases
 (defn twitter-input
@@ -137,7 +136,7 @@
 ; Define the argmap
 (def argmap
   {:error-function (make-twitter-error-function-from-cases (first twitter-train-and-test-cases)
-                                                                  (second twitter-train-and-test-cases))
+                                                           (second twitter-train-and-test-cases))
    :atom-generators twitter-atom-generators
    :max-points 2000
    :max-genome-size-in-initial-program 250


transit conversion not working with advanced compilation

--- examples/osm/src/core.cljs
+++ examples/osm/src/core.cljs
@@ -8,7 +8,7 @@
             [hatti.ona.post-process :refer [integrate-osm-data!]]
             [hatti.ona.urls :as ona-urls]
             [hatti.shared :as shared]
-            [hatti.utils :refer [json->cljs url]]
+            [hatti.utils :refer [json->js url]]
             [hatti.views :as views]))

 (enable-console-print!)
@@ -47,7 +47,7 @@
  (let [data-chan (raw-get "data/31066_data.json")
        osm-chan (raw-get "data/31066_data.osm")
        form-chan (http/get "data/31066_form.json")
-       data (-> (<! data-chan) :body json->cljs)
+       data (-> (<! data-chan) :body json->js js->clj)
        form (-> (<! form-chan) :body flatten-form)
        osm-xml (-> (<! osm-chan) :body)]
    (shared/update-app-data! shared/app-state data :rerank? true)


Adds Java interop and changes remove to checking set

--- 06-code-golf/tf-06.clj
+++ 06-code-golf/tf-06.clj
@@ -1,11 +1,9 @@
 ":";exec java -cp "$HOME/.m2/repository/org/clojure/clojure/1.5.1/clojure-1.5.1.jar" clojure.main $0 $*
-
-(require '[clojure.string :as s])

 (doseq [c (take 25
             (sort-by val >
               (frequencies
                 (remove
-                  #(some (partial = %) (s/split (slurp "../stop_words.txt") #","))
-                  (re-seq #"[a-z]{2,}" (s/lower-case (slurp (first *command-line-args*))))))))]
+                  #(contains? (set (.split (slurp "../stop_words.txt") ",")) %)
+                  (re-seq #"[a-z]{2,}" (.toLowerCase (slurp (first *command-line-args*))))))))]
   (printf "%s - %d\n" (first c) (nth c 1)))


CMR-1885 Modified all-umm-records test to all-umm-c-records test.

--- umm-spec-lib/test/cmr/umm_spec/test/umm_json.clj
+++ umm-spec-lib/test/cmr/umm_spec/test/umm_json.clj
@@ -65,12 +65,12 @@
           parsed (uj/json->umm js/umm-c-schema json)]
       (is (= minimal-example-umm-c-record parsed)))))

-(defspec all-umm-records 100
-  (for-all [umm-record umm-gen/umm-c-generator]
-    (let [json (uj/umm->json umm-record)
+(defspec all-umm-c-records 100
+  (for-all [umm-c-record umm-gen/umm-c-generator]
+    (let [json (uj/umm->json umm-c-record)
           _ (is (empty? (js/validate-umm-json json)))
           parsed (uj/json->umm js/umm-c-schema json)]
-      (is (= umm-record parsed)))))
+      (is (= umm-c-record parsed)))))

 (comment


styling of admin page with bootstrap 3 styles

--- src/foosball/views/admin.clj
+++ src/foosball/views/admin.clj
@@ -7,28 +7,30 @@
         [taoensso.timbre :only [trace debug info warn error fatal spy]]))

 (defn- player-select [action-uri header-label button-label class players]
-  [:form {:action action-uri :method "POST"}
-    [:div.input-append
-     [:h1 header-label]
-     [:select.col-lg-3 {:name "playerid"}
+  [:form.form-inline {:action action-uri :method "POST"}
+   [:h1 header-label]
+   [:div.form-group.col-lg-6
+     [:select.form-control {:name "playerid"}
       [:option "Player"]
       (->> players
-           (map (fn [{:keys [id name]}] [:option {:value id} name])))]
-     [:button.btn {:class class :type "submit" :value "value"} button-label]]])
+           (map (fn [{:keys [id name]}] [:option {:value id} name])))]]
+   [:div.form-group.col-lg-6
+    [:button.btn {:class class :type "submit" :value "value"} button-label]]])

 (defn form [players matches]
   (html5
-   [:form {:action "/admin/player/add" :method "POST"}
-    [:div.input-append
+   [:div
+    [:form.form-inline {:action "/admin/player/add" :method "POST"}
      [:h1 "Add player"]
-     [:input.col-lg-3 {:type "text" :name "playername" :placeholder "New Player"}]
-     [:button.btn.btn-primary {:type "submit" :value "Report"} "Add!"]]]
+     [:div.form-group.col-lg-6
+      [:input.form-control.col-lg-3 {:type "text" :name "playername" :placeholder "New Player"}]]
+     [:div.form-group.col-lg-6
+      [:button.btn.btn-primary {:type "submit" :value "Report"} "Add!"]]]

-   (player-select "/admin/player/activate" "Activate player" "Set active" "btn-success"
-                  (filter (comp not :active) players))
+    (player-select "/admin/player/activate" "Activate player" "Set active" "btn-success"
+                   (filter (comp not :active) players))
+    (player-select "/admin/player/deactivate" "Deactivate player" "Set inactive" "btn-danger"
+                   (filter :active players))

-   (player-select "/admin/player/deactivate" "Deactivate player" "Set inactive" "btn-danger"
-                  (filter :active players))
-
-   [:form {:action "/admin/match/remove" :method "POST"}
-    (match-table-data matches players :admin true)]))
+    [:form {:action "/admin/match/remove" :method "POST"}
+     (match-table-data matches players :admin true)]]))