mirror of
https://github.com/metosin/reitit.git
synced 2025-12-24 02:48:25 +00:00
commit
93470fd176
23 changed files with 408 additions and 135 deletions
23
CHANGELOG.md
23
CHANGELOG.md
|
|
@ -1,5 +1,26 @@
|
|||
## 0.1.2-SNAPSHOT
|
||||
|
||||
### `reitit-core`
|
||||
|
||||
* Better handling of `nil` in route syntax:
|
||||
* explicit `nil` after path string is always handled as `nil` route
|
||||
* `nil` as path string causes the whole route to be `nil`
|
||||
* `nil` as child route is stripped away
|
||||
|
||||
```clj
|
||||
(testing "nil routes are stripped"
|
||||
(is (= [] (r/routes (r/router nil))))
|
||||
(is (= [] (r/routes (r/router [nil ["/ping"]]))))
|
||||
(is (= [] (r/routes (r/router [nil [nil] [[nil nil nil]]]))))
|
||||
(is (= [] (r/routes (r/router ["/ping" [nil "/pong"]])))))
|
||||
```
|
||||
### `reitit-ring`
|
||||
|
||||
* Use HTTP redirect (302) with index-files in `reitit.ring/create-resource-handler`.
|
||||
* `reitit.ring/create-default-handler` now conforms to [RING Spec](https://github.com/ring-clojure/ring/blob/master/SPEC), Fixes [#83](https://github.com/metosin/reitit/issues/83)
|
||||
|
||||
https://github.com/metosin/reitit/issues/83
|
||||
|
||||
### `reitit-schema`
|
||||
|
||||
* updated dependencies:
|
||||
|
|
@ -14,6 +35,8 @@
|
|||
|
||||
### `reitit-swagger-ui`
|
||||
|
||||
* Use HTTP redirect (302) with index-files in `reitit.swagger-ui/create-swagger-ui-handler`.
|
||||
|
||||
* updated dependencies:
|
||||
|
||||
```clj
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ Whole example project is in [`/examples/ring-swagger`](https://github.com/metosi
|
|||
"application/transit+json"}}}})
|
||||
(ring/routes
|
||||
(swagger-ui/create-swagger-ui-handler
|
||||
{:path "", :url "/api/swagger.json"})
|
||||
{:path "/", :url "/api/swagger.json"})
|
||||
(ring/create-default-handler))))
|
||||
|
||||
(defn start []
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
:dependencies [[org.clojure/clojure "1.9.0"]
|
||||
[ring "1.6.3"]
|
||||
[metosin/muuntaja "0.4.1"]
|
||||
[metosin/reitit "0.1.1"]])
|
||||
[metosin/reitit "0.1.2-SNAPSHOT"]])
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
:dependencies [[org.clojure/clojure "1.9.0"]
|
||||
[ring "1.6.3"]
|
||||
[metosin/muuntaja "0.4.1"]
|
||||
[metosin/reitit "0.1.1"]])
|
||||
[metosin/reitit "0.1.2-SNAPSHOT"]])
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
:dependencies [[org.clojure/clojure "1.9.0"]
|
||||
[ring "1.6.3"]
|
||||
[metosin/muuntaja "0.5.0"]
|
||||
[metosin/reitit "0.1.1"]]
|
||||
[metosin/reitit "0.1.2-SNAPSHOT"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-core "0.1.1"
|
||||
(defproject metosin/reitit-core "0.1.2-SNAPSHOT"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -39,14 +39,14 @@
|
|||
(walk-one [pacc macc routes]
|
||||
(if (vector? (first routes))
|
||||
(walk-many pacc macc routes)
|
||||
(let [[path & [maybe-arg :as args]] routes
|
||||
[data childs] (if (vector? maybe-arg)
|
||||
[{} args]
|
||||
[maybe-arg (rest args)])
|
||||
macc (into macc (expand data opts))]
|
||||
(if (seq childs)
|
||||
(walk-many (str pacc path) macc childs)
|
||||
[[(str pacc path) macc]]))))]
|
||||
(when (string? (first routes))
|
||||
(let [[path & [maybe-arg :as args]] routes
|
||||
[data childs] (if (or (vector? maybe-arg) (nil? maybe-arg))
|
||||
[{} args]
|
||||
[maybe-arg (rest args)])
|
||||
macc (into macc (expand data opts))
|
||||
child-routes (walk-many (str pacc path) macc (keep identity childs))]
|
||||
(if (seq childs) (seq child-routes) [[(str pacc path) macc]])))))]
|
||||
(walk-one path (mapv identity data) raw-routes)))
|
||||
|
||||
(defn map-data [f routes]
|
||||
|
|
@ -87,10 +87,10 @@
|
|||
(conflicts-str conflicts)
|
||||
{:conflicts conflicts})))
|
||||
|
||||
(defn name-lookup [[_ {:keys [name]}] opts]
|
||||
(defn name-lookup [[_ {:keys [name]}] _]
|
||||
(if name #{name}))
|
||||
|
||||
(defn find-names [routes opts]
|
||||
(defn find-names [routes _]
|
||||
(into [] (keep #(-> % second :name)) routes))
|
||||
|
||||
(defn- compile-route [[p m :as route] {:keys [compile] :as opts}]
|
||||
|
|
|
|||
|
|
@ -9,18 +9,19 @@
|
|||
|
||||
(s/def ::path (s/with-gen string? #(gen/fmap (fn [s] (str "/" s)) (s/gen string?))))
|
||||
|
||||
(s/def ::arg (s/and any? (complement vector?)))
|
||||
(s/def ::arg (s/and some? (complement vector?)))
|
||||
(s/def ::data (s/map-of keyword? any?))
|
||||
(s/def ::result any?)
|
||||
|
||||
(s/def ::raw-route
|
||||
(s/cat :path ::path
|
||||
:arg (s/? ::arg)
|
||||
:childs (s/* (s/and (s/nilable ::raw-route)))))
|
||||
(s/nilable
|
||||
(s/cat :path ::path
|
||||
:arg (s/? ::arg)
|
||||
:childs (s/* (s/and (s/nilable ::raw-routes))))))
|
||||
|
||||
(s/def ::raw-routes
|
||||
(s/or :route ::raw-route
|
||||
:routes (s/coll-of ::raw-route :into [])))
|
||||
:routes (s/coll-of ::raw-routes :into [])))
|
||||
|
||||
(s/def ::route
|
||||
(s/cat :path ::path
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-ring "0.1.1"
|
||||
(defproject metosin/reitit-ring "0.1.2-SNAPSHOT"
|
||||
:description "Reitit: Ring routing"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -46,9 +46,9 @@
|
|||
| `:not-acceptable` | 406, handler returned `nil`"
|
||||
([]
|
||||
(create-default-handler
|
||||
{:not-found (constantly {:status 404, :body ""})
|
||||
:method-not-allowed (constantly {:status 405, :body ""})
|
||||
:not-acceptable (constantly {:status 406, :body ""})}))
|
||||
{:not-found (constantly {:status 404, :body "", :headers {}})
|
||||
:method-not-allowed (constantly {:status 405, :body "", :headers {}})
|
||||
:not-acceptable (constantly {:status 406, :body "", :headers {}})}))
|
||||
([{:keys [not-found method-not-allowed not-acceptable]}]
|
||||
(fn
|
||||
([request]
|
||||
|
|
@ -96,24 +96,30 @@
|
|||
(fn
|
||||
([request] (handler request))
|
||||
([request respond _] (respond (handler request)))))
|
||||
join-paths (fn [& paths]
|
||||
(str/replace (str/replace (str/join "/" paths) #"([/]+)" "/") #"/$" ""))
|
||||
resource-response (fn [path]
|
||||
(if-let [response (or (paths path) (response/resource-response path options))]
|
||||
(if-let [response (or (paths (join-paths "/" path))
|
||||
(response/resource-response path options))]
|
||||
(response/content-type response (mime-type/ext-mime-type path))))
|
||||
path-or-index-response (fn [path]
|
||||
path-or-index-response (fn [path uri]
|
||||
(or (resource-response path)
|
||||
(let [separator (if-not (str/ends-with? path "/") "/")]
|
||||
(loop [[file & files] index-files]
|
||||
(if file
|
||||
(or (resource-response (str path separator file))
|
||||
(recur files)))))))
|
||||
(loop [[file & files] index-files]
|
||||
(if file
|
||||
(if (resource-response (join-paths path file))
|
||||
(response/redirect (join-paths uri file))
|
||||
(recur files))))))
|
||||
handler (if path
|
||||
(fn [request]
|
||||
(let [uri (:uri request)]
|
||||
(if-let [path (if (>= (count uri) path-size) (subs uri path-size))]
|
||||
(path-or-index-response path))))
|
||||
(path-or-index-response path uri))))
|
||||
(fn [request]
|
||||
(let [path (-> request :path-params parameter)]
|
||||
(or (path-or-index-response path) {:status 404}))))]
|
||||
(let [uri (:uri request)
|
||||
path (-> request :path-params parameter)]
|
||||
(or (path-or-index-response path uri)
|
||||
;; TODO: use generic not-found handler
|
||||
{:status 404}))))]
|
||||
(create handler)))))
|
||||
|
||||
(defn ring-handler
|
||||
|
|
@ -129,7 +135,7 @@
|
|||
(fn
|
||||
([request]
|
||||
(if-let [match (r/match-by-path router (:uri request))]
|
||||
(let [method (:request-method request :any)
|
||||
(let [method (:request-method request)
|
||||
path-params (:path-params match)
|
||||
result (:result match)
|
||||
handler (-> result method :handler (or default-handler))
|
||||
|
|
@ -141,7 +147,7 @@
|
|||
(default-handler request)))
|
||||
([request respond raise]
|
||||
(if-let [match (r/match-by-path router (:uri request))]
|
||||
(let [method (:request-method request :any)
|
||||
(let [method (:request-method request)
|
||||
path-params (:path-params match)
|
||||
result (:result match)
|
||||
handler (-> result method :handler (or default-handler))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-schema "0.1.1"
|
||||
(defproject metosin/reitit-schema "0.1.2-SNAPSHOT"
|
||||
:description "Reitit: Plumatic Schema coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-spec "0.1.1"
|
||||
(defproject metosin/reitit-spec "0.1.2-SNAPSHOT"
|
||||
:description "Reitit: clojure.spec coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-swagger-ui "0.1.1"
|
||||
(defproject metosin/reitit-swagger-ui "0.1.2-SNAPSHOT"
|
||||
:description "Reitit: Swagger-ui support"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
;; with path and url set, swagger validator disabled
|
||||
(swagger-ui/create-swagger-ui-handler
|
||||
{:path \"\"
|
||||
{:path \"/\"
|
||||
:url \"/api/swagger.json\"
|
||||
:config {:validator-url nil})"
|
||||
([]
|
||||
|
|
@ -42,11 +42,11 @@
|
|||
(update $ :root (fnil identity "swagger-ui"))
|
||||
(update $ :url (fnil identity "/swagger.json"))
|
||||
(update $ :config #(->> % (map mixed-case-key) (into {})))
|
||||
(assoc $ :paths {"conf.js" {:headers {"Content-Type" "application/javascript"}
|
||||
:status 200
|
||||
:body (conf-js $)}
|
||||
"config.json" {:headers {"Content-Type" "application/json"}
|
||||
:status 200
|
||||
:body (config-json $)}}))]
|
||||
(assoc $ :paths {"/conf.js" {:headers {"Content-Type" "application/javascript"}
|
||||
:status 200
|
||||
:body (conf-js $)}
|
||||
"/config.json" {:headers {"Content-Type" "application/json"}
|
||||
:status 200
|
||||
:body (config-json $)}}))]
|
||||
(ring/routes
|
||||
(ring/create-resource-handler options))))))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-swagger "0.1.1"
|
||||
(defproject metosin/reitit-swagger "0.1.2-SNAPSHOT"
|
||||
:description "Reitit: Swagger-support"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit "0.1.1"
|
||||
(defproject metosin/reitit "0.1.2-SNAPSHOT"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@
|
|||
[io.pedestal.http.route.definition.table :as table]
|
||||
[io.pedestal.http.route.map-tree :as map-tree]
|
||||
[io.pedestal.http.route.router :as pedestal]
|
||||
[io.pedestal.http.route :as route]))
|
||||
[io.pedestal.http.route :as route]
|
||||
[reitit.ring :as ring]))
|
||||
|
||||
;;
|
||||
;; start repl with `lein perf repl`
|
||||
|
|
@ -62,6 +63,26 @@
|
|||
["/auth/recovery/token/:token" :auth/recovery]
|
||||
["/workspace/:project/:page" :workspace/page]]))
|
||||
|
||||
(def ring-app
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
[["/auth/login" {:get identity}]
|
||||
["/auth/recovery/token/:token" {:get identity}]
|
||||
["/workspace/:project/:page" {:get identity}]])))
|
||||
|
||||
(comment
|
||||
|
||||
(ring-app {:request-method :get, :uri "/auth/login"})
|
||||
|
||||
;; 213ns
|
||||
;; 204ns (remove if)
|
||||
;; 163ns (inline fast-assoc)
|
||||
;; 156ns (don't inline fast-assoc)
|
||||
;; 128ns (single method dispatch)
|
||||
;; 80ns --> (don't inject router & match)
|
||||
(cc/quick-bench
|
||||
(ring-app {:request-method :post, :uri "/auth/login"})))
|
||||
|
||||
(defn routing-test1 []
|
||||
|
||||
(suite "static route")
|
||||
|
|
|
|||
109
perf-test/clj/reitit/json_perf.cljc
Normal file
109
perf-test/clj/reitit/json_perf.cljc
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
(ns reitit.json-perf
|
||||
(:require [criterium.core :as cc]
|
||||
[reitit.perf-utils :refer :all]
|
||||
|
||||
;; reitit
|
||||
[reitit.ring :as ring]
|
||||
[muuntaja.middleware :as mm]
|
||||
|
||||
;; bidi-yada
|
||||
[yada.yada :as yada]
|
||||
[bidi.ring :as bidi-ring]
|
||||
[byte-streams :as bs]
|
||||
|
||||
;; defaults
|
||||
[ring.middleware.defaults :as defaults]
|
||||
[compojure.core :as compojure]
|
||||
[clojure.string :as str]))
|
||||
|
||||
;;
|
||||
;; start repl with `lein perf repl`
|
||||
;; perf measured with the following setup:
|
||||
;;
|
||||
;; Model Name: MacBook Pro
|
||||
;; Model Identifier: MacBookPro113
|
||||
;; Processor Name: Intel Core i7
|
||||
;; Processor Speed: 2,5 GHz
|
||||
;; Number of Processors: 1
|
||||
;; Total Number of Cores: 4
|
||||
;; L2 Cache (per Core): 256 KB
|
||||
;; L3 Cache: 6 MB
|
||||
;; Memory: 16 GB
|
||||
;;
|
||||
|
||||
;; TODO: naive implementation
|
||||
(defn- with-security-headers [response]
|
||||
(update
|
||||
response
|
||||
:headers
|
||||
(fn [headers]
|
||||
(-> headers
|
||||
(assoc "x-frame-options" "SAMEORIGIN")
|
||||
(assoc "x-xss-protection" "1; mode=block")
|
||||
(assoc "x-content-type-options" "nosniff")))))
|
||||
|
||||
(def security-middleware
|
||||
{:name ::security
|
||||
:wrap (fn [handler]
|
||||
(fn [request]
|
||||
(with-security-headers (handler request))))})
|
||||
|
||||
(def reitit-app
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/api/ping"
|
||||
{:get {:handler (fn [_] {:status 200, :body {:ping "pong"}})}}]
|
||||
{:data {:middleware [mm/wrap-format
|
||||
security-middleware]}})))
|
||||
|
||||
(def bidi-yada-app
|
||||
(bidi-ring/make-handler
|
||||
["/api/ping"
|
||||
(yada/resource
|
||||
{:produces {:media-type "application/json"}
|
||||
:methods {:get {:response (fn [_] {:ping "pong"})}}})]))
|
||||
|
||||
(def defaults-app
|
||||
(defaults/wrap-defaults
|
||||
(mm/wrap-format
|
||||
(compojure/GET "/api/ping" [] {:status 200, :body {:ping "pong"}}))
|
||||
defaults/site-defaults))
|
||||
|
||||
(def request {:request-method :get, :uri "/api/ping"})
|
||||
|
||||
(comment
|
||||
(defaults-app request)
|
||||
@(bidi-yada-app request)
|
||||
(reitit-app request))
|
||||
|
||||
(comment
|
||||
(slurp (:body (defaults-app request)))
|
||||
(slurp (:body (reitit-app request)))
|
||||
(bs/to-string (:body @(bidi-yada-app request))))
|
||||
|
||||
|
||||
(defn expect! [body]
|
||||
(assert (str/starts-with? body "{\"ping\":\"pong\"}")))
|
||||
|
||||
(defn perf-test []
|
||||
|
||||
;; 176µs
|
||||
(title "compojure + ring-defaults")
|
||||
(let [f (fn [] (defaults-app request))]
|
||||
(expect! (-> (f) :body slurp))
|
||||
(cc/quick-bench (f)))
|
||||
|
||||
;; 60µs
|
||||
(title "bidi + yada")
|
||||
(let [f (fn [] (bidi-yada-app request))]
|
||||
(expect! (-> (f) deref :body bs/to-string))
|
||||
(cc/quick-bench (f)))
|
||||
|
||||
;; 5.0µs
|
||||
(title "reitit-ring")
|
||||
(let [f (fn [] (reitit-app request))]
|
||||
(expect! (-> (f) :body slurp))
|
||||
(cc/quick-bench (f))))
|
||||
|
||||
(comment
|
||||
(perf-test))
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
(defrecord RequestOrContext [values queue stack])
|
||||
|
||||
(def +items+ 100)
|
||||
(def +items+ 10)
|
||||
|
||||
(defn expected! [x]
|
||||
(assert (= (range +items+) (:values x))))
|
||||
|
|
@ -47,18 +47,19 @@
|
|||
map-request {}
|
||||
record-request (map->RequestOrContext map-request)]
|
||||
|
||||
;; 10.8 µs
|
||||
;; 1000ns
|
||||
(title "middleware - map")
|
||||
(expected! (app map-request))
|
||||
(cc/quick-bench
|
||||
(app map-request))
|
||||
|
||||
;; 4.7 µs
|
||||
;; 365ns
|
||||
(title "middleware - record")
|
||||
(expected! (app record-request))
|
||||
(cc/quick-bench
|
||||
(app record-request))
|
||||
|
||||
;; 6900ns
|
||||
(title "middleware - dynamic")
|
||||
(expected! ((middleware/chain mw identity) record-request))
|
||||
(cc/quick-bench
|
||||
|
|
@ -110,21 +111,21 @@
|
|||
{:enter (interceptor value)}) (range +items+)))
|
||||
ctx (io.pedestal.interceptor.chain/enqueue nil is)]
|
||||
|
||||
;; 78 µs
|
||||
;; 8400ns
|
||||
(title "pedestal")
|
||||
(cc/quick-bench
|
||||
(io.pedestal.interceptor.chain/execute ctx))))
|
||||
|
||||
(defn pedestal-tuned-chain-text []
|
||||
(let [is (map io.pedestal.interceptor/interceptor
|
||||
(map (fn [value]
|
||||
{:enter (interceptor value)}) (range +items+)))
|
||||
ctx (reitit.chain/map->Context (reitit.chain/enqueue nil is))]
|
||||
#_(defn pedestal-tuned-chain-text []
|
||||
(let [is (map io.pedestal.interceptor/interceptor
|
||||
(map (fn [value]
|
||||
{:enter (interceptor value)}) (range +items+)))
|
||||
ctx (reitit.chain/map->Context (reitit.chain/enqueue nil is))]
|
||||
|
||||
;; 67 µs
|
||||
(title "pedestal - tuned")
|
||||
(cc/quick-bench
|
||||
(reitit.chain/execute ctx))))
|
||||
;; 67 µs
|
||||
(title "pedestal - tuned")
|
||||
(cc/quick-bench
|
||||
(reitit.chain/execute ctx))))
|
||||
|
||||
;;
|
||||
;; Naive chain
|
||||
|
|
@ -140,17 +141,17 @@
|
|||
|
||||
(defn interceptor-test []
|
||||
(let [interceptors (map (fn [value] [interceptor value]) (range +items+))
|
||||
app (executor-reduce (interceptor/chain interceptors identity))
|
||||
app (executor-reduce (interceptor/chain interceptors identity {}))
|
||||
map-request {}
|
||||
record-request (map->RequestOrContext map-request)]
|
||||
|
||||
;; 13.5 µs (Map)
|
||||
;; 1900ns
|
||||
(title "interceptors - map")
|
||||
(expected! (app map-request))
|
||||
(cc/quick-bench
|
||||
(app map-request))
|
||||
|
||||
;; 7.2 µs (Record)
|
||||
;; 1300ns
|
||||
(title "interceptors - record")
|
||||
(expected! (app record-request))
|
||||
(cc/quick-bench
|
||||
|
|
@ -212,24 +213,24 @@
|
|||
|
||||
(defn interceptor-chain-test []
|
||||
(let [interceptors (map (fn [value] [interceptor value]) (range +items+))
|
||||
app-reduce (executor-reduce (interceptor/chain interceptors identity))
|
||||
app-queue (executor-queue (interceptor/chain interceptors identity))
|
||||
app-ctx-queue (executor-ctx-queue (interceptor/chain interceptors identity))
|
||||
app-reduce (executor-reduce (interceptor/chain interceptors identity {}))
|
||||
app-queue (executor-queue (interceptor/chain interceptors identity {}))
|
||||
app-ctx-queue (executor-ctx-queue (interceptor/chain interceptors identity {}))
|
||||
request {}]
|
||||
|
||||
;; 14.2 µs
|
||||
;; 2000ns
|
||||
(title "interceptors - reduce")
|
||||
(expected! (app-reduce request))
|
||||
(cc/quick-bench
|
||||
(app-reduce request))
|
||||
|
||||
;; 19.4 µs
|
||||
;; 2500ns
|
||||
(title "interceptors - queue")
|
||||
(expected! (app-queue request))
|
||||
(cc/quick-bench
|
||||
(app-queue request))
|
||||
|
||||
;; 30.9 µs
|
||||
;; 3200ns
|
||||
(title "interceptors - ctx-queue")
|
||||
(expected! (app-ctx-queue request))
|
||||
(cc/quick-bench
|
||||
|
|
|
|||
20
project.clj
20
project.clj
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-parent "0.1.1"
|
||||
(defproject metosin/reitit-parent "0.1.2-SNAPSHOT"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
@ -9,13 +9,13 @@
|
|||
:source-uri "https://github.com/metosin/reitit/{version}/{filepath}#L{line}"
|
||||
:metadata {:doc/format :markdown}}
|
||||
|
||||
:managed-dependencies [[metosin/reitit "0.1.1"]
|
||||
[metosin/reitit-core "0.1.1"]
|
||||
[metosin/reitit-ring "0.1.1"]
|
||||
[metosin/reitit-spec "0.1.1"]
|
||||
[metosin/reitit-schema "0.1.1"]
|
||||
[metosin/reitit-swagger "0.1.1"]
|
||||
[metosin/reitit-swagger-ui "0.1.1"]
|
||||
:managed-dependencies [[metosin/reitit "0.1.2-SNAPSHOT"]
|
||||
[metosin/reitit-core "0.1.2-SNAPSHOT"]
|
||||
[metosin/reitit-ring "0.1.2-SNAPSHOT"]
|
||||
[metosin/reitit-spec "0.1.2-SNAPSHOT"]
|
||||
[metosin/reitit-schema "0.1.2-SNAPSHOT"]
|
||||
[metosin/reitit-swagger "0.1.2-SNAPSHOT"]
|
||||
[metosin/reitit-swagger-ui "0.1.2-SNAPSHOT"]
|
||||
|
||||
[meta-merge "1.0.0"]
|
||||
[ring/ring-core "1.6.3"]
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
|
||||
[ring "1.6.3"]
|
||||
[ikitommi/immutant-web "3.0.0-alpha1"]
|
||||
[metosin/muuntaja "0.5.0"]
|
||||
[metosin/muuntaja "0.6.0-SNAPSHOT"]
|
||||
[metosin/ring-swagger-ui "2.2.10"]
|
||||
[metosin/jsonista "0.2.1"]
|
||||
|
||||
|
|
@ -70,6 +70,8 @@
|
|||
[ikitommi/immutant-web "3.0.0-alpha1"]
|
||||
[io.pedestal/pedestal.route "0.5.3"]
|
||||
[org.clojure/core.async "0.4.474"]
|
||||
[yada "1.2.13"]
|
||||
[ring/ring-defaults "0.3.1"]
|
||||
[ataraxy "0.4.0"]
|
||||
[bidi "2.1.3"]]}
|
||||
:analyze {:jvm-opts ^:replace ["-server"
|
||||
|
|
|
|||
|
|
@ -108,6 +108,12 @@
|
|||
r/segment-router :segment-router
|
||||
r/mixed-router :mixed-router))
|
||||
|
||||
(testing "nil routes are stripped"
|
||||
(is (= [] (r/routes (r/router nil))))
|
||||
(is (= [] (r/routes (r/router [nil ["/ping"]]))))
|
||||
(is (= [] (r/routes (r/router [nil [nil] [[nil nil nil]]]))))
|
||||
(is (= [] (r/routes (r/router ["/ping" [nil "/pong"]])))))
|
||||
|
||||
(testing "route coercion & compilation"
|
||||
|
||||
(testing "custom compile"
|
||||
|
|
|
|||
|
|
@ -267,56 +267,154 @@
|
|||
|
||||
#?(:clj
|
||||
(deftest resource-handler-test
|
||||
(doseq [[test app] [["inside a router"
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
[["/ping" (constantly {:status 200, :body "pong"})]
|
||||
["/files/*" (ring/create-resource-handler)]
|
||||
["/*" (ring/create-resource-handler)]]
|
||||
{:conflicts (constantly nil)})
|
||||
(ring/create-default-handler))]
|
||||
(let [redirect (fn [uri] {:status 302, :body "", :headers {"Location" uri}})
|
||||
request (fn [uri] {:uri uri, :request-method :get})]
|
||||
(testing "inside a router"
|
||||
|
||||
["outside of a router"
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/ping" (constantly {:status 200, :body "pong"})])
|
||||
(ring/routes
|
||||
(ring/create-resource-handler {:path "/files"})
|
||||
(ring/create-resource-handler {:path "/"})
|
||||
(ring/create-default-handler)))]]
|
||||
prefix ["" "/" "/files" "/files/"]
|
||||
:let [request (fn [uri] {:uri (str prefix uri), :request-method :get})]]
|
||||
(testing "from root"
|
||||
(let [app (ring/ring-handler
|
||||
(ring/router
|
||||
["/*" (ring/create-resource-handler)])
|
||||
(ring/create-default-handler))]
|
||||
(testing test
|
||||
(testing "different file-types"
|
||||
(let [response (app (request "/hello.json"))]
|
||||
(is (= "application/json" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "{\"hello\": \"file\"}" (slurp (:body response)))))
|
||||
(let [response (app (request "/hello.xml"))]
|
||||
(is (= "text/xml" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body response))))))
|
||||
|
||||
(testing test
|
||||
(testing "different file-types"
|
||||
(let [response (app (request "/hello.json"))]
|
||||
(is (= "application/json" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "{\"hello\": \"file\"}" (slurp (:body response)))))
|
||||
(let [response (app (request "/hello.xml"))]
|
||||
(is (= "text/xml" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body response))))))
|
||||
(testing "index-files"
|
||||
(let [response (app (request "/docs"))]
|
||||
(is (= (redirect "/docs/index.html") response)))
|
||||
(let [response (app (request "/docs/"))]
|
||||
(is (= (redirect "/docs/index.html") response))))
|
||||
|
||||
(testing "index-files"
|
||||
(let [response (app (request "/docs"))]
|
||||
(is (= "text/html" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "<h1>hello</h1>\n" (slurp (:body response)))))
|
||||
(let [response (app (request "/docs/"))]
|
||||
(is (= "text/html" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "<h1>hello</h1>\n" (slurp (:body response))))))
|
||||
(testing "not found"
|
||||
(let [response (app (request "/not-found"))]
|
||||
(is (= 404 (:status response)))))
|
||||
|
||||
(testing "not found"
|
||||
(let [response (app (request "/not-found"))]
|
||||
(is (= 404 (:status response)))))
|
||||
(testing "3-arity"
|
||||
(let [result (atom nil)
|
||||
respond (partial reset! result)
|
||||
raise ::not-called]
|
||||
(app (request "/hello.xml") respond raise)
|
||||
(is (= "text/xml" (get-in @result [:headers "Content-Type"])))
|
||||
(is (get-in @result [:headers "Last-Modified"]))
|
||||
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result)))))))))
|
||||
|
||||
(testing "3-arity"
|
||||
(let [result (atom nil)
|
||||
respond (partial reset! result)
|
||||
raise ::not-called]
|
||||
(app (request "/hello.xml") respond raise)
|
||||
(is (= "text/xml" (get-in @result [:headers "Content-Type"])))
|
||||
(is (get-in @result [:headers "Last-Modified"]))
|
||||
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result))))))))))
|
||||
(testing "from path"
|
||||
(let [app (ring/ring-handler
|
||||
(ring/router
|
||||
["/files/*" (ring/create-resource-handler)])
|
||||
(ring/create-default-handler))
|
||||
request #(request (str "/files" %))
|
||||
redirect #(redirect (str "/files" %))]
|
||||
(testing test
|
||||
(testing "different file-types"
|
||||
(let [response (app (request "/hello.json"))]
|
||||
(is (= "application/json" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "{\"hello\": \"file\"}" (slurp (:body response)))))
|
||||
(let [response (app (request "/hello.xml"))]
|
||||
(is (= "text/xml" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body response))))))
|
||||
|
||||
(testing "index-files"
|
||||
(let [response (app (request "/docs"))]
|
||||
(is (= (redirect "/docs/index.html") response)))
|
||||
(let [response (app (request "/docs/"))]
|
||||
(is (= (redirect "/docs/index.html") response))))
|
||||
|
||||
(testing "not found"
|
||||
(let [response (app (request "/not-found"))]
|
||||
(is (= 404 (:status response)))))
|
||||
|
||||
(testing "3-arity"
|
||||
(let [result (atom nil)
|
||||
respond (partial reset! result)
|
||||
raise ::not-called]
|
||||
(app (request "/hello.xml") respond raise)
|
||||
(is (= "text/xml" (get-in @result [:headers "Content-Type"])))
|
||||
(is (get-in @result [:headers "Last-Modified"]))
|
||||
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result))))))))))
|
||||
|
||||
(testing "outside a router"
|
||||
|
||||
(testing "from root"
|
||||
(let [app (ring/ring-handler
|
||||
(ring/router [])
|
||||
(ring/routes
|
||||
(ring/create-resource-handler {:path "/"})
|
||||
(ring/create-default-handler)))]
|
||||
(testing test
|
||||
(testing "different file-types"
|
||||
(let [response (app (request "/hello.json"))]
|
||||
(is (= "application/json" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "{\"hello\": \"file\"}" (slurp (:body response)))))
|
||||
(let [response (app (request "/hello.xml"))]
|
||||
(is (= "text/xml" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body response))))))
|
||||
|
||||
(testing "index-files"
|
||||
(let [response (app (request "/docs"))]
|
||||
(is (= (redirect "/docs/index.html") response)))
|
||||
(let [response (app (request "/docs/"))]
|
||||
(is (= (redirect "/docs/index.html") response))))
|
||||
|
||||
(testing "not found"
|
||||
(let [response (app (request "/not-found"))]
|
||||
(is (= 404 (:status response)))))
|
||||
|
||||
(testing "3-arity"
|
||||
(let [result (atom nil)
|
||||
respond (partial reset! result)
|
||||
raise ::not-called]
|
||||
(app (request "/hello.xml") respond raise)
|
||||
(is (= "text/xml" (get-in @result [:headers "Content-Type"])))
|
||||
(is (get-in @result [:headers "Last-Modified"]))
|
||||
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result)))))))))
|
||||
|
||||
(testing "from path"
|
||||
(let [app (ring/ring-handler
|
||||
(ring/router [])
|
||||
(ring/routes
|
||||
(ring/create-resource-handler {:path "/files"})
|
||||
(ring/create-default-handler)))
|
||||
request #(request (str "/files" %))
|
||||
redirect #(redirect (str "/files" %))]
|
||||
(testing test
|
||||
(testing "different file-types"
|
||||
(let [response (app (request "/hello.json"))]
|
||||
(is (= "application/json" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "{\"hello\": \"file\"}" (slurp (:body response)))))
|
||||
(let [response (app (request "/hello.xml"))]
|
||||
(is (= "text/xml" (get-in response [:headers "Content-Type"])))
|
||||
(is (get-in response [:headers "Last-Modified"]))
|
||||
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body response))))))
|
||||
|
||||
(testing "index-files"
|
||||
(let [response (app (request "/docs"))]
|
||||
(is (= (redirect "/docs/index.html") response)))
|
||||
(let [response (app (request "/docs/"))]
|
||||
(is (= (redirect "/docs/index.html") response))))
|
||||
|
||||
(testing "not found"
|
||||
(let [response (app (request "/not-found"))]
|
||||
(is (= 404 (:status response)))))
|
||||
|
||||
(testing "3-arity"
|
||||
(let [result (atom nil)
|
||||
respond (partial reset! result)
|
||||
raise ::not-called]
|
||||
(app (request "/hello.xml") respond raise)
|
||||
(is (= "text/xml" (get-in @result [:headers "Content-Type"])))
|
||||
(is (get-in @result [:headers "Last-Modified"]))
|
||||
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result)))))))))))))
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
(ns reitit.spec-test
|
||||
(:require [clojure.test :refer [deftest testing is are]]
|
||||
(:require [clojure.test :refer [deftest testing is are use-fixtures]]
|
||||
[#?(:clj clojure.spec.test.alpha :cljs cljs.spec.test.alpha) :as stest]
|
||||
[clojure.spec.alpha :as s]
|
||||
[reitit.core :as r]
|
||||
[reitit.spec :as rs]
|
||||
[expound.alpha :as e])
|
||||
[reitit.spec :as rs])
|
||||
#?(:clj
|
||||
(:import (clojure.lang ExceptionInfo))))
|
||||
|
||||
(stest/instrument)
|
||||
(defn instrument-all [f]
|
||||
(try
|
||||
(stest/instrument)
|
||||
(f)
|
||||
(finally
|
||||
(stest/unstrument))))
|
||||
|
||||
(use-fixtures :each instrument-all)
|
||||
|
||||
(deftest router-spec-test
|
||||
|
||||
|
|
@ -40,9 +46,9 @@
|
|||
;; path
|
||||
[:invalid {}]
|
||||
|
||||
;; vector data
|
||||
["/api" []
|
||||
["/ipa"]])))
|
||||
;; nested path
|
||||
["/api"
|
||||
[:ipa]])))
|
||||
|
||||
(testing "routes conform to spec (can't spec protocol functions)"
|
||||
(is (s/valid? ::rs/routes (r/routes (r/router ["/ping"])))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue