rejectedsoftware/vibe.d — pull request #349 by Dicebot (2013-10-18 15:51:57+00:00)
Title: Add support for attribute functions to REST interfaces

Actual change necessary for `funcattr` support is minimal (e6a98a9), which indicates that current `vibe.utils.meta.funcattr` design is quite solid. Some defects in original pull have been found and fixed (361005b)

However, there are also plenty of somewhat unrelated changes in `vibe.http.rest` as I couldn't pass by by without yet another attempt to make it more readable:

1) adjusted to http://vibed.org/style-guide
2) converted large example blocks in comments into documented unittests (which test nothing but are at least guaranteed to compile now)
3) reduced those examples as there is now a much more complete one in test suite
4) implemented a standard way for path formatting (always single "/" between nodes, no trailing "/" unless it is a nested interface index) and covered it with unittests

Each commit should be compilable on its own and can be reviewed independently.

Example of resulting API copied from test suite:
```D
@rootPathFromName
interface Example5API
{
import vibe.utils.meta.funcattr;

@before!authenticate("user") @after!addBrackets()
string getSecret(int num, User user);
}

User authenticate(HTTPServerRequest req, HTTPServerResponse res)
{
return User("admin", true);
}

struct User
{
string name;
bool authorized;
}

string addBrackets(string result, HTTPServerRequest, HTTPServerResponse)
{
return "{" ~ result ~ "}";
}

class Example5 : Example5API
{
string getSecret(int num, User user)
{
import std.conv : to;
import std.string : format;

if (!user.authorized)
return "";

return format("secret #%s for %s", num, user.name);
}
}

static this()
{
auto routes = new URLRouter();
registerRestInterface(routes, new Example5());
auto settings = new HTTPServerSettings();
settings.port = 8080;
settings.bindAddresses = ["::1", "127.0.0.1"];
listenHTTP(settings, routes);

setTimer(dur!"seconds"(1), {
scope(exit)
exitEventLoop(true);
auto api = new RestInterfaceClient!Example5API("http://127.0.0.1:8080");
auto secret = api.getSecret(42, User.init);
assert(secret == "{secret #42 for admin}");
}
}
```