0 | 0 |
(ns puppetlabs.comidi
|
1 | 1 |
(:require [bidi.ring :as bidi-ring]
|
|
2 |
[bidi.schema :as bidi-schema]
|
2 | 3 |
[bidi.bidi :as bidi]
|
3 | 4 |
[clojure.zip :as zip]
|
4 | 5 |
[compojure.core :as compojure]
|
|
20 | 21 |
(schema/pred ks/zipper?))
|
21 | 22 |
|
22 | 23 |
(def http-methods
|
23 | |
#{:any :get :post :put :delete :head})
|
|
24 |
#{:any :get :post :put :delete :head :options})
|
24 | 25 |
|
25 | 26 |
(def RequestMethod
|
26 | |
(schema/enum :any :get :post :put :delete :head))
|
27 | |
|
28 | |
(def RegexPathElement
|
|
27 |
(schema/enum :any :get :post :put :delete :head :options))
|
|
28 |
|
|
29 |
(def RegexPatternSegment
|
29 | 30 |
[(schema/one Pattern "regex") (schema/one schema/Keyword "variable")])
|
30 | 31 |
|
31 | |
(def PathElement
|
32 | |
(schema/conditional
|
33 | |
string? schema/Str
|
34 | |
keyword? schema/Keyword
|
35 | |
vector? RegexPathElement))
|
36 | |
|
37 | 32 |
(def RouteInfo
|
38 | |
{:path [PathElement]
|
|
33 |
{:path [bidi-schema/PatternSegment]
|
39 | 34 |
:request-method RequestMethod})
|
40 | 35 |
|
41 | 36 |
(def RouteInfoWithId
|
|
51 | 46 |
(def RouteMetadata
|
52 | 47 |
{:routes [RouteInfoWithId]
|
53 | 48 |
:handlers {Handler RouteInfoWithId}})
|
54 | |
|
55 | |
(def BidiPattern
|
56 | |
(schema/conditional
|
57 | |
keyword? schema/Keyword
|
58 | |
string? schema/Str
|
59 | |
sequential? [PathElement]))
|
60 | |
|
61 | |
(def BidiRouteDestination
|
62 | |
(schema/conditional
|
63 | |
#(nil? (schema/check Handler %)) Handler
|
64 | |
:else [[(schema/one BidiPattern "pattern")
|
65 | |
(schema/one (schema/recursive #'BidiRouteDestination) "destination")]]))
|
66 | |
|
67 | |
(def BidiRoute
|
68 | |
[(schema/one BidiPattern "pattern")
|
69 | |
(schema/one BidiRouteDestination "destination")])
|
70 | 49 |
|
71 | 50 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
72 | 51 |
;;; Private - route id computation
|
|
118 | 97 |
regex-path-element->route-id-element :- schema/Str
|
119 | 98 |
"Given a Regex path element from comidi route metadata, convert it into a string
|
120 | 99 |
suitable for use in building a route id string."
|
121 | |
[path-element :- RegexPathElement]
|
|
100 |
[path-element :- RegexPatternSegment]
|
122 | 101 |
(-> path-element
|
123 | 102 |
first
|
124 | 103 |
str
|
|
131 | 110 |
suitable for use in building a route id string. This function is mostly
|
132 | 111 |
responsible for determining the type of the path element and dispatching to
|
133 | 112 |
the appropriate function."
|
134 | |
[path-element :- PathElement]
|
|
113 |
[path-element :- bidi-schema/PatternSegment]
|
135 | 114 |
(cond
|
136 | 115 |
(string? path-element)
|
137 | 116 |
(path-element->route-id-element path-element)
|
|
139 | 118 |
(keyword? path-element)
|
140 | 119 |
(pr-str path-element)
|
141 | 120 |
|
142 | |
(nil? (schema/check RegexPathElement path-element))
|
|
121 |
(nil? (schema/check RegexPatternSegment path-element))
|
143 | 122 |
(regex-path-element->route-id-element path-element)
|
144 | 123 |
|
145 | 124 |
:else
|
|
149 | 128 |
route-path->route-id :- schema/Str
|
150 | 129 |
"Given a route path (from comidi route-metadata), build a route-id string for
|
151 | 130 |
the route. This route-id can be used as a unique identifier for a route."
|
152 | |
[route-path :- BidiPattern]
|
|
131 |
[route-path :- bidi-schema/Pattern]
|
153 | 132 |
(->> route-path
|
154 | 133 |
(map route-path-element->route-id-element)
|
155 | 134 |
(filter #(not (empty? %)))
|
|
170 | 149 |
the current path elements of a route as we traverse the Bidi route tree via
|
171 | 150 |
zipper."
|
172 | 151 |
[route-info :- RouteInfo
|
173 | |
pattern :- BidiPattern]
|
|
152 |
pattern :- bidi-schema/Pattern]
|
174 | 153 |
(cond
|
175 | 154 |
(contains? http-methods pattern)
|
176 | 155 |
(assoc-in route-info [:request-method] pattern)
|
177 | 156 |
|
178 | |
(nil? (schema/check RegexPathElement pattern))
|
|
157 |
(nil? (schema/check RegexPatternSegment pattern))
|
179 | 158 |
(update-in route-info [:path] concat [pattern])
|
180 | 159 |
|
181 | 160 |
(sequential? pattern)
|
|
235 | 214 |
"Traverses a Bidi route tree and returns route metadata, which includes a list
|
236 | 215 |
of RouteInfo objects (one per route), plus a mechanism to look up the
|
237 | 216 |
RouteInfo for a given handler."
|
238 | |
[routes :- BidiRoute]
|
|
217 |
[routes :- bidi-schema/RoutePair]
|
239 | 218 |
(let [route-info {:path []
|
240 | 219 |
:request-method :any}
|
241 | 220 |
loc (-> [routes] zip/vector-zip zip/down)]
|
|
293 | 272 |
"Build up a map of metadata describing the routes. This metadata map can be
|
294 | 273 |
used for introspecting the routes after building the handler, and can also
|
295 | 274 |
be used with the `wrap-with-route-metadata` middleware."
|
296 | |
[routes :- BidiRoute]
|
|
275 |
[routes :- bidi-schema/RoutePair]
|
297 | 276 |
(memoized-route-metadata* routes))
|
298 | 277 |
|
299 | 278 |
(schema/defn ^:always-validate
|
|
302 | 281 |
as a :route-info key that can be used to determine which route a given request
|
303 | 282 |
matches."
|
304 | 283 |
[app :- (schema/pred fn?)
|
305 | |
routes :- BidiRoute]
|
|
284 |
routes :- bidi-schema/RoutePair]
|
306 | 285 |
(let [compiled-routes (bidi/compile-route routes)
|
307 | 286 |
route-meta (route-metadata routes)]
|
308 | 287 |
(fn [{:keys [uri path-info] :as req}]
|
|
316 | 295 |
:match-context match-context))))))
|
317 | 296 |
|
318 | 297 |
(schema/defn ^:always-validate
|
319 | |
routes :- BidiRoute
|
|
298 |
routes :- bidi-schema/RoutePair
|
320 | 299 |
"Combines multiple bidi routes into a single data structure; this is largely
|
321 | 300 |
just a convenience function for grouping several routes together as a single
|
322 | 301 |
object that can be passed around."
|
323 | |
[& routes :- [BidiRoute]]
|
|
302 |
[& routes :- [bidi-schema/RoutePair]]
|
324 | 303 |
["" (vec routes)])
|
325 | 304 |
|
326 | 305 |
(schema/defn ^:always-validate
|
327 | |
context :- BidiRoute
|
|
306 |
context :- bidi-schema/RoutePair
|
328 | 307 |
"Combines multiple bidi routes together into a single data structure, but nests
|
329 | 308 |
them all under the given url-prefix. This is similar to compojure's `context`
|
330 | 309 |
macro, but does not accept a binding form. You can still destructure variables
|
331 | 310 |
by passing a bidi pattern for `url-prefix`, and the variables will be available
|
332 | 311 |
to all nested routes."
|
333 | |
[url-prefix :- BidiPattern
|
334 | |
& routes :- [BidiRoute]]
|
|
312 |
[url-prefix :- bidi-schema/Pattern
|
|
313 |
& routes :- [bidi-schema/RoutePair]]
|
335 | 314 |
[url-prefix (vec routes)])
|
336 | 315 |
|
337 | 316 |
(schema/defn ^:always-validate
|
|
339 | 318 |
"Given a bidi route tree, converts into a ring request handler function. You
|
340 | 319 |
may pass an optional handler function which will be wrapped around the
|
341 | 320 |
bidi leaf."
|
342 | |
([routes :- BidiRoute
|
|
321 |
([routes :- bidi-schema/RoutePair
|
343 | 322 |
handler-fn :- (schema/maybe (schema/pred fn?))]
|
344 | 323 |
(let [compiled-routes (bidi/compile-route routes)]
|
345 | 324 |
(make-handler compiled-routes handler-fn)))
|