On Sun, 12 Jul 2015 09:18:21 GMT, Taylor Gronka wrote:

Hi,

I am porting a personal project to vibe.D which is rather large. I have many routes, each with individual route functions. I've had the darndest time trying to split the routes and their functions into separate files.

Right now I am using the basic layout of vibelog:
https://github.com/rejectedsoftware/vibelog/blob/master/source/vibelog/vibelog.d

I have the route functions in separate files, and I create a View class which is full of utility functions that take up a lot of space and don't change much. I like this setup.

The issue I'm running into is - referring to vibelog - how can I pass the database connection m_db to route functions located outside of class VibeLog?

My current solution is to wrap each set of route functions in classes, and inherit-daisy-chain them down to my View class into class VibeLog : View, with View inheriting from UserRoutes, which inherits from MapRoutes, etc.. This looks bad.

Another good option might be if I could pass this vibelog to router.get, such that I could access vibelog.m_db.

Perhaps I could initialize m_db outside of class VibeLog and import it elsewhere.. but I don't like this, and I feel like this might interfere with fiber/thread safety.

Thanks,

One way to pass additional arguments is to construct a delegate (using a lambda in this case):

import vibe.vibe;

shared static this()
{
  auto r = new URLRouter;
  r.get("/", (req, res) => hello(req, res, "42"));

  auto settings = new HTTPServerSettings;
  settings.port = 8080;
  settings.bindAddresses = ["::1", "127.0.0.1"];
  listenHTTP(settings, r);
}

void hello(HTTPServerRequest req, HTTPServerResponse res, string answer)
{
  res.writeBody("Answer: "~answer);
}

There is also the partial function in Phobos that should work for this, too.

If you know that your instance doesn't change over time (or per request), you can just use a global variable to store and access it (global variables are thread-local by default, so there will be no race-conditions). If you have data that changes per request, you can also make use of a global TaskLocal variable, which will be local to the current request (because each request is processed in a separate task):

TaskLocal!string s_answer;

shared static this() {
  // ...

  // for any request, first set the answer
  r.any("*", &setupAnswer);
  // then continue with the normal routes
  r.get("/", &hello);

  // ...
}

void setupAnswer(HTTPServerRequest req, HTTPServerResponse res)
{
  s_answer = "42";
}

void hello(HTTPServerRequest req, HTTPServerResponse res)
{
  res.writeBody("Answer: "~answer);
}

Personally I'm nowadays creating all of my routes implicitly with the vibe.web.web or vibe.web.rest interface generators (see http://vibed.org/docs#web for a short introduction). They avoid the boilerplate code needed to process the various request parameters, including extraction, conversion and validation, and can make the code a lot more concise and readable (as well as less error prone). They are based on a class structure, though, so in that case you'd usually also just use class members:

shared static this() {
  // ...
  r.registerWebInterface(new WebInterface("42"));
  // ...
}

class WebInterface {
  string answer;
  this(string answer) { this.answer = answer; }
  // implicit "GET /" route
  void get(HTTPServerResponse res) { res.writeBody("Answer: "~answer); }
}