refactored creation of a new post + redirection to the location

--- instapapure.clj
+++ instapapure.clj
@@ -15,18 +15,31 @@
     (html
       [:strong "Pong"])))

-(defn post [location title]
-  (layout (str "Posted " title)
-    (html
-      [:a {:href location} title])))
+(defn new-post [location title]
+  ;; save the page to read it later
+  (redirect-to location))

+(defn create-post []
+  (layout
+    "Save a page"
+    (form-to [:post "/post"]
+      [:label "Location:"]
+      [:input {:type "text" :name "l"}]
+      [:br]
+      [:label "Title:"]
+      [:input {:type "text" :name "t"}]
+      [:br]
+      (submit-button "save"))))
+
 (defroutes instapapure-app
    (GET "/public/*"
      (or (serve-file (params :*)) :next))
    (GET "/ping"
      (ping))
+   (GET "/post"
+       (create-post))
    (POST "/post"
-       (post (:l params) (:t params)))
+       (new-post (:l params) (:t params)))
    (ANY "*"
      (page-not-found)))


Accidentally committed in-progress sequencer. Rolling back to previous version.

--- src/overtone/studio/sequencer.clj
+++ src/overtone/studio/sequencer.clj
@@ -1,100 +1,47 @@
 (ns overtone.studio.sequencer
-  (:use [overtone.live]))
+  (:use [overtone.util lib]
+        [overtone.music time]
+        [overtone.sc server node]
+        [overtone.studio rig]))

-(defn- play-sample
-  [samp time vol]
-  (at time (stereo-player samp :vol vol)))
+(defn hit-fn
+  "A function that takes a ref (something dereferencable) and returns
+  a function that will act as a track player.

-(defn determine-time
- [onset-time b-idx beat-dur num-beats]
- (+ onset-time (* b-idx beat-dur)))
+  The track player fn takes a metronome and an instrument, and it will
+  loop forever generating notes by reading from the ref and playing the
+  instrument, passing the val in ref as the first argument."
+  [hit-ref]
+  ; Play a full bar at a time
+  (fn hit-fn [m beat ins]
+    (let [next-bar @hit-ref
+          hit-cnt (count next-bar)
+          next-beat (+ beat hit-cnt)
+          next-tick (m next-beat)]
+      (doall
+        (map-indexed
+          #(when (and (playing?) %2)
+             (at (m (+ beat %1))
+               (ins %2)))
+          next-bar))
+      (apply-at next-tick hit-fn [m next-beat ins]))))

-(defn- schedule-all-beats
-  [bar samp onset-time bar-dur]
-  (let [num-beats (count bar)
-        beat-dur (/ bar-dur num-beats)]
-    (doall
-     (map-indexed (fn [idx beat]
-                    (cond
-                     (= true beat)
-                     (play-sample samp (determine-time onset-time idx beat-dur num-beats) 1)
-
-                     (number? beat)
-                     (play-sample samp (determine-time onset-time idx beat-dur num-beats) (/ beat 10))
-
-                     (sequential? beat)
-                     (schedule-all-beats beat
-                                         samp
-                                         (determine-time onset-time idx beat-dur num-beats)
-                                         beat-dur)))
-                  bar))))
-
-(defn play-rhythm
-  ([patterns* bar-dur*] (play-rhythm patterns* bar-dur* (+ 500 (now)) 0))
-  ([patterns* bar-dur* start-time beat-num]
-     (let [patterns @patterns*
-           bar-dur @bar-dur*]
-       (doall
-        (map (fn [[key [samp pat]]]
-               (let [idx (mod beat-num (count pat))]
-                 (schedule-all-beats (nth pat idx) samp start-time bar-dur)))
-             patterns))
-       (apply-at (+ start-time bar-dur) #'play-rhythm [patterns*
-                                                       bar-dur*
-                                                       (+ start-time bar-dur)
-                                                       (inc beat-num)]))))
-
-(def piano-samples (load-samples "~/Desktop/samples/MIS_Stereo_Piano/Piano/*LOUD*"))
-(def ice-samples (load-samples "~/Desktop/samples/1083-7_Bram_breaking_ice/*.wav"))
-(def p (nth piano-samples 18))
-(def s (nth piano-samples 20))
-(def t (nth piano-samples 21))
-
-(stereo-player t :vol 0.4)
-
-(def bar-dur (atom 1000))
-
-
-(def _ false)
-(def X true)
-
-(def patterns* (atom   {:low-piano [p [[_]]]
-                        :hi-piano  [s [[_]]]
-                        :ice1 [(nth ice-samples 1) [[_]]]
-                        :ice2 [(nth ice-samples 2) [[_]]]
-                        :ice3 [(nth ice-samples 3) [[_]]]
-                        :ice4 [(nth ice-samples 4) [[_]]]
-                        :ice5 [(nth ice-samples 5) [[_]]]}))
-
-(play-rhythm patterns* bar-dur)
-
-(defn update-pat!
-  [key pat]
-  (swap! patterns* (fn [patterns key new-pat]
-                     (let [[samp pat] (get patterns key)]
-                       (assoc patterns key [samp new-pat])))
-         key pat))
-
-(update-pat! :low-piano [[_]])
-(update-pat! :hi-piano  [[_] [_]])
-(update-pat! :ice1  [[X] [_] [X [_ X] X]])
-(update-pat! :ice2  [[X] [X]])
-(update-pat! :ice3  [[X] [_ [X _]]])
-(update-pat! :ice4  [[X] [_ [_ X]]])
-(update-pat! :ice5  [[X] [_] [_ X _]])
-
-;;(stop)
-
-(def server (osc-server 7800))
-
-(defn- handle-msg
-  [samp-name pat]
-  (let [pat (read-string pat)]
-    (println "samp-name: " samp-name ", pat: " pat ", count: " (count pat))
-    (update-pat! (keyword samp-name) pat)))
-
-(osc-handle server "/new-pat" (fn [msg]
-                                (apply #'handle-msg [(first (:args msg)) (second (:args msg))])))
-
-(def cl (osc-client "localhost" 7800))
-(osc-send cl "/new-pat" "hi-piano" (prn-str [[2 5 7 1]]))
+(defn mono-play-fn
+  ""
+  [note-ref]
+  (fn play-fn [m beat ins]
+    (let [next-bar @note-ref
+          hit-cnt (count next-bar)
+          next-beat (+ beat hit-cnt)
+          next-tick (m next-beat)]
+      (doall
+        (map-indexed
+          #(when (and (playing?) %2)
+             (let [b (+ beat %1)
+                   id (at (m b)
+                          (ins %2))]
+               (when (some #{"gate"} (:args ins))
+                 (at (m (inc b))
+                     (ctl id :gate 0)))))
+          next-bar))
+      (apply-at next-tick play-fn [m next-beat ins]))))


the command stored inside an observable instance doesn't need to be a ref, it can just be a string

--- src/subrosa/observable.clj
+++ src/subrosa/observable.clj
@@ -9,11 +9,11 @@
                          void]]))

 (defn -init [string]
-  [[] (ref string)])
+  [[] string])

 (defn -ENGAGENOTIFICATIONSIMMEDIATELY [this channel args]
   (.setChanged this)
-  (.notifyObservers this [@(.state this) channel args]))
+  (.notifyObservers this [(.state this) channel args]))

 (def get-observable
   (memoize (fn [cmd] (subrosa.observable. cmd))))
@@ -22,7 +22,7 @@
   (let [observable (get-observable (.toLowerCase (str cmd)))
         observer (reify java.util.Observer
                    (update [this observable args]
-                           (apply fn args)))]
+                     (apply fn args)))]
     (.addObserver observable observer)
     observer))


Use local Slimer to run on CircleCI

Use local Slimer to run on CircleCI

--- library/test/clj/test/doo/core.clj
+++ library/test/clj/test/doo/core.clj
@@ -91,7 +91,8 @@
 (deftest integration
   (testing "We can compile a cljs project"
     (let [doo-opts {:verbose false
-                    :paths {:karma "karma"}}
+                    :paths {:karma "karma"
+                            :slimer "../example/node_modules/.bin/slimerjs"}}
           compiler-opts {:output-to "out/testable.js"
                          :output-dir "out"
                          :main 'example.runner


removed srcdev

removed srcdev

--- srcdev/webapp_config/settings.clj
+++ srcdev/webapp_config/settings.clj
@@ -1,26 +0,0 @@
-(ns webapp-config.settings)
-
-
-
-
-
-
-(defonce ^:dynamic *mandrill-api-key* "enter_api_key_for_mandrill_here")
-
-(defonce ^:dynamic *environment* "base")
-
-(defonce ^:dynamic *web-server* "127.0.0.1:3000")
-
-(defonce ^:dynamic *database-server* "127.0.0.1")
-
-(defonce ^:dynamic *database-user* "postgres")
-
-(defonce ^:dynamic *database-password* "manager")
-
-(defonce ^:dynamic *database-name* "webdb")
-
-(defonce ^:dynamic *sql-encryption-password* "animal")
-
-(defonce ^:dynamic *show-code* true)
-
-(defonce ^:dynamic *main-page* "connecttous.html")


lein-release plugin: preparing 0.4.6 release

--- re_view/project.clj
+++ re_view/project.clj
@@ -1,4 +1,4 @@
-(defproject re-view "0.4.6-SNAPSHOT"
+(defproject re-view "0.4.6"
   :description "ClojureScript React Library"
   :url "https://www.github.com/braintripping/re-view/tree/master/re_view"
   :license {:name "Eclipse Public License"


Add update-shape event just for shape inplace update.

--- src/uxbox/data/shapes.cljs
+++ src/uxbox/data/shapes.cljs
@@ -94,6 +94,14 @@
       (let [shape (get-in state [:shapes-by-id id])]
         (stsh/dissoc-shape state shape)))))

+(defn update-shape
+  "Just updates in place the shape."
+  [{:keys [id] :as shape}]
+  (reify
+    rs/UpdateEvent
+    (-apply-update [_ state]
+      (update-in state [:shapes-by-id id] merge shape))))
+
 (defn move-shape
   "Mark a shape selected for drawing in the canvas."
   [sid delta]


more useful plugins

more useful plugins

--- profiles.clj
+++ profiles.clj
@@ -5,6 +5,9 @@
              ;[lein-light "0.0.4"]
              [lein-deps-tree "0.1.2"]
              [lein-difftest "2.0.0"]
+             [lein-grep "0.1.0"]
+             [lein-open "0.1.0"]
+             [lein-outdated "1.0.0"]
              ]
    :repl-options {
      :init (do
@@ -13,7 +16,8 @@
        (use 'rubydoc.core)
        (use '[clojure.repl :only (dir-fn)])
        (load-file (str (System/getProperty "user.home") "/.lein/user.clj"))) }
-   :dependencies [[table "0.3.2"] [desc "0.1.0"]
+   :dependencies [[table "0.3.2"]
+                  [desc "0.1.0"]
                   [rubydoc "0.3.0"]]
  }
 }


Add helpers for the reader and int parsing.


Version 0.2.1

Version 0.2.1

--- project.clj
+++ project.clj
@@ -1,4 +1,4 @@
-(defproject clojure-humanize "0.2.1-SNAPSHOT"
+(defproject clojure-humanize "0.2.1"
   :description "produce human readable strings in clojure"
   :url "https://github.com/trhura/clojure-humanize"
   :license {:name "Eclipse Public License"


Upgraded polymer

Upgraded polymer

--- project.clj
+++ project.clj
@@ -30,12 +30,12 @@
             [codox "0.8.10"]
             [lein-ns-dep-graph "0.1.0-SNAPSHOT"]]

-  :bower-dependencies [[polymer "~1.0.4"]
-                       [paper-styles "PolymerElements/paper-styles#~1.0.7"]
-                       [paper-header-panel "PolymerElements/paper-header-panel#~1.0.2"]
-                       [paper-toolbar "PolymerElements/paper-toolbar#~1.0.2"]
-                       [paper-button "PolymerElements/paper-button#~1.0.1"]
-                       [paper-tabs "PolymerElements/paper-tabs#~1.0.0"]]
+  :bower-dependencies [[polymer "1.4.0"]
+                       [paper-styles "PolymerElements/paper-styles#1.1.4"]
+                       [paper-header-panel "PolymerElements/paper-header-panel#1.1.5"]
+                       [paper-toolbar "PolymerElements/paper-toolbar#1.1.4"]
+                       [paper-button "PolymerElements/paper-button#1.0.11"]
+                       [paper-tabs "PolymerElements/paper-tabs#1.5.0"]]

   :bower {:directory "resources/public/components"}


Fixed name of project.

Fixed name of project.

--- project.clj
+++ project.clj
@@ -1,4 +1,4 @@
-(defproject before "0.1.0-SNAPSHOT"
+(defproject overtunes "0.1.0-SNAPSHOT"
   :description "Experiments with Overtone."
   :dependencies	[
     [org.clojure/clojure "1.3.0"]


Version 0.34.2

Version 0.34.2

--- project.clj
+++ project.clj
@@ -1,4 +1,4 @@
-(defproject nhl-score-api "0.34.1"
+(defproject nhl-score-api "0.34.2"
   :description "A JSON API that returns the scores and goals from the latest finished NHL games."
   :license {:name "MIT"
             :url "https://opensource.org/licenses/MIT"}


Opt out external javascript libraries warnings during uberjar build

--- project.clj
+++ project.clj
@@ -79,6 +79,7 @@
                            :asset-path "js/compiled1/out"
                            :source-map-timestamp false
                            :optimizations :advanced
+                           :closure-warnings {:externs-validation :off}
                            :externs [
                              "cljsjs/common/bootstrap.ext.js"
                              "cljsjs/common/jquery-timepicker.ext.js"


elastisch 2.2.0-beta4

elastisch 2.2.0-beta4

--- project.clj
+++ project.clj
@@ -12,7 +12,7 @@
                  [net.jpountz.lz4/lz4 "1.3.0"]
                  [org.xerial.snappy/snappy-java "1.1.1.7"]
                  [org.clojure/core.async "0.1.346.0-17112a-alpha"]
-                 [clojurewerkz/elastisch "2.1.0"]
+                 [clojurewerkz/elastisch "2.2.0-beta4"]
                  [throttler "1.0.0"]
                  [com.climate/claypoole "1.0.0"]
                  [clj-time "0.10.0"]


Upgrade Apache Storm to 1.2.1

Upgrade Apache Storm to 1.2.1

--- project.clj
+++ project.clj
@@ -3,8 +3,8 @@
   :target-path "_build"
   :min-lein-version "2.0.0"
   :jvm-opts ["-client"]
-  :dependencies  [[org.apache.storm/storm-core "1.1.1"]
-                  [org.apache.storm/flux-core "1.1.1"]]
+  :dependencies  [[org.apache.storm/storm-core "1.2.1"]
+                  [org.apache.storm/flux-core "1.2.1"]]
   :jar-exclusions     [#"log4j\.properties" #"org\.apache\.storm\.(?!flux)" #"trident" #"META-INF" #"meta-inf" #"\.yaml"]
   :uberjar-exclusions [#"log4j\.properties" #"org\.apache\.storm\.(?!flux)" #"trident" #"META-INF" #"meta-inf" #"\.yaml"]
   )


Update cljs compiler version.

Update cljs compiler version.

--- project.clj
+++ project.clj
@@ -4,7 +4,7 @@
   :license {:name "Public Domain" :url "http://unlicense.org/"}

   :dependencies [[org.clojure/clojure "1.8.0" :scope "provided"]
-                 [org.clojure/clojurescript "1.8.34" :scope "provided"]
+                 [org.clojure/clojurescript "1.9.89" :scope "provided"]
                  [org.clojure/test.check "0.9.0" :scope "test"]]

   :deploy-repositories {"releases" :clojars


Upgrade to latest reagent

Upgrade to latest reagent

--- project.clj
+++ project.clj
@@ -7,7 +7,7 @@
   :dependencies [[org.clojure/clojure "1.8.0"]
                  [org.clojure/clojurescript "1.9.473"]
                  [org.clojure/core.async "0.2.374"]
-                 [reagent "0.6.0"]
+                 [reagent "0.6.2"]
                  [devcards "0.2.2" :exclusions [cljsjs/react]]
                  [org.clojure/test.check "0.9.0"]
                  [org.clojure/core.match "0.3.0-alpha4"]


Release version 0.8.8.2.

Release version 0.8.8.2.

--- project.clj
+++ project.clj
@@ -1,4 +1,4 @@
-(defproject onyx-app/lein-template "0.8.8.2-SNAPSHOT"
+(defproject onyx-app/lein-template "0.8.8.2"
   :description "Onyx Leiningen application template"
   :url "https://github.com/onyx-platform/onyx-template"
   :license {:name "Eclipse Public License"


move to instaparse 1.3.6

move to instaparse 1.3.6

--- project.clj
+++ project.clj
@@ -3,6 +3,6 @@
   :dependencies [[org.clojure/clojure "1.6.0"]
                  [com.google.code.javaparser/javaparser "1.0.11"]
                  [org.clojure/tools.cli "0.3.1"]
-                 [instaparse "1.3.3"]]
+                 [instaparse "1.3.6"]]
   :resource-paths ["test-resources"]
   :main app.core)


fix infinite nils error

fix infinite nils error

--- src/cake/core.clj
+++ src/cake/core.clj
@@ -168,7 +168,7 @@
         (.write (prn-str [ns ns-forms body] *vars*))
         (.flush))
       (loop [line (.readLine reader)]
-        (when-not (= ":bake.core/result" line)
+        (when-not (or (nil? line) (= ":bake.core/result" line))
           (println line)
           (recur (.readLine reader))))
       (let [result (read-string (.readLine reader))]


mk lib dir if it does not exist when fetching deps

--- src/cake/deps.clj
+++ src/cake/deps.clj
@@ -1,6 +1,6 @@
 (ns cake.deps
   (:use cake uncle.core
-        [cake.file :only [with-root file-exists? rmdir mv file]]
+        [cake.file :only [with-root file-exists? rmdir mv file parent mkdir]]
         [cake.utils :only [cake-exec]]
         [bake.core :only [log os-name os-arch]]
         [clojure.java.shell :only [sh]]
@@ -112,5 +112,6 @@
                    (map-to fetch-deps (keys dep-types)))
                  #(vec (map (memfn getPath) %))))
         (extract-native! lib)
+        (mkdir (parent cache))
         (spit cache (pr-str [(:dependencies *project*) @dep-jars]))))
     overwrite?))


Added REST API for cache management.

Added REST API for cache management.

--- dev-resources/src/cmr/opendap/dev.clj
+++ dev-resources/src/cmr/opendap/dev.clj
@@ -1,6 +1,7 @@
 (ns cmr.opendap.dev
   "CMR OPeNDAP development namespace."
   (:require
+    [cheshire.core :as json]
     [clojure.java.io :as io]
     [clojure.pprint :refer [pprint]]
     [clojure.set :as set]


Updated TOOD comments.

Updated TOOD comments.

--- src/cbfg/kvs.cljs
+++ src/cbfg/kvs.cljs
@@ -4,21 +4,23 @@

 ; A kvs is an abstraction of ordered-KV store.  Concrete examples
 ; would include couchkvs, forestDB, rocksDB, sqlite, bdb, gklite.
-; The mapping is ordered key => sq => item, and ordered sq => item.
+; The mapping is ordered-key => sq => item, and ordered-sq => item.
+;
 ; Abstract operations include...
 ; - multi-get
-; - multi-change (a change is an upsert or delete).
+; - multi-change (a change is an upsert or delete)
 ; - scan-keys
 ; - scan-changes
-; - fsync
-; - stats
-; - has compaction/maintenance abstraction
+; - snapshot
+; - sync (like fsync)
 ;
-; Note that we always close the res-ch.
+; Note on res-ch: the kvs always closes the res-ch on request completion.

 ; TODO: Track age, utilization, compaction for simulated performance.
+; TODO: Compaction/maintenance/re-org/vaccuum abstraction.
 ; TODO: Readers not blocked by writers.
 ; TODO: Mutations shadow old values.
+; TODO: Simulated slow-disks, full-disks, etc.

 (defn make-kc [] ; A kc is a keys & changes map.
   {:keys (sorted-map)      ; key -> sq


Provide util fns for port, context and uri; handy for testing

--- namespaces/common/src/main/clojure/immutant/util.clj
+++ namespaces/common/src/main/clojure/immutant/util.clj
@@ -77,6 +77,22 @@
   unsecure-interface-address
   (partial lookup-interface-address "unsecure"))

+(defn http-port
+  "Returns the HTTP port for the embedded web server"
+  []
+  (.getPort (registry/get "jboss.web.connector.http")))
+
+(defn context-path
+  "Returns the HTTP context path for the deployed app"
+  []
+  (.getName (immutant.registry/get "web-context")))
+
+(defn app-uri
+  "Returns the base URI for the app, given a host [localhost]"
+  [& [host]]
+  (let [host (or host "localhost")]
+    (str "http://" host ":" (http-port) (context-path))))
+
 (defn try-resolve
   "Tries to resolve the given namespace-qualified symbol"
   [sym]


Analytics.

Analytics.

--- src/tryclojure/views/home.clj
+++ src/tryclojure/views/home.clj
@@ -79,5 +79,15 @@
            code is on github.
           "]
          ]]
-       [:div.footer "by mary rose cook"]]]]]))
+       [:div.footer "by mary rose cook"]
+       (javascript-tag
+        (str "
+  var _gaq = _gaq || [];
+  _gaq.push(['_setAccount', 'UA-24453347-2']);
+  _gaq.push(['_trackPageview']);

+  (function() {
+    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+  })();"))]]]]))


Create zip file in a temp directory

Create zip file in a temp directory

Fixes the problem where a zip file can carry over between processing
runs of feeds for the same election and causing problems when trying to
recreate the zip file.

--- src/vip/data_processor/s3.clj
+++ src/vip/data_processor/s3.clj
@@ -75,8 +75,10 @@
   "Uploads the generated xml file to the specified S3 bucket."
   [{:keys [xml-output-file] :as ctx}]
   (let [zip-name (str (zip-filename ctx) ".zip")
-        zip-file (File. zip-name)
-        zip (ZipFile. zip-name)
+        zip-dir (Files/createTempDirectory tmp-path-prefix
+                                           (into-array FileAttribute []))
+        zip-file (File. (.toFile zip-dir) zip-name)
+        zip (ZipFile. zip-file)
         zip-params (doto (ZipParameters.)
                      (.setCompressionLevel
                       Zip4jConstants/DEFLATE_LEVEL_NORMAL))


chart height - time to generalize it

chart height - time to generalize it

--- iwaswhere-web/src/cljs/iwaswhere_web/ui/charts/custom_fields.cljs
+++ iwaswhere-web/src/cljs/iwaswhere_web/ui/charts/custom_fields.cljs
@@ -110,7 +110,7 @@
       (let [indexed (map-indexed (fn [idx [k v]] [idx v]) stats)
             charts (:custom-field-charts options)
             chart-h1 (apply + (map (fn [[k cfg]] (:chart-h cfg)) charts))
-            chart-h 950]
+            chart-h 990]
         (prn chart-h1)
         [:div
          [:svg


use run! instead of did-mount so search re-runs

--- src/braid/ui/views/pages/search.cljs
+++ src/braid/ui/views/pages/search.cljs
@@ -1,6 +1,6 @@
 (ns braid.ui.views.pages.search
   (:require [reagent.core :as r]
-            [reagent.ratom :include-macros true :refer-macros [reaction]]
+            [reagent.ratom :include-macros true :refer-macros [reaction run!]]
             [braid.ui.views.thread :refer [thread-view]]
             [chat.client.dispatcher :refer [dispatch!]]
             [chat.client.reagent-adapter :refer [subscribe]]
@@ -14,75 +14,70 @@
         page (subscribe [:page])
         threads (subscribe [:threads])
         query (subscribe [:search-query])
-        group-id (subscribe [:open-group-id])]
-    (r/create-class
-      {:display-name "search-results-page"
-       :component-did-mount
-       (fn [c]
-         (dispatch! :search-history [@query @group-id]))
-       :reagent-render
-       (fn []
-         (let [status (cond
-                        @loading? :loading
-                        (not (contains? @page :thread-ids)) :searching
-                        (seq (@page :thread-ids)) :done-results
-                        :else :done-empty)]
-           [:div.page.search
-            [:div.title (str "Search for \"" @query "\"")]
-            (case status
-              :searching
-              [:div.content
-               [:div.description
-                "Searching..."]]
+        group-id (subscribe [:open-group-id])
+        do-search (run! (dispatch! :search-history [@query @group-id]))]
+    (fn []
+      (let [status (cond
+                     @loading? :loading
+                     (not (contains? @page :thread-ids)) :searching
+                     (seq (@page :thread-ids)) :done-results
+                     :else :done-empty)
+            _ @do-search]
+        [:div.page.search
+         [:div.title (str "Search for \"" @query "\"")]
+         (case status
+           :searching
+           [:div.content
+            [:div.description
+             "Searching..."]]

-              (:done-results :loading)
-              (let [loaded-threads (reaction (vals (select-keys @threads (@page :thread-ids))))
-                    sorted-threads (reaction (->> @loaded-threads ; sort-by last reply, newest first
-                                                  (sort-by
-                                                    (comp (partial apply max)
-                                                          (partial map :created-at)
-                                                          :messages))
-                                                  reverse))]
-                [:div.content
-                 [:div.description
-                  (if (= status :loading)
-                    "Loading more results..."
-                    (str "Displaying " (count @loaded-threads) "/" (count (@page :thread-ids))))]
-                 [:div.threads
-                  {:ref "threads-div"
-                   :on-scroll ; page in more results as the user scrolls
-                   (fn [e]
-                     (let [div (.. e -target)]
-                       (when (and (= status :done-results)
-                               (< (count @loaded-threads) (count (@page :thread-ids)))
-                               (> 100 (- (.-scrollWidth div)
-                                         (+ (.-scrollLeft div) (.-offsetWidth div)))))
-                         (start-loading!)
-                         (let [already-have (set (map :id @loaded-threads))
-                               to-load (->> (@page :thread-ids)
-                                            (remove already-have)
-                                            (take 25))]
-                           (dispatch! :load-threads
-                                      {:thread-ids to-load
-                                       :on-complete
-                                       (fn []
-                                         (stop-loading!))})))))
-                   :on-wheel ; make the mouse wheel scroll horizontally
-                   (fn [e]
-                     (let [target-classes (.. e -target -classList)
-                           this-elt (.. e -target)]
-                       ; TODO: check if threads-div needs to scroll?
-                       (when (and (or (.contains target-classes "thread")
-                                      (.contains target-classes "threads"))
-                               (= 0 (.-deltaX e) (.-deltaZ e)))
-                         (set! (.-scrollLeft this-elt)
-                               (- (.-scrollLeft this-elt) (.-deltaY e))))))}
-                  (doall
-                    (for [thread @sorted-threads]
-                      ^{:key (:id thread)}
-                      [thread-view thread]))]])
+           (:done-results :loading)
+           (let [loaded-threads (reaction (vals (select-keys @threads (@page :thread-ids))))
+                 sorted-threads (reaction (->> @loaded-threads ; sort-by last reply, newest first
+                                               (sort-by
+                                                 (comp (partial apply max)
+                                                       (partial map :created-at)
+                                                       :messages))
+                                               reverse))]
+             [:div.content
+              [:div.description
+               (if (= status :loading)
+                 "Loading more results..."
+                 (str "Displaying " (count @loaded-threads) "/" (count (@page :thread-ids))))]
+              [:div.threads
+               {:ref "threads-div"
+                :on-scroll ; page in more results as the user scrolls
+                (fn [e]
+                  (let [div (.. e -target)]
+                    (when (and (= status :done-results)
+                            (< (count @loaded-threads) (count (@page :thread-ids)))
+                            (> 100 (- (.-scrollWidth div)
+                                      (+ (.-scrollLeft div) (.-offsetWidth div)))))
+                      (start-loading!)
+                      (let [already-have (set (map :id @loaded-threads))
+                            to-load (->> (@page :thread-ids)
+                                         (remove already-have)
+                                         (take 25))]
+                        (dispatch! :load-threads
+                                   {:thread-ids to-load
+                                    :on-complete
+                                    (fn []
+                                      (stop-loading!))})))))
+                :on-wheel ; make the mouse wheel scroll horizontally
+                (fn [e]
+                  (let [target-classes (.. e -target -classList)
+                        this-elt (.. e -target)]
+                    ; TODO: check if threads-div needs to scroll?
+                    (when (and (or (.contains target-classes "thread")
+                                   (.contains target-classes "threads"))
+                            (= 0 (.-deltaX e) (.-deltaZ e)))
+                      (set! (.-scrollLeft this-elt)
+                            (- (.-scrollLeft this-elt) (.-deltaY e))))))}
+               (doall
+                 (for [thread @sorted-threads]
+                   ^{:key (:id thread)}
+                   [thread-view thread]))]])

-              :done-empty
-              [:div.content
-               [:div.description
-                "No results."]])]))})))
+           :done-empty
+           [:div.content
+            [:div.description "No results."]])]))))


Robin/Stef Basic test

Robin/Stef Basic test

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

-(deftest a-test
-  (testing "FIXME, I fail."
-    (is (= 0 1))))
+(deftest shouldReturn200
+  (let [response (home {})]
+    (testing "success"
+      (is (= 200 (:status response))))))
+


Small fix

Small fix

--- src/cljs/salava/oauth/ui/routes.cljs
+++ src/cljs/salava/oauth/ui/routes.cljs
@@ -3,7 +3,8 @@
             [salava.core.ui.helper :refer [base-path]]
             [salava.oauth.ui.status :as oauth-status]
             [salava.oauth.ui.block]
-            [reagent.session :as session]))
+            [reagent.session :as session]
+            [clojure.string :refer [blank?]]))

 (defn ^:export routes [context]
   {(str (base-path context) "/user") [[["/oauth/" :service] oauth-status/handler]]})


added sorting to matches panel

added sorting to matches panel

--- src/cljs_bjj_tournament/matches.cljs
+++ src/cljs_bjj_tournament/matches.cljs
@@ -7,9 +7,11 @@
                                  selection-list
                                  h-box
                                  title
+                                 label
                                  line
                                  v-box
-                                 hyperlink-href]]))
+                                 hyperlink-href
+                                 radio-button]]))
 (defn match-panel
   []
   (let [matches (subscribe [:matches])
@@ -18,6 +20,7 @@
         choice1 (atom #{})
         choice2 (atom #{})
         division (atom #{(first (vals @divisions))})
+        sort-button (atom :name)
         match-link (fn
                      [m]
                      (let [p1 (@competitors (first m))
@@ -53,15 +56,42 @@
          :label "Create a match"
          :level :level2]
         [h-box
+         :gap "5px"
+         :children
+         [[title
+           :label "Sort by:"
+           :level :level3]
+          [radio-button
+           :model @sort-button
+           :value :name
+           :on-change #(reset! sort-button :name)]
+          [label
+           :label "Name"]
+          [radio-button
+           :model @sort-button
+           :value :yob
+           :on-change #(reset! sort-button :yob)]
+          [label
+           :label "Age"]
+          [radio-button
+           :model @sort-button
+           :value :weight
+           :on-change #(reset! sort-button :weight)]
+          [label
+           :label "Weight"]]]
+        [h-box
          :gap "10px"
          :children
          (let [filter-fn (if (nil? (first @division))
                            (constantly true)
-                           (:filter-fn (first @division)))]
+                           (:filter-fn (first @division)))
+               sort-fn (case @sort-button
+                         :name #(.full-name %)
+                         #(- (float (@sort-button %))))]
            [[selection-list
              :model @choice1
              :on-change #(reset! choice1 %)
-             :choices  (sort-by #(.full-name %)
+             :choices  (sort-by sort-fn
                                 (filter filter-fn
                                         (vals @competitors)))
              :label-fn #(.full-name-club %)
@@ -69,7 +99,7 @@
             [selection-list
              :model @choice2
              :on-change #(reset! choice2 %)
-             :choices (sort-by #(.full-name %)
+             :choices (sort-by sort-fn
                                (filter filter-fn
                                        (vals @competitors)))
              :label-fn #(.full-name-club %)


adds multiple service streaming test (#1053)

--- waiter/integration/waiter/grpc_test.clj
+++ waiter/integration/waiter/grpc_test.clj
@@ -20,6 +20,7 @@
             [clojure.test :refer :all]
             [clojure.tools.logging :as log]
             [clojure.walk :as walk]
+            [plumbing.core :as pc]
             [waiter.correlation-id :as cid]
             [waiter.util.client-tools :refer :all])
   (:import (com.twosigma.waiter.courier
@@ -48,25 +49,26 @@
     (GrpcClient. host port log-function)))

 (defn- basic-grpc-service-parameters
-  []
+  [& {:keys [prefix] :or {prefix "x-waiter-"}}]
   (let [courier-command (courier-server-command "${PORT0} ${PORT1}")]
-    (walk/stringify-keys
-      {:x-waiter-backend-proto "h2c"
-       :x-waiter-cmd courier-command
-       :x-waiter-cmd-type "shell"
-       :x-waiter-concurrency-level 32
-       :x-waiter-cpus 0.2
-       :x-waiter-debug true
-       :x-waiter-grace-period-secs 120
-       :x-waiter-health-check-port-index 1
-       :x-waiter-health-check-proto "http"
-       :x-waiter-idle-timeout-mins 10
-       :x-waiter-max-instances 1
-       :x-waiter-min-instances 1
-       :x-waiter-mem 512
-       :x-waiter-name (rand-name)
-       :x-waiter-ports 2
-       :x-waiter-version "version-does-not-matter"})))
+    (pc/map-keys
+      #(str prefix %)
+      {"backend-proto" "h2c"
+       "cmd" courier-command
+       "cmd-type" "shell"
+       "concurrency-level" 32
+       "cpus" 0.2
+       "debug" true
+       "grace-period-secs" 120
+       "health-check-port-index" 1
+       "health-check-proto" "http"
+       "idle-timeout-mins" 10
+       "max-instances" 1
+       "min-instances" 1
+       "mem" 512
+       "name" (rand-name)
+       "ports" 2
+       "version" "version-does-not-matter"})))

 (defn- rand-str
   "Generates a random string with the specified length."
@@ -78,37 +80,39 @@
   (make-request waiter-url "/waiter-ping" :headers request-headers))

 (defn start-courier-instance
-  [waiter-url]
-  (let [[host _] (str/split waiter-url #":")
-        h2c-port (Integer/parseInt (retrieve-grpc-cleartext-port waiter-url))
-        request-headers (basic-grpc-service-parameters)
-        {:keys [cookies headers] :as response} (ping-courier-service waiter-url request-headers)
-        cookie-header (str/join "; " (map #(str (:name %) "=" (:value %)) cookies))
-        service-id (get headers "x-waiter-service-id")
-        request-headers (assoc request-headers
-                          "cookie" cookie-header
-                          "x-waiter-timeout" "60000")]
-    (assert-response-status response 200)
-    (log/info "ping cid:" (get headers "x-cid"))
-    (log/info "service-id:" service-id)
-    (is service-id)
-    (when-not service-id
-      ;; abort test eagerly when service id is unavailable
-      (throw (ex-info "missing service-id in response" {:response response})))
-    (let [{:keys [ping-response service-state]} (some-> response :body try-parse-json walk/keywordize-keys)]
-      (is (= "received-response" (:result ping-response)) (str ping-response))
-      (is (= "OK" (some-> ping-response :body)) (str ping-response))
-      (is (str/starts-with? (str (some-> ping-response :headers :server)) "courier-health-check") (str ping-response))
-      (assert-response-status ping-response 200)
-      (is (true? (:exists? service-state)) (str service-state))
-      (is (= service-id (:service-id service-state)) (str service-state))
-      (is (contains? #{"Running" "Starting"} (:status service-state)) (str service-state)))
-    (assert-service-on-all-routers waiter-url service-id cookies)
-
-    {:h2c-port h2c-port
-     :host host
-     :request-headers request-headers
-     :service-id service-id}))
+  ([waiter-url]
+   (let [request-headers (basic-grpc-service-parameters)]
+     (start-courier-instance waiter-url request-headers)))
+  ([waiter-url request-headers]
+   (let [[host _] (str/split waiter-url #":")
+         h2c-port (Integer/parseInt (retrieve-grpc-cleartext-port waiter-url))
+         {:keys [cookies headers] :as response} (ping-courier-service waiter-url request-headers)
+         cookie-header (str/join "; " (map #(str (:name %) "=" (:value %)) cookies))
+         service-id (get headers "x-waiter-service-id")
+         request-headers (assoc request-headers
+                           "cookie" cookie-header
+                           "x-waiter-timeout" "60000")]
+     (assert-response-status response 200)
+     (log/info "ping cid:" (get headers "x-cid"))
+     (log/info "service-id:" service-id)
+     (is service-id)
+     (when-not service-id
+       ;; abort test eagerly when service id is unavailable
+       (throw (ex-info "missing service-id in response" {:response response})))
+     (let [{:keys [ping-response service-state]} (some-> response :body try-parse-json walk/keywordize-keys)]
+       (is (= "received-response" (:result ping-response)) (str ping-response))
+       (is (= "OK" (some-> ping-response :body)) (str ping-response))
+       (is (str/starts-with? (str (some-> ping-response :headers :server)) "courier-health-check") (str ping-response))
+       (assert-response-status ping-response 200)
+       (is (true? (:exists? service-state)) (str service-state))
+       (is (= service-id (:service-id service-state)) (str service-state))
+       (is (contains? #{"Running" "Starting"} (:status service-state)) (str service-state)))
+     (assert-service-on-all-routers waiter-url service-id cookies)
+
+     {:h2c-port h2c-port
+      :host host
+      :request-headers request-headers
+      :service-id service-id})))

 (defmacro assert-grpc-ok-status
   "Asserts that the status represents a grpc OK status."
@@ -483,6 +487,100 @@
                       assertion-message))
                 (assert-request-state grpc-client request-headers service-id correlation-id ::success)))))))))

+(deftest ^:parallel ^:integration-slow ^:resource-heavy test-grpc-bidi-streaming-multiple-services
+  (testing-using-waiter-url
+    (let [correlation-id-prefix (rand-name)
+          current-user (retrieve-username)
+          basic-service-parameters (-> (basic-grpc-service-parameters :prefix "")
+                                     (dissoc "debug" "name")
+                                     (assoc "permitted-user" current-user
+                                            "run-as-user" current-user))
+          num-tokens-to-create 5
+          tokens-to-create (map #(str "token" %1 "." correlation-id-prefix) (range num-tokens-to-create))
+          token->service-id-atom (atom {})
+          host-atom (atom nil)
+          port-atom (atom nil)]
+      (try
+        (doseq [token tokens-to-create]
+          (let [token-parameters (assoc basic-service-parameters
+                                   "name" token
+                                   "token" token)
+                _ (log/info "creating token:" token)
+                post-response (post-token waiter-url (walk/keywordize-keys token-parameters))
+                _ (assert-response-status post-response 200)
+                request-headers {"host" token
+                                 "x-waiter-debug" true}
+                {:keys [h2c-port host service-id]} (start-courier-instance waiter-url request-headers)]
+            (log/info "started service:" service-id)
+            (is service-id)
+            (when service-id (swap! token->service-id-atom assoc token service-id))
+            (when host (reset! host-atom host))
+            (when h2c-port (reset! port-atom h2c-port))))
+        (is @host-atom)
+        (is @port-atom)
+        (let [{:keys [cookies]} (make-request waiter-url "/waiter-auth")
+              cookie-header (str/join "; " (map #(str (:name %) "=" (:value %)) cookies))
+              max-message-length 50000
+              task-index-atom (atom -1)
+              host @host-atom
+              h2c-port @port-atom]
+          (parallelize-requests
+            num-tokens-to-create 1
+            (fn test-grpc-bidi-streaming-multiple-services-task []
+              (testing (str "independent mode " max-message-length " messages completion")
+                (let [task-index (swap! task-index-atom inc)
+                      token (nth tokens-to-create task-index)
+                      service-id (get @token->service-id-atom token)
+                      _ (log/info "starting streaming to and from server" {:service-id service-id :token token})
+                      num-messages (+ 50 (* task-index 20))
+                      client-cancellation? (= 2 (mod task-index 2))
+                      cancel-threshold (if client-cancellation? (/ num-messages 2) (inc num-messages))
+                      from (rand-name "f")
+                      correlation-id (str correlation-id-prefix "-in-" max-message-length)
+                      messages (doall (repeatedly num-messages #(rand-str (inc (rand-int max-message-length)))))
+                      request-headers {"cookie" cookie-header
+                                       "x-cid" correlation-id
+                                       "x-waiter-debug" true
+                                       "x-waiter-timeout" "60000"
+                                       "x-waiter-token" token}
+                      ids (map #(str "id-" token "-" %) (range num-messages))
+                      grpc-client (initialize-grpc-client correlation-id host h2c-port)
+                      _ (log/info correlation-id "collecting independent packages...")
+                      rpc-result (.collectPackages grpc-client request-headers ids from messages 10 false
+                                                   cancel-threshold cancel-policy-context 60000)
+                      summaries (.result rpc-result)
+                      ^Status status (.status rpc-result)
+                      assertion-message (->> (cond-> {:correlation-id correlation-id
+                                                      :service-id service-id
+                                                      :summaries (map (fn [^CourierSummary s]
+                                                                        {:num-messages (.getNumMessages s)
+                                                                         :total-length (.getTotalLength s)})
+                                                                      summaries)}
+                                               status (assoc :status {:code (-> status .getCode str)
+                                                                      :description (.getDescription status)}))
+                                          (into (sorted-map))
+                                          str)]
+                  (log/info correlation-id "asserting collected packages...")
+                  (assert-grpc-ok-status status assertion-message)
+                  (if client-cancellation?
+                    (is (<= (count summaries) (inc cancel-threshold)) assertion-message)
+                    (is (= (count messages) (count summaries)) assertion-message))
+                  (when (seq summaries)
+                    (is (= (range 1 (inc (count messages))) (map #(.getNumMessages ^CourierSummary %) summaries))
+                        assertion-message)
+                    (is (= (reductions + (map count messages)) (map #(.getTotalLength ^CourierSummary %) summaries))
+                        assertion-message))
+                  (if client-cancellation?
+                    (assert-request-state grpc-client request-headers service-id correlation-id ::client-cancel)
+                    (assert-request-state grpc-client request-headers service-id correlation-id ::success)))))))
+        (finally
+          (doseq [service-id (vals @token->service-id-atom)]
+            (log/info "deleting service" service-id)
+            (delete-service waiter-url service-id))
+          (doseq [token tokens-to-create]
+            (log/info "deleting token" token)
+            (delete-token-and-assert waiter-url token)))))))
+
 (deftest ^:parallel ^:integration-slow test-grpc-bidi-streaming-client-cancellation
   (testing-using-waiter-url
     (when (grpc-cancellations-supported? waiter-url)


Update ThreeDeeIntersect.clj

Update ThreeDeeIntersect.clj

--- clojure/ThreeDeeIntersect.clj
+++ clojure/ThreeDeeIntersect.clj
@@ -12,8 +12,8 @@
 ;; See the License for the specific language governing permissions and
 ;; limitations under the License.

-;; You really want a terminal 100 chars wide by exactly 45 high to see this
-;; properly.
+;; You really want a terminal at least 100 chars wide and
+;; exactly 46 high to see this properly.
 (defn showSphere
       "Displays the intersection of the given sphere (x, y, z) center points
        and radius r against the plane z=0 in somewhat anti-aliased ASCII art"


Removed Clojure configuration file

Removed Clojure configuration file

--- profiles.clj
+++ profiles.clj
@@ -1,5 +0,0 @@
-{:user {:plugins [[lein-ancient "0.6.8"]
-                  [lein-kibit "0.1.2"]
-                  [jonase/eastwood "0.2.3"]
-                  [venantius/yagni "0.1.4"]
-                  [lein-bikeshed "0.2.0"]]}}


DM | Removed references to user-store atom from user db test

--- test/stonecutter/test/db/user.clj
+++ test/stonecutter/test/db/user.clj
@@ -2,80 +2,82 @@
   (:require [midje.sweet :refer :all]
             [clauth.user :as cl-user]
             [clauth.auth-code :as cl-auth-code]
+            [stonecutter.db.mongo :as m]
             [stonecutter.db.storage :as s]
             [stonecutter.util.uuid :as uuid]
             [stonecutter.db.user :as user]
             [stonecutter.db.storage :as storage]))

+(s/setup-in-memory-stores!)
+(def user-store (m/create-memory-store))
+
 (facts "about storage of users - user storage journey"
-       ;; These facts do not mock the database
-       (s/setup-in-memory-stores!)
        (fact "can store a user"
-             (user/store-user! @storage/user-store "email@server.com" "password")
+             (user/store-user! user-store "email@server.com" "password")
              => (just {:login      "email@server.com"
-                       :name       nil
-                       :url        nil
-                       :confirmed? false
+                          :name       nil
+                          :url        nil
+                          :confirmed? false
                        :uid        anything}))

        (fact "can authenticate a user"
-             (user/authenticate-and-retrieve-user @storage/user-store "email@server.com" "password")
+             (user/authenticate-and-retrieve-user user-store "email@server.com" "password")
              => (contains {:login "email@server.com"
                            :name  nil
                            :url   nil}))

        (fact "can retrieve a user"
-             (user/retrieve-user @storage/user-store "email@server.com")
+             (user/retrieve-user user-store "email@server.com")
              => (contains {:login "email@server.com"
                            :name  nil
                            :url   nil}))

        (fact "can confirm user's account"
-             (let [confirmed-user-record (->> (user/store-user! @storage/user-store "email@server.com" "password")
-                                              (user/confirm-email! @storage/user-store))]
+             (let [confirmed-user-record (->> (user/store-user! user-store "email@server.com" "password")
+                                              (user/confirm-email! user-store))]
                confirmed-user-record =not=> (contains {:confirmation-id anything})
                confirmed-user-record => (contains {:confirmed? true})))

        (fact "can add authorised client for user"
-             (user/add-authorised-client-for-user! @storage/user-store "email@server.com" "a-client-id")
+             (user/add-authorised-client-for-user! user-store "email@server.com" "a-client-id")
              => (contains {:login              "email@server.com"
                            :name               nil
                            :url                nil
                            :authorised-clients ["a-client-id"]}))

        (fact "can remove authorised client for user"
-             (user/remove-authorised-client-for-user! @storage/user-store "email@server.com" "a-client-id")
+             (user/remove-authorised-client-for-user! user-store "email@server.com" "a-client-id")
              => (contains {:login              "email@server.com"
                            :name               nil
                            :url                nil
                            :authorised-clients []}))

        (fact "can change user's password"
-             (user/change-password! @storage/user-store "email@server.com" "new-password")
-             (user/authenticate-and-retrieve-user @storage/user-store "email@server.com" "password") => nil
-             (user/authenticate-and-retrieve-user @storage/user-store "email@server.com" "new-password")
+             (user/change-password! user-store "email@server.com" "new-password")
+             (user/authenticate-and-retrieve-user user-store "email@server.com" "password") => nil
+             (user/authenticate-and-retrieve-user user-store "email@server.com" "new-password")
              => (contains {:login "email@server.com" :name nil :url nil}))

        (fact "can delete a user"
-             (user/delete-user! @storage/user-store "email@server.com") => {}
-             (user/authenticate-and-retrieve-user @storage/user-store "email@server.com" "password") => nil))
+             (user/delete-user! user-store "email@server.com") => {}
+             (user/authenticate-and-retrieve-user user-store "email@server.com" "password") => nil))

 (facts "about is-duplicate-user?"
        (fact "unique email in not a duplicate"
-             (user/is-duplicate-user? @storage/user-store "unique@email.com") => false
+             (user/is-duplicate-user? ...user-store... "unique@email.com") => false
              (provided
-               (user/retrieve-user @storage/user-store "unique@email.com") => nil))
+               (user/retrieve-user ...user-store... "unique@email.com") => nil))

        (fact "duplicate email is a duplicate"
-             (user/is-duplicate-user? @storage/user-store "valid@email.com") => true
+             (user/is-duplicate-user? ...user-store... "valid@email.com") => true
              (provided
-               (user/retrieve-user @storage/user-store "valid@email.com") => ...a-user...))
+               (user/retrieve-user ...user-store... "valid@email.com") => ...a-user...))

        (fact "the email is always lower-cased"
-             (user/is-duplicate-user? @storage/user-store "VALID@EMAIL.COM") => true
+             (user/is-duplicate-user? ...user-store... "VALID@EMAIL.COM") => true
              (provided
-               (user/retrieve-user @storage/user-store "valid@email.com") => ...a-user...)))
+               (user/retrieve-user ...user-store... "valid@email.com") => ...a-user...)))

 (fact "about creating a user record"
       (let [id-gen (constantly "id")]
@@ -87,39 +89,39 @@

 (facts "about storing users"
        (fact "users are stored in the user-store"
-             (user/store-user! @storage/user-store "email@server.com" "password") => {...a-user-key... ...a-user-value...}
+             (user/store-user! ...user-store... "email@server.com" "password") => {...a-user-key... ...a-user-value...}
              (provided
                (user/create-user uuid/uuid "email@server.com" "password") => ...user...
-               (cl-user/store-user @storage/user-store ...user...) => {...a-user-key... ...a-user-value...}))
+               (cl-user/store-user ...user-store... ...user...) => {...a-user-key... ...a-user-value...}))

        (fact "password is removed before returning user"
-             (-> (user/store-user! @storage/user-store "email@server.com" "password")
+             (-> (user/store-user! ...user-store... "email@server.com" "password")
                  :password) => nil
              (provided
                (user/create-user uuid/uuid "email@server.com" "password") => ...user...
-               (cl-user/store-user @storage/user-store ...user...) => {:password "hashedAndSaltedPassword"})))
+               (cl-user/store-user ...user-store... ...user...) => {:password "hashedAndSaltedPassword"})))

 (facts "about authenticating and retrieving users"
        (fact "with valid credentials"
-             (user/authenticate-and-retrieve-user @storage/user-store "email@server.com" "password") => {...a-user-key... ...a-user-value...}
+             (user/authenticate-and-retrieve-user ...user-store... "email@server.com" "password") => {...a-user-key... ...a-user-value...}
              (provided
-               (cl-user/authenticate-user @storage/user-store "email@server.com" "password") => {...a-user-key... ...a-user-value...}))
+               (cl-user/authenticate-user ...user-store... "email@server.com" "password") => {...a-user-key... ...a-user-value...}))

        (fact "password is removed before returning user"
-             (-> (user/authenticate-and-retrieve-user @storage/user-store "email@server.com" "password")
+             (-> (user/authenticate-and-retrieve-user ...user-store... "email@server.com" "password")
                  :password) => nil
              (provided
-               (cl-user/authenticate-user @storage/user-store "email@server.com" "password") => {:password "hashedAndSaltedPassword"}))
+               (cl-user/authenticate-user ...user-store... "email@server.com" "password") => {:password "hashedAndSaltedPassword"}))

        (fact "with invalid credentials returns nil"
-             (user/authenticate-and-retrieve-user @storage/user-store "invalid@credentials.com" "password") => nil
+             (user/authenticate-and-retrieve-user ...user-store... "invalid@credentials.com" "password") => nil
              (provided
-               (cl-user/authenticate-user @storage/user-store "invalid@credentials.com" "password") => nil)))
+               (cl-user/authenticate-user ...user-store... "invalid@credentials.com" "password") => nil)))

 (fact "can retrieve user without authentication"
-      (user/retrieve-user @storage/user-store "email@server.com") => ...a-user...
+      (user/retrieve-user ...user-store... "email@server.com") => ...a-user...
       (provided
-        (cl-user/fetch-user @storage/user-store "email@server.com") => ...a-user...))
+        (cl-user/fetch-user ...user-store... "email@server.com") => ...a-user...))

 (fact "can retrieve user using auth-code"
       (let [auth-code-record (cl-auth-code/create-auth-code @storage/auth-code-store ...client... ...user... ...redirect-uri...)]
@@ -163,14 +165,14 @@

 (facts "about is-authorised-client-for-user?"
        (fact "returns true if client-id is in the users authorised-clients list"
-             (user/is-authorised-client-for-user? @storage/user-store ...email... ...client-id...) => true
+             (user/is-authorised-client-for-user? ...user-store... ...email... ...client-id...) => true
              (provided
-               (user/retrieve-user @storage/user-store ...email...) => {:authorised-clients [...client-id...]}))
+               (user/retrieve-user ...user-store... ...email...) => {:authorised-clients [...client-id...]}))

        (fact "returns false if client-id is in not in the users authorised-clients list"
-             (user/is-authorised-client-for-user? @storage/user-store ...email... ...client-id...) => false
+             (user/is-authorised-client-for-user? ...user-store... ...email... ...client-id...) => false
              (provided
-               (user/retrieve-user @storage/user-store ...email...) => {:authorised-clients [...a-different-client-id...]})))
+               (user/retrieve-user ...user-store... ...email...) => {:authorised-clients [...a-different-client-id...]})))

 (facts "about changing password"
        (fact "update-password returns a function that hashes and updates the user's password"


better user.test

better user.test

--- dev/user.clj
+++ dev/user.clj
@@ -1,3 +1,16 @@
 (ns user
   (:require [clojure.tools.namespace.repl :refer [refresh]]
-            [cats.core :as m]))
+            [clojure.test :refer [run-tests]])
+  (:refer-clojure :exclude [test]))
+
+(defn test
+  ([]
+   (refresh)
+   (run-tests 'test-core
+              'test-builtin
+              'test-maybe
+              'test-either
+              'test-state
+              'test-continuation))
+  ([& namespaces]
+   (apply run-tests namespaces)))


Implemented initial Reagent component.

Implemented initial Reagent component.

--- app/src/clj_web_example/ui/main.cljs
+++ app/src/clj_web_example/ui/main.cljs
@@ -1,3 +1,13 @@
-(ns clj-web-example.ui.main)
+(ns ^:figwheel-always clj-web-example.ui.main
+  (:require [reagent.core :as reagent]))

-(.log js/console "Here we go!!!")
+(defn simple-component []
+  [:div
+   [:h1 "Hello World!"]])
+
+(defn init!
+  []
+  (.log js/console "Here we go! Rendering the components...")
+  (reagent/render [simple-component] (js/document.getElementById "app")))
+
+(init!)


Bump aviso/pretty version

Bump aviso/pretty version

--- lein/profiles.clj
+++ lein/profiles.clj
@@ -14,7 +14,7 @@
                        ;; doesn't have this)
                        #_[im.chit/hara "2.1.11"]
                        [im.chit/vinyasa "0.3.4" :exclusions [org.codehaus.plexus/plexus-utils]]
-                       [io.aviso/pretty "0.1.16"]
+                       [io.aviso/pretty "0.1.20"]
                        [leiningen #= (leiningen.core.main/leiningen-version)  :exclusions [cheshire
                                                                                com.fasterxml.jackson.core/jackson-core
                                                                                com.fasterxml.jackson.dataformat/jackson-dataformat-smile


Added some more prototype data to valuestreams

--- kuona-api/src/kuona_api/handler.clj
+++ kuona-api/src/kuona_api/handler.clj
@@ -150,13 +150,12 @@
   [environment]
   (response (decorate-environment (store/put-document environments environment))))

-
-(defn get-value-stream
-  "Returns a single valuestream object identified by the supplied id"
-  [id]
-  (response
-   {
+(defn value-stream
+  []
+  {
     :id          (util/uuid)
+    :name        "unique-name"
+    :timestamp   (util/timestamp)
     :artifact    {:id "some-unique-identifier" :version "1.0.1"}
     :lead_time   91237842
     :commits     []
@@ -169,8 +168,18 @@
                    :environment {:name "PROD"}
                    :duration    234523}]
     })
-  )
-
+
+(defn get-value-streams
+  "Returns a list of currently defined value streams"
+  []
+  (response {:valuestreams [(value-stream)
+                            (value-stream)
+                            (value-stream)]}))
+
+(defn get-value-stream
+  "Returns a single valuestream object identified by the supplied id"
+  [id]
+  (response (value-stream)))

 (defroutes app-routes
   (GET "/" [] (service-data))
@@ -198,6 +207,7 @@
   (POST "/api/environments/:id/status" request (put-environment-status! (get-in request [:params :id]) (get-in request [:body :status])))
   (POST "/api/environments" request (put-environment! (get-in request [:body :environment])))

+  (GET "/api/valuestreams" [] (get-value-streams))
   (GET "/api/valuestreams/:id" [id] (get-value-stream id))

   (route/not-found "Not Found"))


Test charset zero q-value

Test charset zero q-value

--- test/yada/representation_test.clj
+++ test/yada/representation_test.clj
@@ -125,6 +125,18 @@
             {:charset (charset/to-charset-map "utf-8")})
            [(float 1.0) (float 1.0)])))

+  (testing "Basic match with zero accept quality"
+    (is (= (get-highest-charset-quality
+            {:headers {"accept-charset" "utf-8;q=0"}}
+            {:charset (charset/to-charset-map "utf-8")})
+           :rejected)))
+
+  (testing "Basic match with zero rep quality"
+    (is (= (get-highest-charset-quality
+            {:headers {"accept-charset" "utf-8"}}
+            {:charset (charset/to-charset-map "utf-8;q=0")})
+           :rejected)))
+
   (testing "Basic match with wildcard, multiple choices"
     (is (= (get-highest-charset-quality
             {:headers {"accept-charset" "*;q=0.9,utf-8"}}


notifications on plan page

notifications on plan page

--- frontend/components/org_settings.cljs
+++ frontend/components/org_settings.cljs
@@ -3,9 +3,11 @@
             [frontend.routes :as routes]
             [frontend.datetime :as datetime]
             [frontend.models.organization :as org-model]
+            [frontend.models.plan :as plan-model]
             [frontend.models.repo :as repo-model]
             [frontend.models.user :as user-model]
             [frontend.components.common :as common]
+            [frontend.components.forms :as forms]
             [frontend.state :as state]
             [frontend.utils :as utils :include-macros true]
             [frontend.utils.github :as gh-utils]
@@ -20,7 +22,7 @@
   (:require-macros [cljs.core.async.macros :as am :refer [go go-loop alt!]]
                    [dommy.macros :refer [node sel sel1]]))

-(defn sidebar [{:keys [subpage plan]} owner]
+(defn sidebar [{:keys [subpage plan org-name]} owner]
   (reify
     om/IRender
     (render [_]
@@ -38,7 +40,7 @@
                             {:page :users :text "Users"}])
                 [:li.nav-header "Plan"]
                 (when plan
-                  (if (frontend.models.plan/can-edit-plan? plan)
+                  (if (plan-model/can-edit-plan? plan org-name)
                     (nav-links [{:page :containers :text "Add containers"}
                                 {:page :organizations :text "Organization"}
                                 {:page :billing :text "Billing info"}
@@ -201,14 +203,71 @@
                    [:a.github-icon-link {:href vcs-url}
                     [:i.fa.fa-github]]]]])])]])))))

+(defn plans-trial-notification [plan org-name controls-ch]
+  [:div.row-fluid
+   [:div.alert.alert-success {:class (when (plan-model/trial-over? plan) "alert-error")}
+    [:p
+     (if (plan-model/trial-over? plan)
+       "Your 2-week trial is over!"
+
+       [:span "The " [:strong org-name] " organization has "
+        (plan-model/pretty-trial-time plan) " left in its trial."])]
+    [:p
+     "The trial plan is equivalent to the Solo plan with 6 containers."]
+    (when (and (not (:too_many_extensions plan))
+               (> 3 (plan-model/days-left-in-trial plan)))
+      [:p
+       "Need more time to decide? "
+       (forms/stateful-button
+        [:button.btn.btn-mini.btn-success
+         {:data-success-text "Extended!",
+          :data-loading-text "Extending...",
+          :on-click #(put! controls-ch [:extend-trial {:org-name org-name}])}
+         "Extend your trial"])])]])
+
+(defn plans-piggieback-plan-notification [plan current-org-name]
+  [:div.row-fluid
+   [:div.offset1.span10
+    [:div.alert.alert-success
+     [:p
+      "This organization is covered under " (:org_name plan) "'s plan which has "
+      (:containers plan) " containers."]
+     [:p
+      "If you're an admin in the " (:org_name plan)
+      " organization, then you can change plan settings from the "
+      [:a {:href (routes/v1-org-settings-subpage {:org (:org_name plan)
+                                                  :subpage "plan"})}
+       (:org_name plan) " plan page"] "."]
+     [:p
+      "You can create a separate plan for " current-org-name " by selecting from the plans below."]]]])
+
+(defn plan [data owner]
+  (reify
+    om/IRender
+    (render [_]
+      (let [plan (get-in data state/org-plan-path)
+            org-name (get-in data state/org-name-path)
+            controls-ch (om/get-shared owner [:comms :controls])]
+        (html
+         (if-not plan
+           [:div.loading-spinner common/spinner]
+
+           [:div#billing.plans.pricing.row-fluid
+            (when (plan-model/trial? plan)
+              (plans-trial-notification plan org-name controls-ch))
+            (when (plan-model/piggieback? plan org-name)
+              (plans-piggieback-plan-notification plan org-name))
+            " + $c(HAML.org_plan_summary()) + $c(HAML.pricing_plans()) + $c(HAML.customers_trust()) + $c(HAML.pricing_features()) + $c(HAML.pricing_faq()) + $c(HAML.confirm_plan_modal())"]))))))
+
 (def main-component
   {:users users
    :projects projects
    :plan plan
-   :containers containers
-   :organizations organizations
-   :billing billing
-   :cancel cancel})
+   ;; :containers containers
+   ;; :organizations organizations
+   ;; :billing billing
+   ;; :cancel cancel
+   })

 (defn org-settings [data owner]
   (reify
@@ -221,7 +280,7 @@
                (if-not (:name org-data)
                  [:div.loading-spinner common/spinner]
                  [:div.row-fluid
-                  (om/build sidebar {:subpage subpage :plan plan})
+                  (om/build sidebar {:subpage subpage :plan plan :org-name (:name org-data)})
                   [:div.span9
                    (common/flashes)
                    [:div#subpage


Made user namespace a little more helpful with extra includes

--- dev/user.clj
+++ dev/user.clj
@@ -4,5 +4,11 @@
             [clojure.set :as set]
             [clojure.pprint :refer (pprint)]
             [clojure.test :as test]
+            [clj-time.core :refer (date-time)]
+            [clj-time.format :as tf]
+            [clj-time.coerce :as tc]
+            [korma.core :as k]
+            [korma.db :as db]
             [sdos-site.core :as core]
+            [sdos-site.article :as art]
             [clojure.tools.namespace.repl :refer (refresh refresh-all)]))


Add fn to list rooms with nice commas and ands.

--- src/isla/talk.clj
+++ src/isla/talk.clj
@@ -4,8 +4,22 @@
   (:require [mrc.utils :as utils]))

 (defn room-intro [room]
+(declare list-rooms)
   (str "You are in the " (:name room) ". "
        (:summary room)))

 (defn room-already [name] (str "You are already in the " name "."))
 (defn room-not-allowed [name] (str "You cannot go into the " name "."))
+
+(defmulti list-rooms (fn [rooms] (count rooms)))
+(defmethod list-rooms 0 [rooms] "There is no way out of this room.")
+(defmethod list-rooms 1 [rooms] (str "You can see a door to the " (:name (first rooms))))
+(defmethod list-rooms :default [rooms]
+  (let [all-room-list (reduce (fn [string x]
+                                (str string ", the " (:name x)))
+                              "" rooms)
+        all-but-last-list (rest (butlast (str/split all-room-list #",")))
+        final (str "You can see a door to"
+                   (str/join "," all-but-last-list)
+                   " and the " (:name (last rooms)) ".")]
+  final))


Update project.clj to use new version of slingshot.

--- backend/project.clj
+++ backend/project.clj
@@ -10,13 +10,31 @@
   :description "FIXME: write this!"
   :dependencies [[org.clojure/clojure "1.2.1"]
                  [org.clojure/clojure-contrib "1.2.0"]
-                 [clj-json "0.4.0"] ;; noir pulls in clj-json 0.3.2 which isn't compatible w/ clojure 1.3. Put this dep ahead to pull it in first.
-                 [slingshot "0.9.0"]

-                 [org.codehaus.jackson/jackson-core-asl "1.8.5"] ; clj-json
-                                        ; pulls in 1.5.0, which has a bug which
-                               ; prevents tentacles from working. Put
-                                        ; this dep first
+                 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+                 ;;; TECHNICAL DEBT
+                 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+                 ;; noir pulls in clj-json 0.3.2 which isn't compatible w/
+                 ;; clojure 1.3. Put this dep ahead to pull it in first.
+                 [clj-json "0.4.0"]
+
+                 ;; Slingshot make a breaking change in version 0.8, where
+                 ;; slingshot.core was renamed to slingshot.slingshot.
+                 ;; Unfortunately, pallet and clj-ssh use version 0.2 and 0.5,
+                 ;; but tentacles needs version 0.9. So we made our own which
+                 ;; merged these.
+                 [slingshot "0.9-80af18e4f9541c61ba5a0c62be28c4eb535bcd4c"]
+
+                 ;; clj-json pulls in 1.5.0, which has a bug which prevents
+                 ;; tentacles from working. Putthis dep first
+                 [org.codehaus.jackson/jackson-core-asl "1.8.5"]
+
+
+                 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+                 ;;; Dependencies
+                 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+

                  ;; Web
                  [noir "1.2.0"]