diff --git a/modules/reitit-core/src/reitit/impl.cljc b/modules/reitit-core/src/reitit/impl.cljc index 8d8acaa6..ac24fe6c 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -265,6 +265,61 @@ {:endpoint data :inherit {}})) +(defn dissoc-in + "Dissociates an entry from a nested associative structure returning a new + nested structure. `keys` is a sequence of keys. Any empty maps that result + will not be present in the new structure." + [m [k & ks]] + (if ks + (if-let [nextmap (get m k)] + (let [newmap (dissoc-in nextmap ks)] + (if (seq newmap) + (assoc m k newmap) + (dissoc m k))) + m) + (dissoc m k))) + +(defn transform-inherited-data + [transform-configs data] + (let [short-hands {:consume #(dissoc-in %1 %2) + :inherit (fn [d _] d)}] + (reduce + (fn [acc {:keys [kss transform]}] + (let [transform (or (get short-hands transform) + transform)] + (reduce (fn [{:keys [inherit endpoint]} ks] + (if (get-in inherit ks) + {:inherit (transform inherit ks) :endpoint data} + {:inherit inherit :endpoint endpoint})) + acc + kss))) + {:endpoint nil :inherit data} + transform-configs))) + +(defn mk-intermediate-endpoint-transform + "Make function that returns map for valid endpoints, which are either a leaf path in + the route tree, or have route data for one or more of nested key sequences. + The returned map will contain data for the current endpoint under `:endpoint` and data to be + inherited for children under `:inherit`. + + Provided arg should be a vector of maps with :kss, a seq of seq of keys, and :transform. Expanded route data + will be queried with each of the key sequences. If data exists in the key sequence, the provided `:transform` function + is called with `(transform data ks)`, so that the result is to be passed to children. + Query is repeated for each seq of seq with the same transform, moving on to the next transform. + All transforms are performed before passing data to children. + + If none of the key seqs match, then falsy is returned to indicate that the path should + not be used as an endpoint. + + Optionally, `:transform` may be specified with keywords `:consume` as short-hand for removing the matching data, + or `:inherit` for passing the data as-is to children." + [transform-configs] + (fn [prev-path path meta data childs] + (or (leaf-endpoint prev-path path meta data childs) + (and (map? data) + (seq childs) + (transform-inherited-data transform-configs data))))) + (defmacro goog-extend [type base-type ctor & methods] `(do (def ~type (fn ~@ctor)) diff --git a/modules/reitit-frontend/src/reitit/frontend.cljs b/modules/reitit-frontend/src/reitit/frontend.cljs index 071e296d..378de8ad 100644 --- a/modules/reitit-frontend/src/reitit/frontend.cljs +++ b/modules/reitit-frontend/src/reitit/frontend.cljs @@ -2,7 +2,8 @@ (:require [clojure.set :as set] [reitit.coercion :as coercion] [reitit.coercion :as rc] - [reitit.core :as r]) + [reitit.core :as r] + [reitit.impl :as impl]) (:import goog.Uri goog.Uri.QueryData)) @@ -45,13 +46,17 @@ ([router name path-params] (r/match-by-name router name path-params))) +(def frontend-endpoint (impl/mk-intermediate-endpoint-transform [{:kss [[:name]] + :transform :consume}])) + (defn router "Create a `reitit.core.router` from raw route data and optionally an options map. Enables request coercion. See [[reitit.core/router]] for details on options." ([raw-routes] (router raw-routes {})) ([raw-routes opts] - (r/router raw-routes (merge {:compile rc/compile-request-coercers} opts)))) + (r/router raw-routes (merge {:compile rc/compile-request-coercers + :endpoint frontend-endpoint} opts)))) (defn match-by-name! "Logs problems using console.warn" diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 81c72b94..4ab91a1a 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -65,6 +65,11 @@ ([request respond _] (respond (handle request)))))) +(def ring-endpoint (impl/mk-intermediate-endpoint-transform [{:kss (for [method http-methods] [method :handler]) + :transform :inherit} + {:kss [[:name]] + :transform :consume}])) + ;; ;; public api ;; @@ -93,7 +98,8 @@ ([data opts] (let [opts (merge {:coerce coerce-handler :compile compile-result - ::default-options-handler default-options-handler} opts)] + ::default-options-handler default-options-handler + :endpoint ring-endpoint} opts)] (r/router data opts)))) (defn routes