Harlan Iverson 9 роки тому
батько
коміт
876b0d4ea5
5 змінених файлів з 83 додано та 60 видалено
  1. 30 41
      README.md
  2. 25 9
      core/src/com/github/harlanji/clojure-stack/core.clj
  3. 6 4
      project.clj
  4. 17 3
      project.org
  5. 5 3
      web-client/project.clj

+ 30 - 41
README.md

@@ -60,6 +60,9 @@ A separation of concerns model for use on all platforms.
 
 We use a custom lifecycle dispatcher that uses multimethods, as suggested by the author's documentation.
 
+Actually this style [is much slower](http://insideclojure.org/2015/04/27/poly-perf/) than Protocol based dispatch, 
+but very nice to work with.
+
 ```clojure
 (ns my.component
   (:require [clojure.tools.logging :refer [debugf infof]
@@ -70,66 +73,52 @@ We use a custom lifecycle dispatcher that uses multimethods, as suggested by the
 ; namespaced keywords for dispatch
 ; (when (= *ns* 'my.component) (= ::my-message :my.component/my-message))
 
-(defmethod react :component/start [c]
+(defmethod react :component/start [component]
   (debugf "Starting my.component"))
   
-(defmethod react :component/stop [c]
+(defmethod react :component/stop [component]
   (debugf "Stopping my.component"))
 
-(defmethod react ::my-message [c msg]
-  (infof "Got a message, what are we going to do about it? %s" msg))
+(defmethod react ::my-message [component event]
+  (infof "Got a event, what are we going to do about it? %s" event))
 
 ; alternative syntax (experimental)
 (react-to ::my-message [event]
-  (infof "Got a message, what are we going to do about it? %s" msg))
-```
-
-
+  (infof "Got an event, what are we going to do about it? %s" event))
 ```
 
-(defmacro defupon
-  ""
-  [command & body]
-  (let [react-symbol (symbol (str *ns*) "upon")
-        react-multi `(defmulti ~react-symbol #(:component/event %2))]
-    `(do
-       (when-not ~(find-var react-symbol)
-         (println "Defining default react dispatcher: ")
-         ~react-multi) 
-       (defmethod ~react-symbol ~command [~'component ~'event]
-         ~@body))))
 
+A comparable protocol-based approach loses some of its niceness, but may win in dispatch speed. A macro could be constructed
+to use an in-memory map based dispatch (ie. constant lookup time).
 
-(let [say-reactor '(defupon :say 
-                     (format "say with component=%s, event=%s" component event))]
-  (println (macroexpand-1 say-reactor))
-  (eval say-reactor))
-
-(let [component {:name :my.component}
-      event {:component/event :say
-             :message "hi"}]
-  (upon component event))
-
+```clojure
+(ns my.component
+  (:require [clojure.tools.logging :refer [debugf infof]
+            [component :refer [event-key Lifecycle]]))
+  
+(defrecord Reactor [dispatch]
+  Lifecycle
+  (start [component]
+    (debugf "Starting my.component"))
+  (stop [component]
+    (debugf "Stopping my.component"))
+    
+  React
+  (react [component event]
+    (when-let [handler (get dispatch (event-key event))]
+      (handler component event))))
 
-```
 
-```
+(defn- my-message [component event]
+  (infof "Got an event, what are we going to do about it? %s" event)
 
-(defmacro defaction
-  ""
-  [command & body]
-  `(let [react-symbol# ~(find-var (symbol (str *ns*) "react"))]
-     (when-not react-symbol#
-       (println "Defining default react dispatcher")
-       (defmulti ~'react-symbol# #(:command %2)))
-     (defmethod react-symbol# ~command [~'component ~'event]
-       ~@body)))
+(def component (->Reactor {::my-message my-message}))
 
+;(react component event)
 
 ```
 
 
-
 ### Marathon deployment
 
 Deploy non-Docker packages. Nothing in particular against Docker, but I don't see the case for it with Marathon 

+ 25 - 9
core/src/com/github/harlanji/clojure-stack/core.clj

@@ -2,18 +2,34 @@
 
 (def event-key :component/event)
 
-(defmacro defupon
+(defmacro defrecord+upon
+          "A shortcut to defining component records. Automatically creates a dispatcher that dispatches by
+           :command/event field of the single argument."
+          [event & body]
+          (let [react-symbol (symbol (str *ns*) "upon")
+                react-multi `(defrecord ~react-symbol []
+                                        com.stuartsierra.component/Lifecycle
+                                        (start [component])
+                                        (stop [component]))]
+               `(do
+                  (when-not ~(find-var react-symbol)
+                            (println "Defining default react dispatcher: ")
+                            ~react-multi)
+                  (defmethod ~react-symbol ~event [~'component ~'event]
+                             ~@body))))
+
+(defn defupon-map
   "A shortcut to defining multi-methods. Automatically creates a dispatcher that dispatches by
    :command/event field of the single argument."
-  [event & body]
+  [event body]
   (let [react-symbol (symbol (str *ns*) "upon")
-        react-multi `(defmulti ~react-symbol #(get %2 event-key))]
-       `(do
-          (when-not ~(find-var react-symbol)
-                    (println "Defining default react dispatcher: ")
-                    ~react-multi)
-          (defmethod ~react-symbol ~event [~'component ~'event]
-                     ~@body))))
+        react-multi '(defmulti ~react-symbol #(get %2 event-key))]
+       (do
+          (when-not (find-var react-symbol)
+                    (println "Defining default react dispatcher:" react-multi)
+                    (eval react-multi))
+          (apply defmethod react-symbol event '[component event]
+                 (body component event)))))
 
 (defn event
   "Shortcut methods to create an event."

+ 6 - 4
project.clj

@@ -6,12 +6,14 @@
 
 
   :modules {:dirs ["core"
-                   "pubsub-service"]
+                   "pubsub-service"
+                   "web-client"]
             :versions {com.github.harlanji.clojure-stack/core "0.0.1-SNAPSHOT"
                        com.github.harlanji.clojure-stack/pubsub-service "0.0.1-SNAPSHOT"
                        lein-objcbuild "0.1.10"
-                       org.clojure/clojure "1.7.0"
-                       org.clojure/clojurescript "1.7.170"
-                       com.stuartsierra/component "0.3.1"}
+                       org.clojure/clojure "1.8.0"
+                       org.clojure/clojurescript "1.7.228"
+                       com.stuartsierra/component "0.3.1"
+                       reagent "0.5.1"}
             }
   )

+ 17 - 3
project.org

@@ -14,12 +14,26 @@ As Jordan Z About this.
 
 * Web
 ** TODO Create re-frame Project
-** TODO Create streaming client component
+** DONE Create streaming client component
 
 
-* Core Service
+* Core
 ** TODO Make Pub/Sub Service
 ** TODO Make Materialized KV Service (Aggregate Object)
-** TODO Integrate Component
+** WIP Integrate component
+
+
+
+Added dependency
+
+** TODO Expand upon macro pattern for (react-to :command [m])
+
+Created the defaction, event, upon components
+
+These can stay private, initially offering just the defmulti stuff.
+
+** TODO Make remote atom, channel, promise serializers
+
+
 * Fun
 ** Create Dataflow DAG visualizer

+ 5 - 3
web-client/project.clj

@@ -4,9 +4,9 @@
   :license {:name "Eclipse Public License"
             :url "http://www.eclipse.org/legal/epl-v10.html"}
 
-  :dependencies [[org.clojure/clojure "1.8.0"]
+  :dependencies [[org.clojure/clojure "_"]
                  [ring-server "0.4.0"]
-                 [reagent "0.5.1"
+                 [reagent "_"
                   :exclusions [org.clojure/tools.reader]]
                  [reagent-forms "0.5.13"]
                  [reagent-utils "0.1.7"]
@@ -15,11 +15,12 @@
                  [compojure "1.4.0"]
                  [hiccup "1.0.5"]
                  [environ "1.0.1"]
-                 [org.clojure/clojurescript "1.7.228" :scope "provided"]
+                 [org.clojure/clojurescript "_" :scope "provided"]
                  [secretary "1.2.3"]
                  [venantius/accountant "0.1.6"
                   :exclusions [org.clojure/tools.reader]]
                  [speclj "3.3.1"]
+                 [com.github.harlanji.clojure-stack/core "_"]
                  ]
 
   :plugins [[lein-environ "1.0.1"]
@@ -27,6 +28,7 @@
             [lein-asset-minifier "0.2.4"
              :exclusions [org.clojure/clojure]]
             [speclj "3.3.1"]
+            [lein-modules "0.3.11"]
             ]
 
   :ring {:handler web-client.handler/app