Codebase list comidi-clojure / 07900a1
Merge pull request #14 from scottyw/master (maint) Make use of Bidi schema introduced in 1.20.0 Chris Price 8 years ago
3 changed file(s) with 26 addition(s) and 77 deletion(s). Raw diff Collapse all Expand all
77 [org.clojure/tools.reader "0.8.9"]
88 [ring/ring-core "1.3.2"]
99 [commons-io "2.4"]
10 [bidi "1.18.9" :exclusions [org.clojure/clojurescript]]
10 [bidi "1.21.0" :exclusions [org.clojure/clojurescript]]
1111 [compojure "1.3.3"]
12 [prismatic/schema "0.4.0"]
12 [prismatic/schema "0.4.3"]
1313 [puppetlabs/kitchensink "1.1.0"]]
1414
1515 :deploy-repositories [["releases" {:url "https://clojars.org/repo"
00 (ns puppetlabs.comidi
11 (:require [bidi.ring :as bidi-ring]
2 [bidi.schema :as bidi-schema]
23 [bidi.bidi :as bidi]
34 [clojure.zip :as zip]
45 [compojure.core :as compojure]
2021 (schema/pred ks/zipper?))
2122
2223 (def http-methods
23 #{:any :get :post :put :delete :head})
24 #{:any :get :post :put :delete :head :options})
2425
2526 (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
2930 [(schema/one Pattern "regex") (schema/one schema/Keyword "variable")])
3031
31 (def PathElement
32 (schema/conditional
33 string? schema/Str
34 keyword? schema/Keyword
35 vector? RegexPathElement))
36
3732 (def RouteInfo
38 {:path [PathElement]
33 {:path [bidi-schema/PatternSegment]
3934 :request-method RequestMethod})
4035
4136 (def RouteInfoWithId
5146 (def RouteMetadata
5247 {:routes [RouteInfoWithId]
5348 :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")])
7049
7150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7251 ;;; Private - route id computation
11897 regex-path-element->route-id-element :- schema/Str
11998 "Given a Regex path element from comidi route metadata, convert it into a string
12099 suitable for use in building a route id string."
121 [path-element :- RegexPathElement]
100 [path-element :- RegexPatternSegment]
122101 (-> path-element
123102 first
124103 str
131110 suitable for use in building a route id string. This function is mostly
132111 responsible for determining the type of the path element and dispatching to
133112 the appropriate function."
134 [path-element :- PathElement]
113 [path-element :- bidi-schema/PatternSegment]
135114 (cond
136115 (string? path-element)
137116 (path-element->route-id-element path-element)
139118 (keyword? path-element)
140119 (pr-str path-element)
141120
142 (nil? (schema/check RegexPathElement path-element))
121 (nil? (schema/check RegexPatternSegment path-element))
143122 (regex-path-element->route-id-element path-element)
144123
145124 :else
149128 route-path->route-id :- schema/Str
150129 "Given a route path (from comidi route-metadata), build a route-id string for
151130 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]
153132 (->> route-path
154133 (map route-path-element->route-id-element)
155134 (filter #(not (empty? %)))
170149 the current path elements of a route as we traverse the Bidi route tree via
171150 zipper."
172151 [route-info :- RouteInfo
173 pattern :- BidiPattern]
152 pattern :- bidi-schema/Pattern]
174153 (cond
175154 (contains? http-methods pattern)
176155 (assoc-in route-info [:request-method] pattern)
177156
178 (nil? (schema/check RegexPathElement pattern))
157 (nil? (schema/check RegexPatternSegment pattern))
179158 (update-in route-info [:path] concat [pattern])
180159
181160 (sequential? pattern)
235214 "Traverses a Bidi route tree and returns route metadata, which includes a list
236215 of RouteInfo objects (one per route), plus a mechanism to look up the
237216 RouteInfo for a given handler."
238 [routes :- BidiRoute]
217 [routes :- bidi-schema/RoutePair]
239218 (let [route-info {:path []
240219 :request-method :any}
241220 loc (-> [routes] zip/vector-zip zip/down)]
293272 "Build up a map of metadata describing the routes. This metadata map can be
294273 used for introspecting the routes after building the handler, and can also
295274 be used with the `wrap-with-route-metadata` middleware."
296 [routes :- BidiRoute]
275 [routes :- bidi-schema/RoutePair]
297276 (memoized-route-metadata* routes))
298277
299278 (schema/defn ^:always-validate
302281 as a :route-info key that can be used to determine which route a given request
303282 matches."
304283 [app :- (schema/pred fn?)
305 routes :- BidiRoute]
284 routes :- bidi-schema/RoutePair]
306285 (let [compiled-routes (bidi/compile-route routes)
307286 route-meta (route-metadata routes)]
308287 (fn [{:keys [uri path-info] :as req}]
316295 :match-context match-context))))))
317296
318297 (schema/defn ^:always-validate
319 routes :- BidiRoute
298 routes :- bidi-schema/RoutePair
320299 "Combines multiple bidi routes into a single data structure; this is largely
321300 just a convenience function for grouping several routes together as a single
322301 object that can be passed around."
323 [& routes :- [BidiRoute]]
302 [& routes :- [bidi-schema/RoutePair]]
324303 ["" (vec routes)])
325304
326305 (schema/defn ^:always-validate
327 context :- BidiRoute
306 context :- bidi-schema/RoutePair
328307 "Combines multiple bidi routes together into a single data structure, but nests
329308 them all under the given url-prefix. This is similar to compojure's `context`
330309 macro, but does not accept a binding form. You can still destructure variables
331310 by passing a bidi pattern for `url-prefix`, and the variables will be available
332311 to all nested routes."
333 [url-prefix :- BidiPattern
334 & routes :- [BidiRoute]]
312 [url-prefix :- bidi-schema/Pattern
313 & routes :- [bidi-schema/RoutePair]]
335314 [url-prefix (vec routes)])
336315
337316 (schema/defn ^:always-validate
339318 "Given a bidi route tree, converts into a ring request handler function. You
340319 may pass an optional handler function which will be wrapped around the
341320 bidi leaf."
342 ([routes :- BidiRoute
321 ([routes :- bidi-schema/RoutePair
343322 handler-fn :- (schema/maybe (schema/pred fn?))]
344323 (let [compiled-routes (bidi/compile-route routes)]
345324 (make-handler compiled-routes handler-fn)))
2424 (is (nil? (schema/check Handler {:get (fn [] :foo)})))
2525 (is (nil? (schema/check Handler {:post :foo})))))
2626
27 (deftest pattern-schema-test
28 (testing "pattern schema"
29 (is (nil? (schema/check BidiPattern "/foo")))
30 (is (nil? (schema/check BidiPattern :foo)))
31 (is (nil? (schema/check BidiPattern ["/foo/" :foo "/foo"])))
32 (is (nil? (schema/check BidiPattern ["/foo/" [#".*" :rest]])))))
33
34 (deftest destination-schema-test
35 (testing "route destination schema"
36 (is (nil? (schema/check BidiRouteDestination :foo)))
37 (is (nil? (schema/check BidiRouteDestination (fn [] nil))))
38 (is (nil? (schema/check BidiRouteDestination {:get (fn [] nil)})))
39 (is (nil? (schema/check BidiRouteDestination {:get :my-handler})))
40 (is (nil? (schema/check BidiRouteDestination [[["/foo/" :foo "/foo"] :foo]])))
41 (is (not (nil? (schema/check BidiRouteDestination [["/foo/" :foo "/foo"] :foo]))))
42 (is (nil? (schema/check BidiRouteDestination [[["/foo/" :foo]
43 :foo-handler]
44 [["/bar/" :bar]
45 {:get :bar-handler}]])))))
46
47 (deftest route-schema-test
48 (testing "route schema"
49 (is (nil? (schema/check BidiRoute [:foo :foo])))
50 (is (nil? (schema/check BidiRoute ["/foo" [[:foo :foo]]])))
51 (is (not (nil? (schema/check BidiRoute ["/foo" [:foo :foo]]))))
52 (is (nil? (schema/check BidiRoute ["" [[["/foo/" :foo]
53 :foo-handler]
54 [["/bar/" :bar]
55 {:get :bar-handler}]]])))))
56
5727 (deftest update-route-info-test
5828 (let [orig-route-info {:path []
5929 :request-method :any}]
6939 (testing "keyword path elements get added to the path"
7040 (is (= {:path [:foo]
7141 :request-method :any}
72 (update-route-info* orig-route-info :foo))))
42 (update-route-info* orig-route-info [:foo]))))
7343 (testing "vector path elements get flattened and added to the path"
7444 (is (= {:path ["/foo/" :foo]
7545 :request-method :any}
8757 [["/bar/" :bar]
8858 [["/baz" {:get :baz-handler}]
8959 ["/bam" {:put :bam-handler}]
90 ["/bap" {:any :bap-handler}]]]
60 ["/bap" {:options :bap-handler}]]]
9161 ["/buzz" {:post :buzz-handler}]]]
9262 expected-foo-meta {:path ["" "/foo/" :foo]
9363 :route-id "foo-:foo"
10070 :request-method :put}
10171 expected-bap-meta {:path ["" "/bar/" :bar "/bap"]
10272 :route-id "bar-:bar-bap"
103 :request-method :any}
73 :request-method :options}
10474 expected-buzz-meta {:path ["" "/buzz"]
10575 :route-id "buzz"
10676 :request-method :post}]