mirror of
https://github.com/metosin/reitit.git
synced 2025-12-23 02:41:10 +00:00
commit
095dfb6724
41 changed files with 378 additions and 147 deletions
|
|
@ -10,6 +10,9 @@ jobs:
|
|||
keys:
|
||||
- 'v1-test-{{ checksum "project.clj" }}'
|
||||
- 'v1-test-'
|
||||
- run:
|
||||
name: Install modules
|
||||
command: ./scripts/lein-modules install
|
||||
- run:
|
||||
name: Run tests
|
||||
command: ./scripts/test.sh clj
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ sudo: false
|
|||
language: clojure
|
||||
lein: 2.7.1
|
||||
install:
|
||||
- ./scripts/lein-modules install
|
||||
- lein deps
|
||||
- npm install
|
||||
script:
|
||||
|
|
|
|||
53
README.md
53
README.md
|
|
@ -1,22 +1,47 @@
|
|||
# reitit [](https://travis-ci.org/metosin/reitit) [](https://jarkeeper.com/metosin/reitit)
|
||||
# reitit [](https://travis-ci.org/metosin/reitit)
|
||||
|
||||
A friendly data-driven router for Clojure(Script).
|
||||
|
||||
* Simple data-driven [route syntax](https://metosin.github.io/reitit/basics/route_syntax.md)
|
||||
* [Route conflict resolution](https://metosin.github.io/reitit/advanced/route_conflicts.md)
|
||||
* First-class [route meta-data](https://metosin.github.io/reitit/basics/route_data.md)
|
||||
* Simple data-driven [route syntax](https://metosin.github.io/reitit/basics/route_syntax.html)
|
||||
* Route [conflict resolution](https://metosin.github.io/reitit/basics/route_conflicts.html)
|
||||
* First-class [route meta-data](https://metosin.github.io/reitit/basics/route_data.html)
|
||||
* Bi-directional routing
|
||||
* [Pluggable coercion](https://metosin.github.io/reitit/ring/parameter_coercion.md) ([clojure.spec](https://clojure.org/about/spec))
|
||||
* supports both [Middleware](https://metosin.github.io/reitit/ring/compiling_middleware.md) & Interceptors
|
||||
* [Ring-router](https://metosin.github.io/reitit/ring.html) with data-driven [middleware](https://metosin.github.io/reitit/ring/compiling_middleware.html)
|
||||
* [Pluggable coercion](https://metosin.github.io/reitit/ring/parameter_coercion.html) ([clojure.spec](https://clojure.org/about/spec))
|
||||
* Extendable
|
||||
* Fast
|
||||
|
||||
Ships with example router for [Ring](#ring). See [Issues](https://github.com/metosin/reitit/issues) for roadmap.
|
||||
See [Issues](https://github.com/metosin/reitit/issues) for roadmap.
|
||||
|
||||
## Latest version
|
||||
|
||||
[](http://clojars.org/metosin/reitit)
|
||||
|
||||
## Quick start
|
||||
|
||||
```clj
|
||||
(require '[reitit.core :as r])
|
||||
|
||||
(def router
|
||||
(r/router
|
||||
[["/api/ping" ::ping]
|
||||
["/api/orders/:id" ::order-by-id]]))
|
||||
|
||||
(r/match-by-path router "/api/ping")
|
||||
; #Match{:template "/api/ping"
|
||||
; :meta {:name ::ping}
|
||||
; :result nil
|
||||
; :params {}
|
||||
; :path "/api/ping"}
|
||||
|
||||
(r/match-by-name router ::order-by-id {:id 2})
|
||||
; #Match{:template "/api/orders/:id",
|
||||
; :meta {:name ::order-by-id},
|
||||
; :result nil,
|
||||
; :params {:id 2},
|
||||
; :path "/api/orders/2"}
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
[Check out the full documentation!](https://metosin.github.io/reitit/)
|
||||
|
|
@ -41,6 +66,20 @@ gitbook install
|
|||
gitbook serve
|
||||
```
|
||||
|
||||
To bump up version:
|
||||
|
||||
```bash
|
||||
# new version
|
||||
./scripts/set-version "1.0.0"
|
||||
./scripts/lein-modules install
|
||||
|
||||
# works
|
||||
lein test
|
||||
|
||||
# deploy to clojars
|
||||
./scripts/lein-modules do clean, deploy clojars
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Copyright © 2017 [Metosin Oy](http://www.metosin.fi)
|
||||
|
|
|
|||
|
|
@ -7,14 +7,15 @@
|
|||
* [Path-based Routing](basics/path_based_routing.md)
|
||||
* [Name-based Routing](basics/name_based_routing.md)
|
||||
* [Route data](basics/route_data.md)
|
||||
* [Different Routers](basics/different_routers.md)
|
||||
* [Route conflicts](basics/route_conflicts.md)
|
||||
* [Advanced](advanced/README.md)
|
||||
* [Route conflicts](advanced/route_conflicts.md)
|
||||
* [Route Validation](advanced/route_validation.md)
|
||||
* [Different Routers](advanced/different_routers.md)
|
||||
* [Configuring routers](advanced/configuring_routers.md)
|
||||
* [Ring](ring/README.md)
|
||||
* [Ring-router](ring/ring.md)
|
||||
* [Dynamic extensions](ring/dynamic_extensions.md)
|
||||
* [Data-driven Middleware](ring/data_driven_middleware.md)
|
||||
* [Parameter coercion](ring/parameter_coercion.md)
|
||||
* [Compiling middleware](ring/compiling_middleware.md)
|
||||
* TODO: Swagger & OpenAPI
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Advanced
|
||||
|
||||
* [Route conflicts](advanced/route_conflicts.md)
|
||||
* [Route Validation](advanced/route_validation.md)
|
||||
* [Configuring routers](advanced/configuring_routers.md)
|
||||
* [Route Validation](route_validation.md)
|
||||
* [Different Routers](different_routers.md)
|
||||
* [Configuring routers](configuring_routers.md)
|
||||
|
|
|
|||
18
doc/advanced/different_routers.md
Normal file
18
doc/advanced/different_routers.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Different Routers
|
||||
|
||||
Reitit ships with several different implementations for the `Router` protocol, originally based on the [Pedestal](https://github.com/pedestal/pedestal/tree/master/route) implementation. `router` selects the most suitable implementation by inspecting the expanded routes. The implementation can be set manually using `:router` option, see [configuring routers](advanced/configuring_routers.md).
|
||||
|
||||
| router | description |
|
||||
| ------------------------------|-------------|
|
||||
| `:linear-router` | Matches the routes one-by-one starting from the top until a match is found. Works with any kind of routes.
|
||||
| `:lookup-router` | Fast router, uses hash-lookup to resolve the route. Valid if no paths have path or catch-all parameters.
|
||||
| `:mixed-router` | Creates internally a `:linear-router` and a `:lookup-router` and used them to effectively get best-of-both-worlds. Valid if there are no [Route conflicts](../basics/route_conflicts.md).
|
||||
| `::single-static-path-router` | Fastest possible router: valid only if there is one static route.
|
||||
| `:prefix-tree-router` | TODO: https://github.com/julienschmidt/httprouter#how-does-it-work
|
||||
|
||||
The router name can be asked from the router
|
||||
|
||||
```clj
|
||||
(r/router-name router)
|
||||
; :mixed-router
|
||||
```
|
||||
|
|
@ -4,12 +4,11 @@ Namespace `reitit.spec` contains [clojure.spec](https://clojure.org/about/spec)
|
|||
|
||||
**NOTE:** Use of specs requires to use one of the following:
|
||||
|
||||
* `[org.clojure/clojurescript "1.9.660"]`
|
||||
* `[org.clojure/clojure "1.9.0-alpha19"]`
|
||||
* `[clojure-future-spec "1.9.0-alpha17"]` (Clojure 1.8)
|
||||
* `[org.clojure/clojurescript "1.9.660"]` (or higher)
|
||||
* `[org.clojure/clojure "1.9.0-beta2"]` (or higher)
|
||||
* `[clojure-future-spec "1.9.0-alpha17"]` (if Clojure 1.8 is used)
|
||||
|
||||
## At runtime
|
||||
If route trees are generated at runtime (e.g. from external source like the database), one can use directly the `clojure.spec` functions.
|
||||
## Example
|
||||
|
||||
```clj
|
||||
(require '[clojure.spec.alpha :as s])
|
||||
|
|
@ -36,7 +35,7 @@ If route trees are generated at runtime (e.g. from external source like the data
|
|||
First add a `:dev` dependency to:
|
||||
|
||||
```clj
|
||||
[expound "0.3.0"]
|
||||
[expound "0.3.0"] ; or higher
|
||||
```
|
||||
|
||||
Some bootstrapping:
|
||||
|
|
@ -54,8 +53,9 @@ Some bootstrapping:
|
|||
And we are ready to go:
|
||||
|
||||
```clj
|
||||
(require '[reitit.core :as r])
|
||||
|
||||
(reitit/router
|
||||
(r/router
|
||||
["/api"
|
||||
["/public"
|
||||
["/ping"]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Basics
|
||||
|
||||
* [Route syntax](basics/route_syntax.md)
|
||||
* [Router](basics/router.md)
|
||||
* [Path-based Routing](basics/path_based_routing.md)
|
||||
* [Name-based Routing](basics/name_based_routing.md)
|
||||
* [Route data](basics/route_data.md)
|
||||
* [Different Routers](basics/different_routers.md)
|
||||
* [Route syntax](route_syntax.md)
|
||||
* [Router](router.md)
|
||||
* [Path-based Routing](path_based_routing.md)
|
||||
* [Name-based Routing](name_based_routing.md)
|
||||
* [Route data](route_data.md)
|
||||
* [Route conflicts](route_conflicts.md)
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
# Different Routers
|
||||
|
||||
Reitit ships with several different implementations for the `Router` protocol, originally based on the awesome [Pedestal](https://github.com/pedestal/pedestal/tree/master/route) implementation. `router` selects the most suitable implementation by inspecting the expanded routes. The implementation can be set manually using `:router` ROUTER OPTION.
|
||||
|
||||
| router | description |
|
||||
| ----------------------|-------------|
|
||||
| `:linear-router` | Matches the routes one-by-one starting from the top until a match is found. Works with any kind of routes.
|
||||
| `:lookup-router` | Fastest router, uses hash-lookup to resolve the route. Valid if no paths have path or catch-all parameters.
|
||||
| `:mixed-router` | Creates internally a `:linear-router` and a `:lookup-router` and used them to effectively get best-of-both-worlds. Valid if there are no CONFLICTING ROUTES.
|
||||
| `:prefix-tree-router` | [TODO](https://github.com/julienschmidt/httprouter#how-does-it-work)
|
||||
|
||||
The router name can be asked from the router
|
||||
|
||||
```clj
|
||||
(r/router-name router)
|
||||
; :mixed-router
|
||||
```
|
||||
|
|
@ -1,6 +1,18 @@
|
|||
## Name-based routing
|
||||
## Name-based (reverse) routing
|
||||
|
||||
All routes which `:name` route data defined, can be matched by name.
|
||||
All routes which have `:name` route data defined, can also be matched by name.
|
||||
|
||||
Given a router:
|
||||
|
||||
```clj
|
||||
(require '[reitit.core :as r])
|
||||
|
||||
(def router
|
||||
(r/router
|
||||
[["/api"
|
||||
["/ping" ::ping]
|
||||
["/user/:id" ::user]]]))
|
||||
```
|
||||
|
||||
Listing all route names:
|
||||
|
||||
|
|
@ -9,7 +21,25 @@ Listing all route names:
|
|||
; [:user/ping :user/user]
|
||||
```
|
||||
|
||||
Matching by name:
|
||||
No match returns `nil`:
|
||||
|
||||
```clj
|
||||
(r/match-by-name router ::kikka)
|
||||
nil
|
||||
```
|
||||
|
||||
Matching a route:
|
||||
|
||||
```clj
|
||||
(r/match-by-name router ::ping)
|
||||
; #Match{:template "/api/ping"
|
||||
; :meta {:name :user/ping}
|
||||
; :result nil
|
||||
; :params {}
|
||||
; :path "/api/ping"}
|
||||
```
|
||||
|
||||
If not all path-parameters are set, a `PartialMatch` is returned:
|
||||
|
||||
```clj
|
||||
(r/match-by-name router ::user)
|
||||
|
|
@ -23,7 +53,7 @@ Matching by name:
|
|||
; true
|
||||
```
|
||||
|
||||
We only got a partial match as we didn't provide the needed path-parameters. Let's provide the them too:
|
||||
With provided path-parameters:
|
||||
|
||||
```clj
|
||||
(r/match-by-name router ::user {:id "1"})
|
||||
|
|
|
|||
|
|
@ -6,11 +6,27 @@ Path-based routing is done using the `reitit.core/match-by-path` function. It ta
|
|||
* `PartialMatch`, path matched, missing path-parameters (only in reverse-routing)
|
||||
* `Match`, exact match
|
||||
|
||||
Given a router:
|
||||
|
||||
```clj
|
||||
(require '[reitit.core :as r])
|
||||
|
||||
(def router
|
||||
(r/router
|
||||
[["/api"
|
||||
["/ping" ::ping]
|
||||
["/user/:id" ::user]]]))
|
||||
```
|
||||
|
||||
No match returns `nil`:
|
||||
|
||||
```clj
|
||||
(r/match-by-path router "/hello")
|
||||
; nil
|
||||
```
|
||||
|
||||
Match provides the route information:
|
||||
|
||||
```clj
|
||||
(r/match-by-path router "/api/user/1")
|
||||
; #Match{:template "/api/user/:id"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
# Route conflicts
|
||||
|
||||
Many routing libraries allow single path lookup could match multiple routes. Usually, first match is used. This is not good, especially if route tree is merged from multiple sources - routes might regress to be unreachable without a warning.
|
||||
Many routing libraries allow multiple matches for a single path lookup. Usually, the first match is used and the rest are effecively unreachanle. This is not good, especially if route tree is merged from multiple sources.
|
||||
|
||||
Reitit resolves this by running explicit conflicit resolution when a `Router` is created. Conflicting routes are passed into a `:conflicts` callback. Default implementation throws `ex-info` with a descriptive message.
|
||||
Reitit resolves this by running explicit conflicit resolution when a `router` is called. Conflicting routes are passed into a `:conflicts` callback. Default implementation throws `ex-info` with a descriptive message.
|
||||
|
||||
Examples routes with conflicts:
|
||||
Examples router with conflicting routes:
|
||||
|
||||
```clj
|
||||
(require '[reitit.core :as reitit])
|
||||
(require '[reitit.core :as r])
|
||||
|
||||
(def routes
|
||||
[["/ping"]
|
||||
|
|
@ -20,7 +20,7 @@ Examples routes with conflicts:
|
|||
By default, `ExceptionInfo` is thrown:
|
||||
|
||||
```clj
|
||||
(reitit/router routes)
|
||||
(r/router routes)
|
||||
; CompilerException clojure.lang.ExceptionInfo: Router contains conflicting routes:
|
||||
;
|
||||
; /:user-id/orders
|
||||
|
|
@ -38,7 +38,7 @@ By default, `ExceptionInfo` is thrown:
|
|||
Just logging the conflicts:
|
||||
|
||||
```clj
|
||||
(reitit/router
|
||||
(r/router
|
||||
routes
|
||||
{:conflicts (comp println reitit/conflicts-str)})
|
||||
; Router contains conflicting routes:
|
||||
|
|
@ -1,6 +1,47 @@
|
|||
# Route data
|
||||
|
||||
Routes can have arbitrary meta-data, interpreted by the router (via it's `:compile` hook) or the application itself. For nested routes, route data is accumulated recursively using [meta-merge](https://github.com/weavejester/meta-merge). By default, it appends collections, but it can be overridden to do `:prepend`, `:replace` or `:displace`.
|
||||
Route data is the heart of this library. Routes can have any data attachted to them. Data is interpeted either by the client application or the `Router` via it's `:coerce` and `:compile` hooks. This enables co-existence of both [adaptive and principled](https://youtu.be/x9pxbnFC4aQ?t=1907) components.
|
||||
|
||||
Routes can have a non-sequential route argument that is expanded into route data map when a router is created.
|
||||
|
||||
```clj
|
||||
(require '[reitit.core :as r])
|
||||
|
||||
(def router
|
||||
(r/router
|
||||
[["/ping" ::ping]
|
||||
["/pong" identity]
|
||||
["/users" {:get {:roles #{:admin}
|
||||
:handler identity}}]]))
|
||||
```
|
||||
|
||||
The expanded route data can be retrieved from a router with `routes` and is returned with `match-by-path` and `match-by-name` in case of a route match.
|
||||
|
||||
```clj
|
||||
(r/routes router)
|
||||
; [["/ping" {:name :user/ping}]
|
||||
; ["/pong" {:handler identity]}
|
||||
; ["/users" {:get {:roles #{:admin}
|
||||
; :handler identity}}]]
|
||||
|
||||
(r/match-by-path router "/ping")
|
||||
; #Match{:template "/ping"
|
||||
; :meta {:name :user/ping}
|
||||
; :result nil
|
||||
; :params {}
|
||||
; :path "/ping"}
|
||||
|
||||
(r/match-by-name router ::ping)
|
||||
; #Match{:template "/ping"
|
||||
; :meta {:name :user/ping}
|
||||
; :result nil
|
||||
; :params {}
|
||||
; :path "/ping"}
|
||||
```
|
||||
|
||||
## Nested route data
|
||||
|
||||
For nested route trees, route data is accumulated recursively from root towards leafs using [meta-merge](https://github.com/weavejester/meta-merge). Default behavior for colections is `:append`, but this can be overridden to `:prepend`, `:replace` or `:displace` using the target meta-data.
|
||||
|
||||
An example router with nested data:
|
||||
|
||||
|
|
@ -12,41 +53,47 @@ An example router with nested data:
|
|||
["/admin" {:roles #{:admin}}
|
||||
["/users" ::users]
|
||||
["/db" {:interceptors [::db]
|
||||
:roles ^:replace #{:db-admin}}
|
||||
["/:db" {:parameters {:db String}}
|
||||
["/drop" ::drop-db]
|
||||
["/stats" ::db-stats]]]]]))
|
||||
:roles ^:replace #{:db-admin}}]]]))
|
||||
```
|
||||
|
||||
Resolved route tree:
|
||||
|
||||
```clj
|
||||
(reitit/routes router)
|
||||
(r/routes router)
|
||||
; [["/api/ping" {:interceptors [::api]
|
||||
; :name ::ping}]
|
||||
; :name :user/ping}]
|
||||
; ["/api/admin/users" {:interceptors [::api]
|
||||
; :roles #{:admin}
|
||||
; :name ::users}]
|
||||
; ["/api/admin/db/:db/drop" {:interceptors [::api ::db]
|
||||
; :roles #{:db-admin}
|
||||
; :parameters {:db String}
|
||||
; :name ::drop-db}]
|
||||
; ["/api/admin/db/:db/stats" {:interceptors [::api ::db]
|
||||
; :roles #{:db-admin}
|
||||
; :parameters {:db String}
|
||||
; :name ::db-stats}]]
|
||||
; :name ::users} nil]
|
||||
; ["/api/admin/db" {:interceptors [::api ::db]
|
||||
; :roles #{:db-admin}}]]
|
||||
```
|
||||
|
||||
Route data is returned with `Match` and the application can act based on it.
|
||||
|
||||
## Expansion
|
||||
|
||||
By default, `reitit/Expand` protocol is used to expand the route arguments. It expands keywords into `:name` and functions into `:handler` key in the route data map. It's easy to add custom expanders and one can chenge the whole expand implementation via [router options](../advanced/configuring_routers.md).
|
||||
|
||||
```clj
|
||||
(r/match-by-path router "/api/admin/db/users/drop")
|
||||
; #Match{:template "/api/admin/db/:db/drop"
|
||||
; :meta {:interceptors [::api ::db]
|
||||
; :roles #{:db-admin}
|
||||
; :parameters {:db String}
|
||||
; :name ::drop-db}
|
||||
(require '[reitit.core :as r])
|
||||
|
||||
(def router
|
||||
(r/router
|
||||
[["/ping" ::ping]
|
||||
["/pong" identity]
|
||||
["/users" {:get {:roles #{:admin}
|
||||
:handler identity}}]]))
|
||||
|
||||
(r/routes router)
|
||||
; [["/ping" {:name :user/ping}]
|
||||
; ["/pong" {:handler identity]}
|
||||
; ["/users" {:get {:roles #{:admin}
|
||||
; :handler identity}}]]
|
||||
|
||||
(r/match-by-path router "/ping")
|
||||
; #Match{:template "/ping"
|
||||
; :meta {:name :user/ping}
|
||||
; :result nil
|
||||
; :params {:db "users"}
|
||||
; :path "/api/admin/db/users/drop"}
|
||||
; :params {}
|
||||
; :path "/ping"}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
# Route Syntax
|
||||
|
||||
Raw routes are defined as vectors, which have a String path, optional (non-sequential) route argument and optional child routes. Routes can be wrapped in vectors and lists and `nil` routes are ignored. Paths can have path-parameters (`:id`) or catch-all-parameters (`*path`).
|
||||
Routes are defined as vectors of String path and optional (non-sequential) route argument child routes.
|
||||
|
||||
Routes can be wrapped in vectors and lists and `nil` routes are ignored.
|
||||
|
||||
Paths can have path-parameters (`:id`) or catch-all-parameters (`*path`).
|
||||
|
||||
### Examples
|
||||
|
||||
Simple route:
|
||||
|
||||
|
|
@ -53,7 +59,8 @@ Same routes flattened:
|
|||
["/api/ping" {:name ::ping}]]
|
||||
```
|
||||
|
||||
As routes are just data, it's easy to create them programamtically:
|
||||
### Generating routes
|
||||
As routes are just data, it's easy to create them programmatically:
|
||||
|
||||
```clj
|
||||
(defn cqrs-routes [actions dev-mode?]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Router
|
||||
|
||||
Routes are just data and to do actual routing, we need a Router satisfying the `reitit.core/Router` protocol. Routers are created with `reitit.core/router` function, taking the raw routes and optionally an options map. Raw routes gets expanded and optionally coerced and compiled.
|
||||
Routes are just data and for routing, we need a router instance satisfying the `reitit.core/Router` protocol. Routers are created with `reitit.core/router` function, taking the raw routes and optionally an options map.
|
||||
|
||||
`Router` protocol:
|
||||
The `Router` protocol:
|
||||
|
||||
```clj
|
||||
(defprotocol Router
|
||||
|
|
@ -26,10 +26,25 @@ Creating a router:
|
|||
["/user/:id" ::user]]]))
|
||||
```
|
||||
|
||||
Router flattens the raw routes and expands the route arguments using `reitit.core/Expand` protocol. By default, `Keyword`s are expanded to `:name` and functions are expaned to `:handler`. `nil` routes are removed. The expanded routes can be retrieved with router:
|
||||
Name of the created router:
|
||||
|
||||
```clj
|
||||
(r/router-name router)
|
||||
; :mixed-router
|
||||
```
|
||||
|
||||
The flattened route tree:
|
||||
|
||||
```clj
|
||||
(r/routes router)
|
||||
; [["/api/ping" {:name :user/ping}]
|
||||
; ["/api/user/:id" {:name :user/user}]]
|
||||
```
|
||||
|
||||
### Behind the scenes
|
||||
When router is created, the following steps are done:
|
||||
* route tree is flattened
|
||||
* route arguments are expanded (via `reitit.core/Expand` protocol) and optionally coerced
|
||||
* [route conflicts](advanced/route_conflicts.md) are resolved
|
||||
* actual [router implementation](../advanced/different_routers.md) is selected and created
|
||||
* optionally route meta-data gets compiled
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@
|
|||
|
||||
* [Ring-router](ring.md)
|
||||
* [Dynamic extensions](dynamic_extensions.md)
|
||||
* [Data-driven Middleware](data_driven_middleware.md)
|
||||
* [Parameter coercion](parameter_coercion.md)
|
||||
* [Compiling middleware](compiling_middleware.md)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# Compiling Middleware
|
||||
|
||||
The [meta-data extensions](ring.md#meta-data-based-extensions) are a easy way to extend the system. Routes meta-data can be transformed into any shape (records, functions etc.) in route compilation, enabling fast access at request-time.
|
||||
The [dynamic extensions](dynamic_extensions.md) is a easy way to extend the system. To enable fast lookups into route data, we can compile them into any shape (records, functions etc.) we want, enabling fast access at request-time.
|
||||
|
||||
Still, we can do better. As we know the exact route that interceptor/middleware is linked to, we can pass the (compiled) route information into the interceptor/middleware at creation-time. It can extract and transform relevant data just for it and pass it into the actual request-handler via a closure - yielding faster runtime processing.
|
||||
Still, we can do better. As we know the exact route that middleware/interceptor is linked to, we can pass the (compiled) route information into the middleware/interceptor at creation-time. It can do local reasoning: extract and transform relevant data just for it and pass it into the actual request-handler via a closure - yielding much faster runtime processing. Middleware/interceptor can also decide not to mount itself. Why mount a `wrap-enforce-roles` middleware for a route if there are no roles required for it?
|
||||
|
||||
To do this we use [middleware records](ring.md#middleware-records) `:gen` hook instead of the normal `:wrap`. `:gen` expects a function of `route-meta router-opts => wrap`. Middleware can also return `nil`, which effective unmounts the middleware. Why mount a `wrap-enforce-roles` middleware for a route if there are no roles required for it?
|
||||
To enable this we use [middleware records](data_driven_middleware.md) `:gen` hook instead of the normal `:wrap`. `:gen` expects a function of `route-meta router-opts => wrap`. Middleware can also return `nil`, which effective unmounts the middleware.
|
||||
|
||||
To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:gen`. These are the actual codes are from [`reitit.coercion`](https://github.com/metosin/reitit/blob/master/src/reitit/coercion.cljc):
|
||||
To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:gen`. These are the actual codes are from [`reitit.ring.coercion`](https://github.com/metosin/reitit/blob/master/src/reitit/ring/coercion.cljc):
|
||||
|
||||
## Naive
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ To demonstrate the two approaches, below are response coercion middleware writte
|
|||
(defn wrap-coerce-response
|
||||
"Pluggable response coercion middleware.
|
||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||
and :responses from route meta, otherwise does not mount."
|
||||
and :responses from route meta, otherwise will do nothing."
|
||||
[handler]
|
||||
(fn
|
||||
([request]
|
||||
|
|
@ -27,20 +27,17 @@ To demonstrate the two approaches, below are response coercion middleware writte
|
|||
coercion (-> match :meta :coercion)
|
||||
opts (-> match :meta :opts)]
|
||||
(if (and coercion responses)
|
||||
(let [coercers (response-coercers coercion responses opts)
|
||||
coerced (coerce-response coercers request response)]
|
||||
(coerce-response coercers request (handler request)))
|
||||
(handler request))))
|
||||
(let [coercers (response-coercers coercion responses opts)]
|
||||
(coerce-response coercers request response))
|
||||
response)))
|
||||
([request respond raise]
|
||||
(let [response (handler request)
|
||||
method (:request-method request)
|
||||
(let [method (:request-method request)
|
||||
match (ring/get-match request)
|
||||
responses (-> match :result method :meta :responses)
|
||||
coercion (-> match :meta :coercion)
|
||||
opts (-> match :meta :opts)]
|
||||
(if (and coercion responses)
|
||||
(let [coercers (response-coercers coercion responses opts)
|
||||
coerced (coerce-response coercers request response)]
|
||||
(let [coercers (response-coercers coercion responses opts)]
|
||||
(handler request #(respond (coerce-response coercers request %))))
|
||||
(handler request respond raise))))))
|
||||
```
|
||||
|
|
@ -69,4 +66,4 @@ To demonstrate the two approaches, below are response coercion middleware writte
|
|||
(handler request #(respond (coerce-response coercers request %)) raise)))))))}))
|
||||
```
|
||||
|
||||
The `:gen` -version has 50% less code, is easier to reason about and is 2-4x faster on basic perf tests.
|
||||
The `:gen` -version has 50% less code, is easier to reason about and is twice as faster on basic perf tests.
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
# Middleware Records
|
||||
# Data-driven Middleware
|
||||
|
||||
Reitit supports first-class data-driven middleware via `reitit.middleware/Middleware` records, created with `reitit.middleware/create` function. The following keys have special purpose:
|
||||
Reitit supports first-class data-driven middleware via `reitit.ring.middleware/Middleware` records, created with `reitit.ring.middleware/create` function. The following keys have special purpose:
|
||||
|
||||
| key | description |
|
||||
| -----------|-------------|
|
||||
| `:name` | Name of the middleware as qualified keyword (optional,recommended for libs)
|
||||
| `:wrap` | The actual middleware function of `handler args? => request => response`
|
||||
| `:gen` | Middleware compile function, see [compiling middleware](#compiling-middleware).
|
||||
| `:gen` | Middleware compile function, see [compiling middleware](compiling_middleware.md).
|
||||
|
||||
When routes are compiled, all middleware are expanded (and optionally compiled) into `Middleware` and stored in compilation results for later use (api-docs etc). For actual request processing, they are unwrapped into normal middleware functions producing zero runtime performance penalty. Middleware expansion is backed by `reitit.middleware/IntoMiddleware` protocol, enabling plain clojure(script) maps to be used.
|
||||
When routes are compiled, all middleware are expanded (and optionally compiled) into `Middleware` Records and stored in compilation results for later use (api-docs etc). For actual request processing, they are unwrapped into normal middleware functions and composed together producing zero runtime performance penalty. Middleware expansion is backed by `reitit.middleware/IntoMiddleware` protocol, enabling plain clojure(script) maps to be used.
|
||||
|
||||
A Record:
|
||||
|
||||
|
|
@ -32,3 +32,7 @@ As plain map:
|
|||
:wrap (fn [handler]
|
||||
(wrap handler :api))})
|
||||
```
|
||||
|
||||
### TODO
|
||||
|
||||
more!
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Dynamic extensions
|
||||
|
||||
`ring-handler` injects the `Match` into a request and it can be extracted at runtime with `reitit.ring/get-match`. This can be used to build dynamic extensions to the system.
|
||||
`ring-handler` injects the `Match` into a request and it can be extracted at runtime with `reitit.ring/get-match`. This can be used to build ad-hoc extensions to the system.
|
||||
|
||||
Example middleware to guard routes based on user roles:
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ Example middleware to guard routes based on user roles:
|
|||
(defn wrap-enforce-roles [handler]
|
||||
(fn [{:keys [::roles] :as request}]
|
||||
(let [required (some-> request (ring/get-match) :meta ::roles)]
|
||||
(if (and (seq required) (not (set/intersection required roles)))
|
||||
(if (and (seq required) (not (set/subset? required roles)))
|
||||
{:status 403, :body "forbidden"}
|
||||
(handler request)))))
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
# Parameter coercion
|
||||
|
||||
Reitit provides pluggable parameter coercion via `reitit.coercion.protocol/Coercion` protocol, originally introduced in [compojure-api](https://clojars.org/metosin/compojure-api). Reitit ships with `reitit.coercion.spec/SpecCoercion` providing implemenation for [clojure.spec](https://clojure.org/about/spec) and [data-specs](https://github.com/metosin/spec-tools#data-specs).
|
||||
Reitit provides pluggable parameter coercion via `reitit.ring.coercion.protocol/Coercion` protocol, originally introduced in [compojure-api](https://clojars.org/metosin/compojure-api). Reitit ships with `reitit.ring.coercion.spec/SpecCoercion` providing implemenation for [clojure.spec](https://clojure.org/about/spec) and [data-specs](https://github.com/metosin/spec-tools#data-specs).
|
||||
|
||||
**NOTE**: Before Clojure 1.9.0 is shipped, to use the spec-coercion, one needs to add the following dependencies manually to the project:
|
||||
|
||||
```clj
|
||||
[org.clojure/clojure "1.9.0-alpha20"]
|
||||
[org.clojure/clojure "1.9.0-beta2"]
|
||||
[org.clojure/spec.alpha "0.1.123"]
|
||||
[metosin/spec-tools "0.3.3"]
|
||||
[metosin/spec-tools "0.4.0"]
|
||||
```
|
||||
|
||||
### Ring request and response coercion
|
||||
|
|
@ -18,7 +18,9 @@ To use `Coercion` with Ring, one needs to do the following:
|
|||
* `:parameters` map, with submaps for different parameters: `:query`, `:body`, `:form`, `:header` and `:path`. Parameters are defined in the format understood by the `Coercion`.
|
||||
* `:responses` map, with response status codes as keys (or `:default` for "everything else") with maps with `:schema` and optionally `:description` as values.
|
||||
2. Define a `Coercion` to route meta-data under `:coercion`
|
||||
3. Mount request & response coercion middleware to the routes.
|
||||
3. Mount request & response coercion middleware to the routes (recommended to mount to all routes under router as they mounted only to routes which have the parameters / responses defined):
|
||||
* `reitit.ring.coercion/gen-wrap-coerce-parameters`
|
||||
* `gen-wrap-coerce-parameters/gen-wrap-coerce-responses`
|
||||
|
||||
If the request coercion succeeds, the coerced parameters are injected into request under `:parameters`.
|
||||
|
||||
|
|
@ -28,8 +30,8 @@ If either request or response coercion fails, an descriptive error is thrown.
|
|||
|
||||
```clj
|
||||
(require '[reitit.ring :as ring])
|
||||
(require '[reitit.coercion :as coercion])
|
||||
(require '[reitit.coercion.spec :as spec])
|
||||
(require '[reitit.ring.coercion :as coercion])
|
||||
(require '[reitit.ring.coercion.spec :as spec])
|
||||
|
||||
(def app
|
||||
(ring/ring-handler
|
||||
|
|
@ -56,10 +58,12 @@ If either request or response coercion fails, an descriptive error is thrown.
|
|||
|
||||
#### Example with specs
|
||||
|
||||
Currently, `clojure.spec` [doesn't support runtime transformations via conforming](https://dev.clojure.org/jira/browse/CLJ-2116), so one needs to wrap all specs with `spec-tools.core/spec`.
|
||||
|
||||
```clj
|
||||
(require '[reitit.ring :as ring])
|
||||
(require '[reitit.coercion :as coercion])
|
||||
(require '[reitit.coercion.spec :as spec])
|
||||
(require '[reitit.ring.coercion :as coercion])
|
||||
(require '[reitit.ring.coercion.spec :as spec])
|
||||
(require '[clojure.spec.alpha :as s])
|
||||
(require '[spec-tools.core :as st])
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ Applying the handler:
|
|||
; {:status 200, :body "ok"}
|
||||
```
|
||||
|
||||
The expanded routes:
|
||||
The expanded routes shows the compilation results:
|
||||
|
||||
```clj
|
||||
(-> app (ring/get-router) (reitit/routes))
|
||||
|
|
@ -58,7 +58,7 @@ Handler are also looked under request-method keys: `:get`, `:head`, `:patch`, `:
|
|||
; nil
|
||||
```
|
||||
|
||||
Reverse routing:
|
||||
Name-based reverse routing:
|
||||
|
||||
```clj
|
||||
(-> app
|
||||
|
|
@ -70,9 +70,9 @@ Reverse routing:
|
|||
|
||||
# Middleware
|
||||
|
||||
Middleware can be added with a `:middleware` key, with a vector value of the following:
|
||||
Middleware can be added with a `:middleware` key, either to top-level or under `:request-method` submap. It's value should be a vector value of the following:
|
||||
|
||||
1. ring middleware function `handler -> request -> response`
|
||||
1. normal ring middleware function `handler -> request -> response`
|
||||
2. vector of middleware function `handler ?args -> request -> response` and optinally it's args.
|
||||
|
||||
A middleware and a handler:
|
||||
|
|
@ -96,7 +96,7 @@ App with nested middleware:
|
|||
["/ping" handler]
|
||||
["/admin" {:middleware [[wrap :admin]]}
|
||||
["/db" {:middleware [[wrap :db]]
|
||||
:delete {:middleware [#(wrap % :delete)]
|
||||
:delete {:middleware [[wrap :delete]]
|
||||
:handler handler}}]]])))
|
||||
```
|
||||
|
||||
|
|
|
|||
9
modules/reitit-core/project.clj
Normal file
9
modules/reitit-core/project.clj
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
(defproject metosin/reitit-core "0.1.0-SNAPSHOT"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:plugins [[lein-parent "0.3.2"]]
|
||||
:parent-project {:path "../../project.clj"
|
||||
:inherit [:deploy-repositories :managed-dependencies]}
|
||||
:dependencies [[meta-merge]])
|
||||
9
modules/reitit-ring/project.clj
Normal file
9
modules/reitit-ring/project.clj
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
(defproject metosin/reitit-ring "0.1.0-SNAPSHOT"
|
||||
:description "Reitit: Ring routing"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:plugins [[lein-parent "0.3.2"]]
|
||||
:parent-project {:path "../../project.clj"
|
||||
:inherit [:deploy-repositories :managed-dependencies]}
|
||||
:dependencies [[metosin/reitit-core]])
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
(ns reitit.ring
|
||||
(:require [meta-merge.core :refer [meta-merge]]
|
||||
[reitit.middleware :as middleware]
|
||||
[reitit.core :as reitit]
|
||||
[reitit.ring.middleware :as middleware]
|
||||
[reitit.core :as r]
|
||||
[reitit.impl :as impl]))
|
||||
|
||||
(def http-methods #{:get :head :patch :delete :options :post :put})
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
(with-meta
|
||||
(fn
|
||||
([request]
|
||||
(if-let [match (reitit/match-by-path router (:uri request))]
|
||||
(if-let [match (r/match-by-path router (:uri request))]
|
||||
(let [method (:request-method request :any)
|
||||
params (:params match)
|
||||
result (:result match)
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
(cond-> (impl/fast-assoc request ::match match)
|
||||
(seq params) (impl/fast-assoc :path-params params)))))))
|
||||
([request respond raise]
|
||||
(if-let [match (reitit/match-by-path router (:uri request))]
|
||||
(if-let [match (r/match-by-path router (:uri request))]
|
||||
(let [method (:request-method request :any)
|
||||
params (:params match)
|
||||
result (:result match)
|
||||
|
|
@ -73,4 +73,4 @@
|
|||
(router data nil))
|
||||
([data opts]
|
||||
(let [opts (meta-merge {:coerce coerce-handler, :compile compile-result} opts)]
|
||||
(reitit/router data opts))))
|
||||
(r/router data opts))))
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
(ns reitit.coercion
|
||||
(ns reitit.ring.coercion
|
||||
(:require [clojure.walk :as walk]
|
||||
[spec-tools.core :as st]
|
||||
[reitit.coercion.protocol :as protocol]
|
||||
[reitit.middleware :as middleware]
|
||||
[reitit.ring.middleware :as middleware]
|
||||
[reitit.ring.coercion.protocol :as protocol]
|
||||
[reitit.ring :as ring]
|
||||
[reitit.impl :as impl]))
|
||||
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
(defn wrap-coerce-parameters
|
||||
"Pluggable request coercion middleware.
|
||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||
and :parameters from route meta, otherwise does not mount."
|
||||
and :parameters from route meta, otherwise will do nothing."
|
||||
[handler]
|
||||
(fn
|
||||
([request]
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
(ns reitit.coercion.protocol
|
||||
(ns reitit.ring.coercion.protocol
|
||||
(:refer-clojure :exclude [compile]))
|
||||
|
||||
(defprotocol Coercion
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
(ns reitit.middleware
|
||||
(ns reitit.ring.middleware
|
||||
(:require [meta-merge.core :refer [meta-merge]]
|
||||
[reitit.core :as reitit]))
|
||||
[reitit.core :as r]))
|
||||
|
||||
(defprotocol IntoMiddleware
|
||||
(into-middleware [this meta opts]))
|
||||
|
|
@ -91,13 +91,13 @@
|
|||
(router data nil))
|
||||
([data opts]
|
||||
(let [opts (meta-merge {:compile compile-result} opts)]
|
||||
(reitit/router data opts))))
|
||||
(r/router data opts))))
|
||||
|
||||
(defn middleware-handler [router]
|
||||
(with-meta
|
||||
(fn [path]
|
||||
(some->> path
|
||||
(reitit/match-by-path router)
|
||||
(r/match-by-path router)
|
||||
:result
|
||||
:handler))
|
||||
{::router router}))
|
||||
10
modules/reitit-spec/project.clj
Normal file
10
modules/reitit-spec/project.clj
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
(defproject metosin/reitit-spec "0.1.0-SNAPSHOT"
|
||||
:description "Reitit: clojure.spec coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:plugins [[lein-parent "0.3.2"]]
|
||||
:parent-project {:path "../../project.clj"
|
||||
:inherit [:deploy-repositories :managed-dependencies]}
|
||||
:dependencies [[metosin/reitit-ring]
|
||||
[metosin/spec-tools]])
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
(ns reitit.coercion.spec
|
||||
(ns reitit.ring.coercion.spec
|
||||
(:require [clojure.spec.alpha :as s]
|
||||
[spec-tools.core :as st #?@(:cljs [:refer [Spec]])]
|
||||
[spec-tools.data-spec :as ds]
|
||||
[spec-tools.conform :as conform]
|
||||
[spec-tools.swagger.core :as swagger]
|
||||
[reitit.coercion.protocol :as protocol])
|
||||
[reitit.ring.coercion.protocol :as protocol])
|
||||
#?(:clj
|
||||
(:import (spec_tools.core Spec))))
|
||||
|
||||
11
modules/reitit/project.clj
Normal file
11
modules/reitit/project.clj
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
(defproject metosin/reitit "0.1.0-SNAPSHOT"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:plugins [[lein-parent "0.3.2"]]
|
||||
:parent-project {:path "../../project.clj"
|
||||
:inherit [:deploy-repositories :managed-dependencies]}
|
||||
:dependencies [[metosin/reitit-core]
|
||||
[metosin/reitit-ring]
|
||||
[metosin/reitit-spec]])
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
[spec-tools.core :as st]
|
||||
|
||||
[reitit.core :as reitit]
|
||||
[reitit.ring :as ring]
|
||||
[reitit.core :as ring]
|
||||
[reitit.coercion :as coercion]
|
||||
[reitit.coercion.spec :as spec]
|
||||
[reitit.coercion.protocol :as protocol]
|
||||
|
|
@ -145,7 +145,7 @@
|
|||
|
||||
(comment
|
||||
(do
|
||||
(require '[reitit.ring :as ring])
|
||||
(require '[reitit.core :as ring])
|
||||
(require '[reitit.coercion :as coercion])
|
||||
(require '[reitit.coercion.spec :as spec])
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
[cheshire.core :as json]
|
||||
[clojure.string :as str]
|
||||
[reitit.core :as reitit]
|
||||
[reitit.ring :as ring]
|
||||
[reitit.core :as ring]
|
||||
|
||||
[bidi.bidi :as bidi]
|
||||
|
||||
|
|
|
|||
27
project.clj
27
project.clj
|
|
@ -1,17 +1,21 @@
|
|||
(defproject metosin/reitit "0.1.0-SNAPSHOT"
|
||||
(defproject metosin/reitit-parent "0.1.0-SNAPSHOT"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"
|
||||
:distribution :repo
|
||||
:comments "same as Clojure"}
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:test-paths ["test/clj" "test/cljc"]
|
||||
:deploy-repositories [["releases" :clojars]]
|
||||
:codox {:output-path "doc"
|
||||
:source-uri "https://github.com/metosin/reitit/{version}/{filepath}#L{line}"
|
||||
:metadata {:doc/format :markdown}}
|
||||
|
||||
:dependencies [[meta-merge "1.0.0"]]
|
||||
:managed-dependencies [[metosin/reitit "0.1.0-SNAPSHOT"]
|
||||
[metosin/reitit-core "0.1.0-SNAPSHOT"]
|
||||
[metosin/reitit-ring "0.1.0-SNAPSHOT"]
|
||||
[metosin/reitit-spec "0.1.0-SNAPSHOT"]
|
||||
|
||||
[meta-merge "1.0.0"]
|
||||
[metosin/spec-tools "0.5.0"]]
|
||||
|
||||
:plugins [[jonase/eastwood "0.2.5"]
|
||||
[lein-doo "0.1.8"]
|
||||
|
|
@ -21,11 +25,18 @@
|
|||
[metosin/boot-alt-test "0.4.0-20171019.180106-3"]]
|
||||
|
||||
:profiles {:dev {:jvm-opts ^:replace ["-server"]
|
||||
|
||||
;; all module sources for development
|
||||
:source-paths ["modules/reitit/src"
|
||||
"modules/reitit-core/src"
|
||||
"modules/reitit-ring/src"
|
||||
"modules/reitit-spec/src"]
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.9.0-beta2"]
|
||||
[org.clojure/clojurescript "1.9.946"]
|
||||
|
||||
[metosin/spec-tools "0.5.0"]
|
||||
[org.clojure/spec.alpha "0.1.134"]
|
||||
;; all modules dependencies
|
||||
[metosin/reitit]
|
||||
|
||||
[expound "0.3.1"]
|
||||
[orchestra "2017.08.13"]
|
||||
|
|
@ -38,7 +49,7 @@
|
|||
"-Xmx4096m"
|
||||
"-Dclojure.compiler.direct-linking=true"]
|
||||
:test-paths ["perf-test/clj"]
|
||||
:dependencies [[metosin/compojure-api "2.0.0-alpha10"]
|
||||
:dependencies [[metosin/compojure-api "2.0.0-alpha12"]
|
||||
[io.pedestal/pedestal.route "0.5.3"]
|
||||
[org.clojure/core.async "0.3.443"]
|
||||
[ataraxy "0.4.0"]
|
||||
|
|
|
|||
8
scripts/lein-modules
Executable file
8
scripts/lein-modules
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Modules
|
||||
for ext in reitit-core reitit-ring reitit-spec reitit; do
|
||||
cd modules/$ext; lein "$@"; cd ../..;
|
||||
done
|
||||
7
scripts/set-version
Executable file
7
scripts/set-version
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
ext="sedbak$$"
|
||||
|
||||
find . -name project.clj -exec sed -i.$ext "s/\[metosin\/reitit\(.*\) \".*\"\]/[metosin\/reitit\1 \"$1\"\]/g" '{}' \;
|
||||
find . -name project.clj -exec sed -i.$ext "s/defproject metosin\/reitit\(.*\) \".*\"/defproject metosin\/reitit\1 \"$1\"/g" '{}' \;
|
||||
find . -name "*.$ext" -exec rm '{}' \;
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
(ns reitit.coercion-test
|
||||
(:require [clojure.test :refer [deftest testing is]]
|
||||
[reitit.ring :as ring]
|
||||
[reitit.coercion :as coercion]
|
||||
[reitit.coercion.spec :as spec])
|
||||
[reitit.ring.coercion :as coercion]
|
||||
[reitit.ring.coercion.spec :as spec])
|
||||
#?(:clj
|
||||
(:import (clojure.lang ExceptionInfo))))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(ns reitit.middleware-test
|
||||
(:require [clojure.test :refer [deftest testing is are]]
|
||||
[reitit.middleware :as middleware]
|
||||
[reitit.ring.middleware :as middleware]
|
||||
[clojure.set :as set]
|
||||
[reitit.core :as r])
|
||||
#?(:clj
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
(ns reitit.ring-test
|
||||
(:require [clojure.test :refer [deftest testing is]]
|
||||
[reitit.middleware :as middleware]
|
||||
[reitit.ring :as ring]
|
||||
[clojure.set :as set]
|
||||
[reitit.ring.middleware :as middleware]
|
||||
[reitit.ring :as ring]
|
||||
[reitit.core :as r])
|
||||
#?(:clj
|
||||
(:import (clojure.lang ExceptionInfo))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue