summaryrefslogtreecommitdiff
path: root/src/abra/core.clj
diff options
context:
space:
mode:
Diffstat (limited to 'src/abra/core.clj')
-rw-r--r--src/abra/core.clj94
1 files changed, 94 insertions, 0 deletions
diff --git a/src/abra/core.clj b/src/abra/core.clj
new file mode 100644
index 0000000..3d373eb
--- /dev/null
+++ b/src/abra/core.clj
@@ -0,0 +1,94 @@
+(ns abra.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)))))
+
+(defn add-middleware [handler middleware & args]
+ (let [result (apply middleware handler args)]
+ (vary-meta result
+ assoc ::lookup (fn [[name args]]
+ (let [lookup-fn (-> (deref-if-var handler)
+ meta
+ ::lookup)]
+ (lookup-fn [name args]))))))
+
+(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))))))