diff --git a/lib/adaptor.js b/lib/adaptor.js index 8510d34..91e536e 100644 --- a/lib/adaptor.js +++ b/lib/adaptor.js @@ -9,8 +9,7 @@ "use strict"; var Basestar = require("./basestar"), - Utils = require("./utils"), - _ = require("./lodash"); + Utils = require("./utils"); // Public: Creates a new Adaptor // @@ -34,13 +33,14 @@ var Adaptor = module.exports = function Adaptor(opts) { // misc. details provided in args hash this.details = {}; - _.forEach(opts, function(opt, name) { - if (_.include(["robot", "name", "adaptor", "events"], name)) { - return; - } + for (var name in opts) { + var opt = opts[name], + banned = ["robot", "name", "adaptor", "events"]; - this.details[name] = opt; - }, this); + if (!~banned.indexOf(name)) { + this.details[name] = opt; + } + } }; Utils.subclass(Adaptor, Basestar); diff --git a/lib/cylon.js b/lib/cylon.js index ac2a680..b7f78c3 100644 --- a/lib/cylon.js +++ b/lib/cylon.js @@ -13,8 +13,7 @@ var Async = require("async"); var Logger = require("./logger"), Robot = require("./robot"), Config = require("./config"), - Utils = require("./utils"), - _ = require("./lodash"); + Utils = require("./utils"); var EventEmitter = require("events").EventEmitter; @@ -74,15 +73,19 @@ Cylon.robot = function robot(opts) { // // Returns nothing Cylon.api = function api(Server, opts) { + var isObject = function(arg) { return typeof arg === "object"; }, + isFunction = function(arg) { return typeof arg === "function"; }, + isString = function(arg) { return typeof arg === "string"; }; + // if only passed options (or nothing), assume HTTP server - if (Server == null || _.isObject(Server) && !_.isFunction(Server)) { + if (Server == null || isObject(Server) && !isFunction(Server)) { opts = Server; Server = "http"; } opts = opts || {}; - if (_.isString(Server)) { + if (isString(Server)) { var req = "cylon-api-" + Server; try { @@ -103,7 +106,9 @@ Cylon.api = function api(Server, opts) { ]; } - _.each(messages, function(str) { Logger.error(str); }); + messages.forEach(function(str) { + Logger.error(str); + }); return; } else { throw e; @@ -121,13 +126,22 @@ Cylon.api = function api(Server, opts) { // // Returns nothing Cylon.start = function start() { - var starters = _.map(this.robots, "start"); + var starters = [], + name; + + for (name in this.robots) { + var bot = this.robots[name]; + starters.push(bot.start.bind(bot)); + } Async.parallel(starters, function() { var mode = Utils.fetch(Config, "workMode", "async"); if (mode === "sync") { - _.invoke(this.robots, "startWork"); + for (name in this.robots) { + var bot = this.robots[name]; + bot.startWork.call(bot); + } } }.bind(this)); }; @@ -138,13 +152,15 @@ Cylon.start = function start() { // // Returns the current config Cylon.config = function(opts) { - var logChanges = (opts.logging && !_.isEqual(Config.logging, opts.logging)); + var loggingChanged = ( + opts.logging && Config.logging !== Utils.merge(Config.logging, opts.logging) + ); - if (_.isObject(opts) && !_.isArray(opts)) { - Config = _.merge(Config, opts); + if (typeof opts === "object" && !Array.isArray(opts)) { + Config = Utils.merge(Config, opts); } - if (logChanges) { + if (loggingChanged) { Logger.setup(); } @@ -159,7 +175,12 @@ Cylon.config = function(opts) { Cylon.halt = function halt(callback) { callback = callback || function() {}; - var fns = _.map(this.robots, "halt"); + var fns = []; + + for (var name in this.robots) { + var bot = this.robots[name]; + fns.push(bot.halt.bind(bot)); + } // if robots can"t shut down quickly enough, forcefully self-terminate var timeout = Config.haltTimeout || 3000; @@ -169,9 +190,16 @@ Cylon.halt = function halt(callback) { }; Cylon.toJSON = function() { + var robots = []; + + for (var name in this.robots) { + var bot = this.robots[name]; + robots.push(bot.toJSON.call(bot)); + } + return { - robots: _.invoke(this.robots, "toJSON"), - commands: _.keys(this.commands), + robots: robots, + commands: Object.keys(this.commands), events: this.events }; }; diff --git a/lib/driver.js b/lib/driver.js index 72818a9..21725ec 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -9,8 +9,7 @@ "use strict"; var Basestar = require("./basestar"), - Utils = require("./utils"), - _ = require("./lodash"); + Utils = require("./utils"); // Public: Creates a new Driver // @@ -36,13 +35,14 @@ var Driver = module.exports = function Driver(opts) { this.details = {}; - _.forEach(opts, function(opt, name) { - if (_.include(["robot", "name", "connection", "driver", "events"], name)) { - return; - } + for (var name in opts) { + var opt = opts[name], + banned = ["robot", "name", "connection", "driver", "events"]; - this.details[name] = opt; - }, this); + if (!~banned.indexOf(name)) { + this.details[name] = opt; + } + } }; Utils.subclass(Driver, Basestar); @@ -54,7 +54,7 @@ Driver.prototype.setupCommands = function(commands, proxy) { Utils.proxyFunctionsToObject(commands, proxy, this); - _.forEach(commands, function(command) { + commands.forEach(function(command) { var snake_case = command.replace(/[A-Z]+/g, function(match) { if (match.length > 1) { match = match.replace(/[A-Z]$/, function(m) { @@ -74,7 +74,7 @@ Driver.prototype.toJSON = function() { name: this.name, driver: this.constructor.name || this.name, connection: this.connection.name, - commands: _.keys(this.commands), + commands: Object.keys(this.commands), events: this.events, details: this.details }; diff --git a/lib/initializer.js b/lib/initializer.js index 1331ded..0f75ce9 100644 --- a/lib/initializer.js +++ b/lib/initializer.js @@ -9,15 +9,14 @@ "use strict"; var Registry = require("./registry"), - Config = require("./config"), - _ = require("./lodash"); + Config = require("./config"); function testMode() { return process.env.NODE_ENV === "test" && Config.testMode; } module.exports = function Initializer(type, opts) { - var mod; + var mod, name, prop; mod = Registry.findBy(type, opts[type]); @@ -33,24 +32,28 @@ module.exports = function Initializer(type, opts) { var obj = mod[type](opts); - _.forIn(obj, function(prop, name) { + for (name in obj) { + prop = obj[name]; + if (name === "constructor") { - return; + continue; } - if (_.isFunction(prop)) { + if (typeof prop === "function") { obj[name] = prop.bind(obj); } - }); + } if (testMode()) { var test = Registry.findBy(type, "test")[type](opts); - _.forIn(obj, function(prop, name) { - if (_.isFunction(prop) && !test[name]) { + for (name in obj) { + prop = obj[name]; + + if (typeof prop === "function" && !test[name]) { test[name] = function() { return true; }; } - }); + } return test; } diff --git a/lib/lodash.js b/lib/lodash.js deleted file mode 100644 index 4fbb9fa..0000000 --- a/lib/lodash.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @license - * Lo-Dash 2.4.1 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE - * Build: `lodash compat exports="node" include="bindAll,each,first,forEach,forIn,forOwn,include,invoke,isArray,isEqual,isFunction,isObject,isString,keys,map,merge,result,values" --minify --output ./lib/lodash.js` - */ -;(function(){function n(n,t,r){r=(r||0)-1;for(var e=n?n.length:0;++rr?0:r);++ek;k++)e+="n='"+r.h[k]+"';if((!(r&&x[n])&&m.call(t,n))",r.j||(e+="||(!x[n]&&t[n]!==A[n])"),e+="){"+r.g+"}"; -e+="}"}return(r.b||_t.nonEnumArgs)&&(e+="}"),e+=r.c+";return E",n("d,j,k,m,o,p,q,s,v,A,B,y,I,J,L",t+e+"}")(a,J,ot,st,B,b,Et,O,U.f,ut,X,Ot,H,ft,at)}function y(){var t=(t=o.indexOf)===A?n:t;return t}function v(n){return typeof n=="function"&&it.test(n)}function h(n){var r,e;return!n||at.call(n)!=V||(r=n.constructor,d(r)&&!(r instanceof r))||!_t.argsClass&&b(n)||!_t.nodeClass&&t(n)?false:_t.ownLast?(Ct(n,function(n,t,r){return e=st.call(r,t),false}),false!==e):(Ct(n,function(n,t){e=t}),typeof e=="undefined"||st.call(n,e)) -}function b(n){return n&&typeof n=="object"&&typeof n.length=="number"&&at.call(n)==$||false}function m(n){var t=[];return Ct(n,function(n,r){d(n)&&t.push(r)}),t.sort()}function d(n){return typeof n=="function"}function j(n){return!(!n||!X[typeof n])}function O(n){return typeof n=="string"||n&&typeof n=="object"&&at.call(n)==H||false}function _(n,t,r){var e=-1,o=y(),u=n?n.length:0,f=false;return r=(0>r?dt(0,u+r):r)||0,Et(n)?f=-1e?dt(0,o+e):e||0}else if(e)return e=C(t,r),t[e]===r?e:-1;return n(t,r,e)}function C(n,t,r,e){var u=0,f=n?n.length:u;for(r=r?o.createCallback(r,e,1):P,t=r(t);u>>1,r(n[e])= levels.indexOf(Logger.level)) { return Logger.logger[level].apply(Logger.logger, arguments); diff --git a/lib/robot.js b/lib/robot.js index df96513..7a8213d 100644 --- a/lib/robot.js +++ b/lib/robot.js @@ -11,8 +11,7 @@ var initializer = require("./initializer"), Logger = require("./logger"), Utils = require("./utils"), - Config = require("./config"), - _ = require("./lodash"); + Config = require("./config"); var Async = require("async"), EventEmitter = require("events").EventEmitter; @@ -41,28 +40,38 @@ var Robot = module.exports = function Robot(opts) { "log" ]; - _.bindAll(this, methods); + methods.forEach(function(method) { + this[method] = this[method].bind(this); + }, this); this.initRobot(opts); this.initConnections(opts); this.initDevices(opts); - _.forEach(opts, function(opt, name) { + for (var name in opts) { + var opt = opts[name]; + if (this[name] !== undefined) { - return; + continue; } this[name] = opt; - if (opts.commands == null && _.isFunction(opt)) { + if (opts.commands == null && typeof opt === "function") { this.commands[name] = opt; } - }, this); + } if (opts.commands) { - var cmds = _.result(opts, "commands"); + var cmds; - if (_.isObject(cmds) && !_.isArray(cmds)) { + if (typeof opts.commands === "function") { + cmds = opts.commands.call(this); + } else { + cmds = opts.commands; + } + + if (typeof cmds === "object" && !Array.isArray(cmds)) { this.commands = cmds; } else { var err = "#commands must be an object "; @@ -92,12 +101,26 @@ Robot.randomName = function() { // // Returns an Object containing Robot data Robot.prototype.toJSON = function() { + var connections = [], + devices = [], + n; + + for (n in this.connections) { + var conn = this.connections[n]; + connections.push(conn.toJSON.call(conn)); + } + + for (n in this.devices) { + var device = this.devices[n]; + devices.push(device.toJSON.call(device)); + } + return { name: this.name, - connections: _.invoke(this.connections, "toJSON"), - devices: _.invoke(this.devices, "toJSON"), - commands: _.keys(this.commands), - events: _.isArray(this.events) ? this.events : [] + connections: connections, + devices: devices, + commands: Object.keys(this.commands), + events: Array.isArray(this.events) ? this.events : [] }; }; @@ -159,29 +182,30 @@ Robot.prototype.initConnections = function(opts) { return this.connections; } - if (_.isObject(opts.connections)) { - if (_.isArray(opts.connections)) { + if (typeof opts.connections === "object") { + if (Array.isArray(opts.connections)) { this.performArraySetup(opts.connections, "connection", "connections"); return this.connections; } - _.forIn(opts.connections, function(conn, key) { - var name = _.isString(key) ? key : conn.name; + for (var key in opts.connections) { + var conn = opts.connections[key], + name = typeof key === "string" ? key : conn.name; if (conn.devices) { - _.forIn(conn.devices, function(device, deviceName) { - opts.devices = opts.devices || {}; + opts.devices = opts.devices || {}; + for (var d in conn.devices) { + var device = conn.devices[d]; device.connection = name; - - opts.devices[deviceName] = device; - }); + opts.devices[d] = device; + } delete conn.devices; } this.connection(name, conn); - }, this); + } } return this.connections; @@ -211,7 +235,10 @@ Robot.prototype.device = function(name, device) { device.connection = this.connections[device.connection]; } else { - device.connection = _.first(_.values(this.connections)); + for (var c in this.connections) { + device.connection = this.connections[c]; + break; + } } this.devices[device.name] = initializer("driver", device); @@ -242,16 +269,18 @@ Robot.prototype.initDevices = function(opts) { return this.devices; } - if (_.isObject(opts.devices)) { - if (_.isArray(opts.devices)) { + if (typeof opts.devices === "object") { + if (Array.isArray(opts.devices)) { this.performArraySetup(opts.devices, "device", "devices"); return this.devices; } - _.forIn(opts.devices, function(device, key) { - var name = _.isString(key) ? key : device.name; + for (var key in opts.devices) { + var device = opts.devices[key], + name = typeof key === "string" ? key : device.name; + this.device(name, device); - }, this); + } } return this.devices; @@ -295,7 +324,7 @@ Robot.prototype.start = function(callback) { }.bind(this)); } - if (_.isFunction(callback)) { + if (typeof callback === "function") { callback(err, results); } }.bind(this)); @@ -324,7 +353,11 @@ Robot.prototype.startWork = function() { Robot.prototype.startConnections = function(callback) { this.log("info", "Starting connections."); - var starters = _.map(this.connections, function(conn, name) { + var starters = []; + + var createStarter = function(name) { + var conn = this.connections[name]; + this[name] = conn; return function(cb) { @@ -339,7 +372,11 @@ Robot.prototype.startConnections = function(callback) { this.log("debug", str + "."); return conn.connect.call(conn, cb); }.bind(this); - }, this); + }.bind(this); + + for (var name in this.connections) { + starters.push(createStarter(name)); + } return Async.parallel(starters, callback); }; @@ -354,7 +391,11 @@ Robot.prototype.startDevices = function(callback) { log("info", "Starting devices."); - var starters = _.map(this.devices, function(device, name) { + var starters = []; + + var createStarter = function(name) { + var device = this.devices[name]; + this[name] = device; return function(cb) { @@ -367,7 +408,12 @@ Robot.prototype.startDevices = function(callback) { log("debug", str + "."); return device.start.call(device, cb); }; - }, this); + + }.bind(this); + + for (var name in this.devices) { + starters.push(createStarter(name)); + } return Async.parallel(starters, callback); }; @@ -386,8 +432,19 @@ Robot.prototype.halt = function(callback) { return callback(); } - var devices = _.map(this.devices, "halt"); - var connections = _.map(this.connections, "disconnect"); + var devices = [], + connections = [], + n; + + for (n in this.devices) { + var device = this.devices[n]; + devices.push(device.halt.bind(device)); + } + + for (n in this.connections) { + var conn = this.connections[n]; + connections.push(conn.disconnect.bind(conn)); + } try { Async.parallel(devices, function() { @@ -423,8 +480,8 @@ Robot.prototype.performArraySetup = function(things, typeOfThing, arrayName) { this.log("warn", str); - _.forEach(things, function(t, key) { - var name = _.isString(key) ? key : t.name; + things.forEach(function(t, key) { + var name = typeof key === "string" ? key : t.name; this[typeOfThing](name, t); }, this); }; diff --git a/lib/utils.js b/lib/utils.js index 03d95c7..577f8d9 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -8,8 +8,7 @@ "use strict"; -var _ = require("./lodash"), - monkeyPatches = require("./utils/monkey-patches"); +var monkeyPatches = require("./utils/monkey-patches"); var Utils = module.exports = { // Public: Alias to setInterval, combined with Number monkeypatches below to @@ -94,9 +93,11 @@ var Utils = module.exports = { this.constructor = child; }; - _.forOwn(parent, function(prop, key) { - child[key] = prop; - }); + for (var key in parent) { + if (parent.hasOwnProperty(key)) { + child[key] = parent[key]; + } + } Ctor.prototype = parent.prototype; child.prototype = new Ctor(); @@ -105,11 +106,13 @@ var Utils = module.exports = { }, proxyFunctions: function proxyFunctions(source, target) { - _.forEach(source, function(prop, key) { - if (_.isFunction(prop) && !target[key]) { + for (var key in source) { + var prop = source[key]; + + if (typeof prop === "function" && !target[key]) { target[key] = prop.bind(source); } - }); + } }, // Public: Proxies a list of methods from one object to another. It will not @@ -129,8 +132,8 @@ var Utils = module.exports = { force = force || false; - _.forEach(methods, function(method) { - if (_.isFunction(base[method]) && !force) { + methods.forEach(function(method) { + if (typeof base[method] === "function" && !force) { return; } @@ -197,6 +200,51 @@ var Utils = module.exports = { return fallback; }, + // Public: Merges two arrays/objects together, recursively. + // + // base - what should be merged onto + // source - what should be merged into it + // + // Returns a merged object/array + merge: function(base, source) { + var isArray = Array.isArray(source); + + if (base == null) { + base = isArray ? [] : {}; + } + + // merge objects + if (isArray) { + source.forEach(function(e, i) { + if (typeof base[i] === "undefined") { + base[i] = e; + } else if (typeof e === "object") { + base[i] = Utils.merge(base[i], e); + } else { + if (!~base.indexOf(e)) { + base.push(e); + } + } + }); + } else { + var key; + + for (key in source) { + if (typeof source[key] !== "object" || !source[key]) { + base[key] = source[key]; + } else { + if (base[key]) { + Utils.merge(base[key], source[key]); + } else { + base[key] = source[key]; + } + } + } + } + + return base; + }, + // Public: Given a name, and an array of existing names, returns a unique // name. // diff --git a/spec/lib/utils.spec.js b/spec/lib/utils.spec.js index 4977907..530f5a2 100644 --- a/spec/lib/utils.spec.js +++ b/spec/lib/utils.spec.js @@ -252,4 +252,37 @@ describe("Utils", function() { }); }); }); + + describe("#merge", function() { + var merge = utils.merge; + + var base = { + fruits: ["apple"], + vegetables: ["beet"], + thing: null, + otherThing: "hello!", + data: [{ "user": "barney" }, { "user": "fred" }] + }; + + var source = { + fruits: ["banana"], + vegetables: ["carrot"], + thing: "hello!", + otherThing: null, + data: [{ "age": 36 }, { "age": 40 }] + }; + + var expected = { + data: [ { age: 36, user: "barney" }, { age: 40, user: "fred" } ], + fruits: [ "apple", "banana" ], + vegetables: [ "beet", "carrot" ], + thing: "hello!", + otherThing: null + }; + + it("merges two objects", function() { + var merged = merge(base, source); + expect(merged).to.be.eql(expected); + }); + }); });