Large refactoring

- State handler intermediates between view, server
This commit is contained in:
Nicholas Kariniemi 2013-08-18 14:36:16 +03:00
parent 707fb9f22d
commit 1f9108b96a
9 changed files with 163 additions and 105 deletions

View file

@ -1,5 +1,6 @@
(ns grub.integration-test
(:require [clj-webdriver.taxi :as taxi]
(:require [grub.db :as db]
[clj-webdriver.taxi :as taxi]
[clj-webdriver.core :as webdriver]
[clojure.test :as test]))
@ -23,20 +24,25 @@
(add-grub driver1 grub))
(doseq [grub grubs]
(test/is (taxi/find-element driver2 {:text grub})
"Added grubs should appear in other browser"))))
"Added grubs should appear in other browser")))
(db/clear-grubs))
(defn test-grubs-are-stored-on-server [url driver]
(taxi/to driver url)
(let [grubs (repeatedly 4 get-rand-grub)]
(doseq [grub grubs]
(add-grub driver grub))
(Thread/sleep 200)
(taxi/refresh driver)
(Thread/sleep 200)
(doseq [grub grubs]
(test/is (taxi/find-element driver {:text grub})
"Previously added grubs should be loaded on refresh"))))
"Previously added grubs should be loaded on refresh")))
(db/clear-grubs))
(defn run [port]
(db/connect-and-handle-events "grub-integration-test")
(let [site-url (str "http://localhost:" port)]
(println "Starting integration test")
(let [driver1 (get-driver site-url)
@ -44,4 +50,5 @@
(test-adding-grubs site-url driver1 driver2)
(test-grubs-are-stored-on-server site-url driver1)
(taxi/quit driver1)
(taxi/quit driver2))))
(taxi/quit driver2)))
(db/clear-grubs))

View file

@ -17,18 +17,21 @@
(describe
"grub.db"
(before-all (db/connect-and-handle-events test-db))
(before (mc/drop db/grub-collection))
(describe "Create grub"
(it "should create a grub when a create event comes"
(before (db/clear-grubs))
(describe "Add"
(it "should add a grub when an add event comes"
(let [test-grub "testgrub"
test-id 12345]
(>!! @db/incoming-events {:event :create :_id test-id :grub test-grub})
(>!! @db/incoming-events {:event :add
:_id test-id
:grub test-grub
:completed false})
(short-delay)
(should=
{:_id test-id :grub test-grub :completed false}
(mc/find-one-as-map db/grub-collection {:_id test-id})))))
(describe "Complete grub"
(describe "Complete"
(it "should complete a grub when a complete event comes"
(let [test-grub {:_id 123456 :completed false}]
(mc/insert db/grub-collection test-grub)
@ -38,7 +41,7 @@
{:_id (:_id test-grub) :completed true}
(mc/find-one-as-map db/grub-collection {:_id (:_id test-grub)})))))
(describe "Uncomplete grub"
(describe "Uncomplete"
(it "should uncomplete a grub when an uncomplete event comes"
(let [test-grub {:_id 123456 :completed true}]
(mc/insert db/grub-collection test-grub)
@ -48,7 +51,7 @@
{:_id (:_id test-grub) :completed false}
(mc/find-one-as-map db/grub-collection {:_id (:_id test-grub)})))))
(describe "Delete grub"
(describe "Delete"
(it "should delete a grub when a delete event comes"
(let [test-grub {:_id 123456 :completed true}]
(mc/insert db/grub-collection test-grub)

View file

@ -7,56 +7,61 @@
[grub.macros :refer [log logs]]))
(describe
"grub state"
(before (reset! state/grubs []))
"State"
(describe "Add grub"
(it "should add a grub to the state when an add event comes"
(let [test-grub {:_id 12345 :grub "testgrub" :completed true}
add-event (assoc test-grub :event :add)]
(state/handle-event add-event)
(should-contain test-grub @state/grubs))))
(describe
"event handling:"
(before (reset! state/grubs []))
(describe "Create grub"
(it "should add a new grub to the state when a create event comes"
(let [test-grub {:grub "testgrub"}
expected-grub (assoc test-grub :completed false)
create-event (assoc test-grub :event :create)]
(state/handle-event create-event)
(let [created-grub (first @state/grubs)]
(should= (:grub created-grub) (:grub test-grub)))))
(it "should generate an _id for the new grub"
(let [test-grub {:grub "testgrub"}
create-event (assoc test-grub :event :create)]
(state/handle-event create-event)
(let [added-grub (first (filter #(= (:grub %) (:grub test-grub))
@state/grubs))]
(should-not-be-nil (:_id added-grub))))))
(describe "Add"
(it "should add a grub to the state when an add event comes"
(let [test-grub {:_id 12345 :grub "testgrub" :completed true}
add-event (assoc test-grub :event :add)]
(state/handle-event add-event)
(should-contain test-grub @state/grubs))))
(describe "Complete"
(it "should complete a grub in the state when a complete event comes"
(let [test-grub {:_id 234243 :grub "testgrub" :completed false}
expected-grub (assoc test-grub :completed true)
complete-event (-> (select-keys [:_id])
(assoc :event :complete))]
(reset! state/grubs [test-grub])
(state/handle-event complete-event)
(should-contain expected-grub @state/grubs))))
(describe "Uncomplete"
(it "should uncomplete a grub in the state when an uncomplete event comes"
(let [test-grub {:_id 234243 :grub "testgrub" :completed true}
expected-grub (assoc test-grub :completed false)
complete-event (-> (select-keys [:_id])
(assoc :event :uncomplete))]
(reset! state/grubs [test-grub])
(state/handle-event complete-event)
(should-contain expected-grub @state/grubs))))
(describe "Delete"
(it "should delete a grub from the state when a delete event comes"
(let [test-grub {:_id 234243 :grub "testgrub" :completed true}
delete-event {:_id (:_id test-grub) :event :delete}]
(reset! state/grubs [test-grub])
(state/handle-event delete-event)
(should= [] @state/grubs)))))
(describe "Complete grub"
(it "should complete a grub in the state when a complete event comes"
(let [test-grub {:_id 234243 :grub "testgrub" :completed false}
expected-grub (assoc test-grub :completed true)
complete-event (-> (select-keys [:_id])
(assoc :event :complete))]
(reset! state/grubs [test-grub])
(state/handle-event complete-event)
(should-contain expected-grub @state/grubs))))
(describe "Uncomplete grub"
(it "should uncomplete a grub in the state when an uncomplete event comes"
(let [test-grub {:_id 234243 :grub "testgrub" :completed true}
expected-grub (assoc test-grub :completed false)
complete-event (-> (select-keys [:_id])
(assoc :event :uncomplete))]
(reset! state/grubs [test-grub])
(state/handle-event complete-event)
(should-contain expected-grub @state/grubs))))
(describe "Delete grub"
(it "should delete a grub from the state when a delete event comes"
(let [test-grub {:_id 234243 :grub "testgrub" :completed true}
delete-event {:_id (:_id test-grub) :event :delete}]
(reset! state/grubs [test-grub])
(state/handle-event delete-event)
(should= [] @state/grubs)))))
(describe
"view event handling"
(describe "Create"
(it "should add a new grub to the state when a create event comes"
(let [test-grub {:grub "testgrub"}
create-event (assoc test-grub :event :create)]
(state/handle-view-event create-event)
(js/setTimeout (fn [] (let [created-grub (first @state/grubs)]
(should= (:grub test-grub) (:grub created-grub)))))))
(it "should generate an _id for the new grub"
(let [test-grub {:grub "testgrub"}
create-event (assoc test-grub :event :create)]
(state/handle-event create-event)
(js/setTimeout (fn []
(let [added-grub (first (filter #(= (:grub %) (:grub test-grub))
@state/grubs))]
(should-not-be-nil (:_id added-grub))))))))))

View file

@ -35,10 +35,19 @@
(reload/wrap-reload (handler/site #'routes) {:dirs ["src/clj"]})
(handler/site routes))))
(def default-port 3000)
(def integration-test-port 3456)
(defn start-server [port]
(println (str "Starting server on localhost:" port))
(httpkit/run-server app {:port port}))
(defn run-integration-test []
(let [stop-server (start-server integration-test-port)]
(integration-test/run integration-test-port)
(stop-server)))
(defn -main [& args]
(let [port 3000]
(println (str "Starting server on localhost:" port))
(defonce stop-server (httpkit/run-server app {:port port}))
(when (some #(= % "integration") args)
(integration-test/run port)
(stop-server))))
(if (some #(= % "integration") args)
(run-integration-test)
(start-server default-port)))

View file

@ -11,10 +11,9 @@
(defmulti handle-event :event :default :unknown-event)
(defmethod handle-event :create [event]
(defmethod handle-event :add [event]
(let [grub (-> event
(select-keys [:_id :grub])
(assoc :completed false))]
(select-keys [:_id :grub :completed]))]
(mc/insert grub-collection grub)))
(defmethod handle-event :complete [event]
@ -49,6 +48,9 @@
(>! out grub-event))))
out))
(defn clear-grubs []
(mc/drop grub-collection))
(def default-db "grub")
(defn connect-and-handle-events

View file

@ -9,15 +9,12 @@
[cljs.core.async.macros :refer [go]]))
(defn handle-grub-events []
(let [local-events (view/get-local-events)
[local-events' local-events''] (a/fan-out local-events 2)
remote-events (ws/get-remote-events)
events (a/fan-in [local-events' remote-events])]
(a/do-chan! ws/send-to-server local-events'')
(a/copy-chan state/incoming-events events)))
(a/copy-chan state/incoming-view-events view/outgoing-events)
(a/copy-chan state/incoming-events ws/outgoing-events)
(a/copy-chan ws/incoming-events state/outgoing-events))
(defn init []
(view/render-body)
(view/init)
(ws/connect-to-server)
(handle-grub-events))

View file

@ -4,6 +4,8 @@
[cljs.core.async.macros :refer [go]]))
(def incoming-events (chan))
(def incoming-view-events (chan))
(def outgoing-events (chan))
(def grubs (atom []))
@ -22,13 +24,6 @@
(let [grub (select-keys event [:_id :grub :completed])]
(swap! grubs (fn [current] (conj current grub)))))
(defmethod handle-event :create [event]
(let [grub (-> event
(select-keys [:_id :grub])
(assoc :_id (str "grub-" (.now js/Date)))
(assoc :completed false))]
(swap! grubs (fn [current] (conj current grub)))))
(defmethod handle-event :complete [event]
(swap! grubs
(fn [current]
@ -51,7 +46,33 @@
(defmethod handle-event :unknown-event [event]
(logs "Cannot handle unknown event:" event))
(defn pass-on-view-event [event]
(go (>! incoming-events event))
(go (>! outgoing-events event)))
(defmulti handle-view-event :event :default :unknown-event)
(defmethod handle-view-event :create [event]
(let [create-event (-> event
(assoc :event :add)
(assoc :_id (str "grub-" (.now js/Date)))
(assoc :completed false))]
(pass-on-view-event create-event)))
(defmethod handle-view-event :unknown-event [event]
(pass-on-view-event event))
(defn handle-incoming-events []
(go-loop (handle-event (<! incoming-events))))
(defn handle-incoming-view-events []
(go-loop (handle-view-event (<! incoming-view-events))))
(handle-incoming-events)
(defn init []
(handle-incoming-events)
(handle-incoming-view-events))
(init)

View file

@ -8,6 +8,8 @@
[dommy.macros :refer [deftemplate sel1 node]]
[cljs.core.async.macros :refer [go]]))
(def outgoing-events (chan))
(def add-grub-text
(node [:input.form-control {:id "add-grub-input" :type "text" :placeholder "2 grubs"}]))
@ -92,10 +94,6 @@
grub-events (map-chan (fn [id] {:event :delete :_id id}) ids)]
grub-events)))
(defn get-local-events []
(fan-in [(get-added-events)
(get-completed-events)
(get-deleted-events)]))
(defn render-grub-list [grubs]
(let [grub-list (sel1 :#grubList)
@ -104,7 +102,18 @@
(doseq [grub sorted-grubs]
(dommy/append! grub-list (grub-template grub)))))
(add-watch state/grubs
:grub-add-watch
(fn [key ref old new]
(render-grub-list new)))
(defn push-outgoing-events []
(fan-in outgoing-events [(get-added-events)
(get-completed-events)
(get-deleted-events)]))
(defn watch-for-state-changes []
(add-watch state/grubs
:grub-add-watch
(fn [key ref old new]
(render-grub-list new))))
(defn init []
(render-body)
(watch-for-state-changes)
(push-outgoing-events))

View file

@ -6,18 +6,23 @@
(:require-macros [grub.macros :refer [log logs go-loop]]
[cljs.core.async.macros :refer [go]]))
(def incoming-events (chan))
(def outgoing-events (chan))
(def websocket* (atom nil))
(defn handle-incoming-events []
(go-loop
(let [event (<! incoming-events)]
(.send @websocket* event))))
(defn handle-outgoing-events []
(aset @websocket* "onmessage" (fn [event]
(let [grub-event (cljs.reader/read-string (.-data event))]
(logs "Received:" grub-event)
(go (>! outgoing-events grub-event))))))
(defn connect-to-server []
(reset! websocket* (js/WebSocket. "ws://localhost:3000/ws")))
(defn get-remote-events []
(let [out (chan)]
(aset @websocket* "onmessage" (fn [event]
(let [grub-event (cljs.reader/read-string (.-data event))]
(logs "Received:" grub-event)
(go (>! out grub-event)))))
out))
(defn send-to-server [event]
(.send @websocket* event))
(reset! websocket* (js/WebSocket. "ws://localhost:3000/ws"))
(handle-incoming-events)
(handle-outgoing-events))