more work on state machine implementation (work in progress)

--- qanal/src/samsara/qanal/state_machine.clj
+++ qanal/src/samsara/qanal/state_machine.clj
@@ -1,28 +1,48 @@
 (ns samsara.qanal.state-machine
   (:require [safely.core :refer [sleeper]]))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                                                                            ;;
+;;        ---==| S T A T E   M A C H I N E   U T I L I T I E S |==----        ;;
+;;                                                                            ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

 ;;
 ;; This is the implementation of the state machine described
 ;; at doc/state-machine.md
 ;;

-;;
-;; Available states
-;;
-;; :extract/init
-;; :extract/check
-;; :extract/poll
-;; :retry
-;; :transform/parse
-;; :transform/validate
-;; :transform/transform
-;; :load/init
-;; :load/bulk-load
-;;
-
 ;; sample state
-{:config {:topic "topic1" :partition 2}
- :fns {:init-consumer (fn [cfg] :a-consemer)}
+{
+ ;;
+ ;; the configuration of what this state machine
+ ;; has to consume and where all the data
+ ;; has to be loaded it is part of the `:config` element
+ ;;
+ :config {;; this is the connect string for the kafka
+          ;; brokers, a comma-separated list
+          ;; of host and ports
+          :brokers ""
+
+          ;; this is the topic and partition to consume
+          :topic "topic1" :partition 2
+
+          ;; the elasticsearch endpoint where
+          ;; to load the data
+          :elasticsearch ""
+          }
+
+ ;; TODO: signatures and doc
+ :fns {:init-consumer (fn [cfg] :new-consumer)
+       :check (fn [c cfg] {["topic1" 2] 2345})
+       :poll (fn [c offset] [1 2 3 4])
+       :parse    1
+       :validate 1
+       :tranform 1
+       :init-load 1
+       :bulk-load 1}

  :state :retry

@@ -101,6 +121,8 @@
    :errors/bulk-load-with-errrors 12
    :errors/els-status-4xx         13})

+
+
 (defn make-error
   ([type message data]
    (make-error type message data nil))
@@ -112,7 +134,8 @@
      (ex-info message info cause))))

-(defn retry-error [s e]
+
+(defn retry-action [s e]
   (-> s
       (update-in [:retry :state] (fn [os] (if (not= :retry (:state s)) (:state s) os)))
       (update-in [:retry :attempts] (fn [ov] (inc (or ov 1))))
@@ -121,14 +144,20 @@
       (assoc      :state :retry)))

+
+;; TODO: this needs access to single event
+;; and might be different for every state
+(defn send-to-errors [s e])
+
 (defmethod transition :retry
   [s]
   (with-state-machine s
-    :on-error   retry-error
+    :on-error   retry-action
     :on-success (fn [{{:keys [state]} :retry :as s}] (assoc s :state state))
     (fn [{{:keys [delayer]} :retry}]
       (delayer)
       nil)))
+

 (defn dispatch-error
@@ -145,14 +174,14 @@
      (dispatch-error

        #{:errors/network-error
-         :errors/kafka-unavailable}   retry-error
+         :errors/kafka-unavailable}   retry-action

        :errors/message-to-big         send-to-errors

        :errors/offset-not-available   reset-offset

-       #(= % :errors/topic-not-found) retry-error
-       :errors/partition-not-found    retry-error
+       #(= % :errors/topic-not-found) retry-action
+       :errors/partition-not-found    retry-action

       (fn [s e]
          (throw (make-error :errors/uncategorized \"Unknown error\" {} e))))
@@ -229,21 +258,25 @@
 (defmethod transition :extract
   [s]
   (with-state-machine s
+    ;; error handling
     :on-error   (dispatch-error
-                 :errors/kafka-unavailable    retry-error
-                 :errors/topic-not-found      retry-error
-                 :errors/partition-not-found  retry-error
-                 :errors/network-error        retry-error
+                 :errors/kafka-unavailable    retry-action
+                 :errors/topic-not-found      retry-action
+                 :errors/partition-not-found  retry-action
+                 :errors/network-error        retry-action

                  ;; TODO: err: 4,5
-                 ;;:errors/message-too-big      send-to-errors
+                 :errors/message-too-big      send-to-errors
                  ;;:errors/offset-not-available reset-offset
                  )
+
+    ;; successful dispatch
     :on-success (fn [s]
                   (if (empty? (get-in s [:data :batch]))
                     s
                     (clean-move-to :transform s)))

+    ;; this state processing.
     (fn [s]
       (-> s
           extract-init-consumer
@@ -273,20 +306,65 @@
 ;;                                                                            ;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

-(comment
-  (defmethod transition :transform
-    [s]
-    (with-state-machine s
-      :on-error   retry-error
-      :on-success (fn [s]
-                    (if (empty? (get-in s [:data :batch]))
-                      s
-                      (clean-move-to :transform s)))
-
-      (fn [{{:keys [parse validate transform]} :fns
-           {:keys [batch]} :data
-           config :config :as s}]
-        (-> s
-            extract-init-consumer
-            extract-check
-            extract-poll)))))
+;; TODO: batch -> {:success [] :failed [] :offsets {["t1" 2] 3445}
+;; TODO: parse -> validate -> transform take above map
+;; and do transformation.
+;; end result is: `:success` contains all transformed items
+;; and `:failed` contains all failed items with a structure
+;; as follow {:error the-execption :topic t1 :partition 1 :offset 1 }
+;; and the exception should contain :data with the info of why this
+;; particular item failed
+
+(defmethod transition :transform
+  [s]
+  (with-state-machine s
+    ;; error handling
+    :on-error   (dispatch-error
+                 :errors/invalid-message-format send-to-errors
+                 :errors/invalid-event          send-to-errors
+                 :errors/transformation-error   send-to-errors
+                 )
+
+    ;; successful dispatch
+    :on-success (clean-move-to :load s)
+
+    ;; this state processing.
+    (fn [{{:keys [parse validate transform]} :fns
+         config :config :as s}]
+      (update-in s [:data :batch]
+                 (fn [batch]
+                   (-> {:success batch :failed [] :offsets {}} ;;TODO: calc offsets
+                       parse
+                       validate
+                       transform))))))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                                                                            ;;
+;;                    ---==| S T A T E :   L O A D |==----                    ;;
+;;                                                                            ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmethod transition :load
+  [s]
+  (with-state-machine s
+    ;; error handling
+    :on-error   (dispatch-error
+                 :errors/network-error        retry-action
+                 :errors/els-unavailable      retry-action
+                 :errors/els-status-5xx       retry-action
+                 :errors/els-status-4xx       retry-action
+
+                 :errors/bulk-load-with-errrors send-to-errors
+                 )
+
+    ;; successful dispatch
+    :on-success (clean-move-to :extract s)
+
+    ;; this state processing.
+    (fn [s]
+      (-> s
+          load-init
+          load-bulk-load))))


docs - clarify :secure and :http-only in wrap-cookies

--- ring-core/src/ring/middleware/cookies.clj
+++ ring-core/src/ring/middleware/cookies.clj
@@ -164,8 +164,9 @@
   :domain    - the domain the cookie is valid for
   :max-age   - the maximum age in seconds of the cookie
   :expires   - a date string at which the cookie will expire
-  :secure    - set to true if the cookie is valid for HTTPS only
-  :http-only - set to true if the cookie is valid for HTTP only"
+  :secure    - set to true if the cookie requires HTTPS, prevent HTTP access
+  :http-only - set to true if the cookie is valid for HTTP and HTTPS only
+               (ie. prevent JavaScript access)"
   [handler]
   (fn [request]
     (-> request


client fix

client fix

--- clj-tcp/src/clj_tcp/client.clj
+++ clj-tcp/src/clj_tcp/client.clj
@@ -75,7 +75,7 @@
     (channelRead0 [^ChannelHandlerContext ctx in]
       (info "Read0 Start " (Thread/currentThread)  " read-ch: " read-ch)
       (>!! read-ch (if (instance? ByteBuf in) (buffer->bytes in)  in))
-      (info "Read0 Done " (Thread/currentThread))
+      ;(info "Read0 Done " (Thread/currentThread))

       )
     (exceptionCaught [^ChannelHandlerContext ctx cause]
@@ -123,12 +123,12 @@
   (reify GenericFutureListener
     (operationComplete [this f]
        ;(go (>! write-lock-ch 1))
-       (go
+       (thread
                (try
                   (close-client client)
                   (catch Exception e (do
                                        (error (str "Close listener error " e)  e)
-                                       (>! internal-error-ch [e nil])
+                                       (>!! internal-error-ch [e nil])
                                        )))))))

@@ -344,7 +344,7 @@
      (thread
 	      (loop [local-client client]
 	          (let [ write-ch (:write-ch local-client)
-	                 v (<! write-ch)]
+	                 v (<!! write-ch)]
 	               (cond  (instance? Stop v) nil
                         (instance? Reconnected v) (do
 	                                                        (error ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  Writer received reconnected " (:client v))
@@ -358,7 +358,7 @@
 											                            (error "!!!!! Error while writing " e)
 											                            (thread (>!! internal-error-ch [e 1]))
 			                                    )))
-                              (recur local-client))
+                             (recur local-client))
 	                      )))

                   )


Don't use update-in when assoc-in suffices

--- src/cljs/cglossa/search_inputs/cwb/extended.cljs
+++ src/cljs/cglossa/search_inputs/cwb/extended.cljs
@@ -44,7 +44,7 @@
       (assoc term :pos val)

       ;; default
-      (update-in term [:features] assoc name val))))
+      (assoc-in term [:features name] val))))

 (defn construct-query-terms [parts]


moving combined feature creation to feature repo

--- src/clojure/zensols/model/execute_classifier.clj
+++ src/clojure/zensols/model/execute_classifier.clj
@@ -92,44 +92,6 @@
   (let [model-conf (model-config)]
     (->> ((:feature-metas-fn model-conf))
          (into {}))))
-
-(def ^{:dynamic true :private true} combined-features nil)
-
-(defmacro with-combined-features
-  {:style/indent 0}
-  [& forms]
-  `(binding [combined-features (atom {})]
-     (do ~@forms)))
-
-(defn combine-feature-vals
-  "Apply feature creation functions only where they don't already exist
-  in [[combined-features]]."
-  [fmap]
-  (let [cfeats (if combined-features @combined-features)
-        fmap (->> fmap
-                  (map (fn [[fkey form]]
-                          (let [feats (or (and cfeats (get cfeats fkey))
-                                          ((eval form)))]
-                            {fkey feats})))
-                  (apply merge))]
-    (when combined-features
-      (log/tracef "combined: %s" (pr-str @combined-features))
-      (swap! combined-features merge fmap))
-    (->> fmap vals (apply merge))))
-
-(defmacro combine-features
-  "Combine features from the forms for which features don't already exist in
-  **features**, which is a map with functions as keys and features already
-  created with that function as the value of the respective function."
-  [& forms]
-  `(->> (merge ~@(->> (map (fn [form]
-                             (->> form first
-                                  (ns-resolve (ns-name *ns*))
-                                  var-get
-                                  (#(hash-map % (read-string (str "#" (pr-str form)))))))
-                           forms)
-                      doall))
-        combine-feature-vals))

 (defn- create-instances
   "Create the weka Instances object using sets of features created from


Ignore the revision date when updating metadata

--- src/mdr2/abacus.clj
+++ src/mdr2/abacus.clj
@@ -153,8 +153,9 @@
       errors
       (prod/update!
        (-> (read-file f)
-           ;; ignore production_type when updating metadata
-           (dissoc :production_type))))))
+           ;; ignore production_type and the revision date when
+           ;; updating metadata
+           (dissoc :production_type :revision_date))))))

 (defn wrap-rows
   "Wrap an export record according to ABACUS conventions"


opening find-all-states

opening find-all-states

--- src/mount/core.cljc
+++ src/mount/core.cljc
@@ -167,7 +167,7 @@
 ;;TODO args might need more thinking
 (defn args [] @-args)

-(defn- find-all-states []
+(defn find-all-states []
   (keys @meta-state))

 #?(:clj


compojure-server: Update dependencies to latest version

--- functions/project.clj
+++ functions/project.clj
@@ -14,7 +14,7 @@
   :injections [(require 'pjstadig.humane-test-output)
                (pjstadig.humane-test-output/activate!)]
   :plugins [[lein-cljfmt "0.5.6"]
-	    [lein-ring "0.9.7"]]
+	    [lein-ring "0.12.1"]]
   :main ^:skip-aot wort.core
   :target-path "target/%s"
   :profiles {:uberjar {:aot :all}})


don't nest things inside p's

don't nest things inside p's

--- src-cljs/frontend/components/plan.cljs
+++ src-cljs/frontend/components/plan.cljs
@@ -199,7 +199,6 @@
         (html
          [:div.menu-view
           [:div.content
-           [:p.make
-            (when (:team/plan team)
-              (om/build plan-info {:plan-id (:db/id (:team/plan team))
-                                   :team-uuid (:team/uuid team)}))]]])))))
+           (when (:team/plan team)
+             (om/build plan-info {:plan-id (:db/id (:team/plan team))
+                                  :team-uuid (:team/uuid team)}))]])))))


made it so only admins can see any org settings

--- src-cljs/frontend/controllers/api.cljs
+++ src-cljs/frontend/controllers/api.cljs
@@ -803,6 +803,10 @@
             (assoc-in state/org-plan-path plan)
             (assoc-in state/selected-piggieback-orgs-path (set piggieback-orgs)))))))

+(defmethod api-event [:org-plan :failed]
+  [target message status {:keys [resp context]} state]
+  (assoc-in state state/org-authorized?-path false))
+
 (defmethod api-event [:org-settings :success]
   [target message status {:keys [resp context]} state]
   (let [{:keys [org-name vcs-type]} context]
@@ -810,8 +814,7 @@
       state
       (-> state
           (update-in state/org-data-path merge resp)
-          (assoc-in state/org-loaded-path true)
-          (assoc-in state/org-authorized?-path true)))))
+          (assoc-in state/org-loaded-path true)))))

 (defmethod api-event [:org-settings :failed]
   [target message status {:keys [resp context]} state]


minor refactoring

minor refactoring

--- cljs/src/base/quark/cogs/editor/analyzer.cljs
+++ cljs/src/base/quark/cogs/editor/analyzer.cljs
@@ -95,23 +95,25 @@

 ;(set! ana/core-name? my-core-name?)

+; ================================================================================================================================================

 ; this is bleeding edge, see https://github.com/swannodette/cljs-bootstrap
 ; follow https://github.com/kanaka/cljs-bootstrap/blob/master/REPL.md
 ; => /Users/darwin/github/cljs-bootstrap
-
-(log "ANALYZER INIT")
-
-(defn set-namespace-edn! [cenv ns-name ns-edn]
-  (let [edn (edn/read-string ns-edn)]
-    (log "EDN:" ns-name edn)
-    (swap! cenv assoc-in [::ana/namespaces ns-name] edn)))

 (defn bootstrap []
   ; load cache files
   (set! *target* "nodejs")
   (apply load-file ["/Users/darwin/github/cljs-bootstrap/.cljs_bootstrap/cljs/core$macros.js"])
   true)
+
+; this is important - do not even think about removing it
+(defonce bootstrap-result (bootstrap))
+
+
+(defn set-namespace-edn! [cenv ns-name ns-edn]
+  (let [edn (edn/read-string ns-edn)]
+    (swap! cenv assoc-in [::ana/namespaces ns-name] edn)))

 (defn prepare-clean-compiler-env []
   (let [core-edn (.readFileSync fs "/Users/darwin/github/cljs-bootstrap/resources/cljs/core.cljs.cache.aot.edn" "utf8")
@@ -120,11 +122,6 @@
     (set-namespace-edn! compiler-env 'cljs.core core-edn)
     (set-namespace-edn! compiler-env 'cljs.core$macros macros-edn)
     compiler-env))
-
-; this is important - do not even think to removing it
-(defonce bootstrap-result (bootstrap))
-
-(log "ANALYZER INIT DONE")

 (def ns-symlinks-dir "/Users/darwin/github/quark/cljs/deps/")

@@ -288,11 +285,8 @@
   ([source] (analyze-file source nil))
   ([source opts]
    (binding [ana/*file-defs* (atom #{})]
-     ;(ensure
-     (let [;ns-info (ana/parse-ns res)
-           path (or (:atom-path opts) "unknown/file/path.clj?")
+     (let [path (or (:atom-path opts) "unknown/file/path.clj?")
            opts (dissoc opts :atom-path)]
-       ;(when-not (get-in @env/*compiler* [::namespaces (:ns ns-info) :defs])
        (binding [ana/*cljs-ns* 'cljs.user
                  ana/*cljs-file* path
                  ana/*cljs-warning-handlers* [custom-warning-handler]
@@ -312,13 +306,13 @@
                  (if (= (:op ast) :ns)
                    (recur results (:name ast) (next forms))
                    (recur results ns (next forms))))
-               [ns results]))))))))                         ;)
+               [ns results]))))))))

 (def clean-compiler-env (prepare-clean-compiler-env))

 (defn analyze-full [& args]
+  (log "------------------------------------------------------")
   (let [compiler-env (prepare-clean-compiler-env)]
-    (log "------------------------------------------------------")
     (log "analyze-full" args "compiler:" compiler-env)
     (with-compiler-env compiler-env
       (apply analyze-file args))))


Renamed app to run for consistency with html/css.

--- src/cbfg/world/t1.cljs
+++ src/cbfg/world/t1.cljs
@@ -6,11 +6,11 @@

 ;; How to assign locations to world entities before rendering?

-(def app-controls
+(def run-controls
   (atom {:controls ["play" "step" "pause"]
          :speed 1.0}))

-(def app-history
+(def run-history
   (atom {:snapshots {0 {}
                      10 {:a 1 :b 2 :c 3 :d 4}
                      20 {:a 11 :b 22 :c 33 :d 44}}
@@ -140,10 +140,10 @@
                   [21 :event [:a 21]]
                   [22 :event [:b 22]]]}))

-(def app-world
+(def run-world
   (atom {:a 10 :b 20 :c 30 :d 40}))

-(def app-world-hover
+(def run-world-hover
   (atom nil))

 (defn render-world [app owner]
@@ -154,11 +154,11 @@
   (render-world (get-in app [:snapshots ss-ts] nil) owner))

 (defn event-focus [snapshot-ts event-ts]
-  (when-let [ss (get-in @app-history [:snapshots snapshot-ts])]
-    (reset! app-world-hover ss)))
+  (when-let [ss (get-in @run-history [:snapshots snapshot-ts])]
+    (reset! run-world-hover ss)))

 (defn event-blur [snapshot-ts event-ts]
-  (reset! app-world-hover nil))
+  (reset! run-world-hover nil))

 (defn render-events [app owner]
   (apply dom/ul nil
@@ -187,17 +187,17 @@
             (map (fn [x] (dom/li nil (dom/button nil x)))
                  (:controls app))))
 (defn init-roots []
-  (om/root render-controls app-controls
+  (om/root render-controls run-controls
            {:target (. js/document (getElementById "controls"))})
-  (om/root render-events app-history
+  (om/root render-events run-history
            {:target (. js/document (getElementById "events"))})
-  (om/root render-world app-world
+  (om/root render-world run-world
            {:target (. js/document (getElementById "world"))})
-  (om/root render-world app-world
+  (om/root render-world run-world
            {:target (. js/document (getElementById "world-map"))})
-  (om/root render-world app-world-hover
+  (om/root render-world run-world-hover
            {:target (. js/document (getElementById "world-hover"))})
-  (om/root render-world app-world-hover
+  (om/root render-world run-world-hover
            {:target (. js/document (getElementById "world-map-hover"))}))

 (defn world-vis-init [el-prefix init-event-delay]


Remove unused render-snapshot.

Remove unused render-snapshot.

--- src/cbfg/world/t1.cljs
+++ src/cbfg/world/t1.cljs
@@ -62,9 +62,6 @@
 (defn render-world [app owner]
   (apply dom/ul nil
          (map (fn [[k v]] (dom/li nil (str k ":" v))) app)))
-
-(defn render-snapshot [app owner ss-ts]
-  (render-world (get-in app [:snapshots ss-ts] nil) owner))

 (defn on-event-focus [snapshot-ts event-ts]
   (when-let [ss (get-in @run-history [:snapshots snapshot-ts])]
@@ -158,6 +155,9 @@
         (cbfg.vis/process-events vis event-delay cbfg.vis/vis-event-handlers
                                  event-ch step-ch event-run-ch)
         (cbfg.vis/process-render el-prefix world event-run-ch delayed-event-cb)
+        ;;; (go-loop [vis-ts]
+        ;;; (when-let [[vis-next deltas after event-str] (<! event-run-ch)]
+        ;;; (recur (inc vis-ts))))
         (make-net world
                   (:net-listen-ch @prog-world)
                   (:net-connect-ch @prog-world))


Remove tooltip-card and add description and add button to popover for fun

--- src-cljs/frontend/components/pieces/popover.cljs
+++ src-cljs/frontend/components/pieces/popover.cljs
@@ -1,6 +1,7 @@
 (ns frontend.components.pieces.popover
   (:require [clojure.string :as string]
             [devcards.core :as dc :refer-macros [defcard]]
+            [frontend.components.pieces.button :as button]
             [frontend.components.pieces.icon :as icon]
             [goog.string :as gstring]
             [om.dom :as om-dom]
@@ -208,58 +209,18 @@
                         :body body)
                  trigger)))

-  (defcard tooltip-card
-    (fn [state]
-      (html
-       [:div {:style {:display "flex"
-                      :justify-content "space-around"
-                      :padding-left 150
-                      :padding-right 150
-                      :padding-top 100
-                      :padding-bottom 200}}
-        (with-positioned-element
-         {:placement :left
-          :element (card {:title nil
-                          :body "Content"
-                          :placement :left
-                          :trigger-mode :click
-                          :visible? true})}
-         trigger)
-        (with-positioned-element
-         {:placement :top
-          :element (card {:title nil
-                          :body "Content"
-                          :placement :top
-                          :trigger-mode :click
-                          :visible? true})}
-         trigger)
-        (with-positioned-element
-         {:placement :bottom
-          :element (card {:title nil
-                          :body "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
-                          :placement :bottom
-                          :trigger-mode :click
-                          :visible? true})}
-         trigger)
-        (with-positioned-element
-         {:placement :right
-          :element (card {:title nil
-                          :body "Content"
-                          :placement :right
-                          :trigger-mode :click
-                          :visible? true})}
-         trigger)])))
-
-  (defcard tooltip
-    (fn [state]
-      (html
-       [:div {:style {:display "flex"
-                      :justify-content "space-between"
-                      :padding 50}}
-        (vary-placements tooltip
-                         {:body "Content"})])))
-
-  (defcard popover-card
+  (defcard tooltip-and-popover-card
+    "Simple tooltips and more complex popovers for displaying extended
+    information for things.
+
+    A simple popup provide extra information or operations on hover, focus
+    or click. Compared with tooltips, popovers can provide action elements
+    like links and buttons.
+
+    The tooltip shows on mouse enter and hides on mouse leave and doesn’t support
+    complex text. The default behavior in hover mode is to respond to clicks as
+    well. If you click, it \"pins\" the tooltip. Clicking again or clicking out
+    dismisses it, allowing tooltips to work on touch devices as well."
     (fn [state]
       (html
        [:div {:style {:display "flex"
@@ -298,11 +259,21 @@
                           (html
                             [:div
                              [:p "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."]
-                             [:p [:a {:href "#"} "Click here!"]]])
+                             [:p [:a {:href "#"} "Click here!"]]
+                             [:p (button/button {:kind :primary} "Do Something")]])
                           :placement :right
                           :trigger-mode :click
                           :visible? true})}
          trigger)])))
+
+  (defcard tooltip
+    (fn [state]
+      (html
+       [:div {:style {:display "flex"
+                      :justify-content "space-between"
+                      :padding 50}}
+        (vary-placements tooltip
+                         {:body "Content"})])))

   (defcard popover-click
     (fn [state]
@@ -314,6 +285,7 @@
                          {:title "Title"
                           :body "Content"
                           :trigger-mode :click})])))
+
   (defcard popover-hover
     (fn [state]
       (html


fix link for mobile feature on homepage

fix link for mobile feature on homepage

--- src-cljs/frontend/components/landing.cljs
+++ src-cljs/frontend/components/landing.cljs
@@ -274,8 +274,8 @@
             [:p.content-copy
              "We designed our interface to get out of the way and let you concentrate on your ideas. "
              "It's simple enough to show on your phone and perfect for collaborating on your tablet. "]
-            [:a.feature-link {:href "/features/team"}
-             "Save to iPhone."]]
+            [:a.feature-link {:href "/document/17592197661694"}
+             "Try on iPhone."]]
            [:div.feature-media artwork-mobile]]
           [:div.feature-divider]
           [:div.feature.content {:class (when (contains? active-features "2") "art-visible") :ref "2"}


Fix Launch Darkly reident

Fix Launch Darkly reident

--- src-cljs/frontend/utils/launchdarkly.cljs
+++ src-cljs/frontend/utils/launchdarkly.cljs
@@ -17,7 +17,8 @@
 (defn load-lduser! []
   (swap! local-lduser-state
          (fn [v]
-           (when-not v
+           (if v
+             v
              (or (js->clj js/ldUser) {})))))

 (defn reidentify! [f]
@@ -26,7 +27,7 @@
   (identify @local-lduser-state))

 (defn merge-custom-properties! [m]
-  (reidentify! #(update-in % [:custom] merge m)))
+  (reidentify! #(update-in % ["custom"] merge m)))

 (defn exists? [feature-name]
   (not (nil? (feature-on? feature-name nil))))


Pass the damage-dealing card to (damage) so Chrome Parlor can work.

--- src/clj/game/cards.clj
+++ src/clj/game/cards.clj
@@ -71,7 +71,7 @@
                  :msg "gain [Click][Click]"}]}

    "Amped Up"
-   {:effect (effect (gain :click 3) (damage :brain 1 {:unpreventable true}))}
+   {:effect (effect (gain :click 3) (damage :brain 1 {:unpreventable true :card card}))}

    "Analog Dreamers"
    {:abilities [{:cost [:click 1] :msg "make a run on R&D"
@@ -128,7 +128,7 @@
               :msg "make the Runner take 1 tag or suffer 2 meat damage"
               :effect (req (if (= target "1 tag")
                              (do (gain state :runner :tag 1) (system-msg state side "takes 1 tag"))
-                             (do (damage state :runner :meat 2 {:unboostable true})
+                             (do (damage state :runner :meat 2 {:unboostable true :card card})
                                  (system-msg state side "suffers 2 meat damage"))))}}}

    "Astrolabe"
@@ -208,7 +208,7 @@
     :effect (effect (card-init target))}

    "Brain Cage"
-   {:effect (effect (damage :brain 1) (gain :max-hand-size 3))
+   {:effect (effect (damage :brain 1 {:card card}) (gain :max-hand-size 3))
     :leave-play (effect (lose :max-hand-size 3))}

    "Brain-Taping Warehouse"
@@ -276,7 +276,7 @@
                       :choices ["1 tag" "1 brain damage"] :msg (msg "The Runner takes " target)
                       :effect (req (if (= target "1 tag")
                                      (gain state side :tag 1)
-                                     (damage state side :brain 1)))}}}
+                                     (damage state side :brain 1 {:card card})))}}}

    "Cerebral Imaging: Infinite Frontiers"
    {:effect (req (add-watch state :cerebral-imaging
@@ -290,7 +290,7 @@
     :access {:optional {:req (req installed)
                         :prompt "Pay 3 [Credits] to use Cerebral Overwriter ability?"
                         :cost [:credit 3] :msg (msg "do " (:advance-counter card) " brain damage")
-                        :effect (effect (damage :brain (:advance-counter card)))}}}
+                        :effect (effect (damage :brain (:advance-counter card) {:card card}))}}}

    "Chairman Hiro"
    {:effect (effect (lose :runner :max-hand-size 2))
@@ -315,6 +315,11 @@
                                    :effect (req (if (= target "Draw 1 card")
                                                   (draw state side)
                                                   (lose state side :tag 1)))} card nil))}]}
+
+   "Chrome Parlor"
+   {:events
+    {:pre-damage {:req (req (has? (second targets) :subtype "Cybernetic"))
+                  :effect (effect (damage-prevent target Integer/MAX_VALUE))}}}

    "Chronos Project"
    {:effect (effect (move-zone :runner :discard :rfg))}
@@ -506,7 +511,7 @@

    "Dedicated Response Team"
    {:events {:successful-run-ends {:req (req tagged) :msg "do 2 meat damage"
-                                   :effect (effect (damage :meat 2))}}}
+                                   :effect (effect (damage :meat 2 {:card card}))}}}

    "Dedicated Server"
    {:recurring 2}
@@ -611,7 +616,7 @@
               :msg (msg "do " (count (get-in corp [:servers :remote (last (:server run)) :ices]))
                         " brain damage")
               :effect (req (damage state side :brain
-                                   (count (get-in corp [:servers :remote (last (:server run)) :ices]))))}}}
+                                   (count (get-in corp [:servers :remote (last (:server run)) :ices])) {:card card}))}}}

    "Edward Kim: Humanitys Hammer"
    {:events {:access {:req (req (= (:type target) "Operation")) :once :per-turn
@@ -727,7 +732,7 @@

    "Fetal AI"
    {:access {:req (req (not= (first (:zone card)) :discard)) :msg "do 2 net damage"
-             :effect (effect (damage :net 2))}
+             :effect (effect (damage :net 2 {:card card}))}
     :steal-cost [:credit 2]}

    "Fester"
@@ -933,7 +938,7 @@

    "Hokusai Grid"
    {:events {:successful-run {:req (req this-server) :msg "do 1 net damage"
-                              :effect (req (damage state side :net 1))}}}
+                              :effect (req (damage state side :net 1 {:card card}))}}}

    "Hostage"
    {:prompt "Choose a Connection to install"
@@ -943,8 +948,8 @@

    "Hostile Infrastructure"
    {:events {:trash {:req (req (and (= (:side target) "Corp") (= side :runner)))
-                     :msg "do 1 net damage" :effect (effect (damage :net 1))}}
-    :abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1))}]}
+                     :msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}}
+    :abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}]}

    "Hostile Takeover"
    {:effect (effect (gain :credit 7 :bad-publicity 1))}
@@ -958,7 +963,7 @@
    "House of Knives"
    {:data {:counter 3}
     :abilities [{:counter-cost 1 :msg "do 1 net damage" :req (req (:run @state)) :once :per-run
-                 :effect (effect (damage :net 1))}]}
+                 :effect (effect (damage :net 1 {:card card}))}]}

    "Human First"
    {:events {:agenda-scored {:msg (msg "gain " (:agendapoints target) " [Credits]")
@@ -1060,8 +1065,8 @@
              :purge {:effect (effect (trash card))}}}

    "Jinteki: Personal Evolution"
-   {:events {:agenda-scored {:msg "do 1 net damage" :effect (effect (damage :net 1))}
-             :agenda-stolen {:msg "do 1 net damage" :effect (effect (damage :net 1))}}}
+   {:events {:agenda-scored {:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}
+             :agenda-stolen {:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}}}

    "John Masanori"
    {:events {:successful-run {:msg "draw 1 card" :once :per-turn :once-key :john-masanori-draw
@@ -1350,10 +1355,10 @@
                :msg "add it to his Grip" :effect (effect (move (first (:discard runner)) :hand))}}

    "Neural EMP"
-   {:req (req (:made-run runner-reg)) :effect (effect (damage :net 1))}
+   {:req (req (:made-run runner-reg)) :effect (effect (damage :net 1 {:card card}))}

    "Net-Ready Eyes"
-   {:effect (effect (damage :meat 2 {:unboostable true})) :msg "suffer 2 meat damage"
+   {:effect (effect (damage :meat 2 {:unboostable true :card card})) :msg "suffer 2 meat damage"
     :events {:run {:choices {:req #(and (= (:zone %) [:rig :program])
                                         (has? % :subtype "Icebreaker"))}
                    :msg (msg "give " (:title target) " +1 strength")
@@ -1499,7 +1504,7 @@

    "Philotic Entanglement"
    {:msg (msg "do " (count (:scored runner)) " net damage")
-    :effect (effect (damage :net (count (:scored runner))))}
+    :effect (effect (damage :net (count (:scored runner)) {:card card}))}

    "Plan B"
    {:advanceable :always
@@ -1538,7 +1543,7 @@
                               (when (= (:counter card) 0) (trash state :corp card)))}]}

    "Private Security Force"
-   {:abilities [{:req (req tagged) :cost [:click 1] :effect (effect (damage :meat 1))
+   {:abilities [{:req (req tagged) :cost [:click 1] :effect (effect (damage :meat 1 {:card card}))
                  :msg "do 1 meat damage"}]}

    "Project Atlas"
@@ -1555,7 +1560,7 @@
    {:advanceable :always
     :access {:optional {:prompt "Pay 1 [Credits] to use Project Junebug ability?" :cost [:credit 1]
                         :req (req installed) :msg (msg "do " (* 2 (:advance-counter card)) " net damage")
-                        :effect (effect (damage :net (* 2 (:advance-counter card))))}}}
+                        :effect (effect (damage :net (* 2 (:advance-counter card)) {:card card}))}}}

    "Project Vitruvius"
    {:effect (effect (set-prop card :counter (- (:advance-counter card) 3)))
@@ -1571,7 +1576,7 @@
    "Psychic Field"
    (let [ab {:psi {:req (req installed)
                    :not-equal {:msg (msg "do " (count (:hand runner)) " net damage")
-                               :effect (effect (damage :net (count (:hand runner))))}}}]
+                               :effect (effect (damage :net (count (:hand runner)) {:card card}))}}}]
      {:expose ab :access ab})

    "Public Sympathy"
@@ -1619,7 +1624,7 @@

    "Punitive Counterstrike"
    {:trace {:base 5 :msg (msg "do " (:stole-agenda runner-reg) " meat damage")
-            :effect (effect (damage :meat (get-in runner [:register :stole-agenda])))}}
+            :effect (effect (damage :meat (get-in runner [:register :stole-agenda]) {:card card}))}}

    "Push Your Luck"
    {:player :corp :prompt "Guess the amount the Runner will spend on Push Your Luck"
@@ -1770,7 +1775,7 @@
    "Ronin"
    {:advanceable :always
     :abilities [{:cost [:click 1] :req (req (>= (:advance-counter card) 4))
-                 :msg "do 3 net damage" :effect (effect (damage :net 3) (trash card))}]}
+                 :msg "do 3 net damage" :effect (effect (damage :net 3 {:card card}) (trash card))}]}

    "Running Interference"
    {:prompt "Choose a server" :choices (req servers)
@@ -1785,7 +1790,7 @@

    "Ryon Knight"
    {:abilities [{:msg "do 1 brain damage" :req (req (and this-server (zero? (:click runner))))
-                 :effect (effect (trash card) (damage :brain 1))}]}
+                 :effect (effect (trash card) (damage :brain 1 {:card card}))}]}

    "Sacrificial Clone"
    {:prevent [:meat :net :brain]
@@ -1846,7 +1851,7 @@
             card nil)))}

    "Scorched Earth"
-   {:req (req tagged) :effect (effect (damage :meat 4))}
+   {:req (req tagged) :effect (effect (damage :meat 4 {:card card}))}

    "Scrubber"
    {:recurring 2}
@@ -1899,7 +1904,7 @@
                                    (resolve-ability
                                      state side
                                      {:trace {:base (req (dec (count cards)))
-                                              :effect (effect (damage :net 3))
+                                              :effect (effect (damage :net 3 {:card card}))
                                               :msg "do 3 net damage"}} card nil)))}]}

    "Self-Destruct Chips"
@@ -1914,7 +1919,7 @@

    "Sentinel Defense Program"
    {:events {:damage {:req (req (= target :brain)) :msg "to do 1 net damage"
-                      :effect (effect (damage :net 1)) }}}
+                      :effect (effect (damage :net 1 {:card card})) }}}

    "Server Diagnostics"
    {:events {:corp-turn-begins {:effect (effect (gain :credit 2)) :msg "gain 2 [Credits]"}
@@ -1948,7 +1953,7 @@
                                          (req (if (= target "lose 1 agenda point")
                                                 (do (gain state :runner :agenda-point -1)
                                                     (move state :runner card :scored nil))
-                                                (damage state :corp :net dmg))))}
+                                                (damage state :corp :net dmg {:card card}))))}
                               card targets))}}

    "Shipment from Kaguya"
@@ -1970,7 +1975,7 @@
                      :effect (effect (add-prop :corp target :advance-counter c))} card nil)))}

    "Shock!"
-   {:access {:msg "do 1 net damage" :effect (effect (damage :net 1))}}
+   {:access {:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}}

    "Shoot the Moon"
    {:choices {:req #(and (= (:type %) "ICE") (not (:rezzed %)))
@@ -2003,7 +2008,7 @@
                                        (trash state side c)))}} card))}

    "Skulljack"
-   {:effect (effect (damage :brain 1))
+   {:effect (effect (damage :brain 1 {:card card}))
     :events {:pre-trash {:effect (effect (trash-cost-bonus -1))}}}

    "Snatch and Grab"
@@ -2034,7 +2039,7 @@
    {:access {:optional {:req (req (not= (first (:zone card)) :discard))
                         :prompt "Pay 4 [Credits] to use Snare! ability?" :cost [:credit 4]
                         :msg "do 3 net damage and give the Runner 1 tag"
-                        :effect (effect (damage :net 3) (gain :runner :tag 1))}}}
+                        :effect (effect (damage :net 3 {:card card}) (gain :runner :tag 1))}}}

    "Snitch"
    {:abilities [{:once :per-run :req (req current-ice) :msg (msg "expose " (:title current-ice))
@@ -2059,7 +2064,7 @@

    "Spinal Modem"
    {:effect (effect (gain :memory 1)) :leave-play (effect (lose :memory 1)) :recurring 2
-    :events {:successful-trace {:req (req run) :effect (effect (damage :brain 1))}}}
+    :events {:successful-trace {:req (req run) :effect (effect (damage :brain 1 {:card card}))}}}

    "Spooned"
    {:prompt "Choose a server" :choices (req servers) :effect (effect (run target))}
@@ -2071,7 +2076,7 @@
    {:events {:runner-turn-begins
              {:effect #(if (>= (:counter %3) 2)
                          (do (set-prop %1 %2 %3 :counter 0)
-                             (damage %1 %2 :brain 1 {:unpreventable true})
+                             (damage %1 %2 :brain 1 {:unpreventable true :card %3})
                              (system-msg %1 %2 "takes 1 brain damage from Stim Dealer"))
                          (do (add-prop %1 %2 %3 :counter 1)
                              (gain %1 %2 :click 1)
@@ -2080,7 +2085,7 @@
    "Stimhack"
    {:prompt "Choose a server" :choices (req servers) :msg " take 1 brain damage"
     :effect (effect (gain :credit 9)
-                    (run target {:end-run {:effect (effect (damage :brain 1 {:unpreventable true}))}} card))}
+                    (run target {:end-run {:effect (effect (damage :brain 1 {:unpreventable true :card card}))}} card))}

    "Subliminal Messaging"
    {:effect (effect (gain :credit 1)
@@ -2234,7 +2239,7 @@
                              :effect (effect (add-prop target :counter 1))}}}

    "Titanium Ribs"
-   {:effect (effect (damage :meat 2))}
+   {:effect (effect (damage :meat 2 {:card card}))}

    "Toshiyuki Sakai"
    {:advanceable :always}
@@ -2249,12 +2254,12 @@
                                       :effect (effect (move target :hand))} card nil))}

    "Traffic Accident"
-   {:req (req (>= (:tag runner) 2)) :effect (effect (damage :meat 2))}
+   {:req (req (>= (:tag runner) 2)) :effect (effect (damage :meat 2 {:card card}))}

    "Tri-maf Contact"
    {:abilities [{:cost [:click 1] :msg "gain 2 [Credits]" :once :per-turn
                  :effect (effect (gain :credit 2))}]
-    :leave-play (effect (damage :meat 3 {:unboostable true}))}
+    :leave-play (effect (damage :meat 3 {:unboostable true :card card}))}

    "Trick of Light"
    {:choices {:req #(and (contains? % :advance-counter) (> (:advance-counter %) 0))}
@@ -2349,7 +2354,7 @@
                  :effect (effect (add-prop target :counter 1))}]}

    "Vulcan Coverup"
-   {:msg "do 2 meat damage" :effect (effect (damage :meat 2))
+   {:msg "do 2 meat damage" :effect (effect (damage :meat 2 {:card card}))
     :stolen {:msg "force the Corp to take 1 bad publicity"
              :effect (effect (gain :corp :bad-publicity 1))}}

@@ -2688,7 +2693,7 @@
                  :trace {:base 5
                          :effect #(do (swap! %1 assoc-in [:run :run-effect :end-run]
                                              {:req (req (:successful run)) :msg "do 3 meat damage"
-                                              :effect (effect (damage :meat 3))})
+                                              :effect (effect (damage :meat 3 {:card card}))})
                                       (swap! %1 assoc-in [:run :run-effect :card] %3))}}]}

    "Chimera"
@@ -2706,12 +2711,12 @@
                                    :effect (effect (add-prop target :advance-counter 1) (end-run))}}}]}

    "Chum"
-   {:abilities [{:msg "do 3 net damage" :effect (effect (damage :net 3))}]}
+   {:abilities [{:msg "do 3 net damage" :effect (effect (damage :net 3 {:card card}))}]}

    "Cortex Lock"
    {:abilities [{:label "Do 1 net damage for each unused memory units the Runner has"
                  :msg (msg "do " (:memory runner) " net damage")
-                 :effect (effect (damage :net (:memory runner)))}]}
+                 :effect (effect (damage :net (:memory runner) {:card card}))}]}

    "Crick"
    {:abilities [{:msg "install a card from Archives" :choices (req (:discard corp))
@@ -2727,7 +2732,7 @@
                                         (move state side c :play-area false true)))}}]}

    "Data Mine"
-   {:abilities [{:msg "do 1 net damage" :effect (effect (trash card) (damage :net 1))}]}
+   {:abilities [{:msg "do 1 net damage" :effect (effect (trash card) (damage :net 1 {:card card}))}]}

    "Datapike"
    {:abilities [{:msg "force the Runner to pay 2 [Credits] if able"
@@ -2765,7 +2770,7 @@

    "Fenris"
    {:effect (effect (gain :bad-publicity 1) (system-msg "takes 1 bad publicity"))
-    :abilities [{:msg "do 1 brain damage" :effect (effect (damage :brain 1))}
+    :abilities [{:msg "do 1 brain damage" :effect (effect (damage :brain 1 {:card card}))}
                 {:msg "end the run" :effect (effect (end-run))}]}

    "Fire Wall"
@@ -2776,15 +2781,15 @@
                  :msg (msg "trash " (:title target)) :label "Trash a piece of hardware"
                  :choices (req (get-in runner [:rig :hardware])) :effect (effect (trash target))}
                 {:msg "do 2 meat damage and end the run"
-                 :effect (effect (damage :meat 2 {:unpreventable true}) (end-run))}]}
+                 :effect (effect (damage :meat 2 {:unpreventable true :card card}) (end-run))}]}

    "Galahad"
    {:abilities [{:msg "end the run" :effect (effect (end-run))}]}

    "Gemini"
    {:abilities [{:label "Trace 2"
-                 :trace {:base 2 :msg "do 1 net damage" :effect (effect (damage :net 1))
-                         :kicker {:min 5 :msg "do 1 net damage" :effect (effect (damage :net 1))}}}]}
+                 :trace {:base 2 :msg "do 1 net damage" :effect (effect (damage :net 1) {:card card})
+                         :kicker {:min 5 :msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}}}]}

    "Grim"
    {:effect (effect (gain :bad-publicity 1) (system-msg "takes 1 bad publicity"))
@@ -2811,12 +2816,12 @@
    {:abilities [{:msg "end the run" :effect (effect (end-run))}]}

    "Heimdall 1.0"
-   {:abilities [{:msg "do 1 brain damage" :effect (effect (damage :brain 1))}
+   {:abilities [{:msg "do 1 brain damage" :effect (effect (damage :brain 1 {:card card}))}
                 {:msg "end the run" :effect (effect (end-run))}]}

    "Heimdall 2.0"
-   {:abilities [{:msg "do 1 brain damage" :effect (effect (damage :brain 1))}
-                {:msg "do 1 brain damage and end the run" :effect (effect (damage :brain 1) (end-run))}
+   {:abilities [{:msg "do 1 brain damage" :effect (effect (damage :brain 1 {:card card}))}
+                {:msg "do 1 brain damage and end the run" :effect (effect (damage :brain 1 {:card card}) (end-run))}
                 {:msg "end the run" :effect (effect (end-run))}]}

    "Hourglass"
@@ -2840,7 +2845,7 @@
                  :effect (effect (trash target))}
                 {:label "Trace 1 - Give the Runner 1 tag and do 1 brain damage"
                  :trace {:base 1 :msg "give the Runner 1 tag and do 1 brain damage"
-                         :effect (effect (damage :brain 1) (gain :runner :tag 1))}}]}
+                         :effect (effect (damage :brain 1 {:card card}) (gain :runner :tag 1))}}]}

    "Ichi 2.0"
    {:abilities [{:prompt "Choose a program to trash" :msg (msg "trash " (:title target))
@@ -2848,7 +2853,7 @@
                  :effect (effect (trash target))}
                 {:label "Trace 3 - Give the Runner 1 tag and do 1 brain damage"
                  :trace {:base 3 :msg "give the Runner 1 tag and do 1 brain damage"
-                         :effect (effect (damage :brain 1) (gain :runner :tag 1))}}]}
+                         :effect (effect (damage :brain 1 {:card card}) (gain :runner :tag 1))}}]}

    "IQ"
    {:abilities [{:msg "end the run" :effect (effect (end-run))}]}
@@ -2861,7 +2866,7 @@
    {:abilities [{:msg "make the Runner lose 1 [Credits]" :effect (effect (lose :runner :credit 1))}]}

    "Janus 1.0"
-   {:abilities [{:msg "do 1 brain damage" :effect (effect (damage :brain 1))}]}
+   {:abilities [{:msg "do 1 brain damage" :effect (effect (damage :brain 1 {:card card}))}]}

    "Kitsune"
    {:abilities [{:prompt "Choose a card in HQ" :choices (req (:hand corp))
@@ -2870,7 +2875,7 @@
                  :effect (effect (handle-access targets) (trash card))}]}

    "Komainu"
-   {:abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1))}]}
+   {:abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}]}

    "Lab Dog"
    {:abilities [{:label "Force the Runner trash an installed piece of Hardware"
@@ -2897,9 +2902,9 @@
                  :effect (effect (trash target))}]}

    "Mamba"
-   {:abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1))}
+   {:abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}
                 {:msg "do 1 net damage using 1 power counter"
-                 :counter-cost 1 :effect (effect (damage :net 1))}
+                 :counter-cost 1 :effect (effect (damage :net 1 {:card card}))}
                 {:msg "start a Psi game"
                  :psi {:not-equal {:msg "add 1 power counter"
                                    :effect (effect (add-prop :runner card :counter 1))}}}]}
@@ -2917,7 +2922,7 @@
                  :trace {:base 2 :msg "give the Runner 1 tag" :effect (effect (gain :runner :tag 1))}}]}

    "Merlin"
-   {:abilities [{:msg "do 2 net damage" :effect (effect (damage :net 2))}]}
+   {:abilities [{:msg "do 2 net damage" :effect (effect (damage :net 2 {:card card}))}]}

    "Meru Mati"
    {:abilities [{:msg "end the run" :effect (effect (end-run))}]}
@@ -2964,7 +2969,7 @@
                  :msg (msg "trash " (:title target)) :effect (effect (trash target))}]}

    "Neural Katana"
-   {:abilities [{:msg "do 3 net damage" :effect (effect (damage :net 3))}]}
+   {:abilities [{:msg "do 3 net damage" :effect (effect (damage :net 3 {:card card}))}]}

    "NEXT Bronze"
    {:abilities [{:msg "end the run" :effect (effect (end-run))}]}
@@ -2981,7 +2986,7 @@
                                                         (+ c (count (filter (fn [ice]
                                                                               (and (:rezzed ice) (has? ice :subtype "NEXT")))
                                                                             (:ices server)))))
-                                                      0 (flatten (seq (:servers corp))))))}
+                                                      0 (flatten (seq (:servers corp)))) {:card card}))}
                 {:label "Trash a program" :prompt "Choose a program to trash"
                  :choices {:req #(= (:zone %) [:rig :program])}
                  :msg (msg "trash " (:title target)) :effect (effect (trash target))}]}
@@ -3004,7 +3009,7 @@
                 {:msg "end the run" :effect (effect (end-run))}]}

    "Pup"
-   {:abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1))}]}
+   {:abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}]}

    "Quandary"
    {:abilities [{:msg "end the run" :effect (effect (end-run))}]}
@@ -3058,11 +3063,11 @@
    "Shinobi"
    {:effect (effect (gain :bad-publicity 1) (system-msg "takes 1 bad publicity"))
     :abilities [{:label "Trace 1 - Do 1 net damage"
-                 :trace {:base 1 :msg "do 1 net damage" :effect (effect (damage :net 1))}}
+                 :trace {:base 1 :msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}}
                 {:label "Trace 2 - Do 2 net damage"
-                 :trace {:base 2 :msg "do 2 net damage" :effect (effect (damage :net 2))}}
+                 :trace {:base 2 :msg "do 2 net damage" :effect (effect (damage :net 2 {:card card}))}}
                 {:label "Trace 3 - Do 3 net damage"
-                 :trace {:base 3 :msg "do 3 net damage" :effect (effect (damage :net 3))}}]}
+                 :trace {:base 3 :msg "do 3 net damage" :effect (effect (damage :net 3 {:card card}))}}]}

    "Snoop"
    {:abilities [{:msg "place 1 power counter on Snoop" :effect (effect (add-prop card :counter 1))}
@@ -3089,7 +3094,7 @@
                  :effect (effect (trash target))}]}

    "Swordsman"
-   {:abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1))}
+   {:abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}
                 {:prompt "Choose an AI program to trash" :msg (msg "trashes " (:title target))
                  :label "Trash an AI program" :effect (effect (trash target))
                  :choices (req (filter #(has? % :subtype "AI") (get-in runner [:rig :program])))}]}
@@ -3122,7 +3127,7 @@

    "Tsurugi"
    {:abilities [{:msg "end the run" :effect (effect (end-run))}
-                {:msg "do 1 net damage" :effect (effect (damage :net 1))}]}
+                {:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}]}

    "Turing"
    {:abilities [{:msg "end the run" :effect (effect (end-run))}]}
@@ -3145,12 +3150,12 @@
                  :trace {:base 4 :msg "end the run" :effect (effect (end-run))}}]}

    "Viktor 1.0"
-   {:abilities [{:msg "do 1 brain damage" :effect (effect (damage :brain 1))}
+   {:abilities [{:msg "do 1 brain damage" :effect (effect (damage :brain 1 {:card card}))}
                 {:msg "end the run" :effect (effect (end-run))}]}

    "Viktor 2.0"
    {:abilities [{:msg "do 1 brain damage using 1 power counter" :counter-cost 1
-                 :effect (effect (damage :brain 1))}
+                 :effect (effect (damage :brain 1 {:card card}))}
                 {:label "Trace 2 - Add 1 power counter"
                  :trace {:base 2 :msg "add 1 power counter" :effect (effect (add-prop card :counter 1))}}
                 {:msg "end the run" :effect (effect (end-run))}]}
@@ -3172,7 +3177,7 @@
    {:abilities [{:msg "end the run" :effect (effect (end-run))}]}

    "Wall of Thorns"
-   {:abilities [{:msg "do 2 net damage" :effect (effect (damage :net 2))}
+   {:abilities [{:msg "do 2 net damage" :effect (effect (damage :net 2 {:card card}))}
                 {:msg "end the run" :effect (effect (end-run))}]}

    "Wendigo"
@@ -3185,7 +3190,7 @@

    "Woodcutter"
    {:advanceable :while-rezzed
-    :abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1))}]}
+    :abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}]}

    "Wormhole"
    {:advanceable :always}
@@ -3197,7 +3202,7 @@
    {:abilities [{:msg "end the run" :effect (effect (end-run))}]}

    "Yagura"
-   {:abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1))}
+   {:abilities [{:msg "do 1 net damage" :effect (effect (damage :net 1 {:card card}))}
                 {:msg "look at the top card of R&D"
                  :optional {:prompt (msg "Add " (:title (first (:deck corp))) " to bottom of R&D?")
                             :msg "add the top card of R&D to the bottom"


Fix for infinite recursion Parasite on NEXT Bronze.

--- src/clj/game/cards.clj
+++ src/clj/game/cards.clj
@@ -3075,7 +3075,7 @@
                                          (+ c (count (filter (fn [ice] (and (:rezzed ice) (has? ice :subtype "NEXT")))
                                                              (:ices server)))))
                                      0 (flatten (seq (:servers corp)))))
-    :events (let [nb {:req (req (has? target :subtype "NEXT"))
+    :events (let [nb {:req (req (and (not= (:cid target) (:cid card)) (has? target :subtype "NEXT")))
                    :effect (effect (update-ice-strength card))}]
                  {:rez nb :derez nb :trash nb :card-moved nb})}


Use == in deconstructor instead of =

Use == in deconstructor instead of =

--- src/aurora/language.cljs
+++ src/aurora/language.cljs
@@ -464,11 +464,11 @@
     (let [source (.-values fact)]
       (loop [i 0]
         (if (< i (alength constant-values))
-          (when (= (aget constant-values i) (aget source (aget constant-ixes i)))
+          (when (== (aget constant-values i) (aget source (aget constant-ixes i)))
             (recur (+ i 1)))
           (loop [i 0]
             (if (< i (alength dup-value-ixes))
-              (when (= (aget source (aget dup-value-ixes i)) (aget source (aget dup-var-ixes i)))
+              (when (== (aget source (aget dup-value-ixes i)) (aget source (aget dup-var-ixes i)))
                 (recur (+ i 1)))
               fact)))))))


change Adonis/Eve/Rex/Private Contracts

change Adonis/Eve/Rex/Private Contracts

Changes Adonis Campaign, Eve Campaign, Rex Campaign, and Private Contracts to allow placed counters to be added to any remaining from a prior derez instead if wiping them out.

--- src/clj/game/cards.clj
+++ src/clj/game/cards.clj
@@ -42,7 +42,7 @@
                            :msg "gain [Click]" :effect (effect (gain :runner :click 1))}}}

    "Adonis Campaign"
-   {:data {:counter 12}
+   {:effect (effect (add-prop card :counter 12))
     :events {:corp-turn-begins {:msg "gain 3 [Credits]" :counter-cost 3
                                 :effect (req (gain state :corp :credit 3)
                                              (when (zero? (:counter card)) (trash state :corp card)))}}}
@@ -833,7 +833,7 @@
                         (when caninst (lose state side :credit cost))))))}

    "Eve Campaign"
-   {:data {:counter 16}
+   {:effect (effect (add-prop card :counter 16))
     :events {:corp-turn-begins {:msg "gain 2 [Credits]" :counter-cost 2
                                 :effect (req (gain state :corp :credit 2)
                                              (when (zero? (:counter card)) (trash state :corp card)))}}}
@@ -1907,7 +1907,7 @@
     :effect (effect (rez target {:no-cost true}))}

    "Private Contracts"
-   {:data {:counter 14}
+   {:effect (effect (add-prop card :counter 14))
     :abilities [{:cost [:click 1] :counter-cost 2 :msg "gain 2 [Credits]"
                  :effect (req (gain state :corp :credit 2)
                               (when (= (:counter card) 0) (trash state :corp card)))}]}
@@ -2130,7 +2130,7 @@
     :effect (effect (move target :deck) (shuffle! :deck))}

    "Rex Campaign"
-   {:data [:counter 3]
+   {:effect (effect (add-prop card :counter 3))
     :events {:corp-turn-begins
              {:effect (req (add-prop state side card :counter -1)
                            (when (= (:counter card) 1)


Rename project.clj :debug => :useful.debug

--- src/cake/classloader.clj
+++ src/cake/classloader.clj
@@ -47,7 +47,7 @@
       (wrap-ext-classloader (mapcat to-urls (concat ext-deps ext-dev-deps)))))
   (if-let [cl (classlojure (apply classpath paths))]
     (doto cl
-      (given (:debug *project*)
+      (given (:useful.debug *project*)
              eval-in `(do ~@(rest (resource-forms "useful/debug.clj"))))
       (eval-in '(do (require 'cake)
                     (require 'bake.io)


Reload cljs code when templates change.

Reload cljs code when templates change.

--- src/clj/start/reload.clj
+++ src/clj/start/reload.clj
@@ -11,7 +11,10 @@
       newest)))

 (defn any-modified-cljs [dir k]
-  (any-modified k (filter #(.isFile %) (file-seq (io/file dir)))))
+  (let [files (filter #(.isFile %) (into (file-seq (io/file dir))
+                                         (file-seq (io/file "templates"))))]
+    (pr-str files)
+    (any-modified k files)))

 (defn watch-cljs [handler dir opts]
   (fn [request]


Experiment more with meta-errors

Experiment more with meta-errors

--- src/clojush/evaluate.clj
+++ src/clojush/evaluate.clj
@@ -579,7 +579,7 @@
                                                (:parent2-errors ind))]
                          (cond
                            (or (zero? e) (< e (min p1e p2e))) 0
-                           (some #{e} #{p1e p2e}) 3
+                           (some #{e} [p1e p2e]) 3
                            (< e (max p1e p2e)) 1
                            :else 2))))
                 (vec (repeat (count (:errors ind)) 4)))


parse the HTTPResponse for synchronous fetch

--- src/appengine_magic/services/urlfetch.clj
+++ src/appengine_magic/services/urlfetch.clj
@@ -10,9 +10,6 @@

 (defn- urlify [url]
   (if (string? url) (java.net.URL. url) url))
-
-(defn fetch [url]
-  (.fetch (get-urlfetch-service) (urlify url)))

 (defrecord HTTPResponse [content
 			 final-url
@@ -30,6 +27,9 @@
 		 (parse-headers (.getHeaders r))
 		 (.getResponseCode r)))

+(defn fetch [url]
+  (parse-response (.fetch (get-urlfetch-service) (urlify url))))
+
 (defn fetch-async [url]
   (let [f (.fetchAsync (urlify url))]
     (reify


Fix write-batch usage.

Fix write-batch usage.

--- src/onyx/plugin/kafka.clj
+++ src/onyx/plugin/kafka.clj
@@ -286,12 +286,11 @@
             (fn [fs]
               (-> fs
                   (clear-write-futures!)
-                  (into (comp (mapcat :leaves)
-                              (map
-                               (fn [msg]
-                                 (let [record (message->producer-record key-serializer-fn serializer-fn topic kpartition msg)]
-                                   (.send ^KafkaProducer producer record write-callback)))))
-                        write-batch))))
+                  (into (map
+                         (fn [msg]
+                           (let [record (message->producer-record key-serializer-fn serializer-fn topic kpartition msg)]
+                             (.send ^KafkaProducer producer record write-callback)))
+                         write-batch)))))
     true))

 (def write-defaults {:kafka/request-size 307200})


Fix case where final drain message isn't set as iterator is empty.

--- src/onyx/plugin/kafka.clj
+++ src/onyx/plugin/kafka.clj
@@ -217,38 +217,35 @@

   p/Input
   (poll! [this _ remaining-ms]
-    (if (and iter (.hasNext ^java.util.Iterator iter))
-      (let [rec ^ConsumerRecord (.next ^java.util.Iterator iter)
-            deserialized (some-> rec segment-fn)
-            part (.partition rec)
-            drained-part (ffirst (filter (fn [[_ state]] (= state :drained)) @drained))]
-        (.set watermark (max (.get watermark) (.timestamp rec)))
-
-        (cond (= :done deserialized) ;; TODO: Remove this in favor of target-offsets
-              (do (swap! drained assoc (.partition rec) :emitted)
-                  nil)
-
-              drained-part
-              (do
-               (swap! drained assoc drained-part :emitted)
-               {:type :end-reached :partition drained-part})
-
-              deserialized
-              (let [new-offset (.offset rec)]
-                (set! partition->offset (assoc partition->offset part new-offset))
-                (if-let [target-offset (get target-offsets part)]
-                  (let [tp (TopicPartition. topic part)]
-                    (if (>= new-offset target-offset)
-                      (when (not (paused? consumer part))
-                        (.pause consumer [tp])
-                        (swap! drained assoc (.partition rec) :drained)
-                        ;; only emit message if it's on the boundary
-                        (if (= new-offset target-offset)
-                          deserialized))
-                      deserialized))
-                  deserialized))))
-      (do (set! iter (.iterator ^ConsumerRecords (.poll ^Consumer consumer remaining-ms)))
-          nil))))
+    (if-let [drained-part (ffirst (filter (fn [[_ state]] (= state :drained)) @drained))]
+      (do (swap! drained assoc drained-part :emitted)
+          {:type :end-reached :partition drained-part})
+      (if (and iter (.hasNext ^java.util.Iterator iter))
+        (let [rec ^ConsumerRecord (.next ^java.util.Iterator iter)
+              deserialized (some-> rec segment-fn)
+              part (.partition rec)]
+          (.set watermark (max (.get watermark) (.timestamp rec)))
+
+          (cond (= :done deserialized) ;; TODO: Remove this in favor of target-offsets
+                (do (swap! drained assoc (.partition rec) :emitted)
+                    nil)
+
+                deserialized
+                (let [new-offset (.offset rec)]
+                  (set! partition->offset (assoc partition->offset part new-offset))
+                  (if-let [target-offset (get target-offsets part)]
+                    (let [tp (TopicPartition. topic part)]
+                      (if (>= new-offset target-offset)
+                        (when (not (paused? consumer part))
+                          (.pause consumer [tp])
+                          (swap! drained assoc (.partition rec) :drained)
+                          ;; only emit message if it's on the boundary
+                          (if (= new-offset target-offset)
+                            deserialized))
+                        deserialized))
+                    deserialized))))
+        (do (set! iter (.iterator ^ConsumerRecords (.poll ^Consumer consumer remaining-ms)))
+            nil)))))

 (defn read-messages [{:keys [onyx.core/task-map onyx.core/log-prefix onyx.core/monitoring] :as event}]
   (let [{:keys [kafka/topic kafka/deserializer-fn kafka/target-offsets]} task-map


Calculate origin-y by the height

Calculate origin-y by the height

--- src/play_clj/entities.clj
+++ src/play_clj/entities.clj
@@ -30,7 +30,7 @@
         (let [scale-x (float (or scale-x 1))
               scale-y (float (or scale-y 1))
               origin-x (float (or origin-x (/ width 2)))
-              origin-y (float (or origin-y (/ width 2)))
+              origin-y (float (or origin-y (/ height 2)))
               angle (float (or angle 0))]
           (.draw ^SpriteBatch batch object x y origin-x origin-y width height
             scale-x scale-y angle))


monads:: ending by implementing my own error-state-monad in clojure

--- src/clojure_learning/monads_explained.clj
+++ src/clojure_learning/monads_explained.clj
@@ -241,3 +241,118 @@
 ;; 3. Associativity

 ;; provide:: separation of concerns!
+
+;;;;;;
+;; My Practice:: after watching Brian Beckman on: "don't fear the monads"
+;; Starting with monoids
+;;;;;;
+;; Monoids are defined in terms of a collection of things + an operation on those things.
+;; AND the operation on those things must obey several rules: associativity + identity(unit)
+;; taken a clock as an example, which is a set of 12 numbers and the operation that composes the numbers being +
+;; then: (10 + 7) % 12 -> 5
+;; 10 + 7 = 17(pm is 5)
+;; this operation is defined in terms of identity-rule!
+;; associativity means: (10 + 7) % 12 = 10 + (7 % 12)
+(mod 7 12) ;; -> 7
+
+;; monads are monoids disguised which operate NOT on concrete types, but on monadic-types: Ma, a being a concrete type.
+;; monoids do NOT obey the commutativity rule: a + b != b + a -> this is not required as per the Types DO NOT allign ->
+;; compilation error!
+;; therefore we must be carefull when designing the bind + unit operations as the TYPES MUST ALLIGN!
+;; the "return" function is defined in terms of: a -> Ma
+;; takes a concrete type and returns a monadic-type of a.
+;; the "bind" function: binds an action to a function: Ma -> (fn g: a -> Ma) -> Ma
+;; bind takes an action (can be an action resulted from the unit-func) AND another function, which func takes a concrete
+;; a and MUST return a Ma.
+;; i usually think of monads as they introduced another monadic-type which can be everything, and they all work on the
+;; same monadic-type from now on.
+
+
+;;;;;;;;;;;;;;;
+;; My Implementation for the Error Monad(combined with State monad for persisting error-stacks)
+;;;;;;;;;;;;;;
+;; as a practice exercise we shall implement the Error-Monad. This monad should encapsulate the try-catch mechanism.
+;; it is used in operation that might throw exceptions. a common idiom is to guard the code that might throw exceptions
+;; into a try-catch but this will block us from making computations chainable and will eventually introduce complexity.
+;; The Error monad (also called the Exception monad) embodies the strategy of combining computations that can throw exceptions
+;; by bypassing bound functions from the point an exception is thrown to the point that it is handled.
+((-> (return 5)
+     (lift-divide 0)
+      lift-add 10
+     ))
+
+;; we may want an output that looks like this:
+;; [15 ["caught exception while dividing by 0"]]
+
+;; we might want to by-pass the operation which generates the exception and continue with the execution, and simply
+;; saving the exception message
+(defn return [v]
+  (fn [states-vec] [v states-vec])) ;; takes a v -> Mv;; Mv can be anything, Mv here is the monadic-function-data-type
+
+;; because we defined the monadic type of Mv as being: (func: c -> d), all the lifting functions should use this type
+;; from now on
+
+;; because action is in-fact the monadic type -> it should be defined in terms of the func that takes a -> Ma
+;; same as bind: Ma -> (fn: a -> Ma) -> Ma
+(defn bind [action func]
+  (fn [state-vec]
+    (let [[x errors] (action state-vec)]   ;; action here is represented by the func resulted by return!
+     (try
+       (let [monadic-func (func x)]
+         (monadic-func errors))
+     (catch Exception e
+        [x (conj errors (str "caught exception while performing operation:: " action ", exception is:: " (.getMessage e)))]
+)))))
+
+;; then, defining each lifting functions which MUST return the same monadic-type:: f:a -> Ma
+(defn lift-add [action y]
+  (bind action (fn [x] (return (+ x y)))))
+
+;; using infix notation:
+;; Ma BIND (fn: x -> Ma) -> Ma
+;; (fnc) BIND (fn:x -> fnc) -> fnc
+;; the function passed to bind(2nd arg) is simply a function that takes an arg -> BUT SHOULD RETURN a monadic-type:: the
+;; func-data-type we defined for our [return] monad-operation
+
+((lift-add (return 3) 10) []) ;; [13 []]
+
+((-> (return 3)
+     (lift-add 10)
+     ) []) ;; [13 []] -> cool since there was no error on the way
+
+;; but let's try with division
+(defn lift-divide [action y]
+  (bind action (fn [x] (return (/ x y)))))
+
+((-> (return 3)
+     (lift-add 7)
+     (lift-divide 2)
+     ) []) ;; [5 []]
+
+;; works well, but what about actually dividing by zero? we want to log the error along with the LAST result!
+((-> (return 3)
+     (lift-add 7)
+     (lift-divide 0)) []) ;; [10 ["caught exception:: divide by zero..."]]
+
+;; can we continue??? it would be nice, to log the error and continue...
+((-> (return 3)
+     (lift-add 7)
+     (lift-divide 0)
+     (lift-add 2)) []) ;; [12 ["caught exception:: divide by zero..."]] !!!! now this rocks dude' :)
+
+;; what about two exceptions:
+((-> (return 3)
+     (lift-add 7)
+     (lift-divide 0)
+     (lift-add 2)
+     (lift-divide 0)
+     ) [])
+;; [12 ["caught exception:: divide by zero...", "caught exception:: divide by zero..."]]
+;; !!!! now this freakin rocks dude' :) it logs the errors and continue!!!
+
+
+;; a litlle try-catch practice::
+(try
+  (/ 5 0)
+  (catch ArithmeticException e
+    (println (.getMessage e))))


Resolves TODOs

Resolves TODOs

--- src/expound/alpha.cljc
+++ src/expound/alpha.cljc
@@ -394,12 +394,7 @@
        "Extra input" (extra-input spec-name val path))
      (relevant-specs problems))))

-;; TODO - remove
-(s/def ::fn fn?)
-
 (defmethod problem-group-str :problem/unknown [_type spec-name val path problems]
-  ;; TODO - strong assertion
-  (s/assert ::fn *value-str-fn*)
   (assert (apply = (map :val problems)) (str "All values should be the same, but they are " problems))
   (format
    "%s
@@ -514,7 +509,6 @@
              (str
               (instrumentation-info failure caller)
               (format
-               ;; TODO - add a newline here to make specs look nice
                "%s

 %s


Adding documentation.

Adding documentation.

--- src/rand_circles/core.clj
+++ src/rand_circles/core.clj
@@ -15,7 +15,26 @@
             [incanter interpolation])
   (:gen-class))

+;; Declaring all functions upfront allows me to reorder them in the way that's easiest to understand, rather than in order of dependency.
+
 (declare -main expt-list reciprocal negate rand-in-range rands-in-range rands-with-amplitude linspace zip get-cubic-interpolator sum sum-funcs get-perlin-func)
+
+;; ## How Perlin noise works.
+
+;; Perlin noise is smoother and more natural than a list of random values. It achieves this in the following way:
+
+;; 1. Pick a few random points in some range (i.e. between -1 and 1)
+;; 2. Spread these points evenly over some domain (set of x-values)
+;; 3. Interpolate them, producing a smooth function that intersects each randomly-generated point.
+;; 4. Repeat the process, but choose more points, and in a smaller range (i.e. between -0.5 and 0.5)
+;; 5. Now, add the functions together. This superimposes the rougher function on the first, smoother function.
+;; 6. You can repeat this process of making new functions and summing them all up as many times as you like, though each function will change the output of the noise generator less and less.
+
+;; ## Implementing a Perlin noise generator
+
+;; The function `get-perlin-func` returns a function defined over the domain [0,1] which will output a random value around the range [-1,1]. The first argument, `freq`, defines how many random points to select for the first step explained above. The higher the number, the rougher the noise. If the number is below 3, however, there won't be enough points to fit a cubic interpolator, and the function will fail.
+
+;; The second argument, `levels`, defines how many times to go through the steps above. If `levels` is 1, then only one set of random points will be generated. If `levels` is 2, then two sets of random points will be generated, interpolated, and summed.

 (defn get-perlin-func [freq levels]
   "Return a function, valid over domain [0,1] which produces perlin noise."


Remove debug print

Remove debug print

--- src/freactive/dom.cljs
+++ src/freactive/dom.cljs
@@ -580,7 +580,6 @@
 (defn element*
   ([elem-factory append-children-fn]
    (fn [ns-uri tag tail]
-     (println "element" tag tail)
      (let [[_ tag-name id class] (re-matches re-tag (name tag))
            attrs? (first tail)
            have-attrs (map? attrs?)


cleaned up a bit

cleaned up a bit

--- src/frontend/core.cljs
+++ src/frontend/core.cljs
@@ -136,7 +136,6 @@
   (fn [music-info]
     (swap! app-state assoc :music-info
       (merge (:music-info @app-state) music-info))
-    (println (:music-info @app-state))
     (if (:play-next-upload? @app-state)
       (do
         (.emit socket "file-download-request" (.-id music-info))
@@ -203,7 +202,7 @@
   (fn [room-music-info current-track-id]
     (let [sorted-music-info (.sort room-music-info #(- (.-tracknum %1)
                                                        (.-tracknum %2)))]
-      (swap! app-state assoc :music-info (map #(clj->js %1) (js->clj sorted-music-info)))
-      (println (:music-info @app-state)))
+      (swap! app-state assoc :music-info (map #(clj->js %1)
+                                              (js->clj sorted-music-info))))
     (swap! app-state assoc :current-track-id current-track-id)
     (.emit socket "file-download-request" current-track-id)))


(maint) Fix spelling of producer_timestamp

--- test/puppetlabs/puppetdb/acceptance/node_ttl.clj
+++ test/puppetlabs/puppetdb/acceptance/node_ttl.clj
@@ -86,11 +86,11 @@
                                     :values facts}
                                    "replace facts" 5))
                deactivate (fn [certname stamp]
-                            (do-cmd {:producer_timstamp stamp
-                                :certname certname}
+                            (do-cmd {:producer_timestamp stamp
+                                     :certname certname}
                                     "deactivate node" 3))
                set-expire (fn [certname stamp expire-facts?]
-                            (do-cmd {:producer_timstamp stamp
+                            (do-cmd {:producer_timestamp stamp
                                      :certname certname
                                      :expire {:facts expire-facts?}}
                                     "configure expiration" 1))


typo fix

typo fix

--- src/semantic_csv/core.clj
+++ src/semantic_csv/core.clj
@@ -379,7 +379,7 @@
 ;; <br/>
 ;; ## format-with

-(defn format-with
+(defn format-all
   "Just an alias for `cast-all`."
   [& args]
   (apply cast-all args))
@@ -439,7 +439,7 @@
                            :prepend-header prepend-header}))
           (?>> formatters (format-with formatters))
           ; For save measure
-          (format-with str)
+          (format-all str)
           (batch batch-size)
           (pc/<- (csv/write-csv writer-opts))
           (reduce


Guava 16.0 is incompatable with datomic cassandra bridge. (#767)

Upgrade to 17.0. This shows up in a lein repl. Outside of a lein repl,
you may or may not get a newer version based on how you handle
classpath construction.

--- scheduler/project.clj
+++ scheduler/project.clj
@@ -30,7 +30,7 @@
                  [com.rpl/specter "1.0.1"]

                  ;;Utility
-                 [com.google.guava/guava "16.0"]
+                 [com.google.guava/guava "17.0"]
                  [amalloy/ring-buffer "1.1"]
                  [listora/ring-congestion "0.1.2"]
                  [lonocloud/synthread "1.0.4"]


Bumps version to 1.53.5-SNAPSHOT

Bumps version to 1.53.5-SNAPSHOT

--- scheduler/project.clj
+++ scheduler/project.clj
@@ -13,7 +13,7 @@
 ;; See the License for the specific language governing permissions and
 ;; limitations under the License.
 ;;
-(defproject cook "1.53.4"
+(defproject cook "1.53.5-SNAPSHOT"
   :description "This launches jobs on a Mesos cluster with fair sharing and preemption"
   :license {:name "Apache License, Version 2.0"}
   :dependencies [[org.clojure/clojure "1.8.0"]


Speed up STRIPS type-checking.

Speed up STRIPS type-checking.

--- src/edu/berkeley/ai/util/propositions.clj
+++ src/edu/berkeley/ai/util/propositions.clj
@@ -28,7 +28,8 @@
   (let [obj-map (reduce (fn [m [k & vs]] (assoc-cat m k (if (coll? (first vs)) (first vs) vs))) {} guaranteed-objs)]
     (doseq [k (keys obj-map)] (safe-get types k))
     (assert-is (distinct-elts? (apply concat (vals obj-map))))
-    obj-map))
+    (map-vals set obj-map)))
+;    obj-map))

 (defn check-predicates [types predicates]
   (let [pred-map (map-map seq->vector-pair predicates)]
@@ -56,7 +57,7 @@

 (defn is-type? [types objects obj type]
  ; (prn types objects obj type)
-  (or (includes? (get objects type) obj)
+  (or (contains? (to-set (get objects type)) obj)
       (some #(is-type? types objects obj %) (get types type))))

 (defn check-type [types objects obj type]


Ok, now rng stuff is partially (not all generators) adjusted to State stuff

--- src/fpinscala/purely_functional_state.clj
+++ src/fpinscala/purely_functional_state.clj
@@ -360,3 +360,5 @@
               (make-result-state (- (inc n)) nxt-rng)
               (make-result-state n nxt-rng))))

+;usage example
+((state-sequence-fpins (repeat 5 non-negative-int-fpins-s)) (make-simple-rng-s 42))


Split up available-plugins monolithic fn

available-plugins was returning a useful plugins map and checking
missing plugins. By splitting up this fn, save-user-plugin-dependencies
is finally able to use just part of the fn that it needs and remove an
akward use of options

--- src/lt/objs/plugins.cljs
+++ src/lt/objs/plugins.cljs
@@ -411,37 +411,41 @@
     (doseq [dep missing]
       (discover-deps dep count-down))))

+(defn check-missing
+  "Check a plugins map for outdated or missing :dependencies and prompt
+  to install missing ones"
+  [deps]
+  (when-let [missing? (missing-deps deps)]
+    (popup/popup! {:header "Some plugin dependencies are missing."
+                   :body [:div
+                          [:span "We found that the following plugin dependencies are missing: "]
+                          (for [{:keys [name version]} missing?]
+                            [:div name " " version " "])
+                          [:span "Would you like us to install them?"]]
+                   :buttons [{:label "Cancel"}
+                             {:label "Install all"
+                              :action (fn []
+                                        (install-missing missing?))}]})))
+
 (defn available-plugins
-  [& {:keys [ignore-missing]}]
+  "Return a map of plugins by plugin name based on what's read from filesystem"
+  []
   (let [ds (concat (files/dirs user-plugins-dir)
                    (files/dirs plugins-dir)
                    [settings/user-plugin-dir])
         plugins (->> ds
                      (map plugin-info)
-                     (filterv identity))
-        final (-> (reduce (fn [final p]
-                            (if-let [cur (get final (:name p))]
-                              ;;check if it's newer
-                              (if (deploy/is-newer? (:version cur) (:version p))
-                                (assoc! final (:name p) p)
-                                final)
-                              (assoc! final (:name p) p)))
-                          (transient {})
-                          plugins)
-                  (persistent!))
-        missing? (missing-deps final)]
-    (when (and missing? (not ignore-missing))
-      (popup/popup! {:header "Some plugin dependencies are missing."
-                     :body [:div
-                            [:span "We found that the following plugin dependencies are missing: "]
-                            (for [{:keys [name version]} missing?]
-                              [:div name " " version " "])
-                            [:span "Would you like us to install them?"]]
-                     :buttons [{:label "Cancel"}
-                               {:label "Install all"
-                                :action (fn []
-                                          (install-missing missing?))}]}))
-    final))
+                     (filterv identity))]
+    (-> (reduce (fn [final p]
+                  (if-let [cur (get final (:name p))]
+                    ;;check if it's newer
+                    (if (deploy/is-newer? (:version cur) (:version p))
+                      (assoc! final (:name p) p)
+                      final)
+                    (assoc! final (:name p) p)))
+                (transient {})
+                plugins)
+        (persistent!))))

 (defn uninstall [plugin]
   (when (:dir plugin)
@@ -647,13 +651,15 @@
           :desc "Saves dependencies to user's plugin.edn"
           :reaction (fn [this & opts]
                       ;; Use available-plugins b/c ::plugins aren't always up to date e.g. uninstall
-                      (save-plugins (available-plugins :ignore-missing true))))
+                      (save-plugins (available-plugins))))

 (behavior ::render-installed-plugins
           :triggers #{:refresh!}
           :desc "Plugin Manager: refresh installed plugins"
-          :reaction (fn [this & opts]
-                      (object/merge! app/app {::plugins (apply available-plugins opts)})
+          :reaction (fn [this & {:keys [ignore-missing]}]
+                      (object/merge! app/app {::plugins (available-plugins)})
+                      (when-not ignore-missing
+                        (check-missing (::plugins @app/app)))
                       (let [ul (dom/$ :.plugins (object/->content this))]
                         (dom/empty ul)
                         (dom/append ul (dom/fragment (map installed-plugin-ui (->> @app/app ::plugins vals (sort-by #(.toUpperCase (:name %))))))))))
@@ -721,7 +727,8 @@
                       (object/raise app/app :create-user-plugin)
                       (object/raise app/app :flatten-map-settings)
                       ;;load enabled plugins
-                      (object/merge! app/app {::plugins (available-plugins)})))
+                      (object/merge! app/app {::plugins (available-plugins)})
+                      (check-missing (::plugins @app/app))))

 (behavior ::behaviors.refreshed-load-keys
           :triggers #{:behaviors.refreshed}


Componentise more things.

Componentise more things.

--- src/clj/onyx_dashboard/http/server.clj
+++ src/clj/onyx_dashboard/http/server.clj
@@ -2,6 +2,7 @@
   (:require [clojure.core.async :refer [chan thread <!! alts!!]]
             [onyx-dashboard.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
             [org.httpkit.server :as http-kit-server]
+            [com.stuartsierra.component :as component]
             [clojure.java.io :as io]
             [compojure.route :refer [resources]]
             [compojure.handler :refer [api]]
@@ -27,6 +28,11 @@
             [:security :anti-forgery]
             {:read-token (fn [req] (-> req :params :csrf-token))}))

+(def deployments (atom {}))
+(def tracking (atom {}))
+
+;(deref tracking)
+
 ; Improve fn name
 (defn job-peer-allocated-task [job-allocations job-id peer-id]
   (ffirst
@@ -48,49 +54,98 @@
 ; including chunks that are read e.g. catalog. Then if we end up
 ; with multiple clients viewing the same deployment we can only subscribe once.
 ; Probably not worth the complexity!
-(defn track-deployment [send-fn! client peer-config uid]
-  ; TODO: do we need to close this channel properly when
-  ; cancelling the future so the subscribe-to-log thread doesn't
-  ; keep going?
-  (let [ch (chan 100)]
-    (loop [replica (extensions/subscribe-to-log (:log client) ch)]
-      (let [position (<!! ch)
-            entry (extensions/read-log-entry (:log client) position)
-            new-replica (extensions/apply-log-entry entry replica)
-            diff (extensions/replica-diff entry replica new-replica)
-            job-id (:job (:args entry))
-            tasks (get (:tasks new-replica) job-id)
-            ;complete-tasks (get (:completions new-replica) job-id)
-            ]
-        (case (:fn entry)
-          :submit-job (let [job-id (:id (:args entry))
-                            catalog (extensions/read-chunk (:log client) :catalog job-id)]
-                        (send-fn! uid [:job/submitted-job {:id job-id
-                                                           :entry entry
-                                                           :catalog catalog
-                                                           :created-at (:created-at entry)}]))
-          ; TODO: check if newly submitted jobs will result in peer assigned messages being sent.
-          :volunteer-for-task (let [peer-id (:id (:args entry))
-                                    task (task-allocated-to-peer (:allocations new-replica) peer-id)]
-                                (if task
-                                  (send-fn! uid
-                                            [:job/peer-assigned {:job (:job task)
-                                                                 :task (:task task)
-                                                                 :peer peer-id}])))
-
-          :complete-task (let [task (extensions/read-chunk (:log client) :task (:task (:args entry)))
-                               task-name (:name task)]
-                           (send-fn! uid [:job/completed-task {:name task-name}]))
-          (info "Unable to custom handle entry"))
-        (send-fn! uid [:job/entry entry])
-        ;(send-fn! uid [:deployment/replica {:replica new-replica :diff diff}])
-        (recur new-replica)))))
+(defn track-deployment [send-fn! subscription ch uid]
+  (let [log (:log (:env subscription))]
+    (loop [replica (:replica subscription)]
+      (when-let [position (<!! ch)]
+        (let [entry (extensions/read-log-entry log position)
+              new-replica (extensions/apply-log-entry entry replica)
+              diff (extensions/replica-diff entry replica new-replica)
+              job-id (:job (:args entry))
+              ;complete-tasks (get (:completions new-replica) job-id)
+              tasks (get (:tasks new-replica) job-id)]
+          (case (:fn entry)
+            :submit-job (let [job-id (:id (:args entry))
+                              catalog (extensions/read-chunk log :catalog job-id)]
+                          (send-fn! uid [:job/submitted-job {:id job-id
+                                                             :entry entry
+                                                             :catalog catalog
+                                                             :created-at (:created-at entry)}]))
+            ; TODO: check if newly submitted jobs will result in peer assigned messages being sent.
+            :volunteer-for-task (let [peer-id (:id (:args entry))
+                                      task (task-allocated-to-peer (:allocations new-replica) peer-id)]
+                                  (if task
+                                    (send-fn! uid
+                                              [:job/peer-assigned {:job (:job task)
+                                                                   :task (:task task)
+                                                                   :peer peer-id}])))
+
+            :complete-task (let [task (extensions/read-chunk log :task (:task (:args entry)))
+                                 task-name (:name task)]
+                             (send-fn! uid [:job/completed-task {:name task-name}]))
+            (info "Unable to custom handle entry"))
+          (send-fn! uid [:job/entry entry])
+          ;(send-fn! uid [:deployment/replica {:replica new-replica :diff diff}])
+          (recur new-replica))))))
+
+
+(defrecord LogSubscription [peer-config]
+  component/Lifecycle
+  (start [component]
+    ; Maybe don't need a whole component for this since it already pretty much is one?
+    (info "Start log subscription")
+    (let [sub-ch (chan 100)
+          subscription (onyx.api/subscribe-to-log peer-config sub-ch)]
+      (assoc component :subscription subscription :subscription-ch sub-ch)))
+  (stop [component]
+    (info "Shutting down log subscription")
+    (onyx.api/shutdown-env (:env (:subscription component)))
+    (assoc component :subscription nil :channel nil)))
+
+
+(defrecord TrackDeploymentManager [send-fn! peer-config user-id]
+  component/Lifecycle
+  (start [component]
+    ; Convert to a system?
+    (info "Starting Track Deployment manager " send-fn! peer-config user-id)
+    (let [subscription (component/start (map->LogSubscription {:peer-config peer-config}))]
+      (assoc component
+             :subscription subscription
+             :tracking-fut (future
+                             (track-deployment send-fn!
+                                               (:subscription subscription)
+                                               (:subscription-ch subscription)
+                                               user-id)))))
+  (stop [component]
+    (info "Stopping Track Deployment manager.")
+    (assoc component
+           :subscription (component/stop (:subscription component))
+           :tracking-fut (future-cancel (:tracking-fut component)))))
+
+(defn new-track-deployment-manager [send-fn! peer-config user-id]
+  (map->TrackDeploymentManager {:send-fn! send-fn!
+                                :peer-config peer-config
+                                :user-id user-id}))
+
+(defn stop-tracking! [user-id]
+  (when-let [tracking-manager (@tracking user-id)]
+    (component/stop tracking-manager)
+    (swap! tracking dissoc user-id)))
+
+(defn start-tracking! [send-fn! peer-config deployment-id user-id]
+  (stop-tracking! user-id)
+  (swap! tracking
+         assoc
+         user-id
+         (component/start (new-track-deployment-manager send-fn!
+                                                        (assoc peer-config :onyx/id deployment-id)
+                                                        user-id))))
+
+(defn stop-all-tracking! []
+  (map stop-tracking! (keys @tracking)))

 (defn zk-deployment-entry-stat [client entry]
   (:stat (zk/data client (zk-onyx/prefix-path entry))))
-
-(def deployments (atom {}))
-(def tracking (atom {}))

 (defn distribute-deployments [send-all-fn! deployments]
   (send-all-fn! [:deployment/listing deployments]))
@@ -102,7 +157,7 @@
        (map (fn [[child stat]]
               (vector child
                       {:created-at (java.util.Date. (:ctime stat))
-                       :modified (java.util.Date. (:mtime stat))})))
+                       :modified-at (java.util.Date. (:mtime stat))})))
        (into {})
        (reset! deployments)
        (distribute-deployments send-all-fn!)))
@@ -110,30 +165,11 @@
 (defn event->uid [event]
   (get-in event [:ring-req :cookies "ring-session" :value]))

-; TODO: should be passing tracking around
-(defn stop-tracking! [user-id]
-  (when-let [current (@tracking user-id)]
-    (component/stop (:client current))
-    (future-cancel (:tracking-fut current))))
-
-(defn start-tracking! [send-fn! deployment-id peer-config user-id]
-  (stop-tracking! user-id)
-  (let [client (component/start (system/onyx-client peer-config))]
-    (swap! tracking
-           assoc
-           user-id
-           {:client client
-            :tracking-fut (future
-                            (track-deployment send-fn!
-                                              client
-                                              (assoc peer-config :onyx/id deployment-id)
-                                              user-id))})))
-
 (defn start-event-handler [sente peer-config]
-  (thread
+  (future
     (loop []
       (when-let [event (<!! (:ch-chsk sente))]
-        (info "EVENT:" event)
+        ;(info "EVENT:" event)
         ;; TODO: more sophisticated tracking,
         ;; should track by cluster id rather than user
         ;; and count the number of users tracking it. When the user count drops to 0
@@ -141,13 +177,11 @@
         (let [user-id (event->uid event)]
           (case (:id event)
             :deployment/track (start-tracking! (:chsk-send! sente)
+                                               peer-config
                                                (:?data event)
-                                               peer-config
                                                user-id)
             :deployment/get-listing ((:chsk-send! sente) user-id [:deployment/listing @deployments])
-            :chsk/uidport-close (do
-                                  (future-cancel (@tracking user-id))
-                                  (swap! tracking dissoc user-id))
+            :chsk/uidport-close (stop-tracking! user-id)
             :chsk/ws-ping nil
             (println "Dunno what to do with: " event)))
         (recur)))))
@@ -170,21 +204,24 @@
       (resources "/")
       (resources "/react" {:root "react"})
       (route/not-found "Page not found"))
-    (start-event-handler sente peer-config)
-    (refresh-deployments-watch (partial send-mult-fn
-                                        (:chsk-send! sente)
-                                        (:connected-uids sente))
-                               (zk/connect (:zookeeper/address peer-config)))
-
-    (let [my-ring-handler (ring.middleware.defaults/wrap-defaults my-routes ring-defaults-config)
+
+    (let [event-handler-fut (start-event-handler sente peer-config)
+          ; TODO: no way to currently stop this watch
+          _ (refresh-deployments-watch (partial send-mult-fn
+                                                (:chsk-send! sente)
+                                                (:connected-uids sente))
+                                       (zk/connect (:zookeeper/address peer-config)))
+          my-ring-handler (ring.middleware.defaults/wrap-defaults my-routes ring-defaults-config)
           server (http-kit-server/run-server my-ring-handler {:port 3000})
           uri (format "http://localhost:%s/" (:local-port (meta server)))]
       (println "Http-kit server is running at" uri)
-      (assoc component :server server)))
+      (assoc component :server server :event-handler-fut event-handler-fut)))
   (stop [{:keys [server] :as component}]
     (println "Stopping HTTP Server")
+    (stop-all-tracking!)
+    (future-cancel (:event-handler-fut component))
     (server :timeout 100)
-    (assoc component :server nil)))
+    (assoc component :server nil :event-handler-fut nil)))

 (defn new-http-server [peer-config]
   (map->HttpServer {:peer-config peer-config}))


multiple evidences to badge settings

multiple evidences to badge settings

--- src/cljs/salava/badge/ui/settings.cljs
+++ src/cljs/salava/badge/ui/settings.cljs
@@ -178,41 +178,6 @@
          :dateto   expires_on}]]
       ]]))

-(defn evidence-modal [state]
-    [:div
-     "test"
-
-     ]
-    )
-
-
-(defn evidence-block [evidence-url]
-  [:div.evidence-block evidence-url])
-
-(defn evidences [evidences state]
-  (let [show-evidence-options (cursor state [:show_evidence_options])
-        evidence-url-input (cursor state [:show_url_input])]
-    [:div
-     [:label {:class "col-md-12 sub-heading" :for "evidence"}
-      #_(t :badge/Evidenceurl) "Evidence"]
-     [:div.form-group
-      [:i.fa.fa-plus-square] [:a {:href "#" :on-click #_(mo/open-modal [:badge :evidence] {:state state}) #(do (.preventDefault %)(swap! state assoc :tab [evidence/evidence-form state] :tab-no 6))} "Add new evidence"]
-
-      (when @show-evidence-options
-        [:div.resource-options.inline-block
-         [:div [:i.fa.fa-file] "file"]
-         [:a {:href "#" :on-click #(swap! state assoc :show_url_input true)}[:div [:i.fa.fa-link] "url"]]])
-
-      (when @evidence-url-input
-        [:input {:type      "textbox"
-                 ;:on-change #(toggle-evidence state)
-                 ;:checked    (get-in @state [:badge-settings :show_evidence])
-                 }]
-        )
-
-      ]
-     [evidence-block evidences]]))
-
 (defn settings-tab-content [data state init-data]
   (let [{:keys [id name image_file issued_on expires_on show_evidence revoked rating]} data
          expired? (bh/badge-expired? expires_on)
@@ -266,10 +231,10 @@
                                                    [:div {:class "form-group"}
                                                     [:div {:class "col-md-12"}
                                                      [tag/new-tag-input (cursor state [:badge-settings :tags]) (cursor state [:badge-settings :new-tag])]]]
-                                                   [:div {:class "row"}
+                                                   #_[:div {:class "row"}
                                                     [:label {:class "col-md-12 sub-heading" :for "evidenceurl"}
                                                      (t :badge/Evidenceurl)]]
-                                                   [:div {:class "form-group"}
+                                                   #_[:div {:class "form-group"}
                                                     [:div {:class "col-md-12"}
                                                      [:input {:class       "form-control"
                                                               :type        "text"
@@ -286,7 +251,6 @@
                                                                       :on-change #(toggle-evidence state)
                                                                       :checked    (get-in @state [:badge-settings :show_evidence])}]
                                                              (t :badge/Showevidence)]]])
-                                                   #_[evidences (get-in @state [:badge-settings :evidence_url]) state]
                                                    [evidence/evidence-block data state init-data]]]
                                             [:div.modal-footer
                                              [:button {:type         "button"