routes
routes
for Clojure is inspired and heavily influenced in implementation by bidi
,
but works more like a language than a regular expression.
bidi
is opinionated about your routes' endpoints.
With bidi
, suppose you have this route structure:
["/" {"users" {"" :users
["/" :id] :users}}]
You'd want the path for :users
to be /users
and the path for :users
with :params {:id 7}
to be /users/7
.
Because bidi
uses greedy search, this doesn't work.
In the latter case, it finds the first endpoint that matches the given target,
and serializes the components that led to that endpoint (discarding the excess route parameter),
resulting in /users
.
Suppose instead you had this ordering:
["/" {"users" {["/" :id] :users
"" :users}}]
This would produce the correct path for :users
with :params {:id 7}
, /users/7
,
but it would throw an exception when serializing the path for just :users
,
since it would run with the first match, which requires an :id
parameter,
and there is none such for the index route.
Another use-case is to contextualize a whole group of routes. For example, suppose you have some routes:
(def store-resource-routes
{"/customers" {["/" :id] :customers
"" :customers}
"/products" {["/" :id] :products
"" :products}})
And you wanted to merge and group these under two paths,
/api/v1
and /api/v2
,
but not write the whole thing out twice.
Due to the greedy nature of bidi
's endpoint matching, this would be impossible;
you could never generate paths for any of the api versions besides the first,
and you could not tell which api version a path used from the matched-route's :route-params
.
In this library's routes.extra
module,
there is a (parameterize pattern key value)
function
that wraps a pattern structure in an extension of the Pattern
protocol,
and adds a (required) parameter to all the endpoints under that pattern.
This function creates a new instance of the ParameterizedPattern
record,
and, when generating a path, requires the route context to match the specified {key value}
pair,
and adds that same pair to the route context when resolving the endpoint from a path.
Your routes would look like this:
(def routes
["/api/" {(parameterize "v1" :api-version 1) store-resource-routes
(parameterize "v2" :api-version 2) store-resource-routes}])
With these routes, we can generate the path for :customers
with parameters :api-version 1
:
(generate-path routes {:endpoint :customers :api-version 1})
;=> "/api/v1/customers"
As mentioned, the parameters must be provided, or else there's no match.
(generate-path routes {:endpoint :customers})
;=> nil
And we can resolve endpoints that specify the parameters used to match the given path:
(resolve-endpoint {:path "/api/v2/customers/chb"})
;=> {:endpoint :customers :api-version 2 :id "chb"}
bidi
Failed matches.
bidi
throws on failed matches, routes
returns nil.
Naming
Pattern
→ Pattern
(no change)Matched
→ Routes
match-route
/ match-route*
/ match-pair
/ resolve-handler
→ resolve-endpoint
(yep, only one function to handle them all)match-pattern
→ match-pattern
(no change)path-for
/ unmatch-pair
/ unresolve-handler
→ generate-path
(simple as that!)unmatch-pattern
→ generate-pattern-path
(though this is for patterns and you probably won't call it directly)(ns user
(:require [routes.core :refer [resolve-endpoint generate-path]]))
(def routes
{"/login" :login
["/wiki/" :page] :wiki})
(resolve-endpoint routes {:path "/wiki/Welcome"})
;=> {:endpoint :wiki :page "Welcome"}
(generate-path routes {:endpoint :login})
;=> "/login"
(generate-path routes {:endpoint :wiki :page "Contact"})
;=> "/wiki/Contact"
There are a lot of data-driven (i.e., routes-as-data, as opposed to imperative Compojure-style) cross-platform routing libraries out there.
bidi
was not the first data-driven router library (first commit was 2013-12-20), but it's been the most successful.bidi
; see above for the important differences.bidi
, but with more introspection about the path.routes
,
though I started from bidi
(reitit
's first commit was 2017-08-07, routes
's was 2017-08-24)bidi
, but focused on Ring integrationbidi
, but more of a DSL, oriented around URL conventions
(e.g., slashes separate path components)bidi
, but patterns are described as strings rather than data structures
(e.g., bidi
's ["/account/" [[[:account-uuid "/" :page-uuid] :account/page]]]
becomes
bide
's ["/account/:account-uuid/:page-uuid" :account/page]
).bidi
, but with more helper functions for preparing path components, e.g. (silk/int :id)
Compile production JavaScript output:
lein cljsbuild once production
Run Clojure tests:
lein with-profile test test
Compute Clojure test coverage:
lein with-profile test cloverage
Run the ClojureScript tests:
lein with-profile test doo rhino test once
Run the ClojureScript tests on Chrome:
npm install -g karma-cli
npm install karma karma-cljs-test karma-chrome-launcher
lein with-profile test doo chrome test once
Copyright © 2017 Christopher Brown. Eclipse Public License - v 1.0.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close