summaryrefslogtreecommitdiff
path: root/src/reverse_routing/core.clj
blob: 49360649544d4c66f5c27a498e91753aa6492fb5 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
(ns reverse-routing.core
  (:require [clojure.string :as string]))

(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)]
      (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 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 {})))})))

(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)]
    (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"})
  )