Simple Path Expressions
Simple Path Expressions are a dumbed-down but easier-to-use kind of regular expressions for matching and filling paths and urls. For example, /articles/<year:Y>/<month>/<day>
becomes the regular expression /^/articles/(\d{4})/([^/]+)/([^/]+)$/
.
Features
- Flexible syntax: Simple Path Expressions come in your flavor, too. There's
<unix>
,{python}
and:ruby
-style interpolation, and[unix]
or(:ruby)
as well as<unix?>
,{python?}
and:ruby?
for optional segments. We'll use UNIX-style throughout the documentation. - Named groups: simple path expressions returns named groups as objects rather than arrays, making matches easier to work with.
- Shortcuts to common patterns: specify that a certain URL or path segments needs to be a year or a month or a UUID or a number or a string.
- Object-oriented or functional: two interfaces to the same functionality. Your pick.
Usage
Translate a simple path expression into a regular expression:
var simplex = ;var route = '/articles/<year>/<month>/<day>';var regexp = simplex;var groups = regexpexec '/articles/2014/09/01'// [ '/articles/2014/09/01', '2014', '09', '01' ]console;
Match a path against an expression:
var url = '/articles/2014/05/04';var route = '/articles/<year>/<month>/<day>';var match = simplex;// { year: '2014', month: '05', day: '05'}console;
Similarly to regular expressions, if the string doesn't match the pattern, the resulting match will be null
.
var route = '/archive/<year:Y>/index.html';var path = '/archive/90s/index.html';var match = simplex;assert match == null;
Optional segments are possible too. They're specified using [segment]
or (:segment)
or by adding a question mark after a segment, e.g. <segment?>
, :segment?
or {segment?}
.
var route = '/archive/<year>/[month:y]/[day:y]/index.html';var path = '/archive/2014/index.html';var match = simplex;assert matchyear == '2014';assert matchmonth == undefined;assert matchday == undefined;
Build a path from an expression:
var route = '/articles/<year>/<month>/<day>'; var url = simplex; // '/articles/1988/05/04' console;
You can also use a more object-oriented interface with the PathExp
object. I actually like it more.
var pattern = '/articles/<year>/<month>/<day>';var instance = '/articles/2014/05/04';var path = pattern;assert path == simplex;
Formats
Generally, a placeholder will match anything that isn't a slash. However, it is possible to be more specific by adding a format. Just tack a colon to the placeholder name, followed by the required format. E.g. :title:s
or {year:Y}
depending on your interpolation style.
The available formats are:
Format | RegExp | Description |
---|---|---|
Y | \d{4} |
year, including century |
y | \d{2} |
year, not including century |
M | see code | month, abbreviated |
m | see code | month, 1 or 2 digits |
d | see code | day, 1 or 2 digits |
# | \d+ |
number, any length |
w | \w+ |
word |
s | [\w-]+ |
slug |
u | see code | uuid |
* | [^/]+? |
the default, which matches anything |
Interpolation styles
By default, you can use UNIX, Python and Ruby-style placeholders for interpolation. You can be more picky by specifying with style or styles to allow:
// all stylesvar template = '/posts/<author>/:title';assert template == '/posts/me/first-post';// just rubyvar template = '/posts/<author>/:title' 'ruby';assert template == '/posts/<author>/first-post';
This works for all simple path expression functionality:
var regexp = simplex;var match = simplex;var path = simplex;var pathTemplate = pattern 'ruby';
Learn more
Take a look at the test suite for more example code.
Simple path expressions are inspired by Cody Soyland's Surlex library for Python. Surlex is "a language for URL matching and extraction."
Missing features
This is a list of features I'd like to implement at some point in the future, though there's no clear roadmap detailing if and when they might land.
Traversal
We don't always have full control over the structure of the objects we use to fill in a path expression. When dealing with nested objects, traversal using dot notation could be quite useful:
var template = '/posts/<post.author>/<post.metadata.title>';assert template == '/posts/me/first-post';
Filters
One interesting feature we might implement at some point would be filters.
var simplex = ;var pattern = '/articles/{month|lowercase}/{title|slugify}/';var template = pattern;var path = template;assert path == '/articles/january/hello-world/';
The concept is pretty similar to how filters work in Jinja or how they work in Liquid templates: when you do expression.fill
it will first process the variable month
with a lowercase
function before filling it in.
Formats determine what input to a path expression should look like to make a match. Filters determine how to then post-process that match, or how to pre-process data before using it to fill in a template.
An example of using filters to post-process a match:
var simplex = ;// no filtervar match = simplex;assert match == '1996';assert typeof match == 'string';// integer filtervar match = simplex;assert match == 1996; assert typeof match == 'number';
Filters wouldn't be be a killer feature for a router, but they might be for command-line applications, e.g. as the basis for a more user-friendly version of the linux rename
utility.