Skip to content

Instantly share code, notes, and snippets.

@rikkimax
Created December 23, 2013 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rikkimax/8097560 to your computer and use it in GitHub Desktop.
Save rikkimax/8097560 to your computer and use it in GitHub Desktop.
module common.routing;
public import vibe.http.router;
import std.file : append, write;
__gshared URLRouter urlRouter = new URLRouter();
private string logFile = null;
void setRoutingLogFile(string file) {
logFile = file;
}
enum RouteType {
Get = "get",
Post = "post",
Put = "put",
Delete = "delete",
Any = "any"
}
alias bool function(HTTPServerRequest req, HTTPServerResponse res) RouteFilter;
struct RouteFunction {
RouteType type;
string route = "";
RouteFilter filterFunction = null;
}
struct RouteGroup {
RouteFilter filterFunction = null;
string routeBase = "";
}
struct RouteGroupId {
string routeName = "";
}
struct RouteGroupIds {
// bug in dmd 2.064
/*this(string[] args ...) {
//routeNames = args;
}*/
string[] routeNames;
}
private void FilterFunctionWrapper(alias me, string f, alias filters)(HTTPServerRequest req, HTTPServerResponse res) {
bool result = true;
foreach(filter; filters) {
result = filter(req, res);
if (!result)
break;
}
if (result)
mixin("me." ~ f ~ "(req, res);");
}
/**
* Route manager is the base router (on top of stock URLRouter).
* It provides an singleton class based route controller.
* Supports:
* @RouteFunction(RouteType, route, RouteFilter)
* @RouteGroup(RouteFilter, routeBase)
* @RouteGroupId(name)
* @RouteGroupIds([name, ...])
*/
abstract class Routes {
this(this T)() {
this(cast(T)this);
}
this(T)(T me) {
string routeOutput = "";
foreach(string f; __traits(allMembers, T)) {
mixin("alias symbol = me." ~ f ~ ";");
static if (__traits(compiles, {
mixin("me." ~ f ~ "(cast(HTTPServerRequest)null, cast(HTTPServerResponse)null);"); } )) {
RouteType routeType = cast(RouteType)"";
string route = "";
RouteFilter[] filters;
bool isLastGroupId = false;
handleRouteGroups!(symbol)(isLastGroupId, route, filters);
foreach (UDA; __traits(getAttributes, symbol)) {
static if (is(typeof(UDA) == RouteFunction)) {
routeType = UDA.type;
if (!isLastGroupId)
route ~= UDA.route;
if (UDA.filterFunction !is null)
filters ~= UDA.filterFunction;
}
}
handleRouteFunction!(T, f)(me, routeType, route, filters, routeOutput);
}
}
if (logFile !is null)
append(logFile, routeOutput);
}
protected {
void handleRouteFunction(T, string f)(T me, ref RouteType routeType, ref string route, ref RouteFilter[] filters, ref string routeOutput) {
if (routeType != "") {
routeOutput ~= T.stringof ~ "." ~ f ~ " = " ~
(cast(string)routeType) ~ ":" ~ route ~ "\n";
if (filters.length == 0) {
// we have no filters hurray!
// little to no over head
if (routeType == RouteType.Get)
(cast(URLRouter)urlRouter).get(route, mixin("&me." ~ f));
else if (routeType == RouteType.Post)
(cast(URLRouter)urlRouter).post(route, mixin("&me." ~ f));
else if (routeType == RouteType.Put)
(cast(URLRouter)urlRouter).put(route, mixin("&me." ~ f));
else if (routeType == RouteType.Delete)
(cast(URLRouter)urlRouter).delete_(route, mixin("&me." ~ f));
else if (routeType == RouteType.Any)
(cast(URLRouter)urlRouter).any(route, mixin("&me." ~ f));
} else {
// ok so we have filters..
// now how do we chain them exactly?
if (routeType == RouteType.Get)
(cast(URLRouter)urlRouter).get(route, mixin("&FilterFunctionWrapper!
(me, \"" ~ f ~ "\", filters)"));
else if (routeType == RouteType.Post)
(cast(URLRouter)urlRouter).post(route, mixin("&FilterFunctionWrapper!
(me, \"" ~ f ~ "\", filters)"));
else if (routeType == RouteType.Put)
(cast(URLRouter)urlRouter).put(route, mixin("&FilterFunctionWrapper!
(me, \"" ~ f ~ "\", filters)"));
else if (routeType == RouteType.Delete)
(cast(URLRouter)urlRouter).delete_(route, mixin("&FilterFunctionWrapper!
(me, \"" ~ f ~ "\", filters)"));
else if (routeType == RouteType.Any)
(cast(URLRouter)urlRouter).any(route, mixin("&FilterFunctionWrapper!
(me, \"" ~ f ~ "\", filters)"));
}
}
}
void handleRouteGroups(alias symbol)(ref bool isLastGroupId, ref string route, ref RouteFilter[] filters) {
foreach (UDA; __traits(getAttributes, symbol)) {
static if (is(typeof(UDA) == RouteGroup)) {
// lets just hope this operates in
isLastGroupId = false;
if (UDA.routeBase != "")
route ~= UDA.routeBase;
if (UDA.filterFunction !is null)
filters ~= UDA.filterFunction;
} else static if (is(typeof(UDA) == RouteGroupId)) {
// lets just hope this operates in
isLastGroupId = true;
if (UDA.routeName != "")
route ~= "/" ~ UDA.routeName ~ "s/:" ~ UDA.routeName ~ "Id";
} else static if (is(typeof(UDA) == RouteGroupIds)) {
// lets just hope this operates in
isLastGroupId = true;
foreach(routeName; UDA.routeNames) {
if (routeName != "")
route ~= "/" ~ routeName ~ "s/:" ~ routeName ~ "Id";
}
}
}
}
}
}
/**
* Action router is like the normal router except it has a bunch of name associations
* for functions as well.
* Provides name association for:
* index -> GET /
* show -> GET /:id
* init -> GET /new
* create -> POST /
* update -> PUT /:id
* delete -> DELETE /:id
*
* Built based off of the issue below:
* https://github.com/rejectedsoftware/vibe.d/issues/444#issuecomment-31112692
*/
abstract class ActionRouter : Routes {
this(this T)() {
this(cast(T)this);
}
this(T)(T me) {
super(me);
string routeOutput = "";
foreach(f; __traits(allMembers, T)) {
mixin("alias symbol = me." ~ f ~ ";");
static if (__traits(compiles, {
mixin("me." ~ f ~ "(cast(HTTPServerRequest)null, cast(HTTPServerResponse)null);"); } )) {
RouteType routeType = cast(RouteType)"";
string route = "";
RouteFilter[] filters;
bool isLastGroupId = false;
handleRouteGroups!(symbol)(isLastGroupId, route, filters);
foreach (UDA; __traits(getAttributes, symbol)) {
static if (is(typeof(UDA) == RouteFunction)) {
routeType = UDA.type;
if (!isLastGroupId) {
route ~= UDA.route;
}
if (UDA.filterFunction !is null)
filters ~= UDA.filterFunction;
} else {
switch(f) {
case "index":
routeType = RouteType.Get;
if (route == "")
route = "/";
break;
case "show":
routeType = RouteType.Get;
route ~= "/:id";
break;
case "init":
routeType = RouteType.Get;
route ~= "/new";
break;
case "create":
routeType = RouteType.Post;
if (route == "")
route = "/";
break;
case "update":
routeType = RouteType.Put;
route ~= "/:id";
break;
case "delete":
routeType = RouteType.Delete;
route ~= "/:id";
break;
default:
break;
}
}
}
handleRouteFunction!(T, f)(me, routeType, route, filters, routeOutput);
}
}
if (logFile !is null)
append(logFile, routeOutput);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment