Merge pull request #397 from metosin/SmallThings

0.5.0
This commit is contained in:
Tommi Reiman 2020-05-12 23:36:19 +03:00 committed by GitHub
commit 46fb851444
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 328 additions and 230 deletions

View file

@ -14,18 +14,68 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
## Unreleased ## Unreleased
* Updated deps:
```clj
[metosin/sieppari "0.0.0-alpha9"] is available but we use "0.0.0-alpha8"
[metosin/malli "0.0.1-20200404.091302-14"] is available but we use "0.0.1-20200305.102752-13"
[metosin/ring-swagger-ui "3.25.0"] is available but we use "3.24.3"
[metosin/spec-tools "0.10.2"] is available but we use "0.10.0"
[metosin/schema-tools "0.12.2"] is available but we use "0.12.1"
[metosin/muuntaja "0.6.7"] is available but we use "0.6.6"
[metosin/jsonista "0.2.6"] is available but we use "0.2.5"
[com.bhauman/spell-spec "0.1.2"] is available but we use "0.1.1"
[fipp "0.6.23"] is available but we use "0.6.22"
[ring/ring-core "1.8.1"] is available but we use "1.8.0"
```
### `reitit-core`
* Route conflict resolution and thus, router creation is now an order of magnitude faster.
* Forcing router to be `reitit.core/linear-router` and disabling route conflict resolution totally bypasses route conflict resolution. For cases when router creating speed matters over routing performance:
```clj
(r/router ...zillions-of-routes... {:router r/linear-router, :conflicts nil})
```
### `reitit-frontend` ### `reitit-frontend`
* `reitit.frontend.easy` state is setup before user `on-navigate` callback * `reitit.frontend.easy` state is setup before user `on-navigate` callback
is called the first time, so that `rfe/push-state` and such can be called is called the first time, so that `rfe/push-state` and such can be called
([#315](https://github.com/metosin/reitit/issues/315)) ([#315](https://github.com/metosin/reitit/issues/315))
### `reitit-malli` ### `reitit-ring`
* Update malli to latest version: * `reitit.ring/routes` strips away `nil` routes, fixes [#394](https://github.com/metosin/reitit/issues/394)
* `reitit.ring/create-file-handler` to serve files from filesystem, fixes [#395](https://github.com/metosin/reitit/issues/395)
* **BREAKING**: router option `:reitit.ring/default-options-handler` is deprecated
* fails with router creation time error
* use `:reitit.ring/default-options-endpoint` instead, takes an expandable route data instead just of a handler.
### `reitit-http`
* **BREAKING**: router option `:reitit.http/default-options-handler` is deprecated
* fails with router creation time error
* use `:reitit.http/default-options-endpoint` instead, takes an expandable route data instead just of a handler.
### `reitit-spec`
* lots of bug fixes, see [spec-tools changelog](https://github.com/metosin/spec-tools/blob/master/CHANGELOG.md#0102-2020-05-05)
### `reitit-sieppari`
* changes from Sieppari:
* fixed performance regression bugs, order of magnitude faster dispatching
* **BREAKING**: Out-of-the-box support for `core.async` and `manifold` are dropped, to use them, one needs to explicitely require the following side-effecting namespaces:
* `sieppari.async.core-async` for core.async
* `sieppari.async.manifold` for manifold
### `reitit-swagger`
* default to the new swagger-ui (3.25.0), to get old back add a dependency to:
```clj ```clj
[metosin/malli "0.0.1-20200404.091302-14"] is available but we use "0.0.1-20200305.102752-13" [metosin/ring-swagger-ui "2.2.10"]
``` ```
## 0.4.2 (2020-01-17) ## 0.4.2 (2020-01-17)

View file

@ -14,11 +14,11 @@ Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Co
It accepts the following options: It accepts the following options:
| key | description | | key | description |
| ---------------------------------------|-------------| | ----------------------------------------|-------------|
| `:reitit.middleware/transform` | Function of `[Middleware] => [Middleware]` to transform the expanded Middleware (default: identity). | `:reitit.middleware/transform` | Function of `[Middleware] => [Middleware]` to transform the expanded Middleware (default: identity).
| `:reitit.middleware/registry` | Map of `keyword => IntoMiddleware` to replace keyword references into Middleware | `:reitit.middleware/registry` | Map of `keyword => IntoMiddleware` to replace keyword references into Middleware
| `:reitit.ring/default-options-handler` | Default handler for `:options` method in endpoints (default: default-options-handler) | `:reitit.ring/default-options-endpoint` | Default endpoint for `:options` method (default: default-options-endpoint)
Example router: Example router:

View file

@ -1,6 +1,7 @@
(ns reitit.http (ns reitit.http
(:require [meta-merge.core :refer [meta-merge]] (:require [meta-merge.core :refer [meta-merge]]
[reitit.interceptor :as interceptor] [reitit.interceptor :as interceptor]
[reitit.exception :as ex]
[reitit.ring :as ring] [reitit.ring :as ring]
[reitit.core :as r])) [reitit.core :as r]))
@ -13,11 +14,11 @@
(update acc method expand opts) (update acc method expand opts)
acc)) data ring/http-methods)]) acc)) data ring/http-methods)])
(defn compile-result [[path data] {::keys [default-options-handler] :as opts}] (defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}]
(let [[top childs] (ring/group-keys data) (let [[top childs] (ring/group-keys data)
childs (cond-> childs childs (cond-> childs
(and (not (:options childs)) (not (:handler top)) default-options-handler) (and (not (:options childs)) (not (:handler top)) default-options-endpoint)
(assoc :options {:no-doc true, :handler default-options-handler})) (assoc :options (expand default-options-endpoint opts)))
compile (fn [[path data] opts scope] compile (fn [[path data] opts scope]
(interceptor/compile-result [path data] opts scope)) (interceptor/compile-result [path data] opts scope))
->endpoint (fn [p d m s] ->endpoint (fn [p d m s]
@ -47,11 +48,11 @@
support for http-methods and Interceptors. See documentation on [[reitit.core/router]] support for http-methods and Interceptors. See documentation on [[reitit.core/router]]
for available options. In addition, the following options are available: for available options. In addition, the following options are available:
| key | description | key | description
| ---------------------------------------|------------- | ----------------------------------------|-------------
| `:reitit.interceptor/transform` | Function or vector of functions of type `[Interceptor] => [Interceptor]` to transform the expanded Interceptors (default: identity) | `:reitit.interceptor/transform` | Function or vector of functions of type `[Interceptor] => [Interceptor]` to transform the expanded Interceptors (default: identity)
| `:reitit.interceptor/registry` | Map of `keyword => IntoInterceptor` to replace keyword references into Interceptors | `:reitit.interceptor/registry` | Map of `keyword => IntoInterceptor` to replace keyword references into Interceptors
| `:reitit.http/default-options-handler` | Default handler for `:options` method in endpoints (default: reitit.ring/default-options-handler) | `:reitit.http/default-options-endpoint` | Default endpoint for `:options` method in endpoints (default: reitit.ring/default-options-endpoint)
Example: Example:
@ -66,7 +67,10 @@
([data opts] ([data opts]
(let [opts (merge {:coerce coerce-handler (let [opts (merge {:coerce coerce-handler
:compile compile-result :compile compile-result
::default-options-handler ring/default-options-handler} opts)] ::default-options-endpoint ring/default-options-endpoint} opts)]
(when (contains? opts ::default-options-handler)
(ex/fail! (str "Option :reitit.http/default-options-handler is deprecated."
" Use :reitit.http/default-options-endpoint instead.")))
(r/router data opts)))) (r/router data opts))))
(defn routing-interceptor (defn routing-interceptor

View file

@ -11,7 +11,7 @@
(defn- arities [f] (defn- arities [f]
(->> (class f) (->> (class f)
.getDeclaredMethods .getDeclaredMethods
(filter #(= "invoke" (.getName %))) (filter (fn [^Method m] (= "invoke" (.getName m))))
(map #(alength (.getParameterTypes ^Method %))) (map #(alength (.getParameterTypes ^Method %)))
(set))) (set)))

View file

@ -1,12 +1,12 @@
(ns reitit.ring (ns reitit.ring
(:require [meta-merge.core :refer [meta-merge]] (:require [meta-merge.core :refer [meta-merge]]
[reitit.middleware :as middleware] [reitit.middleware :as middleware]
[reitit.exception :as ex]
[reitit.core :as r] [reitit.core :as r]
[reitit.impl :as impl] [reitit.impl :as impl]
#?@(:clj [[ring.util.mime-type :as mime-type] #?@(:clj [[ring.util.mime-type :as mime-type]
[ring.util.response :as response]]) [ring.util.response :as response]])
[clojure.string :as str] [clojure.string :as str]))
[reitit.exception :as ex]))
(declare get-match) (declare get-match)
(declare get-router) (declare get-router)
@ -108,7 +108,7 @@
(defn routes (defn routes
"Create a ring handler by combining several handlers into one." "Create a ring handler by combining several handlers into one."
[& handlers] [& handlers]
(let [single-arity (apply some-fn handlers)] (let [single-arity (apply some-fn (keep identity handlers))]
(fn (fn
([request] ([request]
(single-arity request)) (single-arity request))
@ -189,6 +189,48 @@
;; TODO: ring.middleware.not-modified/wrap-not-modified ;; TODO: ring.middleware.not-modified/wrap-not-modified
;; TODO: ring.middleware.head/wrap-head ;; TODO: ring.middleware.head/wrap-head
;; TODO: handle etags ;; TODO: handle etags
(defn -create-file-or-resource-handler
[response-fn {:keys [parameter root path loader allow-symlinks? index-files paths not-found-handler]
:or {parameter (keyword "")
root "public"
index-files ["index.html"]
paths (constantly nil)
not-found-handler (constantly {:status 404, :body "", :headers {}})}}]
(let [options {:root root
:loader loader
:index-files? false
:allow-symlinks? allow-symlinks?}
path-size (count path)
create (fn [handler]
(fn
([request] (handler request))
([request respond _] (respond (handler request)))))
join-paths (fn [& paths]
(str/replace (str/replace (str/join "/" paths) #"([/]+)" "/") #"/$" ""))
response (fn [path]
(if-let [response (or (paths (join-paths "/" path))
(response-fn path options))]
(response/content-type response (mime-type/ext-mime-type path))))
path-or-index-response (fn [path uri]
(or (response path)
(loop [[file & files] index-files]
(if file
(if (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 uri))))
(fn [request]
(let [uri (:uri request)
path (-> request :path-params parameter)]
(or (path-or-index-response path uri)
(not-found-handler request)))))]
(create handler))))
#?(:clj
(defn create-resource-handler (defn create-resource-handler
"A ring handler for serving classpath resources, configured via options: "A ring handler for serving classpath resources, configured via options:
@ -202,42 +244,25 @@
| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)" | :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)"
([] ([]
(create-resource-handler nil)) (create-resource-handler nil))
([{:keys [parameter root path loader allow-symlinks? index-files paths not-found-handler] ([opts]
:or {parameter (keyword "") (-create-file-or-resource-handler response/resource-response opts))))
root "public"
index-files ["index.html"] #?(:clj
paths (constantly nil) (defn create-file-handler
not-found-handler (constantly {:status 404, :body "", :headers {}})}}] "A ring handler for serving file resources, configured via options:
(let [options {:root root, :loader loader, :allow-symlinks? allow-symlinks?}
path-size (count path) | key | description |
create (fn [handler] | -------------------|-------------|
(fn | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
([request] (handler request)) | :root | optional resource root, defaults to `\"public\"`
([request respond _] (respond (handler request))))) | :path | optional path to mount the handler to. Works only if mounted outside of a router.
join-paths (fn [& paths] | :loader | optional class loader to resolve the resources
(str/replace (str/replace (str/join "/" paths) #"([/]+)" "/") #"/$" "")) | :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]`
resource-response (fn [path] | :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)"
(if-let [response (or (paths (join-paths "/" path)) ([]
(response/resource-response path options))] (create-file-handler nil))
(response/content-type response (mime-type/ext-mime-type path)))) ([opts]
path-or-index-response (fn [path uri] (-create-file-or-resource-handler response/file-response opts))))
(or (resource-response path)
(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 uri))))
(fn [request]
(let [uri (:uri request)
path (-> request :path-params parameter)]
(or (path-or-index-response path uri)
(not-found-handler request)))))]
(create handler)))))
(defn create-enrich-request [inject-match? inject-router?] (defn create-enrich-request [inject-match? inject-router?]
(cond (cond

View file

@ -27,33 +27,33 @@
[metosin/reitit-frontend "0.4.2"] [metosin/reitit-frontend "0.4.2"]
[metosin/reitit-sieppari "0.4.2"] [metosin/reitit-sieppari "0.4.2"]
[metosin/reitit-pedestal "0.4.2"] [metosin/reitit-pedestal "0.4.2"]
[metosin/ring-swagger-ui "3.24.3"] [metosin/ring-swagger-ui "3.25.0"]
[metosin/spec-tools "0.10.0"] [metosin/spec-tools "0.10.2"]
[metosin/schema-tools "0.12.1"] [metosin/schema-tools "0.12.2"]
[metosin/muuntaja "0.6.6"] [metosin/muuntaja "0.6.7"]
[metosin/jsonista "0.2.5"] [metosin/jsonista "0.2.6"]
[metosin/sieppari "0.0.0-alpha8"] [metosin/sieppari "0.0.0-alpha9"]
[metosin/malli "0.0.1-20200404.091302-14"] [metosin/malli "0.0.1-20200404.091302-14"]
;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111 ;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111
[com.fasterxml.jackson.core/jackson-core "2.10.0"] [com.fasterxml.jackson.core/jackson-core "2.11.0"]
[meta-merge "1.0.0"] [meta-merge "1.0.0"]
[fipp "0.6.22" :exclusions [org.clojure/core.rrb-vector]] [fipp "0.6.23" :exclusions [org.clojure/core.rrb-vector]]
[expound "0.8.4"] [expound "0.8.4"]
[lambdaisland/deep-diff "0.0-47"] [lambdaisland/deep-diff "0.0-47"]
[com.bhauman/spell-spec "0.1.1"] [com.bhauman/spell-spec "0.1.2"]
[ring/ring-core "1.8.0"] [ring/ring-core "1.8.1"]
[io.pedestal/pedestal.service "0.5.7"]] [io.pedestal/pedestal.service "0.5.7"]]
:plugins [[jonase/eastwood "0.3.6"] :plugins [[jonase/eastwood "0.3.11"]
;[lein-virgil "0.1.7"] ;[lein-virgil "0.1.7"]
[lein-doo "0.1.11"] [lein-doo "0.1.11"]
[lein-cljsbuild "1.1.7"] [lein-cljsbuild "1.1.8"]
[lein-cloverage "1.1.2"] [lein-cloverage "1.1.2"]
[lein-codox "0.10.7"] [lein-codox "0.10.7"]
[metosin/bat-test "0.4.3"]] [metosin/bat-test "0.4.4"]]
:profiles {:dev {:jvm-opts ^:replace ["-server"] :profiles {:dev {:jvm-opts ^:replace ["-server"]
@ -80,37 +80,37 @@
[org.clojure/clojurescript "1.10.597"] [org.clojure/clojurescript "1.10.597"]
;; modules dependencies ;; modules dependencies
[metosin/schema-tools] [metosin/schema-tools "0.12.2"]
[metosin/spec-tools] [metosin/spec-tools "0.10.2"]
[metosin/muuntaja] [metosin/muuntaja "0.6.7"]
[metosin/sieppari] [metosin/sieppari]
[metosin/jsonista] [metosin/jsonista "0.2.6"]
[metosin/malli] [metosin/malli]
[lambdaisland/deep-diff] [lambdaisland/deep-diff "0.0-47"]
[meta-merge] [meta-merge "1.0.0"]
[com.bhauman/spell-spec] [com.bhauman/spell-spec "0.1.1"]
[expound] [expound "0.8.4"]
[fipp] [fipp "0.6.23"]
[orchestra "2019.02.06-1"] [orchestra "2019.02.06-1"]
[ring "1.8.0"] [ring "1.8.1"]
[ikitommi/immutant-web "3.0.0-alpha1"] [ikitommi/immutant-web "3.0.0-alpha1"]
[metosin/ring-http-response "0.9.1"] [metosin/ring-http-response "0.9.1"]
[metosin/ring-swagger-ui "2.2.10"] [metosin/ring-swagger-ui "3.25.0"]
[criterium "0.4.5"] [criterium "0.4.5"]
[org.clojure/test.check "0.10.0"] [org.clojure/test.check "1.0.0"]
[org.clojure/tools.namespace "0.3.1"] [org.clojure/tools.namespace "1.0.0"]
[com.gfredericks/test.chuck "0.2.10"] [com.gfredericks/test.chuck "0.2.10"]
[io.pedestal/pedestal.service "0.5.7"] [io.pedestal/pedestal.service "0.5.7"]
[org.clojure/core.async "0.6.532"] [org.clojure/core.async "1.1.587"]
[manifold "0.1.8"] [manifold "0.1.8"]
[funcool/promesa "4.0.2"] [funcool/promesa "5.1.0"]
[com.clojure-goes-fast/clj-async-profiler "0.4.0"] [com.clojure-goes-fast/clj-async-profiler "0.4.1"]
[ring-cors "0.1.13"] [ring-cors "0.1.13"]
[com.bhauman/rebel-readline "0.1.4"]]} [com.bhauman/rebel-readline "0.1.4"]]}
@ -125,16 +125,16 @@
[io.pedestal/pedestal.service "0.5.7"] [io.pedestal/pedestal.service "0.5.7"]
[io.pedestal/pedestal.jetty "0.5.7"] [io.pedestal/pedestal.jetty "0.5.7"]
[calfpath "0.7.2"] [calfpath "0.7.2"]
[org.clojure/core.async "0.6.532"] [org.clojure/core.async "1.1.587"]
[manifold "0.1.8"] [manifold "0.1.8"]
[funcool/promesa "4.0.2"] [funcool/promesa "5.1.0"]
[metosin/sieppari] [metosin/sieppari]
[yada "1.2.16"] [yada "1.2.16"]
[aleph "0.4.6"] [aleph "0.4.6"]
[ring/ring-defaults "0.3.2"] [ring/ring-defaults "0.3.2"]
[ataraxy "0.4.2"] [ataraxy "0.4.2"]
[bidi "2.1.6"] [bidi "2.1.6"]
[janus "1.3.0"]]} [janus "1.3.2"]]}
:analyze {:jvm-opts ^:replace ["-server" :analyze {:jvm-opts ^:replace ["-server"
"-Dclojure.compiler.direct-linking=true" "-Dclojure.compiler.direct-linking=true"
"-XX:+PrintCompilation" "-XX:+PrintCompilation"

View file

@ -6,7 +6,8 @@
[reitit.interceptor.sieppari :as sieppari] [reitit.interceptor.sieppari :as sieppari]
[reitit.http :as http] [reitit.http :as http]
[reitit.ring :as ring] [reitit.ring :as ring]
[reitit.core :as r])) [reitit.core :as r])
(:import (clojure.lang ExceptionInfo)))
(defn interceptor [name] (defn interceptor [name]
{:enter (fn [ctx] (update-in ctx [:request ::i] (fnil conj []) name))}) {:enter (fn [ctx] (update-in ctx [:request ::i] (fnil conj []) name))})
@ -169,6 +170,10 @@
(testing "handler rejects" (testing "handler rejects"
(is (= -406 (:status (app {:request-method :get, :uri "/pong"})))))))))) (is (= -406 (:status (app {:request-method :get, :uri "/pong"}))))))))))
(deftest default-options-handler-test
(testing "Assertion fails when using deprecated options-handler"
(is (thrown? ExceptionInfo (ring/router [] {::ring/default-options-handler (fn [_])})))))
(deftest default-options-handler-test (deftest default-options-handler-test
(let [response {:status 200, :body "ok"}] (let [response {:status 200, :body "ok"}]
@ -199,7 +204,7 @@
[["/get" {:get (constantly response)}] [["/get" {:get (constantly response)}]
["/options" {:options (constantly response)}] ["/options" {:options (constantly response)}]
["/any" (constantly response)]] ["/any" (constantly response)]]
{::http/default-options-handler nil}) {::http/default-options-endpoint nil})
{:executor sieppari/executor})] {:executor sieppari/executor})]
(testing "endpoint with a non-options handler" (testing "endpoint with a non-options handler"

View file

@ -24,6 +24,15 @@
([request respond _] ([request respond _]
(respond (handler request)))) (respond (handler request))))
(deftest routes-test
(testing "nils are removed"
(is (= 123
((ring/routes
(constantly nil)
nil
(constantly 123))
::irrelevant)))))
(deftest ring-router-test (deftest ring-router-test
(testing "all paths should have a handler" (testing "all paths should have a handler"
@ -161,8 +170,8 @@
(deftest default-handler-test (deftest default-handler-test
(let [response {:status 200, :body "ok"} (let [response {:status 200, :body "ok"}
router (ring/router router (ring/router
[["/ping" {:get (constantly response)}] [["/ping" {:get (constantly response)}]
["/pong" (constantly nil)]]) ["/pong" (constantly nil)]])
app (ring/ring-handler router)] app (ring/ring-handler router)]
(testing "match" (testing "match"
@ -188,9 +197,9 @@
(testing "with custom http responses" (testing "with custom http responses"
(let [app (ring/ring-handler router (ring/create-default-handler (let [app (ring/ring-handler router (ring/create-default-handler
{:not-found (constantly {:status -404}) {:not-found (constantly {:status -404})
:method-not-allowed (constantly {:status -405}) :method-not-allowed (constantly {:status -405})
:not-acceptable (constantly {:status -406})}))] :not-acceptable (constantly {:status -406})}))]
(testing "route doesn't match" (testing "route doesn't match"
(is (= -404 (:status (app {:request-method :get, :uri "/"}))))) (is (= -404 (:status (app {:request-method :get, :uri "/"})))))
(testing "method doesn't match" (testing "method doesn't match"
@ -200,7 +209,7 @@
(testing "with some custom http responses" (testing "with some custom http responses"
(let [app (ring/ring-handler router (ring/create-default-handler (let [app (ring/ring-handler router (ring/create-default-handler
{:not-found (constantly {:status -404})}))] {:not-found (constantly {:status -404})}))]
(testing "route doesn't match" (testing "route doesn't match"
(is (= 405 (:status (app {:request-method :post, :uri "/ping"})))))))))) (is (= 405 (:status (app {:request-method :post, :uri "/ping"}))))))))))
@ -445,158 +454,163 @@
(app request))))))) (app request)))))))
#?(:clj #?(:clj
(deftest resource-handler-test (deftest file-resource-handler-test
(let [redirect (fn [uri] {:status 302, :body "", :headers {"Location" uri}}) (let [redirect (fn [uri] {:status 302, :body "", :headers {"Location" uri}})
request (fn [uri] {:uri uri, :request-method :get})] request (fn [uri] {:uri uri, :request-method :get})]
(testing "inside a router"
(testing "from root" (doseq [[name create] [["resource-handler" ring/create-resource-handler]
(let [app (ring/ring-handler ["file-handler" #(ring/create-file-handler (assoc % :root "dev-resources/public"))]]]
(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 "index-files" (testing (str "for " name)
(let [response (app (request "/docs"))] (testing "inside a router"
(is (= (redirect "/docs/index.html") response)))
(let [response (app (request "/docs/"))]
(is (= (redirect "/docs/index.html") response))))
(testing "not found" (testing "from root"
(let [response (app (request "/not-found"))] (let [app (ring/ring-handler
(is (= 404 (:status response))))) (ring/router
["/*" (create nil)])
(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 "3-arity" (testing "index-files"
(let [result (atom nil) (let [response (app (request "/docs"))]
respond (partial reset! result) (is (= (redirect "/docs/index.html") response)))
raise ::not-called] (let [response (app (request "/docs/"))]
(app (request "/hello.xml") respond raise) (is (= (redirect "/docs/index.html") response))))
(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" (testing "not found"
(let [app (ring/ring-handler (let [response (app (request "/not-found"))]
(ring/router (is (= 404 (:status response)))))
["/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" (testing "3-arity"
(let [response (app (request "/docs"))] (let [result (atom nil)
(is (= (redirect "/docs/index.html") response))) respond (partial reset! result)
(let [response (app (request "/docs/"))] raise ::not-called]
(is (= (redirect "/docs/index.html") response)))) (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 "not found" (testing "from path"
(let [response (app (request "/not-found"))] (let [app (ring/ring-handler
(is (= 404 (:status response))))) (ring/router
["/files/*" (create nil)])
(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 "3-arity" (testing "index-files"
(let [result (atom nil) (let [response (app (request "/docs"))]
respond (partial reset! result) (is (= (redirect "/docs/index.html") response)))
raise ::not-called] (let [response (app (request "/docs/"))]
(app (request "/hello.xml") respond raise) (is (= (redirect "/docs/index.html") response))))
(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 "not found"
(let [response (app (request "/not-found"))]
(is (= 404 (:status response)))))
(testing "from root" (testing "3-arity"
(let [app (ring/ring-handler (let [result (atom nil)
(ring/router []) respond (partial reset! result)
(ring/routes raise ::not-called]
(ring/create-resource-handler {:path "/"}) (app (request "/hello.xml") respond raise)
(ring/create-default-handler)))] (is (= "text/xml" (get-in @result [:headers "Content-Type"])))
(testing test (is (get-in @result [:headers "Last-Modified"]))
(testing "different file-types" (is (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result))))))))))
(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" (testing "outside a router"
(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" (testing "from root"
(let [response (app (request "/not-found"))] (let [app (ring/ring-handler
(is (= 404 (:status response))))) (ring/router [])
(ring/routes
(create {: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 "3-arity" (testing "index-files"
(let [result (atom nil) (let [response (app (request "/docs"))]
respond (partial reset! result) (is (= (redirect "/docs/index.html") response)))
raise ::not-called] (let [response (app (request "/docs/"))]
(app (request "/hello.xml") respond raise) (is (= (redirect "/docs/index.html") response))))
(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" (testing "not found"
(let [app (ring/ring-handler (let [response (app (request "/not-found"))]
(ring/router []) (is (= 404 (:status response)))))
(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" (testing "3-arity"
(let [response (app (request "/docs"))] (let [result (atom nil)
(is (= (redirect "/docs/index.html") response))) respond (partial reset! result)
(let [response (app (request "/docs/"))] raise ::not-called]
(is (= (redirect "/docs/index.html") response)))) (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 "not found" (testing "from path"
(let [response (app (request "/not-found"))] (let [app (ring/ring-handler
(is (= 404 (:status response))))) (ring/router [])
(ring/routes
(create {: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 "3-arity" (testing "index-files"
(let [result (atom nil) (let [response (app (request "/docs"))]
respond (partial reset! result) (is (= (redirect "/docs/index.html") response)))
raise ::not-called] (let [response (app (request "/docs/"))]
(app (request "/hello.xml") respond raise) (is (= (redirect "/docs/index.html") response))))
(is (= "text/xml" (get-in @result [:headers "Content-Type"])))
(is (get-in @result [:headers "Last-Modified"])) (testing "not found"
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result))))))))))))) (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)))))))))))))))
(deftest router-available-in-default-branch (deftest router-available-in-default-branch
(testing "1-arity" (testing "1-arity"