summaryrefslogtreecommitdiff
path: root/src/reverse_routing/core.clj
blob: cab8f3378d685335e6b91d7c210d2d6a26a9add6 (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
(ns reverse-routing.core
  (: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]
    (binding [*reverse-routes* (->> handler meta ::routes)
              *root* root]
      (handler request))))

(defn ^:private routing [request & handlers]
  (some #(% request) 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)
        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)
        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)))))