summaryrefslogtreecommitdiff
path: root/src/reverse_routing/core.clj
blob: edc1f57072664b0999e389526b4ebef4078ef03a (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
83
84
85
(ns reverse-routing.core
  (:require clout.core
            compojure.core
            [clojure.string :as string]))

(def ^:private ^:dynamic *lookup-route* nil)
(def ^:private ^:dynamic *root* nil)

(defn ^:private deref-if-var [arg]
  (if (var? arg)
    (deref arg)
    arg))

(defn wrap-reverse-routing [handler & {:keys [root] :or {:root ""}}]
  (fn [request]
    (binding [*lookup-route* (->> handler deref-if-var meta ::lookup)
              *root* root]
      (handler request))))

(defn ^:private lookup-route [route & handlers]
  (->> handlers
       (map (comp ::lookup meta deref-if-var))
       (some #(if % (% route)))))

(defn ^:private routing [request & handlers]
  (some #(% request) handlers))

(defn routes [& handlers]
  (vary-meta #(apply routing % handlers)
             assoc ::lookup #(apply lookup-route % handlers)))

(defmacro let-routes [bindings & handlers]
  `(let ~bindings (routes ~@handlers)))

(defmacro when-routes [cond & handlers]
  `(if ~cond (routes ~@handlers) (routes)))

(defmacro defroutes [name & handlers]
  `(def ~name (routes ~@handlers)))

(defmacro context [path args & routes]
  (let [string-path (if (vector? path) (first path) path)
        path-keys (vec (:keys (clout.core/route-compile string-path)))
        keylen (count path-keys)
        lookup-fn `(fn [[route-name# args#]]
                     (if (>= (count args#) ~keylen)
                       (let [~args args# 
                             r# (try (#'lookup-route [route-name# (vec (drop ~keylen args#))]
                                                     ~@routes)
                                     (catch Exception _#))
                             {uri# :uri, args# :args} r#]
                         (if r#
                           (assoc r#
                             :uri (str ~string-path uri#)
                             :args (vec (concat ~path-keys args#)))))))]
    `(vary-meta (compojure.core/context ~path ~args
                  (routes ~@routes))
                assoc ::lookup ~lookup-fn)))

(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))
        routes-map {:uri string-path
                    :type (keyword (string/lower-case (name type)))
                    :args (vec route-args)}]
    `(vary-meta ~route
                assoc ::lookup (fn [[name# args#]]
                                 (if (and (= name# ~route-name)
                                          (= (count args#) (count ~(vec route-args))))
                                   ~routes-map)))))

(defmacro with-url-fn [f & body]
  `(binding [*lookup-route* (fn [x#] (apply ~f x#))
             *root* ""]
     ~@body))

(defn url-for [route & arg-values]
  (let [spec (*lookup-route* [route arg-values])
        {:keys [uri type args]} spec
        root-path *root*]
    (if spec
      (str root-path
           (reduce (fn [string [name val]]
                     (clojure.string/replace string (str name) (str val)))
                   uri (map vector args arg-values))))))