diff options
Diffstat (limited to 'src/reverse_routing')
-rw-r--r-- | src/reverse_routing/core.clj | 107 |
1 files changed, 44 insertions, 63 deletions
diff --git a/src/reverse_routing/core.clj b/src/reverse_routing/core.clj index 4936064..cab8f33 100644 --- a/src/reverse_routing/core.clj +++ b/src/reverse_routing/core.clj @@ -1,82 +1,63 @@ (ns reverse-routing.core - (:require [clojure.string :as string])) + (:require clout.core + compojure.core + [clojure.string :as string])) + +(def ^:private ^:dynamic *reverse-routes* nil) +(def ^:private ^:dynamic *root* nil) (defn wrap-reverse-routing [handler & {:keys [root] :or {:root ""}}] (fn [request] - (let [route-table (->> handler meta ::routes) - request (assoc request - ::reverse-routes route-table - ::root root)] + (binding [*reverse-routes* (->> handler meta ::routes) + *root* root] (handler request)))) (defn ^:private routing [request & handlers] (some #(% request) handlers)) -(defn routes [& handlers] - (let [merge-first (partial merge-with (fn [x y] x))] - (apply vary-meta #(apply routing % handlers) - update-in [::routes] - merge-first (map (comp ::routes meta) handlers)))) +(defmacro routes [& handlers] + (let [routes-map (->> handlers + (map (comp ::routes meta macroexpand)) + (into {}))] + (vary-meta `(vary-meta (fn [request#] + (#'routing request# ~@handlers)) + assoc ::routes ~routes-map) + assoc ::routes routes-map))) + +(defmacro defroutes [name & handlers] + `(def ~name (routes ~@handlers))) (defmacro context [path args & routes] - (let [string-path (if (vector? path) (first path) path)] - `(with-meta (compojure.core/context ~path ~args ~@routes) - {::routes (let [path-keys# (:keys (clout.core/route-compile ~string-path)) - ~args [] ;; wow. could this be hackier? I doubt it. - ] - (->> (for [routes# (map (comp ::routes meta) (list ~@routes)) - [key# val#] routes#] - [key# {:uri (str ~string-path (:uri val#)) - :args (concat path-keys# (:args val#)) - :type (:type val#)}]) - (into {})))}))) + (let [string-path (if (vector? path) (first path) path) + routes-map (let [path-keys (:keys (clout.core/route-compile string-path)) + keylen (count path-keys)] + (->> (for [route (map (comp ::routes meta macroexpand) routes) + [[path num] {:keys [uri args type]}] route] + [[path (+ num keylen)] {:uri (str string-path uri) + :args (vec (concat path-keys args)) + :type type}]) + (into {})))] + (vary-meta `(vary-meta (compojure.core/context ~path ~args ~@routes) + assoc ::routes ~routes-map) + assoc ::routes routes-map))) (defmacro register-route [route-name [type path args & body :as route]] - (let [string-path (if (vector? path) (first path) path)] - `(with-meta ~route - {::routes {~route-name {:uri ~string-path - :type ~(keyword (string/lower-case (name type))) - :args (:keys (clout.core/route-compile ~string-path))}}}))) - -(defn url-for [request route & arg-values] - (let [{:keys [uri type args]} (-> request ::reverse-routes route) - root-path (::root request)] + (let [string-path (if (vector? path) (first path) path) + route-args (:keys (clout.core/route-compile string-path)) + route-id [route-name (count route-args)] + routes-map {route-id {:uri string-path + :type (keyword (string/lower-case (name type))) + :args (vec route-args)}}] + (vary-meta `(vary-meta ~route assoc ::routes ~routes-map) + assoc ::routes routes-map))) + +(defn url-for [route & arg-values] + (let [{:keys [uri type args]} (get *reverse-routes* [route (count arg-values)]) + root-path *root*] + (assert uri) (str root-path (reduce (fn [string [name val]] (clojure.string/replace string (str name) (str val))) uri (map vector args arg-values))))) -(comment - - (require '[compojure.core :refer [GET]]) - - (def test-routes - (-> (routes - (->> (GET "/user/:id" {{id :id} :params :as request} - (url-for request :get-something id)) - (register-route :get-user)) - (->> (GET "/person/:id" {{id :id} :params :as request} - (url-for request :get-user id)) - (register-route :get-person)) - (context "/something/:id" [id] - (GET "/nom" [] - "something, blah!") - (->> (GET "/name" {:as request} - (url-for request :get-person id)) - (register-route :get-something)) - (->> (GET ["/nam/:blah", :blah #"1\d+"] {{bloo :blah} :params :as request} - (url-for request :get-something-else id 10)) - (register-route :get-something-else)))) - wrap-reverse-routing)) - (test-routes {:request-method :get - :scheme :http - ;;:uri "/something/15/name" - ;;:uri "/person/15" - :uri "/user/15" - ;;:uri "/something/15/nam/12" - ;;:uri "/something/10/nom" - :remote-addr "127.0.0.1" - :server-port 8080 - :server-name "something"}) - ) |