You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openNG/lib/riot/state-router.js

155 lines
3.4 KiB
JavaScript

8 years ago
const Promise = require("bluebird");
const pathToRegexp = require("path-to-regexp");
const url = require("url");
const xtend = require("xtend");
const defaultValue = require("default-value");
const objectToFormData = require("object-to-formdata");
const formDataToObject = require("../formdata-to-object");
8 years ago
module.exports = function() {
let routes = [];
function addRoute(method, path, handler) {
// Mutable arguments? WTF.
8 years ago
let keys = [];
let regex = pathToRegexp(path, keys);
8 years ago
routes.push({ method, path, regex, keys, handler });
}
function getRoute(method, path) {
8 years ago
let matches;
let matchingRoute = routes.find((route) => route.method === method && (matches = route.regex.exec(path)));
if (matchingRoute == null) {
throw new Error("No matching routes found");
} else {
8 years ago
let params = {};
8 years ago
matchingRoute.keys.forEach((key, i) => {
params[key] = matches[i + 1];
});
8 years ago
return {
handler: matchingRoute.handler,
params: params
}
}
}
function handle(method, uri, data) {
8 years ago
return Promise.try(() => {
// FIXME: Support relative paths?
8 years ago
let {path, query} = url.parse(uri, true);
let route = getRoute(method, path);
let tasks = [];
let body;
if (data instanceof FormData) {
body = formDataToObject(data);
} else {
body = data;
}
8 years ago
let req = {
path: path,
query: query,
body: data,
params: route.params,
8 years ago
pass: function(options = {}) {
return Promise.try(() => {
return window.fetch(uri, Object.assign({
method: method,
credentials: true,
body: objectToFormData(this.body)
}, options));
}).then((response) => {
if (!response.ok) {
// FIXME: Is this what we want?
throw new Error(`Got a non-200 response: ${response.status}`, {response: response});
} else {
return Promise.try(() => {
return response.json();
}).then((json) => {
return {
status: response.status,
body: json
}
});
}
});
// FIXME: window.fetch passthrough
},
8 years ago
passRender: function(viewName, options = {}) {
return Promise.try(() => {
return this.pass(options.requestOptions);
8 years ago
}).then((response) => {
let locals = defaultValue(options.locals, {});
let combinedLocals = xtend(locals, response.body);
res.render(viewName, combinedLocals, options.renderOptions);
});
}
8 years ago
}
8 years ago
let res = {
render: function(viewName, locals = {}, options = {}) {
tasks.push({
type: "render",
8 years ago
viewName, locals, options
});
},
8 years ago
open: function(path, options = {}) {
tasks.push({
type: "open",
8 years ago
path, options
});
},
8 years ago
close: function(options = {}) {
tasks.push({
type: "close",
8 years ago
options
});
},
8 years ago
notify: function(message, options = {}) {
tasks.push({
type: "notify",
8 years ago
message, options
});
},
8 years ago
error: function(error, context = {}) {
tasks.push({
type: "error",
8 years ago
error, context
});
}
8 years ago
}
8 years ago
return Promise.try(() => {
return route.handler(req, res);
8 years ago
}).then((result) => {
return {
result: result,
actions: tasks
8 years ago
}
});
});
}
8 years ago
let api = {
get: addRoute.bind(api, "get"),
post: addRoute.bind(api, "post"),
put: addRoute.bind(api, "put"),
delete: addRoute.bind(api, "delete"),
head: addRoute.bind(api, "head"),
patch: addRoute.bind(api, "patch"),
addRoute: addRoute,
handle: handle
8 years ago
}
return api;
}