From ca749fd03ed2dd3c4ea59d827fefb5f9d060088f Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Mon, 16 Jun 2014 11:42:33 -0700 Subject: [PATCH 1/9] Fix an edge case and improve tests for API --- lib/api.js | 2 +- test/specs/api.spec.js | 189 ++++++++++++++++++++++++++++++++--------- 2 files changed, 148 insertions(+), 43 deletions(-) diff --git a/lib/api.js b/lib/api.js index 84ce2be..9098f84 100644 --- a/lib/api.js +++ b/lib/api.js @@ -98,7 +98,7 @@ API.prototype.createServer = function createServer() { API.prototype.setupAuth = function setupAuth() { var authfn = function auth(req, res, next) { next(); }; - if (typeof(this.auth) === 'object' && this.auth.type) { + if (!!this.auth && typeof(this.auth) === 'object' && this.auth.type) { var type = this.auth.type, module = "./api/auth/" + type, filename = path.normalize(__dirname + "/" + module + ".js"), diff --git a/test/specs/api.spec.js b/test/specs/api.spec.js index 4eb39da..47f9304 100644 --- a/test/specs/api.spec.js +++ b/test/specs/api.spec.js @@ -2,77 +2,182 @@ var express = require('express'), https = require('https'), - fs = require('fs'); + fs = require('fs'), + path = require('path'); var API = source('api'), - Utils = source('utils'); + Utils = source('utils'), + Logger = source('logger'); + +var MockRequest = require('../support/mock_request'), + MockResponse = require('../support/mock_response'); describe("API", function() { - var api, opts; + var api; + + beforeEach(function() { + api = new API(); + }); describe("constructor", function() { + var mod; beforeEach(function() { - stub(https, 'createServer').returns({ listen: spy() }); - - api = new API(opts); + mod = new API({ + host: "0.0.0.0", + port: "1234" + }); }); - afterEach(function() { - https.createServer.restore(); + it("sets @express to an Express instance", function() { + expect(api.express.listen).to.be.a('function'); + }) + + it("sets default values", function() { + var sslPath = path.normalize(__dirname + "/../../lib/api/ssl/"); + + expect(api.host).to.be.eql('127.0.0.1'); + expect(api.port).to.be.eql('3000'); }); - it("sets @opts to the passed opts object", function() { - expect(api.opts).to.be.eql(opts); + it("overrides default values if passed to constructor", function() { + expect(mod.host).to.be.eql('0.0.0.0'); + expect(mod.port).to.be.eql('1234'); }); - it("sets @host to 127.0.0.1 by default", function() { - expect(api.host).to.be.eql("127.0.0.1") - }); - - it("sets @post to 3000 by default", function() { - expect(api.port).to.be.eql("3000") - }); - - it("sets @express to an Express server instance", function() { - expect(api.express).to.be.a('function'); - - var methods = ['get', 'post', 'put', 'delete']; - - for (var i = 0; i < methods.length; i++) { - expect(api.express[methods[i]]).to.be.a('function'); - } - }); - - it("sets @server.https to a https.createServer", function() { - expect(https.createServer).to.be.calledWith(); - }); - - it("sets the server's title", function() { + it("sets the server title", function() { var title = api.express.get('title'); - expect(title).to.be.eql("Cylon API Server"); + expect(title).to.be.eql('Cylon API Server'); }); - }); + describe("default", function() { + var d = API.prototype.defaults; - describe("ssl disabled", function () { + it("host", function() { + expect(d.host).to.be.eql('127.0.0.1'); + }); + it("port", function() { + expect(d.port).to.be.eql('3000'); + }); + + it("auth", function() { + expect(d.auth).to.be.eql(false); + }); + + it("CORS", function() { + expect(d.CORS).to.be.eql(''); + }); + + it("ssl", function() { + var sslDir = path.normalize(__dirname + "/../../lib/api/ssl/"); + expect(d.ssl.key).to.be.eql(sslDir + "server.key"); + expect(d.ssl.cert).to.be.eql(sslDir + "server.crt"); + }); + }); + + describe("#createServer", function() { + it("sets @express to an express server", function() { + api.express = null; + api.createServer(); + expect(api.express).to.be.a('function'); + }); + + context("if SSL is configured", function() { + it("sets @server to a https.Server instance", function() { + api.createServer(); + expect(api.server).to.be.an.instanceOf(https.Server); + }); + }); + + context("if SSL is not configured", function() { + beforeEach(function() { + api.ssl = false; + stub(Logger, 'warn'); + api.createServer(); + }); + + afterEach(function() { + Logger.warn.restore(); + }); + + it("logs that the API is insecure", function() { + expect(Logger.warn).to.be.calledWithMatch("insecure connection") + }); + + it("sets @server to @express", function() { + expect(api.server).to.be.eql(api.express); + }); + }); + }); + + describe("#setupAuth", function() { + context("when auth.type is basic", function() { + beforeEach(function() { + api.auth = { type: 'basic', user: 'user', pass: 'pass' } + }); + + it('returns a basic auth middleware function', function() { + var fn = api.setupAuth(), + req = new MockRequest(), + res = new MockResponse(), + next = spy(); + + var auth = 'Basic ' + new Buffer('user:pass').toString('base64') + + req.headers.authorization = auth; + + fn(req, res, next); + expect(next).to.be.called; + }); + }); + + context("when auth is null", function() { + beforeEach(function() { + api.auth = null; + }); + + it("returns a pass-through middleware function", function() { + var fn = api.setupAuth(), + next = spy(); + + fn(null, null, next); + expect(next).to.be.called; + }); + }); + }); + + describe("#listen", function() { beforeEach(function() { - stub(https, 'createServer').returns({ listen: spy() }); + // we create a plain HTTP server to avoid a log message from Node + api.ssl = false; + api.createServer(); + api.express.set('title', 'Cylon API Server'); - opts = { ssl: false } + stub(api.server, 'listen').yields(); + stub(Logger, 'info'); - api = new API(opts); + api.listen(); }); afterEach(function() { - https.createServer.restore(); + api.server.listen.restore(); + Logger.info.restore(); }); - it("doesn't create https server", function() { - expect(https.createServer).not.to.be.calledWith(); + it("listens on the configured host and port", function() { + expect(api.server.listen).to.be.calledWith('3000', '127.0.0.1'); }); + context("when the server is running", function() { + it("logs that it's online and listening", function() { + var online = "Cylon API Server is now online.", + listening = "Listening at http://127.0.0.1:3000"; + + expect(Logger.info).to.be.calledWith(online); + expect(Logger.info).to.be.calledWith(listening); + }); + }); }); }); From af8cfbe3333ef446bf8efc173698eca05981ad04 Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Mon, 16 Jun 2014 13:09:13 -0700 Subject: [PATCH 2/9] Fix JSHint errors --- lib/api.js | 6 +++--- lib/connection.js | 3 +-- lib/cylon.js | 2 +- lib/driver.js | 7 +------ lib/io/digital-pin.js | 2 +- lib/logger/null_logger.js | 2 +- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/lib/api.js b/lib/api.js index 9098f84..a216dce 100644 --- a/lib/api.js +++ b/lib/api.js @@ -58,13 +58,13 @@ var API = module.exports = function API(opts) { for (var p in container) { req.commandParams.push(container[p]); - }; + } return next(); }); // load route definitions - this.express.use('/', require('./api/routes')) + this.express.use('/', require('./api/routes')); }; API.prototype.defaults = { @@ -90,7 +90,7 @@ API.prototype.createServer = function createServer() { cert: fs.readFileSync(this.ssl.cert) }, this.express); } else { - Logger.warn("API using insecure connection. We recommend using an SSL certificate with Cylon.") + Logger.warn("API using insecure connection. We recommend using an SSL certificate with Cylon."); this.server = this.express; } }; diff --git a/lib/connection.js b/lib/connection.js index f652db2..ba2c9f1 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -47,7 +47,7 @@ module.exports = Connection = function Connection(opts) { this.adaptor = this.initAdaptor(opts); Utils.proxyFunctionsToObject(this.adaptor.commands, this.adaptor, this.self); -} +}; Utils.subclass(Connection, EventEmitter); @@ -69,7 +69,6 @@ Connection.prototype.toJSON = function() { // // Returns the result of the supplied callback function Connection.prototype.connect = function(callback) { - var msg = "Connecting to " + this.name; var msg = "Connecting to '" + this.name + "'"; if (this.port != null) { diff --git a/lib/cylon.js b/lib/cylon.js index b457513..024a945 100644 --- a/lib/cylon.js +++ b/lib/cylon.js @@ -99,7 +99,7 @@ if (process.platform === "win32") { readline.createInterface(io).on("SIGINT", function() { process.emit("SIGINT"); }); -}; +} process.on("SIGINT", function() { Cylon.halt(function() { diff --git a/lib/driver.js b/lib/driver.js index 8f0eee1..3095bf1 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -12,11 +12,6 @@ var Basestar = require('./basestar'), Logger = require('./logger'), Utils = require('./utils'); -// The Driver class is a base class for Driver classes in external Cylon -// modules to use. It offers basic functions for starting/halting that -// descendant classes can use. -var Driver; - // Public: Creates a new Driver // // opts - hash of acceptable params @@ -24,7 +19,7 @@ var Driver; // device - Device the driver will use to proxy commands/events // // Returns a new Driver -module.exports = Driver = function Driver(opts) { +var Driver = module.exports = function Driver(opts) { if (opts == null) { opts = {}; } diff --git a/lib/io/digital-pin.js b/lib/io/digital-pin.js index 092ec55..9363bb1 100644 --- a/lib/io/digital-pin.js +++ b/lib/io/digital-pin.js @@ -28,7 +28,7 @@ var DigitalPin = module.exports = function DigitalPin(opts) { this.status = 'low'; this.ready = false; this.mode = opts.mode; -} +}; Utils.subclass(DigitalPin, EventEmitter); diff --git a/lib/logger/null_logger.js b/lib/logger/null_logger.js index d8c6d55..af21b14 100644 --- a/lib/logger/null_logger.js +++ b/lib/logger/null_logger.js @@ -1,6 +1,6 @@ // The NullLogger is designed for cases where you want absolutely nothing to // print to anywhere. Every proxied method from the Logger returns a noop. -var NullLogger = module.exports = function NullLogger() {} +var NullLogger = module.exports = function NullLogger() {}; NullLogger.prototype.toString = function() { return "NullLogger"; From 6f42b061b311913f9e5de97d791bc68ce1d16dcb Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Tue, 17 Jun 2014 16:00:36 -0700 Subject: [PATCH 3/9] Add #fetch Utility function The #fetch utility acts in (roughly) the same way as Ruby's Hash#fetch function. This addition should hopefully make constructors more rigorous and help to prevent a few classes of errors. --- lib/utils.js | 55 ++++++++++++++++++++++++++++++++++++++++ test/specs/utils.spec.js | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/lib/utils.js b/lib/utils.js index 2df38af..1d5dcfb 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -183,6 +183,61 @@ var Utils = module.exports = { }; }, + // Public: Analogue to Ruby's Hash#fetch method for looking up object + // properties. + // + // obj - object to do property lookup on + // property - string property name to attempt to look up + // fallback - either: + // - a fallback value to return if `property` can't be found + // - a function to be executed if `property` can't be found. The function + // will be passed `property` as an argument. + // + // Examples + // + // var object = { property: "hello world" }; + // fetch(object, "property"); + // //=> "hello world" + // + // fetch(object, "notaproperty", "default value"); + // //=> "default value" + // + // var notFound = function(prop) { return prop + " not found!" }; + // fetch(object, "notaproperty", notFound) + // // "notaproperty not found!" + // + // var badFallback = function(prop) { prop + " not found!" }; + // fetch(object, "notaproperty", badFallback) + // // Error: no return value from provided callback function + // + // fetch(object, "notaproperty"); + // // Error: key not found: "notaproperty" + // + // Returns the value of obj[property], a fallback value, or the results of + // running 'fallback'. If the property isn't found, and 'fallback' hasn't been + // provided, will throw an error. + fetch: function(obj, property, fallback) { + if (obj.hasOwnProperty(property)) { + return obj[property]; + } + + if (fallback === void 0) { + throw new Error('key not found: "' + property + '"'); + } + + if (typeof(fallback) === 'function') { + var value = fallback(property); + + if (value === void 0) { + throw new Error('no return value from provided fallback function'); + } + + return value; + } + + return fallback; + }, + // Public: Adds necessary utils to global namespace, along with base class // extensions. // diff --git a/test/specs/utils.spec.js b/test/specs/utils.spec.js index 30733d4..066b0f4 100644 --- a/test/specs/utils.spec.js +++ b/test/specs/utils.spec.js @@ -222,4 +222,52 @@ describe("Utils", function() { expect(proxy.boundMethod("Hello", "World")).to.eql(["Hello", "World"]); }) }); + + describe("#fetch", function() { + var fetch = utils.fetch, + obj = { property: 'hello world', 'false': false, 'null': null }; + + context("if the property exists on the object", function() { + it("returns the value", function() { + expect(fetch(obj, 'property')).to.be.eql('hello world'); + expect(fetch(obj, 'false')).to.be.eql(false); + expect(fetch(obj, 'null')).to.be.eql(null); + }); + }); + + context("if the property doesn't exist on the object", function() { + context("and no fallback value has been provided", function() { + it("throws an Error", function() { + var fn = function() { return fetch(obj, "notaproperty"); }; + expect(fn).to.throw(Error, 'key not found: "notaproperty"'); + }); + }); + + context("and a fallback value has been provided", function() { + it('returns the fallback value', function() { + expect(fetch(obj, 'notakey', 'fallback')).to.be.eql('fallback'); + }); + }); + + context("and a fallback function has been provided", function() { + context("if the function has no return value", function() { + it("throws an Error", function() { + var fn = function() { fetch(obj, 'notakey', function() {}); }, + str = 'no return value from provided fallback function'; + + expect(fn).to.throw(Error, str); + }); + }); + + context("if the function returns a value", function() { + it("returns the value returned by the fallback function", function() { + var fn = function(key) { return "Couldn't find " + key }, + value = "Couldn't find notakey"; + + expect(fetch(obj, 'notakey', fn)).to.be.eql(value); + }); + }); + }); + }); + }); }); From e8520e3de2562ded3e3f7aaa55c11aed4de0c36b Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Tue, 17 Jun 2014 19:15:38 -0700 Subject: [PATCH 4/9] Experiment with Utils.fetch in Driver class --- lib/driver.js | 11 ++++------- test/specs/driver.spec.js | 4 ---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/driver.js b/lib/driver.js index 3095bf1..a62d210 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -20,14 +20,11 @@ var Basestar = require('./basestar'), // // Returns a new Driver var Driver = module.exports = function Driver(opts) { - if (opts == null) { - opts = {}; - } + opts = opts || {}; - this.self = this; - this.name = opts.name; - this.device = opts.device; - this.connection = this.device.connection; + this.name = Utils.fetch(opts, 'name'); + this.device = Utils.fetch(opts, 'device'); + this.connection = Utils.fetch(this.device, 'connection'); }; Utils.subclass(Driver, Basestar); diff --git a/test/specs/driver.spec.js b/test/specs/driver.spec.js index 0d4ba88..fa5d176 100644 --- a/test/specs/driver.spec.js +++ b/test/specs/driver.spec.js @@ -18,10 +18,6 @@ describe("Driver", function() { }); describe("#constructor", function() { - it("sets @self as a reference to the driver", function() { - expect(driver.self).to.be.eql(driver); - }); - it("sets @name to the provided name", function() { expect(driver.name).to.be.eql('driver'); }); From 6fa87fbf7c7090f2927f682635898a10ed2859e0 Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Tue, 17 Jun 2014 19:18:12 -0700 Subject: [PATCH 5/9] Consolidate logstring generation into private fn --- lib/connection.js | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/connection.js b/lib/connection.js index ba2c9f1..4c4101d 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -69,14 +69,7 @@ Connection.prototype.toJSON = function() { // // Returns the result of the supplied callback function Connection.prototype.connect = function(callback) { - var msg = "Connecting to '" + this.name + "'"; - - if (this.port != null) { - msg += " on port " + this.port; - } - - msg += "."; - + var msg = this._logstring("Connecting to"); Logger.info(msg); return this.adaptor.connect(callback); }; @@ -87,14 +80,7 @@ Connection.prototype.connect = function(callback) { // // Returns nothing Connection.prototype.disconnect = function(callback) { - var msg = "Disconnecting from '" + this.name + "'"; - - if (this.port != null) { - msg += " on port " + this.port; - } - - msg += "."; - + var msg = this._logstring("Disconnecting from"); Logger.info(msg); return this.adaptor.disconnect(callback); }; @@ -125,3 +111,15 @@ Connection.prototype.halt = function(callback) { Logger.info(msg); return this.disconnect(callback); }; + +Connection.prototype._logstring = function _logstring(action) { + var msg = action + " '" + this.name + "'"; + + if (this.port != null) { + msg += " on port " + this.port; + } + + msg += "."; + + return msg; +}; From 5acfd4aed492b0808b67e641d8d365eb228f3597 Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Tue, 17 Jun 2014 19:35:07 -0700 Subject: [PATCH 6/9] Remove Utils#bind in preference of built-in fn We were re-implementing Function.prototype.bind, so let's just use that. --- lib/connection.js | 2 +- lib/device.js | 4 ++-- lib/robot.js | 2 +- lib/utils.js | 22 ---------------------- test/specs/utils.spec.js | 19 ------------------- 5 files changed, 4 insertions(+), 45 deletions(-) diff --git a/lib/connection.js b/lib/connection.js index 4c4101d..0ec33d2 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -37,7 +37,7 @@ module.exports = Connection = function Connection(opts) { opts.id = Math.floor(Math.random() * 10000); } - this.connect = Utils.bind(this.connect, this); + this.connect = this.connect.bind(this); this.self = this; this.robot = opts.robot; diff --git a/lib/device.js b/lib/device.js index acbcdae..ba1472a 100644 --- a/lib/device.js +++ b/lib/device.js @@ -28,8 +28,8 @@ var Device = module.exports = function Device(opts) { opts = {}; } - this.halt = Utils.bind(this.halt, this); - this.start = Utils.bind(this.start, this); + this.halt = this.halt.bind(this); + this.start = this.start.bind(this); this.robot = opts.robot; this.name = opts.name; diff --git a/lib/robot.js b/lib/robot.js index d23d9ac..e2c38dd 100644 --- a/lib/robot.js +++ b/lib/robot.js @@ -70,7 +70,7 @@ var Robot = module.exports = function Robot(opts) { ]; methods.forEach(function(method) { - self[method] = Utils.bind(self[method], self); + self[method] = self[method].bind(self); }); this.name = opts.name || this.constructor.randomName(); diff --git a/lib/utils.js b/lib/utils.js index 1d5dcfb..5a7718f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -161,28 +161,6 @@ var Utils = module.exports = { return base; }, - // Public: Binds an argument to a caller - // - // fn - function to bind - // me - value for 'this' scope inside the function - // - // Examples - // - // var me = { hello: "Hello World" }; - // var proxy = { boundMethod: function() { return this.hello; } }; - // - // proxy.boundMethod = bind(proxy.boundMethod, me); - // proxy.boundMethod(); - // - // //=> "Hello World" - // - // Returns a function wrapper - bind: function bind(fn, me) { - return function() { - return fn.apply(me, arguments); - }; - }, - // Public: Analogue to Ruby's Hash#fetch method for looking up object // properties. // diff --git a/test/specs/utils.spec.js b/test/specs/utils.spec.js index 066b0f4..a4432de 100644 --- a/test/specs/utils.spec.js +++ b/test/specs/utils.spec.js @@ -204,25 +204,6 @@ describe("Utils", function() { }); }); - describe("#bind", function() { - var me = { hello: "Hello World" }, - proxy = { boundMethod: function() { return this.hello; } }; - - it("binds the 'this' scope for the method", function() { - proxy.boundMethod = function() { return this.hello; }; - proxy.boundMethod = utils.bind(proxy.boundMethod, me); - - expect(proxy.boundMethod()).to.eql("Hello World"); - }); - - it("passes arguments along to bound functions", function() { - proxy.boundMethod = function(hello, world) { return [hello, world]; }; - proxy.boundMethod = utils.bind(proxy.boundMethod, me); - - expect(proxy.boundMethod("Hello", "World")).to.eql(["Hello", "World"]); - }) - }); - describe("#fetch", function() { var fetch = utils.fetch, obj = { property: 'hello world', 'false': false, 'null': null }; From 5974eb80cd99ba9e8fc4dc0bf9feb18892f75ad1 Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Wed, 18 Jun 2014 16:02:54 -0700 Subject: [PATCH 7/9] Just use arguments directly instead of array --- lib/utils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 5a7718f..e362976 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -121,8 +121,7 @@ var Utils = module.exports = { var fn = function(method) { return base[method] = function() { - var args = arguments.length >= 1 ? [].slice.call(arguments, 0) : []; - return target[method].apply(target, args); + return target[method].apply(target, arguments); }; }; From 5ce0d37640442f2c5596391629ea280fbec2f0c7 Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Tue, 24 Jun 2014 10:37:59 -0700 Subject: [PATCH 8/9] Stop using #fetch here for now, it just breaks tests --- lib/driver.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/driver.js b/lib/driver.js index a62d210..05f01be 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -22,9 +22,9 @@ var Basestar = require('./basestar'), var Driver = module.exports = function Driver(opts) { opts = opts || {}; - this.name = Utils.fetch(opts, 'name'); - this.device = Utils.fetch(opts, 'device'); - this.connection = Utils.fetch(this.device, 'connection'); + this.name = opts.name; + this.device = opts.device; + this.connection = this.device.connection; }; Utils.subclass(Driver, Basestar); From f0d8b0360b3af22dc5696c03454c359daac295db Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Thu, 3 Jul 2014 09:35:24 -0700 Subject: [PATCH 9/9] Remove `self` references in favor of #bind --- lib/adaptor.js | 1 - lib/api.js | 18 +++++++----------- lib/connection.js | 5 ++--- lib/robot.js | 20 ++++++++------------ test/specs/adaptor.spec.js | 4 ---- test/specs/connection.spec.js | 4 ---- test/specs/utils.spec.js | 3 +-- 7 files changed, 18 insertions(+), 37 deletions(-) diff --git a/lib/adaptor.js b/lib/adaptor.js index 7e0559a..e44885d 100644 --- a/lib/adaptor.js +++ b/lib/adaptor.js @@ -29,7 +29,6 @@ module.exports = Adaptor = function Adaptor(opts) { opts = {}; } - this.self = this; this.name = opts.name; this.connection = opts.connection; }; diff --git a/lib/api.js b/lib/api.js index a216dce..bb31c3d 100644 --- a/lib/api.js +++ b/lib/api.js @@ -17,8 +17,6 @@ var express = require('express'), var Logger = require('./logger'); var API = module.exports = function API(opts) { - var self = this; - if (opts == null) { opts = {}; } @@ -31,17 +29,17 @@ var API = module.exports = function API(opts) { this.express.set('title', 'Cylon API Server'); - this.express.use(self.setupAuth()); + this.express.use(this.setupAuth()); this.express.use(bodyParser()); this.express.use(express["static"](__dirname + "/../node_modules/robeaux/")); // set CORS headers for API requests this.express.use(function(req, res, next) { - res.set("Access-Control-Allow-Origin", self.CORS || "*"); + res.set("Access-Control-Allow-Origin", this.CORS || "*"); res.set("Access-Control-Allow-Headers", "Content-Type"); res.set('Content-Type', 'application/json'); return next(); - }); + }.bind(this)); // extracts command params from request this.express.use(function(req, res, next) { @@ -113,13 +111,11 @@ API.prototype.setupAuth = function setupAuth() { }; API.prototype.listen = function() { - var self = this; - this.server.listen(this.port, this.host, null, function() { - var title = self.express.get('title'); - var protocol = self.ssl ? "https" : "http"; + var title = this.express.get('title'); + var protocol = this.ssl ? "https" : "http"; Logger.info(title + " is now online."); - Logger.info("Listening at " + protocol + "://" + self.host + ":" + self.port); - }); + Logger.info("Listening at " + protocol + "://" + this.host + ":" + this.port); + }.bind(this)); }; diff --git a/lib/connection.js b/lib/connection.js index 0ec33d2..f603987 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -39,14 +39,13 @@ module.exports = Connection = function Connection(opts) { this.connect = this.connect.bind(this); - this.self = this; this.robot = opts.robot; this.name = opts.name; this.connection_id = opts.id; this.port = opts.port; this.adaptor = this.initAdaptor(opts); - Utils.proxyFunctionsToObject(this.adaptor.commands, this.adaptor, this.self); + Utils.proxyFunctionsToObject(this.adaptor.commands, this.adaptor, this); }; Utils.subclass(Connection, EventEmitter); @@ -93,7 +92,7 @@ Connection.prototype.disconnect = function(callback) { // Returns the set-up adaptor Connection.prototype.initAdaptor = function(opts) { Logger.debug("Loading adaptor '" + opts.adaptor + "'."); - return this.robot.initAdaptor(opts.adaptor, this.self, opts); + return this.robot.initAdaptor(opts.adaptor, this, opts); }; // Public: Halt the adaptor's connection diff --git a/lib/robot.js b/lib/robot.js index e2c38dd..5786928 100644 --- a/lib/robot.js +++ b/lib/robot.js @@ -53,8 +53,6 @@ var Robot = module.exports = function Robot(opts) { opts = {}; } - var self = this; - var methods = [ "toString", "registerDriver", @@ -70,8 +68,8 @@ var Robot = module.exports = function Robot(opts) { ]; methods.forEach(function(method) { - self[method] = self[method].bind(self); - }); + this[method] = this[method].bind(this); + }.bind(this)); this.name = opts.name || this.constructor.randomName(); this.connections = {}; @@ -209,21 +207,19 @@ Robot.prototype._createDevice = function(device) { // // Returns the result of the work Robot.prototype.start = function() { - var self = this; - var begin = function(callback) { - self.work.call(self, self); - self.running = true; - self.emit('working'); + this.work.call(this, this); + this.running = true; + this.emit('working'); Logger.info('Working.'); callback(null, true); - }; + }.bind(this); Async.series([ - self.startConnections, - self.startDevices, + this.startConnections, + this.startDevices, begin ], function(err) { if (!!err) { diff --git a/test/specs/adaptor.spec.js b/test/specs/adaptor.spec.js index 2e968fb..deda000 100644 --- a/test/specs/adaptor.spec.js +++ b/test/specs/adaptor.spec.js @@ -11,10 +11,6 @@ describe("Adaptor", function() { var adaptor = new Adaptor({ name: 'adaptor', connection: connection }); describe("#constructor", function() { - it("sets @self as a reference to the adaptor", function() { - expect(adaptor.self).to.be.eql(adaptor); - }); - it("sets @name to the provided name", function() { expect(adaptor.name).to.be.eql('adaptor'); }); diff --git a/test/specs/connection.spec.js b/test/specs/connection.spec.js index 33b1a6a..d9e36dd 100644 --- a/test/specs/connection.spec.js +++ b/test/specs/connection.spec.js @@ -13,10 +13,6 @@ describe("Connection", function() { var connection = robot.connections.loopback; describe("#constructor", function() { - it("sets @self as a circular reference", function() { - expect(connection.self).to.be.eql(connection); - }); - it("sets @robot to the passed robot", function() { expect(connection.robot).to.be.eql(robot); }); diff --git a/test/specs/utils.spec.js b/test/specs/utils.spec.js index a4432de..c44bfd2 100644 --- a/test/specs/utils.spec.js +++ b/test/specs/utils.spec.js @@ -164,9 +164,8 @@ describe("Utils", function() { var TestClass = (function() { function TestClass() { - this.self = this; this.testInstance = new ProxyClass; - utils.proxyFunctionsToObject(methods, this.testInstance, this.self, true); + utils.proxyFunctionsToObject(methods, this.testInstance, this, true); } return TestClass;