Merge pull request #191 from hybridgroup/refactor/utils

Refactor Utils (mostly) out of global namespace
This commit is contained in:
Ron Evans 2014-06-06 18:21:46 -04:00
commit 836b7938db
24 changed files with 191 additions and 170 deletions

View File

@ -9,7 +9,8 @@
"use strict";
var Basestar = require('./basestar'),
Logger = require('./logger');
Logger = require('./logger'),
Utils = require('./utils');
// The Adaptor class is a base class for Adaptor classes in external Cylon
// modules to use. It offers basic functions for connecting/disconnecting that
@ -34,7 +35,7 @@ module.exports = Adaptor = function Adaptor(opts) {
this.commandList = [];
};
subclass(Adaptor, Basestar);
Utils.subclass(Adaptor, Basestar);
// Public: Exposes all commands the adaptor will respond to/proxy
//

View File

@ -8,10 +8,10 @@
"use strict";
require('./utils');
var EventEmitter = require('events').EventEmitter;
var Utils = require('./utils');
// Basestar is a base class to be used when writing external Cylon adaptors and
// drivers. It provides some useful base methods and functionality
//
@ -23,7 +23,7 @@ module.exports = Basestar = function Basestar(opts) {
this.self = this;
}
subclass(Basestar, EventEmitter);
Utils.subclass(Basestar, EventEmitter);
// Public: Proxies calls from all methods in the object to a target object
//
@ -35,7 +35,7 @@ subclass(Basestar, EventEmitter);
// Returns the klass where the methods have been proxied
Basestar.prototype.proxyMethods = function(methods, target, source, force) {
if (force == null) { force = false; }
return proxyFunctionsToObject(methods, target, source, force);
return Utils.proxyFunctionsToObject(methods, target, source, force);
};
// Public: Defines an event handler that proxies events from a source object

View File

@ -8,11 +8,10 @@
'use strict';
require("./utils");
var EventEmitter = require('events').EventEmitter;
var Logger = require('./logger');
var Logger = require('./logger'),
Utils = require('./utils');
// The Connection class represents the interface to
// a specific group of hardware devices. Examples would be an
@ -38,7 +37,7 @@ module.exports = Connection = function Connection(opts) {
opts.id = Math.floor(Math.random() * 10000);
}
this.connect = bind(this.connect, this);
this.connect = Utils.bind(this.connect, this);
this.self = this;
this.robot = opts.robot;
@ -47,10 +46,10 @@ module.exports = Connection = function Connection(opts) {
this.port = opts.port;
this.adaptor = this.initAdaptor(opts);
proxyFunctionsToObject(this.adaptor.commands(), this.adaptor, this.self);
Utils.proxyFunctionsToObject(this.adaptor.commands(), this.adaptor, this.self);
}
subclass(Connection, EventEmitter);
Utils.subclass(Connection, EventEmitter);
// Public: Exports basic data for the Connection
//

View File

@ -19,6 +19,7 @@ var Cylon = module.exports = {
Logger: Logger,
Driver: require('./driver'),
Adaptor: require('./adaptor'),
Utils: Utils,
IO: {
DigitalPin: require('./io/digital-pin')

View File

@ -8,11 +8,10 @@
'use strict';
require('./utils');
var EventEmitter = require('events').EventEmitter;
var Logger = require('./logger');
var Logger = require('./logger'),
Utils = require('./utils');
// The Artoo::Device class represents the interface to
// a specific individual hardware devices. Examples would be a digital
@ -34,8 +33,8 @@ module.exports = Device = function Device(opts) {
opts = {};
}
this.halt = bind(this.halt, this);
this.start = bind(this.start, this);
this.halt = Utils.bind(this.halt, this);
this.start = Utils.bind(this.start, this);
this.self = this;
this.robot = opts.robot;
@ -44,10 +43,10 @@ module.exports = Device = function Device(opts) {
this.connection = this.determineConnection(opts.connection) || this.defaultConnection();
this.driver = this.initDriver(opts);
proxyFunctionsToObject(this.driver.commands(), this.driver, this.self);
Utils.proxyFunctionsToObject(this.driver.commands(), this.driver, this.self);
};
subclass(Device, EventEmitter);
Utils.subclass(Device, EventEmitter);
// Public: Starts the device driver
//

View File

@ -9,7 +9,8 @@
'use strict';
var Basestar = require('./basestar'),
Logger = require('./logger');
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
@ -35,7 +36,7 @@ module.exports = Driver = function Driver(opts) {
this.commandList = [];
};
subclass(Driver, Basestar);
Utils.subclass(Driver, Basestar);
// Public: Exposes all commands the driver will respond to/proxy
//

View File

@ -11,6 +11,8 @@
var FS = require('fs'),
EventEmitter = require('events').EventEmitter;
var Utils = require('../utils');
var GPIO_PATH = "/sys/class/gpio";
var GPIO_READ = "in";
@ -28,7 +30,7 @@ var DigitalPin = module.exports = function DigitalPin(opts) {
this.mode = opts.mode;
}
subclass(DigitalPin, EventEmitter);
Utils.subclass(DigitalPin, EventEmitter);
DigitalPin.prototype.connect = function(mode) {
var _this = this;
@ -81,7 +83,7 @@ DigitalPin.prototype.digitalRead = function(interval) {
if (this.mode !== 'r') { this._setMode('r'); }
every(interval, function() {
Utils.every(interval, function() {
FS.readFile(_this._valuePath(), function(err, data) {
if (err) {
var error = "Error occurred while reading from pin " + _this.pinNum;

View File

@ -8,11 +8,10 @@
'use strict';
require('./utils');
var Connection = require("./connection"),
Device = require("./device"),
Logger = require('./logger'),
Utils = require('./utils'),
config = require('./config');
var Async = require("async"),
@ -43,7 +42,7 @@ var Robot;
// name: 'sphero', driver: 'sphero'
//
// work: (me) ->
// every 1.second(), ->
// Utils.every 1.second(), ->
// me.sphero.roll 60, Math.floor(Math.random() * 360//
module.exports = Robot = function Robot(opts) {
if (opts == null) {
@ -66,7 +65,7 @@ module.exports = Robot = function Robot(opts) {
for (var i = 0; i < methods.length ; i++) {
var method = methods[i];
this[method] = bind(this[method], this);
this[method] = Utils.bind(this[method], this);
}
this.robot = this;
@ -101,7 +100,7 @@ module.exports = Robot = function Robot(opts) {
}
};
subclass(Robot, EventEmitter);
Utils.subclass(Robot, EventEmitter);
// Public: Generates a random name for a Robot.
//
@ -276,7 +275,7 @@ Robot.prototype.initAdaptor = function(adaptorName, connection, opts) {
connection: connection,
extraParams: opts
});
return proxyTestStubs(adaptor.commands(), testAdaptor);
return Utils.proxyTestStubs(adaptor.commands(), testAdaptor);
} else {
return adaptor;
}
@ -339,7 +338,7 @@ Robot.prototype.initDriver = function(driverName, device, opts) {
extraParams: opts
});
return proxyTestStubs(driver.commands(), testDriver);
return Utils.proxyTestStubs(driver.commands(), testDriver);
} else {
return driver;
}

View File

@ -8,7 +8,8 @@
"use strict";
var Adaptor = require('../adaptor');
var Adaptor = require('../adaptor'),
Utils = require('../utils');
var Loopback;
@ -16,7 +17,7 @@ module.exports = Loopback = function Loopback() {
Loopback.__super__.constructor.apply(this, arguments);
};
subclass(Loopback, Adaptor);
Utils.subclass(Loopback, Adaptor);
Loopback.prototype.commands = function() {
return ['ping'];

View File

@ -8,7 +8,8 @@
'use strict';
var Driver = require('../driver');
var Driver = require('../driver'),
Utils = require('../utils');
var Ping;
@ -16,7 +17,7 @@ module.exports = Ping = function Ping() {
Ping.__super__.constructor.apply(this, arguments);
};
subclass(Ping, Driver);
Utils.subclass(Ping, Driver);
Ping.prototype.commands = function() {
return ['ping'];

View File

@ -8,7 +8,8 @@
"use strict";
var Adaptor = require('../adaptor')
var Adaptor = require('../adaptor'),
Utils = require('../utils');
var TestAdaptor;
@ -16,6 +17,6 @@ module.exports = TestAdaptor = function TestAdaptor() {
TestAdaptor.__super__.constructor.apply(this, arguments);
};
subclass(TestAdaptor, Adaptor);
Utils.subclass(TestAdaptor, Adaptor);
TestAdaptor.adaptor = function(opts) { return new TestAdaptor(opts); };

View File

@ -8,7 +8,8 @@
'use strict';
var Driver = require('../driver');
var Driver = require('../driver'),
Utils = require('../utils');
var TestDriver;
@ -16,6 +17,6 @@ module.exports = TestDriver = function TestDriver() {
TestDriver.__super__.constructor.apply(this, arguments);
};
subclass(TestDriver, Driver);
Utils.subclass(TestDriver, Driver);
TestDriver.driver = function(opts) { return new TestDriver(opts); };

View File

@ -1,84 +1,12 @@
/*
* utils
* Cylon - Utils
* cylonjs.com
*
* Copyright (c) 2013 The Hybrid Group
* Licensed under the Apache 2.0 license.
*/
// Public: Monkey-patches Number to have Rails-like //seconds() function.
// Warning, due to the way the Javascript parser works, applying functions on
// numbers is kind of weird. See examples for details.
//
// Examples
//
// 2.seconds()
// //=> SyntaxError: Unexpected token ILLEGAL
//
// 10..seconds()
// //=> 10000
//
// (5).seconds()
// //=> 5000
// // This is the preferred way to represent numbers when calling these
// // methods on them
//
// Returns an integer representing time in milliseconds
Number.prototype.seconds = function() {
return this * 1000;
};
// Public: Alias for Number::seconds, see comments for that method
//
// Examples
//
// 1.second()
// //=> 1000
//
// Returns an integer representing time in milliseconds
Number.prototype.second = function() {
return this.seconds(this);
};
// Public: Convert value from old scale (start, end) to (0..1) scale
//
// start - low point of scale to convert value from
// end - high point of scale to convert value from
//
// Examples
//
// (5).fromScale(0, 10)
// //=> 0.5
//
// Returns an integer representing the scaled value
Number.prototype.fromScale = function(start, end) {
return (this - Math.min(start, end)) / (Math.max(start, end) - Math.min(start, end));
};
// Public: Convert value from (0..1) scale to new (start, end) scale
//
// start - low point of scale to convert value to
// end - high point of scale to convert value to
//
// Examples
//
// (0.5).toScale(0, 10)
// //=> 5
//
// Returns an integer representing the scaled value
Number.prototype.toScale = function(start, end) {
var i = this * (Math.max(start, end) - Math.min(start, end)) + Math.min(start, end);
if (i < Math.min(start, end)) {
return Math.min(start, end);
} else if (i > Math.max(start,end)){
return Math.max(start, end);
} else {
return i;
}
};
global.Utils = {
var Utils = module.exports = {
// Public: Alias to setInterval, combined with Number monkeypatches below to
// create an artoo-like syntax.
//
@ -142,10 +70,6 @@ global.Utils = {
}
},
// Copies
slice: [].slice,
hasProp: {}.hasOwnProperty,
// Public: Function to use for class inheritance. Copy of a CoffeeScript helper
// function.
//
@ -160,10 +84,14 @@ global.Utils = {
//
// Returns subclass
subclass: function subclass(child, parent) {
var ctor = function() { this.constructor = child; };
var ctor = function() {
this.constructor = child;
};
for (var key in parent) {
if (hasProp.call(parent, key)) { child[key] = parent[key]; }
if (Object.hasOwnProperty.call(parent, key)) {
child[key] = parent[key];
}
}
ctor.prototype = parent.prototype;
@ -183,8 +111,13 @@ global.Utils = {
//
// Returns base
proxyFunctionsToObject: function proxyFunctionsToObject(methods, target, base, force) {
if (base == null) { base = this; }
if (force == null) { force = false; }
if (base == null) {
base = this;
}
if (force == null) {
force = false;
}
var fn = function(method) {
return base[method] = function() {
@ -195,8 +128,9 @@ global.Utils = {
for (var i = 0; i < methods.length; i++) {
var method = methods[i];
if (!force) {
if (typeof base[method] === 'function') { continue; }
if (!force && typeof(base[method]) === 'function') {
continue;
}
fn(method);
@ -212,10 +146,15 @@ global.Utils = {
//
// Returns base
proxyTestStubs: function proxyTestStubs(methods, base) {
if (base == null) { base = this; }
if (base == null) {
base = this;
}
methods.forEach(function(method) {
base[method] = function() { return true; };
base[method] = function() {
return true;
};
base.commandList.push(method);
});
@ -239,28 +178,108 @@ global.Utils = {
//
// Returns a function wrapper
bind: function bind(fn, me) {
return function() { return fn.apply(me, arguments); };
return function() {
return fn.apply(me, arguments);
};
},
// Public: Adds all methods from Cylon.Utils directly to the global
// namespace.
// Public: Adds necessary utils to global namespace, along with base class
// extensions.
//
// Examples
//
// Number.prototype.seconds // undefined
// after // undefined
//
// Utils.bootstrap();
// (after === Utils.after) // true
//
// Number.prototype.seconds // [function]
// (after === Utils.after) // true
//
// Returns Cylon.Utils
bootstrap: function bootstrap() {
for (util in this) {
// we're not going to attach the 'bootstrap' method
if (!(util === "bootstrap")) {
global[util] = this[util];
}
}
global.every = this.every;
global.after = this.after;
global.constantly = this.constantly;
addCoreExtensions();
return this;
}
};
module.exports = Utils.bootstrap();
var addCoreExtensions = function addCoreExtensions() {
// Public: Monkey-patches Number to have Rails-like //seconds() function.
// Warning, due to the way the Javascript parser works, applying functions on
// numbers is kind of weird. See examples for details.
//
// Examples
//
// 2.seconds()
// //=> SyntaxError: Unexpected token ILLEGAL
//
// 10..seconds()
// //=> 10000
//
// (5).seconds()
// //=> 5000
// // This is the preferred way to represent numbers when calling these
// // methods on them
//
// Returns an integer representing time in milliseconds
Number.prototype.seconds = function() {
return this * 1000;
};
// Public: Alias for Number::seconds, see comments for that method
//
// Examples
//
// 1.second()
// //=> 1000
//
// Returns an integer representing time in milliseconds
Number.prototype.second = function() {
return this.seconds(this);
};
// Public: Convert value from old scale (start, end) to (0..1) scale
//
// start - low point of scale to convert value from
// end - high point of scale to convert value from
//
// Examples
//
// (5).fromScale(0, 10)
// //=> 0.5
//
// Returns an integer representing the scaled value
Number.prototype.fromScale = function(start, end) {
return (this - Math.min(start, end)) / (Math.max(start, end) - Math.min(start, end));
};
// Public: Convert value from (0..1) scale to new (start, end) scale
//
// start - low point of scale to convert value to
// end - high point of scale to convert value to
//
// Examples
//
// (0.5).toScale(0, 10)
// //=> 5
//
// Returns an integer representing the scaled value
Number.prototype.toScale = function(start, end) {
var i = this * (Math.max(start, end) - Math.min(start, end)) + Math.min(start, end);
if (i < Math.min(start, end)) {
return Math.min(start, end);
} else if (i > Math.max(start,end)){
return Math.max(start, end);
} else {
return i;
}
};
};
Utils.bootstrap();

View File

@ -3,7 +3,8 @@
var EventEmitter = require('events').EventEmitter;
var Adaptor = source("adaptor"),
Logger = source('logger');
Logger = source('logger'),
Utils = source('utils');
describe("Adaptor", function() {
var connection = new EventEmitter;

View File

@ -4,7 +4,8 @@ var express = require('express'),
https = require('https'),
fs = require('fs');
var API = source('api');
var API = source('api'),
Utils = source('utils');
describe("API", function() {
var api, opts;

View File

@ -1,6 +1,7 @@
"use strict";
var Basestar = source('basestar');
var Basestar = source('basestar'),
Utils = source('utils');
var EventEmitter = require('events').EventEmitter;
@ -34,7 +35,7 @@ describe('Basestar', function() {
this.proxyMethods(methods, this.testInstance, this, true);
}
subclass(TestClass, Basestar);
Utils.subclass(TestClass, Basestar);
it('can alias methods', function() {
var testclass = new TestClass;
@ -70,8 +71,8 @@ describe('Basestar', function() {
});
}
subclass(ProxyClass, Basestar);
subclass(EmitterClass, Basestar);
Utils.subclass(ProxyClass, Basestar);
Utils.subclass(EmitterClass, Basestar);
it("proxies events from one class to another", function() {
var eventSpy = spy(),

View File

@ -1,7 +1,8 @@
"use strict";
var Robot = source("robot"),
Logger = source('logger');
Logger = source('logger'),
Utils = source('utils');
describe("Cylon.Connection", function() {
var robot = new Robot({

View File

@ -1,6 +1,7 @@
"use strict";
var Cylon = source("cylon");
var Cylon = source("cylon"),
Utils = source('utils');
var API = source('api'),
Logger = source('logger'),

View File

@ -3,7 +3,8 @@
var Ping = source('test/ping'),
Device = source("device"),
Robot = source("robot"),
Logger = source('logger');
Logger = source('logger'),
Utils = source('utils');
describe("Cylon.Device", function() {
var robot = new Robot({

View File

@ -2,7 +2,8 @@
var fs = require('fs');
var DigitalPin = source('io/digital-pin');
var DigitalPin = source('io/digital-pin'),
Utils = source('utils');
describe("Cylon.IO.DigitalPin", function() {
var pin = new DigitalPin({ pin: '4', mode: 'w' })
@ -195,12 +196,12 @@ describe("Cylon.IO.DigitalPin", function() {
context("if the mode isn't 'r'", function() {
before(function() {
stub(global, 'every');
stub(Utils, 'every');
stub(pin, '_setMode');
});
after(function() {
global.every.restore();
Utils.every.restore();
pin._setMode.restore();
});

View File

@ -3,7 +3,8 @@
var EventEmitter = require('events').EventEmitter;
var Driver = source("driver"),
Logger = source('logger');
Logger = source('logger'),
Utils = source('utils');
describe("Driver", function() {
var device = {

View File

@ -1,6 +1,7 @@
'use strict';
var Logger = source('logger');
var Logger = source('logger'),
Utils = source('utils');
describe('Logger', function() {
after(function() {

View File

@ -2,7 +2,8 @@
var Device = source('device'),
Connection = source('connection'),
Robot = source("robot");
Robot = source("robot"),
Utils = source('utils');
describe("Robot", function() {
var work = spy();

View File

@ -95,20 +95,6 @@ describe("Utils", function() {
});
});
describe("#slice", function() {
it("performs array slices", function() {
var arr = [1, 2, 3, 4, 5];
expect(slice.call(arr, 1)).to.be.eql([2, 3, 4, 5]);
});
});
describe("hasProp", function() {
it("checks objects have properties", function() {
var obj = { test: 'test' };
expect(hasProp.call(obj, 'test')).to.be.true;
});
});
describe("#subclass", function() {
var BaseClass = (function() {
function BaseClass(opts) {
@ -123,7 +109,7 @@ describe("Utils", function() {
})();
var SubClass = (function(klass) {
subclass(SubClass, klass);
utils.subclass(SubClass, klass);
function SubClass(opts) {
SubClass.__super__.constructor.apply(this, arguments);
@ -164,7 +150,7 @@ describe("Utils", function() {
function TestClass() {
this.self = this;
this.testInstance = new ProxyClass;
proxyFunctionsToObject(methods, this.testInstance, this.self, true);
utils.proxyFunctionsToObject(methods, this.testInstance, this.self, true);
}
return TestClass;
@ -190,7 +176,7 @@ describe("Utils", function() {
var methods = ["hello", "goodbye"],
base = { commandList: [] };
proxyTestStubs(methods, base);
utils.proxyTestStubs(methods, base);
expect(base.commandList).to.be.eql(methods);
});
@ -198,7 +184,7 @@ describe("Utils", function() {
var methods = ["hello", "goodbye"],
base = { commandList: [] };
expect(proxyTestStubs(methods, base)).to.be.eql(base);
expect(utils.proxyTestStubs(methods, base)).to.be.eql(base);
});
});
@ -208,14 +194,14 @@ describe("Utils", function() {
it("binds the 'this' scope for the method", function() {
proxy.boundMethod = function() { return this.hello; };
proxy.boundMethod = bind(proxy.boundMethod, me);
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 = bind(proxy.boundMethod, me);
proxy.boundMethod = utils.bind(proxy.boundMethod, me);
expect(proxy.boundMethod("Hello", "World")).to.eql(["Hello", "World"]);
})