split landing page into components

split landing page into components

--- frontend/components/landing_b.cljs
+++ frontend/components/landing_b.cljs
@@ -8,10 +8,12 @@
             [frontend.components.drawings :as drawings]
             [frontend.components.shared :as shared]
             [frontend.env :as env]
+            [frontend.scroll :as scroll]
             [frontend.state :as state]
             [frontend.stefon :as stefon]
             [frontend.utils :as utils :include-macros true]
             [frontend.utils.github :refer [auth-url]]
+            [frontend.utils.seq :refer [select-in]]
             [goog.events]
             [goog.dom]
             [goog.style]
@@ -20,45 +22,12 @@
             [sablono.core :as html :refer-macros [html]])
   (:require-macros [frontend.utils :refer [defrender]]
                    [cljs.core.async.macros :as am :refer [go go-loop alt!]]
-                   [dommy.macros :refer [sel1 node]]))
+                   [dommy.macros :refer [sel1 node]])
+  (:import [goog.ui IdGenerator]))

 (defn maybe-set-state! [owner korks value]
   (when (not= (om/get-state owner korks) value)
     (om/set-state! owner korks value)))
-
-(defn mount-scroll-handler [owner]
-  (let [logo (om/get-node owner "center-logo")
-        prolog-cta (om/get-node owner "prolog-cta")
-        home-prolog (om/get-node owner "home-prolog")
-        epilog-cta (om/get-node owner "epilog-cta")
-        home-epilog (om/get-node owner "home-epilog")
-        purpose-article (om/get-node owner "purpose-article")
-        potential-article (om/get-node owner "potential-article")
-        scroll-ch (om/get-state owner [:scroll-ch])
-        scroll-handler #(let [vh (.-height (goog.dom/getViewportSize))
-                              nav-height 70]
-                          ;; we could optimize these, but calculating this is surprisingly fast (less than 10 ms)
-
-                          (maybe-set-state! owner [:header-logo-visible] (neg? (.-bottom (.getBoundingClientRect logo))))
-                          (maybe-set-state! owner [:header-cta-visible] (neg? (.-bottom (.getBoundingClientRect prolog-cta))))
-                          (maybe-set-state! owner [:header-bkg-visible] (< (.-bottom (.getBoundingClientRect home-prolog)) nav-height))
-                          (maybe-set-state! owner [:header-cta-invisible] (< (.-top (.getBoundingClientRect epilog-cta)) vh))
-                          (maybe-set-state! owner [:header-bkg-invisible] (< (.-top (.getBoundingClientRect home-epilog)) nav-height))
-                          (maybe-set-state! owner [:first-fig-animate] (< (.-bottom (.getBoundingClientRect purpose-article)) vh))
-                          (maybe-set-state! owner [:second-fig-animate] (< (.-bottom (.getBoundingClientRect potential-article)) vh))
-                          (maybe-set-state! owner [:header-bkg-scroller] (min (js/Math.abs (.-top (.getBoundingClientRect home-epilog)))
-                                                                              (js/Math.abs (- nav-height (.-bottom (.getBoundingClientRect home-prolog))))
-                                                                              nav-height)))]
-    (om/set-state! owner [:scroll-key]
-                   (goog.events/listen
-                    js/window
-                    "scroll"
-                    #(put! scroll-ch %)))
-    (go-loop []
-             (when-let [event (<! scroll-ch)]
-               (utils/swallow-errors (scroll-handler))
-               (recur)))
-    (scroll-handler)))

 (def circle-logo
   (html
@@ -177,255 +146,409 @@
                  :tools #{:ruby :rails :postgres :javascript :node}
                  :position 4}))

-(defn home [app owner]
-  (reify
+(def nav-height 70)
+
+(defn nav [data owner]
+  (reify
+    om/IDisplayName (display-name [_] "Home Nav")
+    om/IRender
+    (render [_]
+      (let [controls-ch (om/get-shared owner [:comms :controls])]
+        (html
+         [:nav.home-nav {:style (if (> 70 (:header-bkg-scroller data))
+                                  {:background-size (str "100% " (:header-bkg-scroller data) "px")}
+                                  {})
+                         :class (concat
+                                 (when (:header-logo-visible data) ["logo-visible"])
+                                 (when (:header-cta-visible data) ["cta-visible"])
+                                 (when (:header-bkg-visible data) ["bkg-visible"])
+                                 (when (:header-bkg-invisible data) ["bkg-invisible"])
+                                 (when (:header-cta-invisible data) ["cta-invisible"]))}
+          [:a.promo {:href "http://blog.circleci.com/continuous-delivery-with-docker-containers/"}
+                                        ; "What is Continuous Integration?"
+           "Learn how we support Docker."]
+          [:a.login {:href (auth-url)
+                     :on-click #(put! controls-ch [:track-external-link-clicked
+                                                   {:event "Auth GitHub"
+                                                    :properties {:source "header-log-in"}
+                                                    :path (auth-url)}])}
+           "Log In"]
+          [:a.logo-circleci {:on-click #(put! controls-ch [:home-scroll-logo-clicked])}
+           [:figure circle-logo]]
+          [:a.action {:href (auth-url)
+                      :role "button"
+                      :on-click #(put! controls-ch [:track-external-link-clicked
+                                                    {:event "Auth GitHub"
+                                                     :properties {:source "header-cta"}
+                                                     :path (auth-url)}])}
+           "Sign Up Free"]])))))
+
+(defn prolog [data owner {:keys [logo-visibility-callback
+                                 cta-visibility-callback
+                                 prolog-visibility-callback
+                                 header-overlap-callback]}]
+  (reify
+    om/IDisplayName (display-name [_] "Home Prolog")
+    om/IInitState (init-state [_] {:scroll-id (.getNextUniqueId (.getInstance IdGenerator))
+                                   :logo-visible? (atom nil)
+                                   :cta-visible? (atom nil)
+                                   :prolog-visible? (atom nil)
+                                   :header-overlap-px (atom 0)})
     om/IDidMount
     (did-mount [_]
-      (mount-scroll-handler owner))
-    om/IWillUnmount
-    (will-unmount [_]
-      (goog.events/unlistenByKey (om/get-state owner [:scroll-key]))
-      (close! (om/get-state owner [:scroll-ch])))
+      (scroll/register-fn
+       (om/get-state owner :scroll-id)
+       #(let [logo (om/get-node owner "center-logo")
+              cta (om/get-node owner "prolog-cta")
+              prolog (om/get-node owner "home-prolog")
+              logo-visible? (neg? (.-bottom (.getBoundingClientRect logo)))
+              cta-visible? (neg? (.-bottom (.getBoundingClientRect cta)))
+              prolog-visible? (>= (.-bottom (.getBoundingClientRect prolog)) nav-height)
+              cta-visible? (neg? (.-bottom (.getBoundingClientRect cta)))
+              header-overlap-px (- nav-height (.-bottom (.getBoundingClientRect prolog)))
+              ;; normalize the overlap
+              header-overlap-px (min nav-height (max 0 header-overlap-px))]
+          (when-not (= logo-visible? @(om/get-state owner :logo-visible?))
+            (reset! (om/get-state owner :logo-visible?) logo-visible?)
+            (logo-visibility-callback logo-visible?))
+          (when-not (= cta-visible? @(om/get-state owner :cta-visible?))
+            (reset! (om/get-state owner :cta-visible?) cta-visible?)
+            (cta-visibility-callback cta-visible?))
+          (when-not (= prolog-visible? @(om/get-state owner :prolog-visible?))
+            (reset! (om/get-state owner :prolog-visible?) prolog-visible?)
+            (prolog-visibility-callback prolog-visible?))
+          (when-not (= header-overlap-px @(om/get-state owner :header-overlap-px))
+            (reset! (om/get-state owner :header-overlap-px) header-overlap-px)
+            (header-overlap-callback header-overlap-px)))))
+    om/IWillUnmount (will-unmount [_] (scroll/deregister-fn (om/get-state owner :scroll-id)))
+    om/IRender
+    (render [_]
+      (let [controls-ch (om/get-shared owner [:comms :controls])]
+        (html
+         [:section.home-prolog {:ref "home-prolog"}
+          [:a.home-action {:href (auth-url)
+                           :role "button"
+                           :ref "prolog-cta"
+                           :on-click #(put! controls-ch [:track-external-link-clicked {:event "Auth GitHub"
+                                                                                :properties {:source "prolog-cta"}
+                                                                                :path (auth-url)}])}
+           "Sign Up Free"]
+          [:div.home-cover]
+          [:div.home-top-shelf]
+          [:div.home-slogans
+           [:h1.slogan.proverb {:item-prop "Ship better code, faster."
+                                :alt       "Let's just authorize first."}
+            "Ship better code, faster."]
+           [:h3.slogan.context {:item-prop "You have a product to focus on, let Circle handle your"
+                                :alt       "Signing up using your GitHub login lets us start really fast."}
+            "You have a product to focus on, let Circle handle your"]
+           [:h3.slogan.context {:item-prop "Continuous Integration & Deployment."
+                                :alt       "Currently, we must request permissions in bulk."}
+            "Continuous Integration & Deployment."]]
+          [:div.home-avatars
+           [:div.avatars
+            [:div.avatar-github
+             (common/ico :github)]
+            [:div.avatar-circle {:ref "center-logo"}
+             (common/ico :logo)]]]
+          [:div.home-bottom-shelf
+           [:a {:on-click #(put! controls-ch [:home-scroll-1st-clicked])}
+            "Learn more"
+            (common/ico :chevron-down)]]])))))
+
+(defn purpose [data owner]
+  (reify
+    om/IDisplayName (display-name [_] "Home Purpose")
+    om/IInitState (init-state [_] {:scroll-id (.getNextUniqueId (.getInstance IdGenerator))})
+    om/IDidMount
+    (did-mount [_]
+      (scroll/register-fn
+       (om/get-state owner :scroll-id)
+       #(let [article (om/get-node owner "purpose-article")
+              vh (.-height (goog.dom/getViewportSize))
+              animate? (< (.-bottom (.getBoundingClientRect article)) vh)]
+          (maybe-set-state! owner [:first-fig-animate] animate?))))
+    om/IWillUnmount (will-unmount [_] (scroll/deregister-fn (om/get-state owner :scroll-id)))
+    om/IRender
+    (render [_]
+      (let [controls-ch (om/get-shared owner [:comms :controls])]
+        (html
+         [:section.home-purpose {:class (when (om/get-state owner [:first-fig-animate]) "animate")}
+          [:div.home-top-shelf]
+          [:div.home-purpose-content
+           [:div.home-drawings
+            [:figure]
+            [:figure]
+            [:figure drawings/drawing-dashboard]]
+           [:div.home-articles
+            [:article {:ref "purpose-article"}
+             [:h1 "Launches are dead, long live iteration."]
+             [:p "We believe that rapid iteration, tight feedback loops, and team communication are the keys to a great product workflow.
+                       That's why we designed the world's leading continuous integration and delivery solution.
+                       Continuous integration and delivery is revolutionizing the way development teams operate by reducing barriers between your ideas and your production code.
+                       Remember, it doesn't count until it ships."]
+             [:p
+              [:a.shopify-link {:href "/stories/shopify"}
+               "See how Shopify does it"
+               (common/ico :slim-arrow-right)]]]]]
+          [:div.home-bottom-shelf
+           [:a {:on-click #(put! controls-ch [:home-scroll-2nd-clicked])}
+            ;; "Continue" ; hold off on this line of copy, it's cleanr w/o
+            (common/ico :chevron-down)]]])))))
+
+(defn practice [app owner]
+  (reify
+    om/IDisplayName (display-name [_] "Home Practice")
+    om/IRender
+    (render [_]
+      (let [controls-ch (om/get-shared owner [:comms :controls])
+            selected-customer (get-in app state/customer-logo-customer-path :shopify)
+            selected-toolset (get-in app state/selected-toolset-path :languages)]
+        (html
+         [:section.home-practice
+          (let [hovered-customer (om/get-state owner [:hovered-customer])
+                tools (get-in customer-brands [hovered-customer :tools] #{})]
+            [:div.practice-tools
+             [:article
+              (for [[toolset template] supported-tools]
+                (html ; use html in for loops for perf
+                 [:div.supported-tools {:class (when (= selected-toolset toolset) "active")}
+                  [:div.supported-tools-upper
+                   (for [tool (:upper template)]
+                     (html
+                      [:div.supported-tool {:class (when (contains? tools tool) "hover")
+                                            :alt (name tool)}
+                       [:figure
+                        [:svg {:class (str "tool-logo-" (name tool))
+                               :x "0" :y "0" :view-box "0 0 100 100"}
+                         [:path {:d (get tools-logos tool)}]]]]))]
+                  [:div.supported-tools-lower
+                   (for [tool (:lower template)]
+                     (html
+                      [:div.supported-tool {:class (when (contains? tools tool) "hover")
+                                            :alt (name tool)}
+                       [:figure
+                        [:svg {:class (str "tool-logo-" (name tool))
+                               :x "0" :y "0" :view-box "0 0 100 100"}
+                         [:path {:d (get tools-logos tool)}]]]]))]]))]])
+
+          [:div.practice-articles
+           [:article
+            [:h1 "Devs rely on us to just work; we support the right tools."]
+            [:p
+             (for [toolset ["Languages" "databases" "queues" "browsers" "deployment"]]
+               (html
+                [:a {:on-mouse-enter #(put! controls-ch [:toolset-clicked {:toolset (keyword (str/lower-case toolset))}])} toolset] ", "))
+             "we support all of your tools.
+              If it runs on Linux, then it will work on CircleCI.
+              We'll even be around to help you install your own tools.
+              The best development teams in the world trust us as their continuous integration and delivery solution because of our unmatched support and our ability to scale with them.
+              We're built for teams."]]]
+          [:div.practice-customers
+           [:article
+            [:div.customers-brands {:class (str "selected-" (get-in customer-brands [selected-customer :position]))}
+             (for [[customer template] customer-brands]
+               (html
+                [:a.customers-brand {:on-click #(put! controls-ch [:customer-logo-clicked {:customer customer}])
+                                     :on-mouse-enter #(om/set-state! owner [:hovered-customer] customer)
+                                     :on-mouse-leave #(om/set-state! owner [:hovered-customer] nil)}
+                 (get customer-logos customer)]))]
+            [:div.quote-card
+             [:p (get-in customer-brands [selected-customer :quote])]
+             [:footer
+              [:img {:src (stefon/asset-path (get-in customer-brands [selected-customer :cite-avatar]))}]
+              [:cite
+               (get-in customer-brands [selected-customer :cite])
+               [:br]
+               (get-in customer-brands [selected-customer :cite-title])
+               " at "
+               (get-in customer-brands [selected-customer :name])]]]]]
+          [:div.home-bottom-shelf
+           [:a {:on-click #(put! controls-ch [:home-scroll-3rd-clicked])}
+            ;; "Continue" ; hold off on this line of copy, it's cleanr w/o
+            (common/ico :chevron-down)]]])))))
+
+(defn potential [data owner]
+  (reify
+    om/IDisplayName (display-name [_] "Home Potential")
+    om/IInitState (init-state [_] {:scroll-id (.getNextUniqueId (.getInstance IdGenerator))})
+    om/IDidMount
+    (did-mount [_]
+      (scroll/register-fn
+       (om/get-state owner :scroll-id)
+       #(let [article (om/get-node owner "potential-article")
+              vh (.-height (goog.dom/getViewportSize))
+              animate? (< (.-bottom (.getBoundingClientRect article)) vh)]
+          (maybe-set-state! owner [:second-fig-animate] animate?))))
+    om/IWillUnmount (will-unmount [_] (scroll/deregister-fn (om/get-state owner :scroll-id)))
+    om/IRender
+    (render [_]
+      (let [controls-ch (om/get-shared owner [:comms :controls])]
+        (html
+         [:section.home-potential {:class (when (om/get-state owner [:second-fig-animate]) "animate")}
+          [:div.home-top-shelf]
+          [:div.home-potential-content
+           [:div.home-articles
+            [:article {:ref "potential-article"}
+             [:h1 "Look under the hood & check the bullet points."]
+             [:div.home-potential-bullets
+              [:ul
+               [:li "Quick & easy setup"]
+               [:li "Lightning fast builds"]
+               [:li "Deep Customization"]
+               [:li "Easy debugging"]]
+              [:ul
+               [:li "Smart notifications"]
+               [:li "Loving support"]
+               [:li "Automatic parallelization"]
+               [:li "Continuous Deployment"]]
+              [:ul
+               [:li "Build artifacts"]
+               [:li "Clean build environments"]
+               [:li "GitHub Integration"]
+               [:li "Open Source Support"]]]]]
+           [:div.home-drawings
+            [:figure]
+            [:figure]
+            [:figure drawings/draw-build-large]]]
+          [:div.home-bottom-shelf
+           [:a {:on-click #(put! controls-ch [:home-scroll-4th-clicked])}
+            ;; "Continue" ; hold off on this line of copy, it's cleanr w/o
+            (common/ico :chevron-down)]]])))))
+
+(defn epilog [data owner {:keys [cta-visibility-callback
+                                 epilog-visibility-callback
+                                 header-overlap-callback]}]
+  (reify
+    om/IDisplayName (display-name [_] "Home Epilog")
+    om/IInitState (init-state [_] {:scroll-id (.getNextUniqueId (.getInstance IdGenerator))
+                                   :cta-visible? (atom nil)
+                                   :epilog-visible? (atom nil)
+                                   :header-overlap-px (atom 0)})
+    om/IDidMount
+    (did-mount [_]
+      (scroll/register-fn
+       (om/get-state owner :scroll-id)
+       #(let [vh (.-height (goog.dom/getViewportSize))
+              cta (om/get-node owner "epilog-cta")
+              epilog (om/get-node owner "home-epilog")
+              cta-visible? (< (.-top (.getBoundingClientRect cta)) vh)
+              epilog-visible? (< (.-top (.getBoundingClientRect epilog)) nav-height)
+              header-overlap-px (Math/abs (.-top (.getBoundingClientRect epilog)))
+              ;; normalize the overlap
+              header-overlap-px (utils/inspect (min nav-height (max 0 header-overlap-px)))]
+          (when-not (= cta-visible? @(om/get-state owner :cta-visible?))
+            (reset! (om/get-state owner :cta-visible?) cta-visible?)
+            (cta-visibility-callback cta-visible?))
+          (when-not (= epilog-visible? @(om/get-state owner :epilog-visible?))
+            (reset! (om/get-state owner :epilog-visible?) epilog-visible?)
+            (epilog-visibility-callback epilog-visible?))
+          (when-not (= header-overlap-px @(om/get-state owner :header-overlap-px))
+            (reset! (om/get-state owner :header-overlap-px) header-overlap-px)
+            (header-overlap-callback header-overlap-px)))))
+    om/IWillUnmount (will-unmount [_] (scroll/deregister-fn (om/get-state owner :scroll-id)))
+    om/IRender
+    (render [_]
+      (let [controls-ch (om/get-shared owner [:comms :controls])]
+        (html
+         [:section.home-epilog {:ref "home-epilog"}
+          [:a.home-action {:href (auth-url)
+                           :ref "epilog-cta"
+                           :role "button"
+                           :on-click #(put! controls-ch [:track-external-link-clicked {:event "Auth GitHub"
+                                                                                :properties {:source "epilog-cta"}
+                                                                                :path (auth-url)}])}
+           "Sign Up Free"]
+          [:div.home-cover]
+          [:div.home-top-shelf]
+          [:div.home-slogans
+           [:h1.slogan.proverb {:item-prop "So, ready to ship faster?"
+                                :alt       "Let's just authorize first."}
+            "So, ready to ship faster?"]
+           [:h3.slogan.context {:item-prop "Next you'll just need to sign in using your GitHub account."
+                                :alt       "Signing up using your GitHub login lets us start really fast."}
+            "Next you'll just need to sign in using your GitHub account."]
+           [:h3.slogan.context {:item-prop "Still not convinced yet? Check out our pricing."
+                                :alt       "Currently, we must request permissions in bulk."}
+            "Still not convinced yet? Check out our "
+            [:a {:href "pricing"} "pricing"]
+            "."]]
+          [:div.home-avatars
+           [:div.avatars
+            [:div.avatar-github
+             (common/ico :github)]
+            [:div.avatar-circle
+             (common/ico :logo)]]]
+          [:div.home-bottom-shelf
+           [:span.home-footer-bait
+            "About Us"
+            (common/ico :chevron-down)]
+           [:div.home-footer
+            [:div.home-footer-logo
+             (common/ico :logo)]
+            [:nav.home-footer-about
+             [:h5 "CircleCI"]
+             [:a {:href "/tour"} "Tour"]
+             [:a {:href "/about"} "About"]
+             [:a "Support"]
+             [:a {:href "/about#contact"} "Press"]
+             [:a {:href "/jobs"} "Jobs"]
+             [:a {:href "http://blog.circleci.com"} "Blog"]]
+            [:nav.home-footer-product
+             [:h5 "Product"]
+             [:a {:href "/pricing"} "Pricing"]
+             [:a {:href "/docs"} "Documentation"]
+             [:a {:href "/privacy"} "Privacy"]
+             [:a {:href "/security"} "Security"]
+             [:a {:href "/enterprise"} "Enterprise"]
+             [:a {:href "/changelog"} "Changelog"]]
+            [:nav.home-footer-contact
+             [:h5 "Contact"]
+             [:a {:href "https://twitter.com/circleci"} "Twitter"]
+             [:a {:href "mailto:sayhi@circleci.com"} "Email"]
+             [:a {:href ""} "Support Chat"]
+             [:a {:href ""} "Phone"]
+             [:a {:href "https://goo.gl/maps/uhkLn"} "San Francisco, CA"]]]]])))))
+
+(defn home [app owner]
+  (reify
+    om/IDisplayName (display-name [_] "Homepage B")
     om/IInitState
     (init-state [_]
       {:header-logo-visible false
        :header-cta-visible false
        :header-bkg-visible false
        :header-cta-invisible false
-       :header-bkg-invisible false
-       :first-fig-animate false
-       :second-fig-animate false
-       :scroll-ch (chan (sliding-buffer 1))
-       :hovered-customer nil})
-    om/IRenderState
-    (render-state [_ state]
-      (let [ab-tests (:ab-tests app)
-            controls-ch (om/get-shared owner [:comms :controls])
-            selected-customer (get-in app state/customer-logo-customer-path :shopify)
-            selected-toolset (get-in app state/selected-toolset-path :languages)]
-        (html [:div.home.page
-               [:nav.home-nav {:style (if (> 70 (:header-bkg-scroller state))
-                                        {:background-size (str "100% " (:header-bkg-scroller state) "px")}
-                                        {})
-                               :class (concat
-                                       (when (:header-logo-visible state) ["logo-visible"])
-                                       (when (:header-cta-visible state) ["cta-visible"])
-                                       (when (:header-bkg-visible state) ["bkg-visible"])
-                                       (when (:header-bkg-invisible state) ["bkg-invisible"])
-                                       (when (:header-cta-invisible state) ["cta-invisible"]))}
-                [:a.promo {:href "http://blog.circleci.com/continuous-delivery-with-docker-containers/"}
-                 ; "What is Continuous Integration?"
-                 "Learn how we support Docker."]
-                [:a.login {:href (auth-url)
-                           :on-click #(put! controls-ch [:track-external-link-clicked {:event "Auth GitHub"
-                                                                                :properties {:source "header-log-in"}
-                                                                                :path (auth-url)}])}
-                 "Log In"]
-                [:a.logo-circleci {:on-click #(put! controls-ch [:home-scroll-logo-clicked])}
-                 [:figure circle-logo]]
-                [:a.action {:href (auth-url)
-                            :role "button"
-                            :on-click #(put! controls-ch [:track-external-link-clicked {:event "Auth GitHub"
-                                                                                :properties {:source "header-cta"}
-                                                                                :path (auth-url)}])}
-                 "Sign Up Free"]]
-               [:section.home-prolog {:ref "home-prolog"}
-                [:a.home-action {:href (auth-url)
-                                 :role "button"
-                                 :ref "prolog-cta"
-                                 :on-click #(put! controls-ch [:track-external-link-clicked {:event "Auth GitHub"
-                                                                                :properties {:source "prolog-cta"}
-                                                                                :path (auth-url)}])}
-                 "Sign Up Free"]
-                [:div.home-cover]
-                [:div.home-top-shelf]
-                [:div.home-slogans
-                 [:h1.slogan.proverb {:item-prop "Ship better code, faster."
-                                      :alt       "Let's just authorize first."}
-                  "Ship better code, faster."]
-                 [:h3.slogan.context {:item-prop "You have a product to focus on, let Circle handle your"
-                                      :alt       "Signing up using your GitHub login lets us start really fast."}
-                  "You have a product to focus on, let Circle handle your"]
-                 [:h3.slogan.context {:item-prop "Continuous Integration & Deployment."
-                                      :alt       "Currently, we must request permissions in bulk."}
-                  "Continuous Integration & Deployment."]]
-                [:div.home-avatars
-                 [:div.avatars
-                  [:div.avatar-github
-                   (common/ico :github)]
-                  [:div.avatar-circle {:ref "center-logo"}
-                   (common/ico :logo)]]]
-                [:div.home-bottom-shelf
-                 [:a {:on-click #(put! controls-ch [:home-scroll-1st-clicked])}
-                  "Learn more"
-                  (common/ico :chevron-down)]]]
-               [:section.home-purpose {:class (when (:first-fig-animate state) ["animate"])}
-                [:div.home-top-shelf]
-                [:div.home-purpose-content
-                 [:div.home-drawings
-                  [:figure]
-                  [:figure]
-                  [:figure
-                   (om/build drawings/drawing-dashboard app)]]
-                 [:div.home-articles
-                  [:article {:ref "purpose-article"}
-                   [:h1 "Launches are dead, long live iteration."]
-                   [:p "We believe that rapid iteration, tight feedback loops, and team communication are the keys to a great product workflow.
-                       That's why we designed the world's leading continuous integration and delivery solution.
-                       Continuous integration and delivery is revolutionizing the way development teams operate by reducing barriers between your ideas and your production code.
-                       Remember, it doesn't count until it ships."]
-                   [:p
-                    [:a.shopify-link {:href "/stories/shopify"}
-                     "See how Shopify does it"
-                     (common/ico :slim-arrow-right)]]]]]
-                [:div.home-bottom-shelf
-                 [:a {:on-click #(put! controls-ch [:home-scroll-2nd-clicked])}
-                  ;; "Continue" ; hold off on this line of copy, it's cleanr w/o
-                  (common/ico :chevron-down)]]]
-               [:section.home-practice
-                (let [hovered-customer (:hovered-customer state)
-                      tools (get-in customer-brands [hovered-customer :tools] #{})]
-                 [:div.practice-tools
-                  [:article
-                   (for [[toolset template] supported-tools]
-                    [:div.supported-tools {:class (when (= selected-toolset toolset) "active")}
-                     [:div.supported-tools-upper
-                      (for [tool (:upper template)]
-                       [:div.supported-tool {:class (when (contains? tools tool) "hover")
-                                             :alt (name tool)}
-                        [:figure
-                         [:svg {:class (str "tool-logo-" (name tool))
-                               :x "0" :y "0" :view-box "0 0 100 100"}
-                          [:path {:d (get tools-logos tool)}]]]])]
-                     [:div.supported-tools-lower
-                      (for [tool (:lower template)]
-                       [:div.supported-tool {:class (when (contains? tools tool) "hover")
-                                             :alt (name tool)}
-                        [:figure
-                         [:svg {:class (str "tool-logo-" (name tool))
-                               :x "0" :y "0" :view-box "0 0 100 100"}
-                          [:path {:d (get tools-logos tool)}]]]])]])]])
-
-                [:div.practice-articles
-                 [:article
-                  [:h1 "Devs rely on us to just work; we support the right tools."]
-                  [:p
-                   (for [toolset ["Languages" "databases" "queues" "browsers" "deployment"]]
-                     (list
-                      [:a {:on-mouse-enter #(put! controls-ch [:toolset-clicked {:toolset (keyword (str/lower-case toolset))}])} toolset] ", "))
-                   "we support all of your tools.
-                   If it runs on Linux, then it will work on CircleCI.
-                   We'll even be around to help you install your own tools.
-                   The best development teams in the world trust us as their continuous integration and delivery solution because of our unmatched support and our ability to scale with them.
-                   We're built for teams."]]]
-                [:div.practice-customers
-                 [:article
-                  [:div.customers-brands {:class (str "selected-" (get-in customer-brands [selected-customer :position]))}
-                   (for [[customer template] customer-brands]
-                     [:a.customers-brand {:on-click #(put! controls-ch [:customer-logo-clicked {:customer customer}])
-                                          :on-mouse-enter #(om/set-state! owner [:hovered-customer] customer)
-                                          :on-mouse-leave #(om/set-state! owner [:hovered-customer] nil)}
-                      (get customer-logos customer)])]
-                  [:div.quote-card
-                   [:p (get-in customer-brands [selected-customer :quote])]
-                   [:footer
-                    [:img {:src (stefon/asset-path (get-in customer-brands [selected-customer :cite-avatar]))}]
-                    [:cite
-                     (get-in customer-brands [selected-customer :cite])
-                     [:br]
-                     (get-in customer-brands [selected-customer :cite-title])
-                     " at "
-                     (get-in customer-brands [selected-customer :name])]]]]]
-                [:div.home-bottom-shelf
-                 [:a {:on-click #(put! controls-ch [:home-scroll-3rd-clicked])}
-                  ;; "Continue" ; hold off on this line of copy, it's cleanr w/o
-                  (common/ico :chevron-down)]]]
-               [:section.home-potential {:class (when (:second-fig-animate state) ["animate"])}
-                [:div.home-top-shelf]
-                [:div.home-potential-content
-                 [:div.home-articles
-                  [:article {:ref "potential-article"}
-                   [:h1 "Look under the hood & check the bullet points."]
-                   [:div.home-potential-bullets
-                    [:ul
-                     [:li "Quick & easy setup"]
-                     [:li "Lightning fast builds"]
-                     [:li "Deep Customization"]
-                     [:li "Easy debugging"]]
-                    [:ul
-                     [:li "Smart notifications"]
-                     [:li "Loving support"]
-                     [:li "Automatic parallelization"]
-                     [:li "Continuous Deployment"]]
-                    [:ul
-                     [:li "Build artifacts"]
-                     [:li "Clean build environments"]
-                     [:li "GitHub Integration"]
-                     [:li "Open Source Support"]]]]]
-                 [:div.home-drawings
-                  [:figure]
-                  [:figure]
-                  [:figure
-                   (om/build drawings/draw-build-large app)]]]
-                [:div.home-bottom-shelf
-                 [:a {:on-click #(put! controls-ch [:home-scroll-4th-clicked])}
-                  ;; "Continue" ; hold off on this line of copy, it's cleanr w/o
-                  (common/ico :chevron-down)]]]
-               [:section.home-epilog {:ref "home-epilog"}
-                [:a.home-action {:href (auth-url)
-                                 :ref "epilog-cta"
-                                 :role "button"
-                                 :on-click #(put! controls-ch [:track-external-link-clicked {:event "Auth GitHub"
-                                                                                :properties {:source "epilog-cta"}
-                                                                                :path (auth-url)}])}
-                 "Sign Up Free"]
-                [:div.home-cover]
-                [:div.home-top-shelf]
-                [:div.home-slogans
-                 [:h1.slogan.proverb {:item-prop "So, ready to ship faster?"
-                                      :alt       "Let's just authorize first."}
-                  "So, ready to ship faster?"]
-                 [:h3.slogan.context {:item-prop "Next you'll just need to sign in using your GitHub account."
-                                      :alt       "Signing up using your GitHub login lets us start really fast."}
-                  "Next you'll just need to sign in using your GitHub account."]
-                 [:h3.slogan.context {:item-prop "Still not convinced yet? Check out our pricing."
-                                      :alt       "Currently, we must request permissions in bulk."}
-                  "Still not convinced yet? Check out our "
-                  [:a {:href "pricing"} "pricing"]
-                  "."]]
-                [:div.home-avatars
-                 [:div.avatars
-                  [:div.avatar-github
-                   (common/ico :github)]
-                  [:div.avatar-circle
-                   (common/ico :logo)]]]
-                [:div.home-bottom-shelf
-                 [:span.home-footer-bait
-                  "About Us"
-                  (common/ico :chevron-down)]
-                 [:div.home-footer
-                  [:div.home-footer-logo
-                   (common/ico :logo)]
-                  [:nav.home-footer-about
-                   [:h5 "CircleCI"]
-                   [:a {:href "/tour"} "Tour"]
-                   [:a {:href "/about"} "About"]
-                   [:a "Support"]
-                   [:a {:href "/about#contact"} "Press"]
-                   [:a {:href "/jobs"} "Jobs"]
-                   [:a {:href "http://blog.circleci.com"} "Blog"]]
-                  [:nav.home-footer-product
-                   [:h5 "Product"]
-                   [:a {:href "/pricing"} "Pricing"]
-                   [:a {:href "/docs"} "Documentation"]
-                   [:a {:href "/privacy"} "Privacy"]
-                   [:a {:href "/security"} "Security"]
-                   [:a {:href "/enterprise"} "Enterprise"]
-                   [:a {:href "/changelog"} "Changelog"]]
-                  [:nav.home-footer-contact
-                   [:h5 "Contact"]
-                   [:a {:href "https://twitter.com/circleci"} "Twitter"]
-                   [:a {:href "mailto:sayhi@circleci.com"} "Email"]
-                   [:a {:href ""} "Support Chat"]
-                   [:a {:href ""} "Phone"]
-                   [:a {:href "https://goo.gl/maps/uhkLn"} "San Francisco, CA"]]]]]])))))
+       :header-bkg-invisible false})
+    om/IRender
+    (render [_]
+      (html
+       [:div.home.page
+        ;; xxx less stuff in here
+        (om/build nav (om/get-state owner))
+        (om/build prolog {} {:opts {:logo-visibility-callback
+                                    (fn [visible?]
+                                      (om/set-state! owner :header-logo-visible visible?))
+                                    :cta-visibility-callback
+                                    (fn [visible?]
+                                      (om/set-state! owner :header-cta-visible visible?))
+                                    :prolog-visibility-callback
+                                    (fn [visible?]
+                                      (om/set-state! owner :header-bkg-visible (not visible?)))
+                                    :header-overlap-callback
+                                    (fn [visible-px]
+                                      (om/set-state! owner :header-bkg-scroller visible-px))}})
+        (om/build purpose {})
+        (om/build practice (select-in app [state/customer-logo-customer-path
+                                           state/selected-toolset-path]))
+        ;; xxx: use state and determine animate elsewhere
+        (om/build potential {})
+        (om/build epilog {} {:opts {:cta-visibility-callback
+                                    (fn [visible?]
+                                      (om/set-state! owner :header-cta-invisible visible?))
+                                    :epilog-visibility-callback
+                                    (fn [visible?]
+                                      (om/set-state! owner :header-bkg-invisible visible?))
+                                    :header-overlap-callback
+                                    (fn [visible-px]
+                                      (om/set-state! owner :header-bkg-scroller visible-px))}})]))))


Make sure no lazyness left

Make sure no lazyness left

--- src/adventofcode1/twentyfour.clj
+++ src/adventofcode1/twentyfour.clj
@@ -569,6 +569,14 @@
 ;; cannot check :ret since we return a tree
 ;;        :ret  ::breadcrumbs

+;; single threaded (map)
+;; adventofcode1.twentyfour> (mw.utils/timed "foo" (solve-1 sample3))
+;; "Timed: foo solve-1: 13.047683 msecs"
+;; nil
+;; adventofcode1.twentyfour> (mw.utils/timed "foo" (solve-1 puzzle-input-a))
+;; "Timed: foo solve-1: 26944.700927 msecs"
+;; nil
+
 (defn find-breadcrumbs
   ([puzzle](find-breadcrumbs [(get (:dp puzzle) \0)] #{} puzzle))
   ([breadcrumbs digits puzzle]
@@ -593,16 +601,25 @@
       nil
       (doseq [x res] (print-res x)))))

+(defn count-res
+  [res]
+  (if (map? res)
+    1
+    (if (= res :deadend)
+      0
+      (apply + (map count-res res)))))
+
 (defn solve-1
   [puzzle]
   (let [board (board-make puzzle)
         all (all-paths board)
-        _ (println all)
+        ;; _ (println all)
         numcells (numbered-cells board)
         pd (pos-digit-map numcells)
         dp (digit-pos-map numcells)
         res (find-breadcrumbs {:all all :dp dp :pd pd})]
-    (print-res res)))
+;;    (print-res res)
+    (count-res res)))

 (clojure.spec.test/instrument)


test clean up, verify* refactor not worth the macro :shit: ing required

--- test/github_contributions/github_test.clj
+++ test/github_contributions/github_test.clj
@@ -7,35 +7,26 @@
             [github-contributions.github :as github]))

 (defn send-event-fn [& args])
+(def sse-context {:request {}})

 (defn stream-contributions
   []
- (github/stream-contributions send-event-fn))
-
-(defn yo [& args]
-  "yo")
-
-(deftest yo-test
-  (mocking [yo]
-           (yo :first)
-           (yo :second)
-           (verify-nth-call-args-for 1 yo :first)
-           (verify-nth-call-args-for 2 yo :second)))
+  (github/stream-contributions send-event-fn sse-context "defunkt"))

 (deftest stream-contributions-receives-403-from-github
   []
   (with-redefs [repos/user-repos (constantly fixtures/response-403)]
     (mocking [send-event-fn]
-             (github/stream-contributions send-event-fn {} "defunkt")
-             (verify-nth-call-args-for 1 send-event-fn {} "error"
+             (stream-contributions)
+             (verify-nth-call-args-for 1 send-event-fn sse-context "error"
                                        "Rate limit has been exceeded for Github's API. Please try again later."))))

 (deftest stream-contributions-receives-404-from-github
   []
   (with-redefs [repos/user-repos (constantly fixtures/response-404)]
     (mocking [send-event-fn]
-             (github/stream-contributions send-event-fn {} "defunkt")
-             (verify-nth-call-args-for 1 send-event-fn {} "error"
+             (stream-contributions)
+             (verify-nth-call-args-for 1 send-event-fn sse-context "error"
                                        "Received a 404 from Github. Please try again later."))))

 ;; because conjure only supports =
@@ -48,13 +39,14 @@
                 repos/specific-repo (constantly fixtures/response-specific-repo)
                 repos/contributors (constantly fixtures/response-contributors)]
     (mocking [send-event-fn]
-             (github/stream-contributions send-event-fn {} "defunkt")
-             (verify-nth-call-args-for 1 send-event-fn {} "message"
+             (stream-contributions)
+             (verify-nth-call-args-for 1 send-event-fn sse-context "message"
                                        "defunkt has 1 forks. Fetching data...")
-             (verify-nth-call-args-for 2 send-event-fn {} "results" expected-row)
-             (verify-nth-call-args-for 3 send-event-fn {} "message"
+             (verify-nth-call-args-for 2 send-event-fn sse-context "results" expected-row)
+             (verify-nth-call-args-for 3 send-event-fn sse-context "message"
                                        "defunkt has contributed to 1 of 1 forks.")
-             (verify-nth-call-args-for 4 send-event-fn {} "end-message" "defunkt"))))
+             (verify-nth-call-args-for 4 send-event-fn sse-context "end-message" "defunkt"))))
+
 (deftest rank-ending-test
   (are [num ending]
        (is (= ending (github/rank-ending num)))


document that against-background can be nested

--- test/midje/sweet/t_background_nesting.clj
+++ test/midje/sweet/t_background_nesting.clj
@@ -23,15 +23,17 @@
 		       (fact (+ (middlemost) (outermost)) => 35))))

-(deftest three-levels-of-nesting
+(deftest three-levels-of-nesting-one-duplicated
   (in-separate-namespace
    (background (outermost) => 2
 	       (middlemost) => 'a)
-   (against-background [ (middlemost) => 33
+   (against-background [ (middlemost 2) => 33
 			 (innermost) => 'c]
-		       (fact
-			 (against-background (innermost) => 8)
-			 (+ (middlemost) (outermost) (innermost)) => 43))))
+
+     (against-background [ (middlemost 1) => -43 ]
+       (fact
+	 (against-background (innermost) => 8)
+	 (+ (middlemost 2) (middlemost 1) (outermost) (innermost)) => 0)))))

 (against-background [ (middlemost) => 33 ]
   (deftest spanning-deftest-does-not-work


Include low level number in route ID

Include low level number in route ID

--- src/route_ccrs/active_routes.clj
+++ src/route_ccrs/active_routes.clj
@@ -7,6 +7,7 @@
 (defn route-id [r]
   (select-keys r [:contract
                   :part_no
+                  :lowest_level
                   :bom_type_db
                   :routing_revision_no
                   :routing_alternative_no]))


Fixed messages not being read by logstash.

--- src/riemann/logstash.clj
+++ src/riemann/logstash.clj
@@ -109,4 +109,4 @@
       (when (:metric event)
         (with-pool [client pool (:claim-timeout opts)]
                    (let [string (event-to-json (merge event {:source (:host event)}))]
-                     (send-line client string)))))))+                     (send-line client (str string "\n"))))))))


Makefile.boot: FSTAR_BOOT: allowing to select a different binary for bootstrapping

--- src/Makefile.boot
+++ src/Makefile.boot
@@ -23,9 +23,10 @@

 CACHE_DIR?=./.cache.boot

-FSTAR_C=$(FSTAR) $(OTHERFLAGS) --cache_checked_modules	        	\
-        --lax --MLish --no_location_info              \
-	--odir ocaml-output $(addprefix --include , $(INCLUDE_PATHS))   \
+FSTAR_BOOT ?= $(FSTAR)
+FSTAR_C=$(FSTAR_BOOT) $(OTHERFLAGS) --cache_checked_modules		\
+        --lax --MLish --no_location_info				\
+        --odir ocaml-output $(addprefix --include , $(INCLUDE_PATHS))	\
         --warn_error -272-241 --cache_dir $(CACHE_DIR)

 # Each "project" for the compiler is in its own namespace.  We want to


Changes min-length for name field from 5 to 3.

--- src/cadence/model/validators.clj
+++ src/cadence/model/validators.clj
@@ -13,8 +13,8 @@
   (rule (re-find #"^[a-zA-z][\-_\w]*( [\-_\w]+)*\w$" username)
         [:username (str "Username must match this regex "
                         "/^[a-zA-z][\\-_\\w]*( [\\-_\\w]+)*\\w$/")])
-  (rule (min-length? name 5)
-        [:name "Your name must be at least 5 characters long."])
+  (rule (min-length? name 3)
+        [:name "Your name must be at least 3 characters long."])
   (rule (max-length? name 64)
         [:name "Your name must be no more than 64 characters long."])
   (rule (re-find #"^[a-zA-z][\-'\w]*( [\-'\w]+)*\w$" name)


Show 30 events in the latest events

Show 30 events in the latest events

--- src/servisne_info/repository.clj
+++ src/servisne_info/repository.clj
@@ -105,7 +105,7 @@
   (mq/with-collection @db "events"
     (mq/find {})
     (mq/sort (array-map :created-at -1))
-    (mq/limit 100)))
+    (mq/limit 30)))

 (defn find-events-for-yesterday []
   (let [today (t/plus (t/now) (t/minutes 60))


Add some TODO's.

Add some TODO's.

--- src/cats/core.cljc
+++ src/cats/core.cljc
@@ -67,6 +67,8 @@
    (let [ctx (ctx/get-current ctx-or-val)]
      (p/mempty ctx))))

+
+;; TODO: improve performance
 (defn mappend
   [& svs]
   {:pre [(seq svs)]}
@@ -121,6 +123,8 @@
    (p/mzero (ctx/get-current)))
   ([ctx]
    (p/mzero ctx)))
+
+;; TODO: improve performance

 (defn mplus
   [& mvs]
@@ -163,6 +167,8 @@
   (let [ctx (ctx/get-current af)]
     (reduce (partial p/fapply ctx) af avs)))

+
+;; TODO: review it, seems wrong and should be a macro?
 (defn when
   "Given an expression and a monadic value,
   if the expression is logical true, return the monadic value.
@@ -172,6 +178,7 @@
   ([ctx b mv]
    (if b mv (return ctx nil))))

+;; TODO: review it, seems wrong and should be a macro?
 (defn unless
   "Given an expression and a monadic value,
   if the expression is not logical true, return the monadic value.


bug fixes in calling the listener

bug fixes in calling the listener

--- sherpa-clojure/src/main/clojure/sherpa/sherpa_server.clj
+++ sherpa-clojure/src/main/clojure/sherpa/sherpa_server.clj
@@ -37,12 +37,17 @@
                    :message (str "Unknown message type: " msg)}))

 (defmacro add-rpc [msg return-type]
-  `(defmethod sherpa-rpc ~msg [m# listener# request#]
-              (to-avro (assoc
-                           ((symbol ~msg) listener# PROTOCOL (from-avro (.get request# ~msg)
-                                                                        PROTOCOL))
-                         :sherpa-type (keyword ~return-type))
-                       PROTOCOL)))
+  `(defmethod sherpa-rpc ~msg [m# listener# avro-request#]
+              (println "query rpc, msg=" ~msg ", avro-req=" avro-request#)
+              (let [request# (from-avro avro-request# PROTOCOL)
+                    x# (println "  req=" request#)
+                    protocol-fn# ~(resolve (symbol msg))
+                    x# (println "  protocol-fn=" protocol-fn#)
+                    response# (protocol-fn# listener# request#)
+                    x# (println "  response=" response#)
+                    avro-resp# (to-avro (assoc response# :sherpa-type (keyword ~return-type)) PROTOCOL)
+                    x# (println "  avro-resp=" avro-resp#)]
+                avro-resp#)))

 (add-rpc "query" "QueryResponse")
 (add-rpc "data" "DataResponse")
@@ -56,7 +61,8 @@
                      (reify MessageResponder
                             (respond [this msg request]
                                      (try
-                                       (sherpa-rpc (.getName msg) listener PROTOCOL (.get request ))
+                                       (let [msg-name (.getName msg)]
+                                         (sherpa-rpc msg-name listener (.get request msg-name)))
                                        (catch Throwable t
                                          (do (.printStackTrace t)
                                              ;; TODO throw ErrorResponse


implement Akshara Sareen

implement Akshara Sareen

--- src/clj/game/cards-resources.clj
+++ src/clj/game/cards-resources.clj
@@ -29,6 +29,12 @@
                                  :choices {:req #(and (card-is? % :side :runner) (installed? %) (not (card-is? % :cid (:cid card))))}
                                  :effect (effect (gain :credit 3) (trash target {:unpreventable true}))}
                                card nil))}]}
+
+   "Akshara Sareen"
+   {:in-play [:click 1 :click-per-turn 1]
+    :msg "give each player 1 additional [Click] to spend during their turn"
+    :effect (effect (gain :corp :click-per-turn 1))
+    :leave-play (effect (lose :corp :click-per-turn 1))}

    "Always Be Running"
    {:abilities [{:once :per-turn


Reorganize

Reorganize

--- src/clj/sparqlab/routes/home.clj
+++ src/clj/sparqlab/routes/home.clj
@@ -163,19 +163,19 @@
                                                 :prohibited (map :prohibited prohibits)
                                                 :required (map :required requires)
                                                 :lang lang)
-            exercise-status (get (cookie/get-exercise-statuses request) id)]
-        (cond-> (layout/render "evaluation.html"
-                               (assoc (merge (i18n/base-locale request)
-                                             (i18n/base-evaluation-locale request)
-                                             exercise
-                                             verdict)
-                                      :title (tr [:evaluation/title] [(:name exercise)])))
+            exercise-status (get (cookie/get-exercise-statuses request) id)
+            data (merge (i18n/base-locale request)
+                        (i18n/base-evaluation-locale request)
+                        exercise
+                        verdict
+                        {:title (tr [:evaluation/title] [(:name exercise)])})]
+        (cond-> (layout/render "evaluation.html" data)
           (and (:equal? verdict) (not= exercise-status "revealed")) (cookie/mark-exercise-as-solved id)))
-      (layout/render "sparql_syntax_error.html"
-                     (merge (i18n/base-locale request)
-                            (assoc (format-invalid-query validation-result)
-                                   :expected-label (tr [:sparql-syntax-error/expected-label])
-                                   :title (tr [:sparql-syntax-error/title])))))))
+      (let [error-data (merge (i18n/base-locale request)
+                              (format-invalid-query validation-result)
+                              {:expected-label (tr [:sparql-syntax-error/expected-label])
+                               :title (tr [:sparql-syntax-error/title])})]
+        (layout/render "sparql_syntax_error.html" error-data)))))

 (defn search-exercises
   "Search exercises for a `search-term` or several SPARQL `search-constructs`."


pass coerced value into update processing

--- src/clj_money/models/helpers.clj
+++ src/clj_money/models/helpers.clj
@@ -1,5 +1,6 @@
 (ns clj-money.models.helpers
   (:require [clojure.pprint :refer [pprint]]
+            [clojure.tools.logging :as log]
             [clj-money.util :refer [pprint-and-return]]
             [clj-money.coercion :as coercion]
             [clj-money.validation :as validation]
@@ -98,6 +99,6 @@
       (let [validated (validate options s model)]
         (if (validation/valid? validated)
           (do
-            (process-options options s model :before-save :update)
-            ((:find options) s (:id model)))
+            (process-options options s validated :before-save :update)
+            ((:find options) s (:id validated)))
           validated)))))


How can a space, not be a space?

How can a space, not be a space?

--- src/cljs/clj_mini_apps/pong.cljs
+++ src/cljs/clj_mini_apps/pong.cljs
@@ -84,7 +84,7 @@
         btm-wall (- 490 7.5)
         new-y-dir (if (= + (:y-dir (:ball state))) - +)]
     ;Check wall collision
-    (if (not (and (> ball-y top-wall)
+    (if (not (and (> ball-y top-wall)
                   (< ball-y btm-wall)))
       (as-> state state
         (assoc-in state [:ball :y-dir] new-y-dir)


Do not translate the brand

Do not translate the brand

--- src/cljs/hyphen_keeper/i18n.cljs
+++ src/cljs/hyphen_keeper/i18n.cljs
@@ -9,7 +9,7 @@
   (or (lang-attribute) "de"))

 (def translations
-  {:en {:brand "Hyphenation"
+  {:en {:brand "Hyphenations"
         :edit "Edit"
         :delete "Delete"
         :save "Save"
@@ -34,7 +34,7 @@
         :similar "Similar words"
         :old-spelling "Old Spelling"
         :new-spelling "New Spelling"}
-   :de {:brand "Trennungen"
+   :de {:brand "Hyphenations"
         :edit "Editieren"
         :delete "Löschen"
         :save "Speichern"


Fix link location in welcome page

Fix link location in welcome page

--- src/cljs/salava/badge/ui/my.cljs
+++ src/cljs/salava/badge/ui/my.cljs
@@ -175,7 +175,7 @@
       [:li
        "Add a " [:a {:href "/user/edit/profile"} "profile picture"]
        ", a short bio or contact information to your "
-       [:a {:href "/user/edit"} "profile"] "."]
+       [:a {:href (str "/user/profile/" (session/get-in [:user :id]))} "profile"] "."]
       [:li
        "Do you already have Open Badges saved to Mozilla Backpack? "
        [:a {:href "/badge/import"} "Import your badges"]


Mark code only for clj

Mark code only for clj

--- src/spec_tools/core.cljc
+++ src/spec_tools/core.cljc
@@ -266,21 +266,23 @@

 #?(:clj (declare ^:private coll-spec-fn))

-(defn- -vector [env n v]
-  (if-not (= 1 (count v))
-    (throw
-      (ex-info
-        "only single maps allowed in nested vectors"
-        {:k n :v v}))
-    `(spec (s/coll-of ~(coll-spec-fn env n (first v)) :into []))))
-
-(defn- -set [env n v]
-  (if-not (= 1 (count v))
-    (throw
-      (ex-info
-        "only single maps allowed in nested sets"
-        {:k n :v v}))
-    `(spec (s/coll-of ~(coll-spec-fn env n (first v)) :into #{}))))
+#?(:clj
+   (defn- -vector [env n v]
+     (if-not (= 1 (count v))
+       (throw
+         (ex-info
+           "only single maps allowed in nested vectors"
+           {:k n :v v}))
+       `(spec (s/coll-of ~(coll-spec-fn env n (first v)) :into [])))))
+
+#?(:clj
+   (defn- -set [env n v]
+     (if-not (= 1 (count v))
+       (throw
+         (ex-info
+           "only single maps allowed in nested sets"
+           {:k n :v v}))
+       `(spec (s/coll-of ~(coll-spec-fn env n (first v)) :into #{})))))

 #?(:clj
    (defn- -map [env n m]


[#10976] Fix crash on kk login with enabled fingerprint

--- src/status_im/keycard/login.cljs
+++ src/status_im/keycard/login.cljs
@@ -167,6 +167,7 @@
                    :save-password? true))
        :keycard/login-with-keycard
        {:multiaccount-data multiaccount-data
+        :key-uid           key-uid
         :password          encryption-public-key
         :chat-key          whisper-private-key}})))


OK if #include files don't exist

OK if #include files don't exist

They could be only necessary for a given profile. Better to flag a
warning that abort.

--- src/aero/core.clj
+++ src/aero/core.clj
@@ -56,7 +56,10 @@

 (defmethod reader 'include
   [opts tag value]
-  (read-config value opts))
+  (let [f (io/file value)]
+    (if (.exists f)
+      (read-config value opts)
+      {:aero/warning (format "Configuration file does not exist: %s" f)})))

 (defmethod reader 'join
   [opts tag value]


correctly check if the host is about to do gc

--- src/bzzz/core.clj
+++ src/bzzz/core.clj
@@ -30,15 +30,15 @@
   (< (- @timer* (get (second than) :update 0)) @acceptable-discover-time-diff*))

 (defn not-doing-gc? [than]
-  (let [diff (- @timer* (get (second than) :next-gc-at (+ @timer* 100000)))]
-    (not (< 2 (abs diff))))) ;; regardless if we are 2 seconds before gc
+  (let [diff (- (get (second than) :next-gc-at (+ @timer* 100000)) @timer*)]
+    (not (< (abs diff) 2)))) ;; regardless if we are 2 seconds before gc
                              ;; or 2 seconds after, try to skip this host

 (defn possible-hosts [list]
   (let [p (filter #(and (rescent? %1) (not-doing-gc? %1)) list)]
     (if (= 0 (count p))
       (let [more (filter rescent? list)]
-        (log/info "found host after ignoring the non gc ones, dump:" list @timer* @discover-hosts* @peers*)
+        (log/info "found host after ignoring the gcing ones, dump:" list @timer* @discover-hosts* @peers*)
         (first (rand-nth more)))
       (first (rand-nth p)))))


maybe we need allow-methods :-)

maybe we need allow-methods :-)

--- src/xhr_redirect_demo/server.clj
+++ src/xhr_redirect_demo/server.clj
@@ -53,6 +53,7 @@
     (let [resp-json (json/generate-string [which (get @calls (:uri req))])
           hdr-base {"Content-Type" "application/json;charset=utf-8"
                     "Access-Control-Allow-Origin" "*"
+                    "Access-Control-Allow-Methods" "GET,OPTIONS"
                     "Access-Control-Allow-Headers" "Xhr-Demo-Request"
                     "Access-Control-Expose-Headers" "Xhr-Demo-Response"}]
       (case (:request-method req)


Refine postgres.filter/sql-str spec to be used from maps

--- backend/specs/akvo/lumen/specs/postgres.clj
+++ backend/specs/akvo/lumen/specs/postgres.clj
@@ -35,7 +35,7 @@

 (s/fdef postgres.filter/sql-str
   :args (s/cat
-	 :columns ::db.dsv/columns
+	 :columns (s/coll-of ::db.dsv/column :distinct true)
 	 :filters (s/nilable ::postgres.filter/filters))
   :ret string?)


[#875] Fix add-tenant regression

[#875] Fix add-tenant regression

--- backend/src/akvo/lumen/admin/add_tenant.clj
+++ backend/src/akvo/lumen/admin/add_tenant.clj
@@ -15,8 +15,8 @@
   - https://login.akvo.org for production
   - https://kc.akvotest.org for the test environment"
   (:require [akvo.lumen.admin.util :as util]
+            [akvo.lumen.component.keycloak :as keycloak]
             [akvo.lumen.config :refer [error-msg]]
-            [akvo.lumen.component.keycloak :as keycloak]
             [akvo.lumen.lib.share-impl :refer [random-url-safe-string]]
             [akvo.lumen.util :refer [conform-email squuid]]
             [cheshire.core :as json]
@@ -88,7 +88,7 @@
     (if (= (:kc-url env) "http://localhost:8080")
       (when (= (.getProtocol url) "https")
         (throw (ex-info "Use http in development mode" {:url v})))
-      (when (= (.getProtocol url) "https")
+      (when (not= (.getProtocol url) "https")
         (throw (ex-info "Url should use https" {:url v}))))
     (format "%s://%s" (.getProtocol url) (.getHost url))))


[#1276] Fix test case - trimmable titles

--- backend/test/akvo/lumen/import/csv_test.clj
+++ backend/test/akvo/lumen/import/csv_test.clj
@@ -61,4 +61,4 @@
                                                                 :version 1})
           titles (map :title (:rows dataset))
           trimmable? #(or (string/starts-with? " " %) (string/ends-with? " " %))]
-      (is (not (every? trimmable? titles))))))
+      (is (every? trimmable? titles)))))


todomvc: removed unused import

todomvc: removed unused import

--- examples/todomvc/src/app/spec.cljs
+++ examples/todomvc/src/app/spec.cljs
@@ -4,8 +4,7 @@
             [app.reconciler :refer [reconcile]]
             [middleware.schema :as schema]
             [middleware.history :as h]
-            [middleware.persistence :as persistence]
-            [middleware.devtools :as devtools]))
+            [middleware.persistence :as persistence]))

 (defn new-spec
   [history storage storage-key todo-titles]
@@ -16,6 +15,6 @@

       ; Debugger deals with persistence itself, so we have to blacklist it here to get rid of loading conflicts.
       ; Token is blacklisted because on start we want the app to read current token from the urlbar instead of storage.
-      (persistence/add storage storage-key {:blacklist #{::devtools/debugger ::h/token}})
+      (persistence/add storage storage-key {:blacklist #{:middleware.devtools/debugger ::h/token}})

       (h/add history)))


Added local variable visitor.

Added local variable visitor.

--- src/dependency_reader/reader.clj
+++ src/dependency_reader/reader.clj
@@ -23,70 +23,74 @@
                                  })

 (defn- fix-type-name
-  [^String name]
-  (.replaceAll (.replaceAll name "/" ".") "\\[\\]" ""))
+  [^String type-name]
+  (.replaceAll (.replaceAll type-name "/" ".") "\\[\\]" ""))

-(defn- fix-field-descriptor
-  [^String field-descriptor]
-  (fix-type-name (.getClassName (org.objectweb.asm.Type/getType field-descriptor))))
+(defn- fix-descriptor
+  [^String desc]
+  (fix-type-name (.getClassName (org.objectweb.asm.Type/getType desc))))

 (defn- add-dependency
-  [dependencies dependency type]
+  [dependencies dependency dependency-type]
   (if (contains? dependencies dependency)
     (let [existing-dependency-types (dependencies dependency)
-          new-dependency-types      (conj existing-dependency-types type)]
+          new-dependency-types      (conj existing-dependency-types dependency-type)]
       (merge dependencies {dependency new-dependency-types}))
-    (merge dependencies {dependency #{type}})))
+    (merge dependencies {dependency #{dependency-type}})))

 (defn- add-dependencies
-  [dependencies new-dependencies type]
+  [dependencies new-dependencies dependency-type]
   (if (or (empty? new-dependencies) (nil? new-dependencies))
     dependencies
     (loop [result                 dependencies
            current-dependency     (first new-dependencies)
            remaining-dependencies (rest  new-dependencies)]
       (if (empty? remaining-dependencies)
-        (add-dependency result current-dependency type)
-        (recur (add-dependency result current-dependency type) (first remaining-dependencies) (rest remaining-dependencies))))))
+        (add-dependency result current-dependency dependency-type)
+        (recur (add-dependency result current-dependency dependency-type) (first remaining-dependencies) (rest remaining-dependencies))))))

 (defn- typeof
-  [access]
+  [access-bitmask]
   (cond
-    (not= 0 (bit-and access org.objectweb.asm.Opcodes/ACC_INTERFACE))  :interface
-    (not= 0 (bit-and access org.objectweb.asm.Opcodes/ACC_ENUM))       :enum
-    (not= 0 (bit-and access org.objectweb.asm.Opcodes/ACC_ANNOTATION)) :annotation
+    (not= 0 (bit-and access-bitmask org.objectweb.asm.Opcodes/ACC_INTERFACE))  :interface
+    (not= 0 (bit-and access-bitmask org.objectweb.asm.Opcodes/ACC_ENUM))       :enum
+    (not= 0 (bit-and access-bitmask org.objectweb.asm.Opcodes/ACC_ANNOTATION)) :annotation
     :else                                                              :class))

 (defn- visit
-  [class-info version access class-name signature super-name interfaces]
+  [class-info version access-bitmask class-name signature super-name interfaces]
   (let [fixed-class-name      (fix-type-name class-name)
         fixed-super-name      (fix-type-name super-name)
         fixed-interface-names (vec (map fix-type-name interfaces))
         existing-dependencies (:dependencies class-info)]
     (merge class-info
            { :name              fixed-class-name
-             :type              (typeof access)
+             :type              (typeof access-bitmask)
              :class-version     version
              :class-version-str (version-name-map version)
              :dependencies      (add-dependency (add-dependencies existing-dependencies fixed-interface-names :implements)
                                                 fixed-super-name :extends) } )))

 (defn- visit-field
-  [class-info access field-name desc signature value]
-  (merge class-info
-         {
-           :dependencies (add-dependency (:dependencies class-info) (fix-field-descriptor desc) :uses)
-         }))
+  [class-info access-bitmask field-name desc signature value]
+  (let [existing-dependencies (:dependencies class-info)
+        fixed-field-name      (fix-descriptor desc)]
+    (merge class-info
+           {
+             :dependencies (add-dependency existing-dependencies fixed-field-name :uses)
+           })))

 (defn- visit-annotation
-  [class-info name desc]
-  (merge class-info
-         {
-           ;####TODO!!!!
-         }))
+  [class-info annotation-name desc]
+  (let [existing-dependencies (:dependencies class-info)
+        fixed-annotation-type (fix-descriptor desc)]
+    (merge class-info
+           {
+             :dependencies (add-dependency existing-dependencies fixed-annotation-type :inner-class)  ; Not sure this is correct
+           })))

 (defn- visit-method
-  [class-info access name ^String desc signature exceptions]
+  [class-info access-bitmask method-name ^String desc signature exceptions]
   (let [fixed-exception-names (vec (map fix-type-name exceptions))
         existing-dependencies (:dependencies class-info)
         argument-types        (vec (map #(fix-type-name (.getClassName %)) (org.objectweb.asm.Type/getArgumentTypes desc)))
@@ -96,12 +100,24 @@
              :dependencies (add-dependency (add-dependencies (add-dependencies existing-dependencies fixed-exception-names :uses) argument-types :uses) return-type :uses)
            })))

+; This doesn't appear to be useful - it's called regardless of who the parent of the inner class is
 (defn- visit-inner-class
-  [class-info name outer-name inner-name access]
-  (merge class-info
-         {
-           ;####TODO!!!!
-         }))
+  [class-info inner-class-name outer-name inner-name access-bitmask]
+  (let [existing-dependencies  (:dependencies class-info)
+        fixed-inner-class-name (fix-type-name inner-class-name)]
+    (merge class-info
+           {
+             :dependencies (add-dependency existing-dependencies fixed-inner-class-name :inner-class)
+           })))
+
+(defn- visit-local-variable
+  [class-info local-variable-name desc signature start end index]
+  (let [existing-dependencies     (:dependencies class-info)
+        fixed-local-variable-type (fix-descriptor desc)]
+    (merge class-info
+           {
+             :dependencies (add-dependency existing-dependencies fixed-local-variable-type :uses)
+           })))

 (defn class-info
   "Takes an input stream of class bytes and returns the dependencies it contains as a map with this shape:
@@ -111,7 +127,7 @@
     :type                  :class OR :interface OR :annotation OR :enum
     :class-version         49
     :class-version-str     \"1.5\"
-    :dependencies          {\"dependentTypeName\" #{:extends :implements :uses :inner-class}
+    :dependencies          {\"dependentTypeName\" #{:extends :implements :uses :inner-class :parent-class}
                             ...}
   }

@@ -122,9 +138,9 @@
                                    :class-version     nil
                                    :class-version-str nil
                                    :dependencies      {} })
-        class-reader       (new org.objectweb.asm.ClassReader class-input-stream)
+        class-reader       (org.objectweb.asm.ClassReader. class-input-stream)
         annotation-visitor (proxy [org.objectweb.asm.AnnotationVisitor]
-                                  [org.objectweb.asm.Opcodes/ASM4] ; constructor params
+                                  [org.objectweb.asm.Opcodes/ASM4]
                                   (visitAnnotation [name desc]
                                     (swap! result visit-annotation name desc)
                                     ;annotation-visitor))
@@ -133,20 +149,22 @@
                                   [org.objectweb.asm.Opcodes/ASM4]
                                   (visitAnnotation [desc visible]
                                     annotation-visitor))
-        class-visitor      (proxy [org.objectweb.asm.ClassVisitor] ; classes & interfaces
-                                  [org.objectweb.asm.Opcodes/ASM4] ; constructor params
-                                  ; Overridden functions
-                                  (visit [version access class-name signature super-name interfaces]
-                                    (swap! result visit version access class-name signature super-name interfaces))
-                                  (visitField [access field-name desc signature value]
-                                    (swap! result visit-field access field-name desc signature value)
+        method-visitor     (proxy [org.objectweb.asm.MethodVisitor]
+                                  [org.objectweb.asm.Opcodes/ASM4]
+                                  (visitLocalVariable [local-variable-name desc signature start end index]
+                                    (swap! result visit-local-variable local-variable-name desc signature start end index)))
+        class-visitor      (proxy [org.objectweb.asm.ClassVisitor]
+                                  [org.objectweb.asm.Opcodes/ASM4]
+                                  (visit [version access-bitmask class-name signature super-name interfaces]
+                                    (swap! result visit version access-bitmask class-name signature super-name interfaces))
+                                  (visitField [access-bitmask field-name desc signature value]
+                                    (swap! result visit-field access-bitmask field-name desc signature value)
                                     field-visitor)
-                                  (visitMethod [access name desc signature exceptions]
-                                    (swap! result visit-method access name desc signature exceptions)
-                                    ;method-visitor)
-                                    nil)
-                                  (visitInnerClass [name outer-name inner-name access]
-                                    (swap! result visit-inner-class name outer-name inner-name access))
+                                  (visitMethod [access-bitmask method-name desc signature exceptions]
+                                    (swap! result visit-method access-bitmask method-name desc signature exceptions)
+                                    method-visitor)
+;                                  (visitInnerClass [inner-class-name outer-name inner-name access-bitmask]
+;                                    (swap! result visit-inner-class inner-class-name outer-name inner-name access-bitmask))
                            )]
     (.accept class-reader class-visitor 0)
     @result))
@@ -154,5 +172,5 @@
 (defn class-info-from-file
   "As per class-file, but takes a filename as a string."
   [^String filename]
-  (with-open [class-input-stream (new java.io.FileInputStream filename)]
-    (class-info class-input-stream)))+  (with-open [class-input-stream (java.io.BufferedInputStream. (java.io.FileInputStream. filename))]
+    (class-info class-input-stream)))


Debugging build system by updating backend version

--- backend/project.clj
+++ backend/project.clj
@@ -1,4 +1,4 @@
-(defproject org.akvo/lumen "0.24.0-SNAPSHOT"
+(defproject org.akvo/lumen "0.25.0-SNAPSHOT"
   :description "Akvo Lumen backend"
   :url "https://github.com/akvo/akvo-lumen"
   :license {:name "GNU Affero General Public License 3.0"


entities - 1st cut of rejig to new code style.

--- src/kixi/hecuba/api/entities.clj
+++ src/kixi/hecuba/api/entities.clj
@@ -10,63 +10,75 @@
    [liberator.core :refer (defresource)]
    [liberator.representation :refer (ring-response)]))

+(defn index-post! [commander querier ctx]
+  (let [request           (-> ctx :request)
+        entity            (-> request decode-body stringify-values)
+        project-id        (-> entity :project-id)
+        property-code     (-> entity :property-code)
+        [username _]      (sec/get-username-password request querier)
+        user-id           (-> (hecuba/items querier :user {:username username}) first :id)]
+    (when (and project-id property-code)
+      (when-not (empty? (first (hecuba/items querier :project {:id project-id})))
+        {:entity-id (hecuba/upsert! commander :entity (-> entity
+                                                          (assoc :user-id user-id)
+                                                          (dissoc :device-ids)))}))))
+
+(defn index-handle-created [handlers ctx]
+  (let [request (:request ctx)
+        routes  (:modular.bidi/routes ctx)
+        id      (:entity-id ctx)]
+    (if-not (empty? id)
+      (let [location (bidi/path-for routes (:entity @handlers) :entity-id id)]
+        (when-not location
+          (throw (ex-info "No path resolved for Location header"
+                          {:entity-id id})))
+        (ring-response {:headers {"Location" location}
+                        :body (json/encode {:location location
+                                            :status "OK"
+                                            :version "4"})}))
+      (ring-response {:status 422
+                      :body "Provide valid projectId and propertyCode."}))))
+
+(defn resource-exists? [querier ctx]
+  (let [id (get-in ctx [:request :route-params :entity-id])]
+    (when-let [item (hecuba/item querier :entity id)]
+      {::item item})))
+
+(defn resource-handle-ok [querier ctx]
+  (let [request      (:request ctx)
+        route-params (:route-params request)
+        ids          (map :id (hecuba/items querier :device route-params))
+        item         (assoc (::item ctx) :device-ids ids)]
+      (util/render-item request item)))
+
+(defn resource-put! [commander querier ctx]
+  (let [request      (:request ctx)
+        entity       (-> request decode-body stringify-values)
+        entity-id    (-> entity :entity-id)
+        [username _] (sec/get-username-password request querier)
+        user-id      (-> (hecuba/items querier :user {:username username}) first :id)]
+      (hecuba/upsert! commander :entity (-> entity
+                                            (assoc :user-id user-id
+                                                   :id entity-id)
+                                            (dissoc :device-ids)))))
+
+(defn [commander ctx]
+  (hecuba/delete! commander :entity (get-in ctx [::item :id])))
+
 (defresource index [{:keys [commander querier]} handlers]
   :allowed-methods #{:post}
   :available-media-types #{"application/json"}
   :known-content-type? #{"application/json"}
   :authorized? (authorized? querier :entity)
-
-  :post!
-  (fn [{request :request}]
-    (let [body          (-> request :body)
-          entity        (-> request decode-body stringify-values)
-          project-id    (-> entity :project-id)
-          property-code (-> entity :property-code)
-          [username _]  (sec/get-username-password request querier)
-          user-id       (-> (hecuba/items querier :user {:username username}) first :id)]
-      (when (and project-id property-code)
-        (when-not (empty? (first (hecuba/items querier :project {:id project-id})))
-          {:entity-id (hecuba/upsert! commander :entity (-> entity
-                                                    (assoc :user-id user-id)
-                                                    (dissoc :device-ids)))}))))
-
-  :handle-created
-  (fn [{{routes :modular.bidi/routes} :request id :entity-id}]
-    (if-not (empty? id)
-      (let [location (bidi/path-for routes (:entity @handlers) :entity-id id)]
-        (when-not location
-          (throw (ex-info "No path resolved for Location header"
-                          {:entity-id id})))
-        (ring-response {:headers {"Location" location} :body (json/encode {:location location :status "OK" :version "4"})}))
-      (ring-response {:status 422 :body "Provide valid projectId and propertyCode."}))))
+  :post! (partial index-post! commander querier)
+  :handle-created (partial index-handle-created ))

 (defresource resource [{:keys [commander querier]} handlers]
   :allowed-methods #{:get :delete :put}
   :available-media-types #{"application/json"}
   :authorized? (authorized? querier :entity)
-
-  :exists?
-  (fn [{{{id :entity-id} :route-params} :request}]
-    (when-let [item (hecuba/item querier :entity id)]
-      {::item item}))
-
-  :handle-ok
-  (fn [{item ::item {mime :media-type} :representation {routes :modular.bidi/routes route-params :route-params} :request :as req}]
-    (let [item (assoc item :device-ids (map :id (hecuba/items querier :device route-params)))]
-      (util/render-item req item)))
-
-  :put!
-  (fn [{request :request}]
-    (let [entity        (-> request decode-body stringify-values)
-          entity-id     (-> entity :entity-id)
-          [username _]  (sec/get-username-password request querier)
-          user-id       (-> (hecuba/items querier :user {:username username}) first :id)]
-      (hecuba/upsert! commander :entity (-> entity
-                                     (assoc :user-id user-id)
-                                     (assoc :id entity-id)
-                                     (dissoc :device-ids)))))
-
+  :exists? (partial resource-exists? querier)
+  :handle-ok (partial resource-handle-ok querier)
+  :put! (partial resource-put! commander)
   :respond-with-entity? (constantly true)
-
-  :delete! (fn [{{id :id :as i} ::item}]
-             (hecuba/delete! commander :entity id)))
+  :delete! (partial resource-delete! commander))


center spinner on dashboard

center spinner on dashboard

--- frontend/components/dashboard.cljs
+++ frontend/components/dashboard.cljs
@@ -30,33 +30,31 @@
             page (js/parseInt (get-in nav-data [:query-params :page] 0))
             builds-per-page (:builds-per-page data)]
         (html
-         [:div#dashboard
-          [:section
-           (cond (nil? builds) [:div.loading-spinner common/spinner]
-                 (and (empty? builds)
-                      projects
-                      (empty? projects)) [:div
-                                          [:h2 "You don't have any projects in CircleCI!"]
-                                          [:p "Why don't you add a repository or two on the "
-                                           [:a {:href (routes/v1-add-projects)} "Manage Projects page"] "?"]]
-                 :else
-                 (list
-                  (when (and plan (show-trial-notice? plan))
-                    (om/build project-common/trial-notice plan))
-                  (om/build builds-table/builds-table builds {:opts {:show-actions? false
-                                                                     :show-branch? (not (:branch nav-data))
-                                                                     :show-project? (not (:repo nav-data))}})
-                  [:div.recent-builds-pager
-                   [:a
-                    {:href (routes/v1-dashboard-path (assoc nav-data :page (max 0 (dec page))))
-                     ;; no newer builds if you're on the first page
-                     :class (when (zero? page) "disabled")}
-                    [:i.fa.fa-long-arrow-left]
-                    [:span " Newer builds"]]
-                   [:a
-                    {:href (routes/v1-dashboard-path (assoc nav-data :page (inc page)))
-                     ;; no older builds if you have less builds on the page than an
-                     ;; API call returns
-                     :class (when (> builds-per-page (count builds)) "disabled")}
-                    [:span "Older builds "]
-                    [:i.fa.fa-long-arrow-right]]]))]])))))
+         (cond (nil? builds) [:div.loading-spinner common/spinner]
+              (and (empty? builds)
+                   projects
+                   (empty? projects)) [:div
+                                       [:h2 "You don't have any projects in CircleCI!"]
+                                       [:p "Why don't you add a repository or two on the "
+                                        [:a {:href (routes/v1-add-projects)} "Manage Projects page"] "?"]]
+              :else
+              [:div.dashboard
+               (when (and plan (show-trial-notice? plan))
+                 (om/build project-common/trial-notice plan))
+               (om/build builds-table/builds-table builds {:opts {:show-actions? false
+                                                                  :show-branch? (not (:branch nav-data))
+                                                                  :show-project? (not (:repo nav-data))}})
+               [:div.recent-builds-pager
+                [:a
+                 {:href (routes/v1-dashboard-path (assoc nav-data :page (max 0 (dec page))))
+                  ;; no newer builds if you're on the first page
+                  :class (when (zero? page) "disabled")}
+                 [:i.fa.fa-long-arrow-left]
+                 [:span " Newer builds"]]
+                [:a
+                 {:href (routes/v1-dashboard-path (assoc nav-data :page (inc page)))
+                  ;; no older builds if you have less builds on the page than an
+                  ;; API call returns
+                  :class (when (> builds-per-page (count builds)) "disabled")}
+                 [:span "Older builds "]
+                 [:i.fa.fa-long-arrow-right]]]]))))))


Fix rect calc for image drawing

Fix rect calc for image drawing

--- src/zaffre/imageutil.clj
+++ src/zaffre/imageutil.clj
@@ -118,33 +118,35 @@
   (.flip dbytes)
   dimg)

-(defn clip [min-v v max-v]
-  (max min-v (min v max-v)))
+(defn rect-intersect [x0 y0 x1 y1 x2 y2 x3 y3]
+  (let [x4 (max x0 x2)
+        y4 (max y0 y2)
+        x5 (min x1 x3)
+        y5 (min y1 y3)]
+    (when (and (< x4 x5)
+               (< y4 y5))
+      [x4 y4 x5 y5])))

 (defn draw-image [{dwidth :width dheight :height :as dest-img}
                   {swidth :width sheight :height :as src-img}
                   x y]
-  (let [;; source corner in dest coord-space
-        sx1       (+ x swidth)
-        sy1       (+ y sheight)
-        ;; clip x and y to dest rect
-        clipped-x (clip 0 x dwidth)
-        clipped-y (clip 0 y dheight)
-        ;; convert clipped xy to src coords
-        sx0    (- clipped-x x)
-        sy0    (- clipped-y y)
-
-        ;; clip src corner
-        clipped-x1 (clip 0 sx1 dwidth)
-        clipped-y1 (clip 0 sy1 dheight)
-        ;; convert clipped corder to src coords
-        sx1    (- clipped-x1 x)
-        sy1    (- clipped-y1 y)
-        ;; find src width/height
-        width  (- x swidth)
-        height (- y sheight)]
-    (log/info "copying from [" sx0 sy0 "] [" sx1 sy1 "] to" x y)
-    (copy-sub-image dest-img src-img x y sx0 sy0 sx1 sy1)))
+  (let [;; dest image rect
+        x0 0
+        y0 0
+        x1 dwidth
+        y1 dheight
+        ;; src image rect
+        x2 x
+        y2 y
+        x3 (+ x swidth)
+        y3 (+ y sheight)]
+    (when-let [[x4 y4 x5 y5] (rect-intersect x0 y0 x1 y1 x2 y2 x3 y3)]
+      (let [sx0 (- x4 x)
+            sy0 (- y4 y)
+            sx1 (- x5 x)
+            sy1 (- y5 y)]
+        (log/info "copying from [" sx0 sy0 "] [" sx1 sy1 "] to" x y)
+        (copy-sub-image dest-img src-img x y sx0 sy0 sx1 sy1)))))

 (defn resize [{swidth :width sheight :height :as img} width height]


increasing timings in my-time test, seems to be fragile on travis

--- test/lambdacd/test_util_test.clj
+++ test/lambdacd/test_util_test.clj
@@ -7,8 +7,8 @@
   (reset! a "hello")
   (reset! a "world"))

-(defn some-step-taking-10ms [arg & _]
-  (Thread/sleep 10)
+(defn some-step-taking-50ms [arg & _]
+  (Thread/sleep 50)
   {:foo :bar})

@@ -29,4 +29,4 @@

 (deftest timing-test
   (testing "that my-time more or less accurately measures the execution time of a step"
-    (is (close? 2 10 (my-time (some-step-taking-10ms {}))))))+    (is (close? 10 50 (my-time (some-step-taking-50ms {}))))))


test for GET /api/query

test for GET /api/query

--- test/metabase/api/query_test.clj
+++ test/metabase/api/query_test.clj
@@ -158,3 +158,58 @@
        ;; wait 100ms for QueryExecution to complete. If it takes longer than that, it's probably brokesies
        (Thread/sleep 100)
        ((user->client :rasta) :get 200 (format "query/%d/results" id)))]))
+
+;; ## GET /api/query
+;; Fetch Queries for the current Org
+(expect-eval-actual-first
+    (let [[query-1 query-2] (sel :many Query :database_id (:id @test-db) (order :id :ASC))
+          rasta (match-$ (fetch-user :rasta)
+                  {:common_name "Rasta Toucan"
+                   :date_joined $
+                   :last_name "Toucan"
+                   :id $
+                   :is_superuser false
+                   :last_login $
+                   :first_name "Rasta"
+                   :email "rasta@metabase.com"})
+          db (match-$ @test-db
+               {:created_at $
+                :engine "h2"
+                :id $
+                :details $
+                :updated_at $
+                :name "Test Database"
+                :organization_id (:id @test-org)
+                :description nil})]
+      [(match-$ query-1
+         {:creator rasta
+          :database_id (:id @test-db)
+          :name $
+          :type "rawsql"
+          :creator_id (user->id :rasta)
+          :updated_at $
+          :details {:timezone nil
+                    :sql "SELECT COUNT(*) FROM VENUES;"}
+          :id $
+          :database db
+          :version 1
+          :public_perms 0
+          :created_at $})
+       (match-$ query-2
+         {:creator rasta
+          :database_id (:id @test-db)
+          :name $
+          :type "rawsql"
+          :creator_id (user->id :rasta)
+          :updated_at $
+          :details {:timezone nil
+                    :sql "SELECT COUNT(*) FROM VENUES;"}
+          :id $
+          :database db
+          :version 1
+          :public_perms 0
+          :created_at $})])
+  (do (cascade-delete Query :database_id (:id @test-db))
+      (create-query)
+      (create-query)
+      ((user->client :rasta) :get 200 "query" :org (:id @test-org))))


Fixing up quote characters

Fixing up quote characters

--- config/software/libxslt.clj
+++ config/software/libxslt.clj
@@ -28,9 +28,9 @@
                           "--with-libxml-include-prefix=/opt/opscode/embedded/include"
                           "--with-libxml-libs-prefix=/opt/opscode/embedded/lib"]}
                   {:command (if (is-os? "solaris2") "perl" "true")
-                   :args [ "-pi" "-e" "s/^(LIBXSLT_VERSION_SCRIPT = \$.+)$/\# Woof/g"
+                   :args [ "-pi" "-e" "s/^(LIBXSLT_VERSION_SCRIPT = \\$.+)/\\# Woof/g"
                            (str *omnibus-build-dir* "/libxslt-1.1.26/libxslt/Makefile") ]}
                   {:command (if (is-os? "solaris2") "perl" "true")
-                   :args [ "-pi" "-e" "s/^#LIBXSLT_VERSION_SCRIPT.+)$/LIBXSLT_VERSION_SCRIPT =/g"
+                   :args [ "-pi" "-e" "s/^#LIBXSLT_VERSION_SCRIPT.+)/LIBXSLT_VERSION_SCRIPT =/g"
                            (str *omnibus-build-dir* "/libxslt-1.1.26/libxslt/Makefile") ]}
                   {:command "make" :args ["install"]}])


refactored out magic string name

refactored out magic string name

--- clojure/src/microservices_workshop/core.clj
+++ clojure/src/microservices_workshop/core.clj
@@ -10,13 +10,14 @@
 (def ^{:const true}
   default-exchange-name "rapids")

-(def qname "")
+(def ^{:const true}
+  qname "")

 (defn create-connection [host bus]
   (let [conn (rmq/connect {:uri (format "amqp://%s:%s@%s/%s" bus bus host bus)})
         ch   (lch/open conn)
         q    (.getQueue (lq/declare ch qname {:exclusive false :auto-delete false}))]
-      (le/declare ch "rapids" "fanout" {:durable true})
+      (le/declare ch default-exchange-name "fanout" {:durable true})
       (lq/bind ch q default-exchange-name)
       {:conn conn :ch ch}))


Tweak frequencies on generators.

Tweak frequencies on generators.

--- test/smallex/test/generators.clj
+++ test/smallex/test/generators.clj
@@ -28,7 +28,9 @@
   (gen/elements aliases))

 (def collection-generators
-  (map #(gen/resize 20 %) [string char-set]))
+  (mapv vector
+        [3 1]
+        (map #(gen/resize 10 %) [string char-set])))

 (declare operation)

@@ -39,11 +41,11 @@
     (gen/sized
      (fn [size]
        (let [smaller-ops (gen/resize (dec size) (operation alias-gen))
-             arg-gen (cond-> (conj collection-generators alias-gen)
+             arg-gen (cond-> (conj collection-generators [2 alias-gen])
                              (pos? size)
                              ;; v- hack to get more nested ops
-                             (conj smaller-ops smaller-ops smaller-ops))]
-         (->> (gen/vector (gen/one-of arg-gen) 1 max-args)
+                             (conj [7 smaller-ops]))]
+         (->> (gen/vector (gen/frequency arg-gen) 1 max-args)
               (gen/fmap
                (fn [args]
                  (with-meta {:type :op, :value op, :args args}
@@ -95,9 +97,9 @@

 (defn expression
   [alias-gen]
-  (gen/frequency [[8 (operation alias-gen)]
-                  [4 (gen/one-of collection-generators)]
-                  [1 alias-gen]]))
+  (gen/frequency (conj collection-generators
+                       [10 (operation alias-gen)]
+                       [2 alias-gen])))

 (defn ppm [obj]
   (let [orig-dispatch pprint/*print-pprint-dispatch*]


Updating the postinst/postrm lines

Updating the postinst/postrm lines

--- src/fatty/core.clj
+++ src/fatty/core.clj
@@ -90,7 +90,7 @@
 (defn build-deb
   "Builds a deb"
   [project-name version os-data]
-  (let [status (sh "fpm" "-s" "dir" "-t" "deb" "-v" version "-n" project-name "/opt/opscode" "-m" "Opscode, Inc." "--post-install" "../debian/postinst" "--post-uninstall" "../debian/postrm" "--description" "The full stack install of Opscode Chef" "--url" "http://www.opscode.com" :dir "./pkg") ]
+  (let [status (sh "fpm" "-s" "dir" "-t" "deb" "-v" version "-n" project-name "/opt/opscode" "-m" "Opscode, Inc." "--post-install" "../source/postinst" "--post-uninstall" "../source/postrm" "--description" "The full stack install of Opscode Chef" "--url" "http://www.opscode.com" :dir "./pkg") ]
     (log-sh-result status
                    (str "Created debian package")
                    (str "Failed to create debian package"))))
@@ -98,7 +98,7 @@
 (defn build-rpm
   "Builds a rpm"
   [project-name version os-data]
-  (let [status (sh "fpm" "-s" "dir" "-t" "rpm" "-v" version "-n" project-name "/opt/opscode" "-m" "Opscode, Inc." "--post-install" "../debian/postinst" "--post-uninstall" "../debian/postrm" "--description" "The full stack install of Opscode Chef" "--url" "http://www.opscode.com" :dir "./pkg")]
+  (let [status (sh "fpm" "-s" "dir" "-t" "rpm" "-v" version "-n" project-name "/opt/opscode" "-m" "Opscode, Inc." "--post-install" "../source/postinstall" "--post-uninstall" "../source/postrm" "--description" "The full stack install of Opscode Chef" "--url" "http://www.opscode.com" :dir "./pkg")]
     (log-sh-result status
                    (str "Created rpm package")
                    (str "Failed to create rpm package"))))


Adding chef build, non-source builds

Adding chef build, non-source builds

--- src/fatty/core.clj
+++ src/fatty/core.clj
@@ -47,9 +47,11 @@
                                ["sudo" "make" "install"]]))

 (def ruby (software :source "ruby-1.9.2-p0"
-                    :steps [["./configure" "--prefix=/opt/opscode/embedded" "--with-opt-dir=/opt/opscode/embedded" "--enable-shared"]
+                    :steps [["./configure" "--prefix=/opt/opscode/embedded" "--with-opt-dir=/opt/opscode/embedded" "--enable-shared" "--disable-install-doc"]
                             ["make"]
                             ["sudo" "make" "install"]]))
+
+(def chef (software :steps [["/opt/embedded/bin/gems" "install" "chef" "-n" "/opt/opscode/bin"]]))

 (defn- log-sh-result
   [status true-log false-log]
@@ -74,17 +76,19 @@
 (defn clean
   "Clean a previous build directory"
   [soft]
-  (let [status (sh "rm" "-rf" (.getPath (file-str fatty-build-dir "/" (soft :source))) :return-map true)]
-    (log-sh-result status
-                   (str "Removed old build directory for " (soft :source))
-                   (str "Failed to remove old build directory for " (soft :source)))))
+  (if (= (contains? soft :source))
+    (let [status (sh "rm" "-rf" (.getPath (file-str fatty-build-dir "/" (soft :source))) :return-map true)]
+      (log-sh-result status
+                     (str "Removed old build directory for " (soft :source))
+                     (str "Failed to remove old build directory for " (soft :source))))))

 (defn prep
   "Prepare to build a software package by copying its source to a pristine build directory"
   [soft]
-  (do
-    (.mkdirs fatty-build-dir)
-    (copy-source-to-build soft)))
+  (if (= (contains? soft :source))
+    (do
+      (.mkdirs fatty-build-dir)
+      (copy-source-to-build soft))))

 (defn execute-step
   "Run a build step"
@@ -102,7 +106,9 @@
     (execute-step step (.getPath (file-str fatty-build-dir "/"
                                            (if (= (contains? soft :build-subdir) true)
                                              (str (soft :source) "/" (soft :build-subdir))
-                                             (soft :source)))))))
+                                             (if (= (contains? soft :source) true)
+                                               (soft :source)
+                                               ""))))))) ; It's cool, we just want the top build directory if there is no source

 (defn build
   "Build a software package - runs prep for you"


Fix `:simple` optimizations for node.js

Fix `:simple` optimizations for node.js

--- src/huon/log.cljs
+++ src/huon/log.cljs
@@ -15,6 +15,9 @@
   "Start capturing console output. Apps that want to display log output should
   call this function, but libraries that depend on Huon for logging should not."
   []
+  ;; Ensure gclosure logging works in node.js with various compiler
+  ;; optimizations enabled. See https://dev.clojure.org/jira/browse/CLJS-2930.
+  (.setConsole goog.debug.Console js/console)
   (set! (.-showLoggerName (.getFormatter console)) false)
   (.setCapturing console true))


no loading connection

no loading connection

--- src/kvm/clone.clj
+++ src/kvm/clone.clj
@@ -45,8 +45,7 @@
 (defn clear-all-macs [xml]
   (zip/xml-zip (tree-edit xml interface? clear-mac)))

-;; (def connection (connect "qemu+ssh://ronen@charon/system"))
-(def connection (connect "qemu+ssh://ronen@localhost/system"))
+;; (def connection (connect "qemu+ssh://ronen@localhost/system"))

 (defn domain-zip [c id]
   (-> (get-domain c id) xml-desc parse))


Add a behaviour macro to name reactions

Add a behaviour macro to name reactions

--- src/lt/macros.clj
+++ src/lt/macros.clj
@@ -1,5 +1,16 @@
 (ns lt.macros
   (:require [clojure.walk :as walk]))
+
+(defn- namify [keyword]
+  (symbol (.replace (name keyword) "." "__DOT__")))
+
+(defmacro behavior [name & {:keys [reaction] :as r}]
+  (if (and (seq? reaction) (= 'fn (first reaction)))
+    (let [[_ args & body] reaction]
+      `(do
+         (defn- ~(namify name) ~args ~@body)
+         (lt.object/behavior* ~name ~@(apply concat (assoc r :reaction (namify name))))))
+    `(lt.object/behavior* ~name ~@(apply concat r))))

 (defmacro defui [sym params hiccup & events]
   `(defn ~sym ~params


carry-history: add core.match dep

carry-history: add core.match dep

--- contrib/history/project.clj
+++ contrib/history/project.clj
@@ -5,7 +5,9 @@
   :license {:name "MIT" :url "http://opensource.org/licenses/MIT"}

   :dependencies [[org.clojure/clojure "1.8.0" :scope "provided"]
-                 [org.clojure/clojurescript "1.8.51" :scope "provided"]]
+                 [org.clojure/clojurescript "1.8.51" :scope "provided"]
+
+                 [org.clojure/core.match "0.3.0-alpha4" :scope "provided"]]

   :pedantic? :abort