From 34ed6cea0204e1e9d3f5cedc7f8a4fb26ec22aa7 Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Wed, 17 Dec 2014 13:00:59 -0800 Subject: [PATCH 1/3] Add Lo-Dash + loader --- lib/lodash.js | 21 +++++++++++++++++++++ package.json | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 lib/lodash.js diff --git a/lib/lodash.js b/lib/lodash.js new file mode 100644 index 0000000..72844f8 --- /dev/null +++ b/lib/lodash.js @@ -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"); +} diff --git a/package.json b/package.json index 141fcca..8abfa04 100644 --- a/package.json +++ b/package.json @@ -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" } } From 6cbf2db122d7dc4f37add2d07f56a40e225f308d Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Wed, 17 Dec 2014 14:42:34 -0800 Subject: [PATCH 2/3] Lodashify --- lib/adaptor.js | 13 ++-- lib/connection.js | 26 ++++---- lib/cylon.js | 43 +++++-------- lib/device.js | 26 ++++---- lib/driver.js | 21 +++---- lib/robot.js | 150 +++++++++++++++++++--------------------------- lib/utils.js | 45 +++++++------- 7 files changed, 140 insertions(+), 184 deletions(-) diff --git a/lib/adaptor.js b/lib/adaptor.js index b0de545..023ef55 100644 --- a/lib/adaptor.js +++ b/lib/adaptor.js @@ -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); diff --git a/lib/connection.js b/lib/connection.js index dc28b1f..3d987af 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -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; } diff --git a/lib/cylon.js b/lib/cylon.js index b3dcb10..0aea4cc 100644 --- a/lib/cylon.js +++ b/lib/cylon.js @@ -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) }; }; diff --git a/lib/device.js b/lib/device.js index b4c6f2d..ee1c974 100644 --- a/lib/device.js +++ b/lib/device.js @@ -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; } diff --git a/lib/driver.js b/lib/driver.js index 9c4ce3c..8c74a82 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -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 }; }; diff --git a/lib/robot.js b/lib/robot.js index 7c79671..4dd9a29 100644 --- a/lib/robot.js +++ b/lib/robot.js @@ -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,31 +71,29 @@ 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; - if (typeof cmds === "object") { + if (_.isObject(cmds)) { this.commands = cmds; } - if (typeof cmds === "function") { + if (_.isFunction(cmds)) { var result = cmds.call(this, this); - if (typeof result === "object" && !Array.isArray(result)) { + if (_.isObject(result) && !_.isArray(result)) { this.commands = result; } else { throw new Error("#commands function must return an object"); @@ -125,23 +122,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 +155,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 +173,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 +212,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 +228,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 +250,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 +279,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 +330,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 +358,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 +386,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; }; diff --git a/lib/utils.js b/lib/utils.js index 61ada99..eec6660 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -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; }, From c9e16fe8c220362d2f990d2e68f934f732fa64ef Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Wed, 17 Dec 2014 15:07:03 -0800 Subject: [PATCH 3/3] Use _.result to get commands from options. --- lib/robot.js | 18 ++++++------------ spec/lib/robot.spec.js | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/robot.js b/lib/robot.js index 4dd9a29..b839b43 100644 --- a/lib/robot.js +++ b/lib/robot.js @@ -84,20 +84,14 @@ var Robot = module.exports = function Robot(opts) { }, this); if (opts.commands) { - var cmds = opts.commands; + var cmds = _.result(opts, "commands"); - if (_.isObject(cmds)) { + if (_.isObject(cmds) && !_.isArray(cmds)) { this.commands = cmds; - } - - if (_.isFunction(cmds)) { - var result = cmds.call(this, this); - - if (_.isObject(result) && !_.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); } } diff --git a/spec/lib/robot.spec.js b/spec/lib/robot.spec.js index 05a09b1..9f59862 100644 --- a/spec/lib/robot.spec.js +++ b/spec/lib/robot.spec.js @@ -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" ); }); });