diff options
Diffstat (limited to 'src/abra/core.clj')
-rw-r--r-- | src/abra/core.clj | 94 |
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)))))) |