native.js 3.57 KB
/*jshint node:true*/
"use strict";


/*!
 * Root handlers
 */


var nativeGet = nativePropertyGet;
var nativePost = nativePropertyPost;


/*!
 * Property handlers
 */


function nativePropertyHook(req, next) {
	var path = req.params["*"].split("/");
	var obj = req.nativeRoot;
	var parent;
	var property;

	while (path.length) {
		property = path.shift();

		if (!(property in obj)) {
			delete req.nativeTarget;
			return next();
		}

		parent = obj;
		obj = obj[property];
	}

	req.nativeParent = parent;
	req.nativeProperty = property;
	req.nativeTarget = obj;

	next();
}


function nativeArrayCount(req, cb) {
	if (!("nativeTarget" in req)) {
		return cb.notFound();
	}

	cb(null, req.nativeTarget.length);
}


function nativeArrayList(req, offset, limit, cb) {
	if (!("nativeTarget" in req)) {
		return cb.notFound();
	}

	var target = req.nativeTarget;
	var arr;

	if (limit > 0) {
		arr = target.slice(offset, offset + limit);
	} else {
		arr = target.slice(offset);
	}

	cb(null, arr);
}


function nativeObjectCount(req, cb) {
	cb(null, Object.keys(req.nativeTarget).length);
}


function nativeObjectList(req, offset, limit, cb) {
	var keys = Object.keys(req.nativeTarget);

	if (limit > 0) {
		keys = keys.slice(offset, offset + limit);
	} else {
		keys = keys.slice(offset);
	}

	cb(null, keys);
}


function nativePropertyGet(req, cb) {
	if (!("nativeTarget" in req)) {
		return cb.notFound;
	}

	var target = req.nativeTarget;

	if (Array.isArray(target)) {
		if (req.options.rawArrays) {
			cb(null, target);
		} else {
			cb.list(nativeArrayCount, nativeArrayList);
		}
	} else if (typeof target === "object") {
		if (req.options.objectCollections) {
			cb.list(nativeObjectCount, nativeObjectList);
		} else {
			cb(null, target);
		}
	} else {
		cb(null, target);
	}
}


function nativePropertyPut(req, isPatch, cb) {
	var parent = req.nativeParent;
	var property = req.nativeProperty;
	var target = req.nativeTarget;

	var value = req.body;

	if ("_value" in value) {
		value = value._value;
	}

	if (isPatch) {
		if (typeof target !== "object") {
			return cb.methodNotAllowed();
		}

		Object.keys(req.body).forEach(function(key) {
			target[key] = value[key];
		});
	} else {
		parent[property] = value;
	}

	cb();
}


function nativePropertyPost(req, cb) {
	if (!("nativeTarget" in req)) {
		return cb.notFound();
	}

	var target = req.nativeTarget;
	var created;

	if (Array.isArray(target)) {
		var value = req.body;

		if ("_value" in value) {
			value = value._value;
		}

		created = value;

		target.push(value);
	} else if (typeof target === "object") {
		if (!("_key" in req.body) || !("_value" in req.body) || typeof req.body._key !== "string") {
			return cb.badRequest();
		}

		created = target[req.body._key] = req.body._value;
	} else {
		cb.methodNotAllowed();
	}

	if (req.options.postResponse) {
		cb(null, created);
	} else {
		cb.created();
	}
}


function nativePropertyDelete(req, cb) {
	if (!("nativeTarget" in req)) {
		return cb.notFound();
	}

	var parent = req.nativeParent;
	var property = req.nativeProperty;

	if (Array.isArray(parent) && !req.options.sparseArrays) {
		parent.splice(property, 1);
	} else {
		delete parent[property];
	}

	cb();
}




/*!
 * Native resource definition helper
 */


module.exports = function(name, obj) {
	var resource = this.sub(name);

	resource
		.hook(function nativeHook(req, next) {
			req.nativeRoot = req.nativeTarget = obj;
			next();
		})
		.get(nativeGet)
		.post(nativePost);

	resource.sub("*")
		.hook(nativePropertyHook)
		.get(nativePropertyGet)
		.put(nativePropertyPut)
		.post(nativePropertyPost)
		.del(nativePropertyDelete);

	return resource;
};