diff --git a/backend/src/uxbox/util/dispatcher.clj b/backend/src/uxbox/util/dispatcher.clj index 7c93a8db7..75b6fc3fe 100644 --- a/backend/src/uxbox/util/dispatcher.clj +++ b/backend/src/uxbox/util/dispatcher.clj @@ -16,9 +16,9 @@ [uxbox.util.spec :as us] [uxbox.util.exceptions :as ex]) (:import + clojure.lang.IDeref + clojure.lang.MapEntry java.util.Map - java.util.List - java.util.Map$Entry java.util.HashMap)) (definterface IDispatcher @@ -27,9 +27,15 @@ (deftype Dispatcher [reg attr interceptors] IDispatcher (add [this key f metadata] - (.put ^Map reg key (Map/entry f metadata)) + (.put ^Map reg key (MapEntry/create f metadata)) nil) + clojure.lang.IDeref + (deref [_] + {:registry reg + :attr attr + :interceptors interceptors}) + clojure.lang.IFn (invoke [_ params] (let [key (get params attr) @@ -38,8 +44,8 @@ (p/rejected (ex/error :type :not-found :code :method-not-found :hint "No method found for the current request.")) - (let [f (.getKey ^Map$Entry entry) - m (.getValue ^Map$Entry entry) + (let [f (.key ^MapEntry entry) + m (.val ^MapEntry entry) d (p/deferred)] (sp/execute (conj interceptors f) @@ -57,25 +63,53 @@ `(defonce ~sname (Dispatcher. (HashMap.) ~dispatch-by ~interceptors))) +(defn parse-defmethod + [args] + (loop [r {} + s 0 + v (first args) + n (rest args)] + (case s + 0 (if (symbol? v) + (recur (assoc r :sym v) 1 (first n) (rest n)) + (throw (ex-info "first arg to `defmethod` should be a symbol" {}))) + 1 (if (qualified-keyword? v) + (recur (-> r + (assoc :key (keyword (name v))) + (assoc :meta {:spec v :doc nil})) + 3 (first n) (rest n)) + (recur r (inc s) v n)) + 2 (if (simple-keyword? v) + (recur (-> r + (assoc :key v) + (assoc :meta {:doc nil})) + 3 (first n) (rest n)) + (throw (ex-info "second arg to `defmethod` should be a keyword" {}))) + 3 (if (string? v) + (recur (update r :meta assoc :doc v) (inc s) (first n) (rest n)) + (recur r 4 v n)) + 4 (if (map? v) + (recur (update r :meta merge v) (inc s) (first n) (rest n)) + (recur r 5 v n)) + 5 (if (vector? v) + (assoc r :args v :body n) + (throw (ex-info "missing arguments vector" {})))))) (defmacro defmethod - [sname key metadata args & rest] - (s/assert symbol? sname) - (s/assert keyword? key) - (s/assert map? metadata) - (s/assert vector? args) - (let [f `(fn ~args ~@rest)] + [& args] + (let [{:keys [key meta sym args body]} (parse-defmethod args) + f `(fn ~args ~@body)] `(do - (s/assert dispatcher? ~sname) - (.add ~sname ~key ~f ~metadata) - ~sname))) + (s/assert dispatcher? ~sym) + (.add ~sym ~key ~f ~meta) + ~sym))) (def spec-interceptor "An interceptor that conforms the request with the user provided spec." {:enter (fn [{:keys [request] :as data}] (let [{:keys [spec]} (meta request)] - (if spec + (if-let [spec (s/get-spec spec)] (let [result (s/conform spec request)] (if (not= result ::s/invalid) (assoc data :request result)