deep-routes
Dynamic url to route mapping.
deep-routes library comes with :
- a small DSL to describe routes parsing.
- flat map management tools
- deeply stuctured map management tools
Maps (deeply structured or flat) could contains OCM entries that will be resolved when traversing.
install
npm install deep-routes
or
bower install deep-routes
Route DSL
It has been developped to clarify route parsing description, and to allow relative route matching in structured maps (see below).
Example : /campaign/?s:id/update/?q:query/?(i:start/i:end)
could matchs :
- /campaign/
- /campaign/anID
- /campaign/anID/update
- /campaign/anID/update/?some=query
- /campaign/anID/update/?some=query/12/24
- /campaign/anID/update/12/24
Usage
var deep = require("deepjs");
require("deep-routes/lib/route");
var route = new deep.Route("/campaign/?s:id/update/?q:query/?(i:start/i:end)");
var r = route.match("/campaign/12/update/?foo");
console.log(r);
Rules
- starting with
?
: parts is optional - starting with
!
: anything but following - parts group : surrounded with parenthesis
- disjonction : surrounded with square brackets, separated with comas. e.g. [campaign,!product]
- variable catch : parts name start with x: where 'x' is a variable type (see below). e.g. "s:id" or "q:query"
- parts name equal $ : says that tested route need to ends here
Additionnaly, if whole route start with '?' or '!', it is seen as a optional or negated block.
variables types
i:
integerf:
floatq:
queryString. anything starting with '?'r:
rql query. anything starting with '?'s:
string. anything.p:
path. used at end of route. says : take the rest of tested route. e.g. /campaign/p:path
will match /campaign/my/long/path
add custom type
adding date :
deep.Route.addType("d:", function(input) {
var r = parseInt(input, 10);
if (!isNaN(r) && r !== Infinity)
return new Date(r);
return null;
});
Flat map
A "flat" map is just an object containing, at first level, "routes" as properties keys and "controllers" as properties values (absolutly free shaped object).
example :
var deep = require("deepjs");
require("deep-routes/lib/flat");
var map = {
"/product/?s:id":{
title:"product controller"
},
"/register/?s:email":{
title:"register controller"
},
};
deep.route.flatMapper(map)
.done(function(mapper){
var r = mapper.match("/product/12");
console.log(r);
})
.elog();
Standard restful expansion
When using deep.route.flatMapper for restful services mapping, you could ask to expand simple routes (routes without variable catch) with default restful routing pattern (catching optional id, queries or subpath).
It means :
var deep = require("deepjs");
require("deep-routes/lib/flat");
var map = {
"/campaign":{
title:"campaign controller"
},
"/register/?s:email":{
title:"register controller"
},
};
deep.route.flatMapper(map, true)
.done(function(mapper){
var r = mapper.match("/campaign/12");
console.log(r);
})
.elog();
/campaign/
has been expanded in /campaign/?[(s:id/?p:path),q:query]
Deeply structured map
A structured map is defined by a structure of objects containing "route" and/or "subs" properties, the two only keywords of structured map.
The "route" entry gives the route matcher of your controller, and "subs" property contains optional subcontrollers.
The routing algorithm is like an automate that try to match any "route" property founded in current level's entries.
If "route" matchs (or if there is no route property, which means always match), it selects current entry, and if current entry contains a "subs" property then algorithm appying route matching recursively on it.
So the algorithm finally selects a tree of controllers that match a uri.
Additionnaly, any "route" entry could be absolute (starting with "/"), or relative to last matched index :
./foo
: says try to match "foo" after last matched index../foo
: says try to match "foo" after last matched index - 1../../foo
: says try to match "foo" after last matched index - 2- ...
It means :
var map = {
topbar:{
},
home:{
route:"/[home,$]"
},
product:{
route:"/product",
subs:{
list:{
route:"./q:query/?(i:start/i:end)"
},
detail:{
route:"./detail/s:id"
},
comment:{
route:"./comment/s:id"
}
}
},
};
Possibles routes matched by this map :
/
(selected : topbar, home)/home
(selected : topbar, home)/product/?some=query
(selected : topbar, products, list)/product/?some=query/0/9
(selected : topbar, products, list)/product/?some=query/detail/53
(selected : topbar, products, list, detail)/product/?some=query/detail/53/comment/12
(selected : topbar, products, list, detail, comment)/product/?some=query/comment/12
(selected : topbar, products, list, comment)/product/?some=query/0/9/detail/53
(selected : topbar, products, list, detail)/product/?some=query/0/9/detail/53/comment/12
(selected : topbar, products, list, detail, comment)/product/?some=query/0/9/comment/12
(selected : topbar, products, list, comment)/product/detail/53
(selected : topbar, products, detail)/product/detail/53/comment/12
(selected : topbar, products, detail, comment)/product/comment/12
(selected : topbar, products, detail)
The idea, with relative route management, is to break coupling between parent and children, to obtain clean MVC pattern where neither parent nor children know anything about each other.
As a controller could define its own route management (as "list", "detail" and "comment" in example above), independently of previously "consummmed" route's parts, it allows us to reattach or reuse a controller elsewhere without anychange.
Structured maps and views
As structured maps have been developped principaly for views and subviews management, it works hand in hand with deep-views.
var deep = require("deepjs");
require("deep-views/lib/view");
require("deep-routes/lib/structured");
var map = {
topbar:deep.View({
how:"<div>topbar content</div>",
where:"dom.appendTo::#topbar"
}),
home:deep.View({
route:"/[home,$]",
how:"<div>home content</div>",
where:"dom.appendTo::#content"
}),
profile:deep.View({
route:"/profile/?s:name",
how:"<div>hello { name | 'dude' } !</div>",
where:"dom.appendTo::#content"
})
};
deep.route.structuredMapper(map)
.done(function(node){
var r = node.match("/campaign/john");
console.log(r);
})
.elog();
load and refresh sequence
relink
inner view route and getRoute
Licence
LGPL 3.0