Raven Path Generators and Route Enumeration
Raven, the URL dispatching library on which Vinland is based, has been updated to add a couple new features, in addition to general cleanup, additional docstrings, and the addition of declaim statements.
New features:
- path generators
- list-routes utility
Path generators
A lambda function is now compiled for each route symbol passed to COMPILE-ROUTER
.
Any dynamic path parameters are passed to the function as keyword arguments in order to return a path string.
Macro ROUTE-PATH
can be used to lookup and call the path generator function for the specified route name.
As an example, suppose the following Raven router has been compiled:
(defparameter *router* (foo.lisp.raven:compile-router
`(("/widgets" ,'widgets)
("/widgets/:widget-id" ,'widget))))
(defparameter *web* (funcall *router* :clack))
Now, from your routes, you can call ROUTE-PATH
to return a path string.
Package FOO.LISP.VINLAND/WEB
re-exports ROUTE-PATH
from Raven, and also defines macro ROUTE-URL
,
which returns the same string, except prefixed by the origin.
Example:
(foo.lisp.raven:route-path 'widgets)
;; => "/widgets"
(foo.lisp.raven:route-path 'widget :|widget-id| "xyz")
;; => "/widgets/xyz"
Vinland example:
(foo.lisp.vinland/web:route-path 'widget :|widget-id| "xyz")
;; => "/widgets/xyz"
;; NOTE: *ORIGIN* is set by default in Vinland controllers.
(let ((foo.lisp.vinland:*origin* "http://acme.example.com"))
(foo.lisp.vinland/web:route-url 'widget :|widget-id| "xyz"))
;; => "http://acme.example.com/widgets/xyz"
The call to COMPILE-ROUTER
to return a router function happens after loading route handlers and other code,
so to make the path generators accessible from controllers and other files loaded before router compilation,
the path-generators are stored in a hash-table that is dynamically bound on dispatch.
If say, you want to call ROUTE-PATH
from dao.lisp or another file loaded prior to the controllers, you are free to do so:
the hash-table containing the path generators stores each entry with a keyword key, coerced from the route-name symbol.
This means that route-name symbols must now be unique between packages, so the following will no longer compile:
(defparameter *router* (foo.lisp.raven:compile-router
`(("/about" ,'about)
("/foo/about" ,'foo:about))))
List-routes
The router function returned from COMPILE-ROUTER
can now list routes matching a specified prefix,
returning either a list or printing to standard-out.
(defparameter *router* (foo.lisp.raven:compile-router
`(("/" ,'root)
("/widgets" ,'widgets)
("/widgets/:widget-id" ,'widget))))
The following returns a list of ROUTE-INFO structs for all routes:
(funcall *router* '(:list-routes "/"))
To return only the widgets routes, run:
(funcall *router* '(:list-routes "/widgets"))
To print to *STANDARD-OUTPUT*
without returning a list (slightly better formatted), you can call:
(funcall *router* '(:print-routes "/widgets"))
NOTE: you can also find a specific matching route; this isn’t a new feature but worth pointing out here:
(funcall *router* '(:find-route "/widgets/xyz"))
The CLI files in the Vinland skeleton have been updated, so that a list-routes
subcommand can now be
called from the generated binary (created with (asdf:make :demo-app)
):
The following example prints all routes to standard-out:
./bin/demo-app list-routes /