Merge pull request #246 from hybridgroup/add/lodash

Add Lo-Dash
This commit is contained in:
Ron Evans 2014-12-17 16:07:51 -08:00
commit 919a54540f
10 changed files with 167 additions and 195 deletions

View File

@ -9,7 +9,8 @@
"use strict";
var Basestar = require("./basestar"),
Utils = require("./utils");
Utils = require("./utils"),
_ = require("./lodash");
// Public: Creates a new Adaptor
//
@ -33,11 +34,13 @@ var Adaptor = module.exports = function Adaptor(opts) {
// misc. details provided in args hash
this.details = {};
for (var opt in opts) {
if (["robot", "name", "adaptor"].indexOf(opt) < 0) {
this.details[opt] = opts[opt];
_.forEach(opts, function(opt, name) {
if (_.include(["robot", "name", "adaptor"], opt)) {
return;
}
}
this.details[name] = opt;
}, this);
};
Utils.subclass(Adaptor, Basestar);

View File

@ -9,7 +9,8 @@
"use strict";
var Registry = require("./registry"),
Config = require("./config");
Config = require("./config"),
_ = require("./lodash");
var testMode = function() {
return process.env.NODE_ENV === "test" && Config.testMode;
@ -25,8 +26,7 @@ var testMode = function() {
//
// Returns the newly set-up connection
module.exports = function Connection(opts) {
var module,
prop;
var module;
opts = opts || {};
@ -43,24 +43,24 @@ module.exports = function Connection(opts) {
var adaptor = module.adaptor(opts);
for (prop in adaptor) {
if (~["constructor"].indexOf(prop)) {
continue;
_.forEach(adaptor, function(prop, name) {
if (name === "constructor") {
return;
}
if (typeof adaptor[prop] === "function") {
adaptor[prop] = adaptor[prop].bind(adaptor);
if (_.isFunction(prop)) {
adaptor[name] = prop.bind(adaptor);
}
}
});
if (testMode()) {
var testAdaptor = Registry.findByAdaptor("test").adaptor(opts);
for (prop in adaptor) {
if (typeof adaptor[prop] === "function" && !testAdaptor[prop]) {
testAdaptor[prop] = function() { return true; };
_.forEach(adaptor, function(prop, name) {
if (_.isFunction(prop) && !testAdaptor[name]) {
testAdaptor[name] = function() { return true; };
}
}
});
return testAdaptor;
}

View File

@ -13,7 +13,8 @@ var Async = require("async");
var Logger = require("./logger"),
Robot = require("./robot"),
Config = require("./config"),
Utils = require("./utils");
Utils = require("./utils"),
_ = require("./lodash");
var Cylon = module.exports = {
Logger: Logger,
@ -83,17 +84,13 @@ Cylon.api = function api(opts) {
//
// Returns nothing
Cylon.start = function start() {
var starters = [];
for (var bot in this.robots) {
starters.push(this.robots[bot].start);
}
var starters = _.map(this.robots, "start");
Async.parallel(starters, function() {
var mode = Utils.fetch(Config, "workMode", "async");
if (mode === "sync") {
for (var bot in this.robots) {
this.robots[bot].startWork();
}
_.invoke(this.robots, "startWork");
}
}.bind(this));
};
@ -104,15 +101,13 @@ Cylon.start = function start() {
//
// Returns the current config
Cylon.config = function(opts) {
var loggingChanged = (opts.logging && Config.logging !== opts.logging);
var logChanges = (opts.logging && !_.isEqual(Config.logging, opts.logging));
if (opts && typeof(opts) === "object" && !Array.isArray(opts)) {
for (var o in opts) {
Config[o] = opts[o];
}
if (_.isObject(opts) && !_.isArray(opts)) {
Config = _.merge(Config, opts);
}
if (loggingChanged) {
if (logChanges) {
Logger.setup();
}
@ -126,30 +121,20 @@ Cylon.config = function(opts) {
// Returns nothing
Cylon.halt = function halt(callback) {
callback = callback || function() {};
var fns = _.map(this.robots, "halt");
// if robots can"t shut down quickly enough, forcefully self-terminate
var timeout = Config.haltTimeout || 3000;
Utils.after(timeout, callback);
var fns = [];
for (var bot in this.robots) {
var robot = this.robots[bot];
fns.push(robot.halt.bind(robot));
}
Async.parallel(fns, callback);
};
Cylon.toJSON = function() {
var robots = [];
for (var bot in this.robots) {
robots.push(this.robots[bot].toJSON());
}
return {
robots: robots,
commands: Object.keys(this.commands)
robots: _.invoke(this.robots, "toJSON"),
commands: _.keys(this.commands)
};
};

View File

@ -9,7 +9,8 @@
"use strict";
var Registry = require("./registry"),
Config = require("./config");
Config = require("./config"),
_ = require("./lodash");
var testMode = function() {
return process.env.NODE_ENV === "test" && Config.testMode;
@ -26,8 +27,7 @@ var testMode = function() {
//
// Returns a new Device
module.exports = function Device(opts) {
var module,
prop;
var module;
if (opts.module) {
module = Registry.register(opts.module);
@ -44,24 +44,24 @@ module.exports = function Device(opts) {
var driver = module.driver(opts);
for (prop in driver) {
if (~["constructor"].indexOf(prop)) {
continue;
_.forEach(driver, function(prop, name) {
if (name === "constructor") {
return;
}
if (typeof driver[prop] === "function") {
driver[prop] = driver[prop].bind(driver);
if (_.isFunction(prop)) {
driver[name] = prop.bind(driver);
}
}
});
if (testMode()) {
var testDriver = Registry.findByDriver("test").driver(opts);
for (prop in driver) {
if (typeof driver[prop] === "function" && !testDriver[prop]) {
testDriver[prop] = function() { return true; };
_.forEach(driver, function(prop, name) {
if (_.isFunction(prop) && !testDriver[name]) {
testDriver[name] = function() { return true; };
}
}
});
return testDriver;
}

View File

@ -9,7 +9,8 @@
"use strict";
var Basestar = require("./basestar"),
Utils = require("./utils");
Utils = require("./utils"),
_ = require("./lodash");
// Public: Creates a new Driver
//
@ -34,11 +35,13 @@ var Driver = module.exports = function Driver(opts) {
this.details = {};
for (var opt in opts) {
if (["robot", "name", "connection", "driver"].indexOf(opt) < 0) {
this.details[opt] = opts[opt];
_.forEach(opts, function(opt, name) {
if (_.include(["robot", "name", "connection", "driver"], opt)) {
return;
}
}
this.details[name] = opt;
}, this);
};
Utils.subclass(Driver, Basestar);
@ -50,9 +53,7 @@ Driver.prototype.setupCommands = function(commands, proxy) {
this.proxyMethods(commands, proxy, this);
for (var i = 0; i < commands.length; i++) {
var command = commands[i];
_.forEach(commands, function(command) {
var snake_case = command.replace(/[A-Z]+/g, function(match) {
if (match.length > 1) {
match = match.replace(/[A-Z]$/, function(m) {
@ -64,7 +65,7 @@ Driver.prototype.setupCommands = function(commands, proxy) {
}).replace(/^_/, "");
this.commands[snake_case] = this[command];
}
}, this);
};
Driver.prototype.toJSON = function() {
@ -72,7 +73,7 @@ Driver.prototype.toJSON = function() {
name: this.name,
driver: this.constructor.name || this.name,
connection: this.connection.name,
commands: Object.keys(this.commands),
commands: _.keys(this.commands),
details: this.details
};
};

21
lib/lodash.js Normal file
View File

@ -0,0 +1,21 @@
"use strict";
// loader for Lo-Dash.
//
// will load the compatability version on JS implementations with less support
// for ES5 features (Tessel, etc)
var isTessel = function() {
try {
require("tessel");
return true;
} catch (e) {
return false;
}
};
if (isTessel()) {
module.exports = require("lodash/dist/lodash.compat");
} else {
module.exports = require("lodash");
}

View File

@ -12,7 +12,8 @@ var initConnection = require("./connection"),
initDevice = require("./device"),
Logger = require("./logger"),
Utils = require("./utils"),
Config = require("./config");
Config = require("./config"),
_ = require("./lodash");
var Async = require("async"),
EventEmitter = require("events").EventEmitter;
@ -52,9 +53,7 @@ var Robot = module.exports = function Robot(opts) {
"initConnections"
];
methods.forEach(function(method) {
this[method] = this[method].bind(this);
}, this);
_.bindAll(this, methods);
this.name = opts.name || Robot.randomName();
this.connections = {};
@ -72,35 +71,27 @@ var Robot = module.exports = function Robot(opts) {
this.initConnections(opts);
this.initDevices(opts);
for (var n in opts) {
var opt = opts[n];
if (this[n] !== undefined) {
continue;
_.forEach(opts, function(opt, name) {
if (this[name] !== undefined) {
return;
}
this[n] = opt;
this[name] = opt;
if (typeof opt === "function" && opts.commands == null) {
this.commands[n] = opt;
if (opts.commands == null && _.isFunction(opt)) {
this.commands[name] = opt;
}
}
}, this);
if (opts.commands) {
var cmds = opts.commands;
var cmds = _.result(opts, "commands");
if (typeof cmds === "object") {
if (_.isObject(cmds) && !_.isArray(cmds)) {
this.commands = cmds;
}
if (typeof cmds === "function") {
var result = cmds.call(this, this);
if (typeof result === "object" && !Array.isArray(result)) {
this.commands = result;
} else {
throw new Error("#commands function must return an object");
}
} else {
var err = "#commands must be an object ";
err += "or a function that returns an object";
throw new Error(err);
}
}
@ -125,23 +116,11 @@ Robot.randomName = function() {
//
// Returns an Object containing Robot data
Robot.prototype.toJSON = function() {
var devices = [],
connections = [],
n;
for (n in this.connections) {
connections.push(this.connections[n].toJSON());
}
for (n in this.devices) {
devices.push(this.devices[n].toJSON());
}
return {
name: this.name,
connections: connections,
devices: devices,
commands: Object.keys(this.commands)
connections: _.invoke(this.connections, "toJSON"),
devices: _.invoke(this.devices, "toJSON"),
commands: _.keys(this.commands)
};
};
@ -170,11 +149,9 @@ Robot.prototype.connection = function(name, conn) {
//
// Returns initialized connections
Robot.prototype.initConnections = function(opts) {
Logger.info("Initializing connections.");
var str;
var isArray = Array.isArray;
Logger.info("Initializing connections.");
if (opts.connection == null && opts.connections == null) {
return this.connections;
@ -190,20 +167,17 @@ Robot.prototype.initConnections = function(opts) {
return this.connections;
}
if (typeof opts.connections === "object" && !isArray(opts.connections)) {
for (var name in opts.connections) {
this.connection(name, opts.connections[name]);
if (_.isObject(opts.connections)) {
if (_.isArray(opts.connections)) {
str = "Specifying connections as an array is deprecated. ";
str += "It will be removed in 1.0.0.";
Logger.warn(str);
}
}
if (isArray(opts.connections)) {
str = "Specifying connections as an array is deprecated. ";
str += "It will be removed in 1.0.0.";
Logger.warn(str);
opts.connections.forEach(function(conn) {
this.connection(conn.name, conn);
_.forEach(opts.connections, function(conn, key) {
var name = _.isString(key) ? key : conn.name;
this.connection(name, conn);
}, this);
}
@ -232,12 +206,9 @@ Robot.prototype.device = function(name, device) {
process.emit("SIGINT");
}
device.connection = this.connections[device.connection];
device.connection = this.connections[device.connection];
} else {
for (var conn in this.connections) {
device.connection = this.connections[conn];
break;
}
device.connection = _.first(_.values(this.connections));
}
this.devices[device.name] = initDevice(device);
@ -251,8 +222,7 @@ Robot.prototype.device = function(name, device) {
//
// Returns initialized devices
Robot.prototype.initDevices = function(opts) {
var isArray = Array.isArray,
str;
var str;
Logger.info("Initializing devices.");
@ -274,18 +244,17 @@ Robot.prototype.initDevices = function(opts) {
return this.devices;
}
if (typeof opts.devices === "object" && !isArray(opts.devices)) {
for (var name in opts.devices) {
this.device(name, opts.devices[name]);
}
}
if (_.isObject(opts.devices)) {
if (_.isArray(opts.devices)) {
str = "Specifying devices as an array is deprecated. ";
str += "It will be removed in 1.0.0.";
if (isArray(opts.devices)) {
str = "Specifying devices as an array is deprecated.";
str += "It will be removed in 1.0.0.";
Logger.warn(str);
opts.devices.forEach(function(device) {
this.device(device.name, device);
Logger.warn(str);
}
_.forEach(opts.devices, function(device, key) {
var name = _.isString(key) ? key : device.name;
this.device(name, device);
}, this);
}
@ -304,25 +273,29 @@ Robot.prototype.start = function(callback) {
var mode = Utils.fetch(Config, "workMode", "async");
var start = function() {
if (mode === "async") {
this.startWork();
}
}.bind(this);
Async.series([
this.startConnections,
this.startDevices,
function(callback) {
if (mode === "async") {
this.startWork();
}
callback(null, true);
}.bind(this)
start
], function(err, results) {
if (!!err) {
Logger.fatal("An error occured while trying to start the robot:");
Logger.fatal(err);
if (typeof(this.error) === "function") {
this.error.call(this, err);
}
this.emit("error", err);
}
if (typeof(callback) === "function") {
if (_.isFunction(callback)) {
callback(err, results);
}
}.bind(this));
@ -351,11 +324,11 @@ Robot.prototype.startWork = function() {
Robot.prototype.startConnections = function(callback) {
Logger.info("Starting connections.");
var starters = Object.keys(this.connections).map(function(n) {
var conn = this[n] = this.connections[n];
var starters = _.map(this.connections, function(conn, name) {
this[name] = conn;
return function(cb) {
var str = "Starting connection '" + n + "'";
var str = "Starting connection '" + name + "'";
if (conn.host) {
str += " on host " + conn.host;
@ -379,11 +352,11 @@ Robot.prototype.startConnections = function(callback) {
Robot.prototype.startDevices = function(callback) {
Logger.info("Starting devices.");
var starters = Object.keys(this.devices).map(function(n) {
var device = this[n] = this.devices[n];
var starters = _.map(this.devices, function(device, name) {
this[name] = device;
return function(cb) {
var str = "Starting device '" + n + "'";
var str = "Starting device '" + name + "'";
if (device.pin) {
str += " on pin " + device.pin;
@ -407,19 +380,12 @@ Robot.prototype.startDevices = function(callback) {
Robot.prototype.halt = function(callback) {
callback = callback || function() {};
var fns = Object.keys(this.devices).map(function(d) {
var device = this.devices[d];
return device.halt.bind(device);
}, this);
var devices = _.map(this.devices, "halt");
var connections = _.map(this.connections, "disconnect");
Async.parallel(fns, function() {
var fns = Object.keys(this.connections).map(function(c) {
var connection = this.connections[c];
return connection.disconnect.bind(connection);
}, this);
Async.parallel(fns, callback);
}.bind(this));
Async.parallel(devices, function() {
Async.parallel(connections, callback);
});
this.running = false;
};

View File

@ -8,6 +8,8 @@
"use strict";
var _ = require("./lodash");
var addCoreExtensions = function addCoreExtensions() {
var max = Math.max,
min = Math.min;
@ -181,11 +183,9 @@ var Utils = module.exports = {
this.constructor = child;
};
for (var key in parent) {
if (Object.hasOwnProperty.call(parent, key)) {
child[key] = parent[key];
}
}
_.forOwn(parent, function(prop, key) {
child[key] = prop;
});
Ctor.prototype = parent.prototype;
child.prototype = new Ctor();
@ -194,11 +194,11 @@ var Utils = module.exports = {
},
proxyFunctions: function proxyFunctions(source, target) {
for (var opt in source) {
if (!target[opt] && typeof source[opt] === "function") {
target[opt] = source[opt].bind(source);
_.forEach(source, function(prop, key) {
if (_.isFunction(prop) && !target[key]) {
target[key] = prop.bind(source);
}
}
});
},
// Public: Proxies a list of methods from one object to another. It will not
@ -216,25 +216,20 @@ var Utils = module.exports = {
base = this;
}
if (force == null) {
force = false;
}
force = force || false;
var proxy = function(method) {
return base[method] = function() {
return target[method].apply(target, arguments);
};
};
for (var i = 0; i < methods.length; i++) {
var method = methods[i];
if (!force && typeof(base[method]) === "function") {
continue;
_.forEach(methods, function(method) {
if (!_.isFunction(target[method])) {
return;
}
proxy(method);
}
if (_.isFunction(base[method]) && !force) {
return;
}
base[method] = target[method].bind(target);
});
return base;
},

View File

@ -46,6 +46,7 @@
"async": "0.9.0",
"express": "4.10.5",
"body-parser": "1.10.0",
"robeaux": "0.3.0"
"robeaux": "0.3.0",
"lodash": "2.4.1"
}
}

View File

@ -139,7 +139,7 @@ describe("Robot", function() {
it("throws an error", function() {
expect(fn).to.throw(
Error,
"#commands function must return an object"
"#commands must be an object or a function that returns an object"
);
});
});