♻️ Refactor dispatcher implementation.

Simplify code and probabluy improve performance.
This commit is contained in:
Andrey Antukh 2020-02-05 23:49:26 +01:00
parent cd8a907a86
commit 1ac6e466ce
9 changed files with 82 additions and 76 deletions

View file

@ -12,11 +12,9 @@
[uxbox.util.dispatcher :as uds])) [uxbox.util.dispatcher :as uds]))
(uds/defservice handle (uds/defservice handle
{:dispatch-by ::type :dispatch-by ::type
:interceptors [uds/spec-interceptor :wrap [uds/wrap-spec
uds/wrap-errors uds/wrap-error])
#_logging-interceptor
#_context-interceptor]})
(defmacro defmutation (defmacro defmutation
[key & rest] [key & rest]

View file

@ -22,7 +22,7 @@
[uxbox.util.data :as data] [uxbox.util.data :as data]
[uxbox.util.uuid :as uuid] [uxbox.util.uuid :as uuid]
[uxbox.util.storage :as ust] [uxbox.util.storage :as ust]
[vertx.core :as vc])) [vertx.util :as vu]))
(def thumbnail-options (def thumbnail-options
{:width 800 {:width 800
@ -142,7 +142,7 @@
(ex/raise :type :validation (ex/raise :type :validation
:code :image-type-not-allowed :code :image-type-not-allowed
:hint "Seems like you are uploading an invalid image.")) :hint "Seems like you are uploading an invalid image."))
(p/let [image-opts (vc/blocking (images/info (:path content))) (p/let [image-opts (vu/blocking (images/info (:path content)))
image-path (persist-image-on-fs content) image-path (persist-image-on-fs content)
thumb-opts thumbnail-options thumb-opts thumbnail-options
thumb-path (persist-image-thumbnail-on-fs thumb-opts image-path) thumb-path (persist-image-thumbnail-on-fs thumb-opts image-path)
@ -179,13 +179,13 @@
(defn persist-image-on-fs (defn persist-image-on-fs
[{:keys [name path] :as upload}] [{:keys [name path] :as upload}]
(vc/blocking (vu/blocking
(let [filename (fs/name name)] (let [filename (fs/name name)]
(ust/save! media/media-storage filename path)))) (ust/save! media/media-storage filename path))))
(defn persist-image-thumbnail-on-fs (defn persist-image-thumbnail-on-fs
[thumb-opts input-path] [thumb-opts input-path]
(vc/blocking (vu/blocking
(let [input-path (ust/lookup media/media-storage input-path) (let [input-path (ust/lookup media/media-storage input-path)
thumb-data (images/generate-thumbnail input-path thumb-opts) thumb-data (images/generate-thumbnail input-path thumb-opts)
[filename ext] (fs/split-ext (fs/name input-path)) [filename ext] (fs/split-ext (fs/name input-path))

View file

@ -30,7 +30,8 @@
[uxbox.util.blob :as blob] [uxbox.util.blob :as blob]
[uxbox.util.storage :as ust] [uxbox.util.storage :as ust]
[uxbox.util.uuid :as uuid] [uxbox.util.uuid :as uuid]
[vertx.core :as vc])) [uxbox.util.time :as tm]
[vertx.util :as vu]))
;; --- Helpers & Specs ;; --- Helpers & Specs
@ -172,7 +173,7 @@
(ex/raise :type :validation (ex/raise :type :validation
:code :image-type-not-allowed :code :image-type-not-allowed
:hint "Seems like you are uploading an invalid image.")) :hint "Seems like you are uploading an invalid image."))
(vc/blocking (vu/blocking
(let [thumb-opts {:width 256 (let [thumb-opts {:width 256
:height 256 :height 256
:quality 75 :quality 75

View file

@ -25,7 +25,7 @@
[uxbox.util.blob :as blob] [uxbox.util.blob :as blob]
[uxbox.util.uuid :as uuid] [uxbox.util.uuid :as uuid]
[uxbox.util.storage :as ust] [uxbox.util.storage :as ust]
[vertx.core :as vc])) [vertx.util :as vu]))
;; --- Helpers & Specs ;; --- Helpers & Specs
@ -187,7 +187,7 @@
:code :image-type-not-allowed :code :image-type-not-allowed
:hint "Seems like you are uploading an invalid image.")) :hint "Seems like you are uploading an invalid image."))
(p/let [image-opts (vc/blocking (images/info (:path content))) (p/let [image-opts (vu/blocking (images/info (:path content)))
image-path (imgs/persist-image-on-fs content) image-path (imgs/persist-image-on-fs content)
thumb-opts imgs/thumbnail-options thumb-opts imgs/thumbnail-options
thumb-path (imgs/persist-image-thumbnail-on-fs thumb-opts image-path) thumb-path (imgs/persist-image-thumbnail-on-fs thumb-opts image-path)
@ -245,6 +245,6 @@
(defn- copy-image! (defn- copy-image!
[path] [path]
(vc/blocking (vu/blocking
(let [image-path (ust/lookup media/media-storage path)] (let [image-path (ust/lookup media/media-storage path)]
(ust/save! media/media-storage (fs/name image-path) image-path)))) (ust/save! media/media-storage (fs/name image-path) image-path))))

View file

@ -9,11 +9,9 @@
[uxbox.util.dispatcher :as uds])) [uxbox.util.dispatcher :as uds]))
(uds/defservice handle (uds/defservice handle
{:dispatch-by ::type :dispatch-by ::type
:interceptors [uds/spec-interceptor :wrap [uds/wrap-spec
uds/wrap-errors uds/wrap-error])
#_logging-interceptor
#_context-interceptor]})
(defmacro defquery (defmacro defquery
[key & rest] [key & rest]

View file

@ -8,7 +8,7 @@
(:require (:require
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[cuerdas.core :as str] [cuerdas.core :as str]
[vertx.core :as vc] [vertx.util :as vu]
[uxbox.core :refer [system]] [uxbox.core :refer [system]]
[uxbox.common.exceptions :as ex] [uxbox.common.exceptions :as ex]
[uxbox.util.uuid :as uuid] [uxbox.util.uuid :as uuid]
@ -36,5 +36,5 @@
(defn handle-on-context (defn handle-on-context
[p] [p]
(->> (vc/get-or-create-context system) (->> (vu/current-context system)
(vc/handle-on-context p))) (vu/handle-on-context p)))

View file

@ -13,7 +13,7 @@
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[cuerdas.core :as str] [cuerdas.core :as str]
[postal.core :as postal] [postal.core :as postal]
[vertx.core :as vc] [vertx.util :as vu]
[promesa.core :as p] [promesa.core :as p]
[uxbox.common.exceptions :as ex] [uxbox.common.exceptions :as ex]
[uxbox.config :as cfg] [uxbox.config :as cfg]
@ -49,7 +49,7 @@
(defn send-email (defn send-email
[email] [email]
(vc/blocking (vu/blocking
(let [config (get-smtp-config cfg/config) (let [config (get-smtp-config cfg/config)
result (if (:enabled config) result (if (:enabled config)
(postal/send-message config email) (postal/send-message config email)

View file

@ -21,47 +21,43 @@
java.util.HashMap)) java.util.HashMap))
(definterface IDispatcher (definterface IDispatcher
(^void add [key f metadata])) (^void add [key f]))
(deftype Dispatcher [reg attr interceptors] (defn- wrap-handler
[items handler]
(reduce #(%2 %1) handler items))
(deftype Dispatcher [reg attr wrap-fns]
IDispatcher IDispatcher
(add [this key f metadata] (add [this key f]
(.put ^Map reg key (MapEntry/create f metadata)) (let [f (wrap-handler wrap-fns f)]
this) (.put ^Map reg key f)
this))
clojure.lang.IDeref clojure.lang.IDeref
(deref [_] (deref [_]
{:registry reg {:registry reg
:attr attr :attr attr
:interceptors interceptors}) :wrap-fns wrap-fns})
clojure.lang.IFn clojure.lang.IFn
(invoke [_ params] (invoke [_ params]
(let [key (get params attr) (let [key (get params attr)
entry (.get ^Map reg key)] f (.get ^Map reg key)]
(if (nil? entry) (when (nil? f)
(p/rejected (ex/error :type :not-found (ex/raise :type :not-found
:code :method-not-found :code :method-not-found
:hint "No method found for the current request.")) :hint "No method found for the current request."))
(let [f (.key ^MapEntry entry) (f params))))
m (.val ^MapEntry entry)
d (p/deferred)]
(sp/execute (conj interceptors f)
(with-meta params m)
#(p/resolve! d %)
#(p/reject! d %))
d)))))
(defn dispatcher? (defn dispatcher?
[v] [v]
(instance? IDispatcher v)) (instance? IDispatcher v))
(defmacro defservice (defmacro defservice
[sname {:keys [dispatch-by interceptors]}] [sname & {:keys [dispatch-by wrap]}]
`(defonce ~sname (Dispatcher. (HashMap.) `(def ~sname (Dispatcher. (HashMap.) ~dispatch-by ~wrap)))
~dispatch-by
~interceptors)))
(defn parse-defmethod (defn parse-defmethod
[args] [args]
(loop [r {} (loop [r {}
@ -94,40 +90,53 @@
(assoc r :args v :body n) (assoc r :args v :body n)
(throw (ex-info "missing arguments vector" {})))))) (throw (ex-info "missing arguments vector" {}))))))
(defn add-method
[^Dispatcher dsp key f meta]
(let [f (with-meta f meta)]
(.add dsp key f)
dsp))
(defmacro defmethod (defmacro defmethod
[& args] [& args]
(let [{:keys [key meta sym args body]} (parse-defmethod args) (let [{:keys [key meta sym args body]} (parse-defmethod args)
f `(fn ~args ~@body)] f `(fn ~args ~@body)]
`(do `(do
(s/assert dispatcher? ~sym) (s/assert dispatcher? ~sym)
(.add ~(with-meta sym {:tag 'uxbox.util.dispatcher.IDispatcher}) (add-method ~sym ~key ~f ~meta))))
~key ~f ~meta)
~sym)))
(def spec-interceptor (defn wrap-spec
"An interceptor that conforms the request with the user provided [handler]
spec." (let [mdata (meta handler)
{:enter (fn [{:keys [request] :as data}] spec (s/get-spec (:spec mdata))]
(let [{:keys [spec]} (meta request)] (if (nil? spec)
(if-let [spec (s/get-spec spec)] handler
(let [result (s/conform spec request)] (with-meta
(fn [params]
(let [result (s/conform spec params)]
(if (not= result ::s/invalid) (if (not= result ::s/invalid)
(assoc data :request result) (handler result)
(let [data (s/explain-data spec request)] (let [data (s/explain-data spec params)]
(ex/raise :type :validation (ex/raise :type :validation
:code :spec-validation :code :spec-validation
:explain (with-out-str :explain (with-out-str
(expound/printer data)) (expound/printer data))
:data (::s/problems data))))) :data (::s/problems data))))))
data)))}) (assoc mdata ::wrap-spec true)))))
(def wrap-errors (defn wrap-error
{:error [handler]
(fn [data] (let [mdata (meta handler)]
(let [error (:error data) (with-meta
mdata (meta (:request data))] (fn [params]
(assoc data :error (ex/error :type :service-error (try
(-> (handler params)
(p/catch' (fn [error]
(ex/raise :type :service-error
:name (:spec mdata) :name (:spec mdata)
:cause error))))}) :cause error))))
(catch Throwable error
(p/rejected (ex/error :type :service-error
:name (:spec mdata)
:cause error)))))
(assoc mdata ::wrap-error true))))

View file

@ -12,7 +12,7 @@
[uxbox.tests.helpers :as th] [uxbox.tests.helpers :as th]
[uxbox.util.storage :as ust] [uxbox.util.storage :as ust]
[uxbox.util.uuid :as uuid] [uxbox.util.uuid :as uuid]
[vertx.core :as vc])) [vertx.util :as vu]))
(t/use-fixtures :once th/state-init) (t/use-fixtures :once th/state-init)
(t/use-fixtures :each th/database-reset) (t/use-fixtures :each th/database-reset)