diff --git a/.jshintrc b/.jshintrc index a55b4ddc8..db63befe3 100644 --- a/.jshintrc +++ b/.jshintrc @@ -8,6 +8,7 @@ //"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually //"unused": true, // Check for unused functions and variables "loopfunc": true, // allow functions to be defined in loops - //"expr": true, // allow ternery operator syntax... - "sub": true // don't warn that foo['bar'] should be written as foo.bar + //"expr": true, // allow ternery operator syntax... + "sub": true, // don't warn that foo['bar'] should be written as foo.bar + "proto": true // allow setting of __proto__ in node < v0.12 } diff --git a/red/runtime/nodes/registry/registry.js b/red/runtime/nodes/registry/registry.js index fadf1f51b..a437981be 100644 --- a/red/runtime/nodes/registry/registry.js +++ b/red/runtime/nodes/registry/registry.js @@ -326,6 +326,27 @@ function getCaller(){ return stack[0].getFileName(); } +function inheritNode(constructor) { + if(Object.getPrototypeOf(constructor.prototype) === Object.prototype) { + util.inherits(constructor,Node); + } else { + var proto = constructor.prototype; + while(Object.getPrototypeOf(proto) !== Object.prototype) { + proto = Object.getPrototypeOf(proto); + } + //TODO: This is a partial implementation of util.inherits >= node v5.0.0 + // which should be changed when support for node < v5.0.0 is dropped + // see: https://github.com/nodejs/node/pull/3455 + proto.constructor.super_ = Node; + if(Object.setPrototypeOf) { + Object.setPrototypeOf(proto, Node.prototype); + } else { + // hack for node v0.10 + proto.__proto__ = Node.prototype; + } + } +} + function registerNodeConstructor(type,constructor) { if (nodeConstructors[type]) { throw new Error(type+" already registered"); @@ -333,7 +354,10 @@ function registerNodeConstructor(type,constructor) { //TODO: Ensure type is known - but doing so will break some tests // that don't have a way to register a node template ahead // of registering the constructor - util.inherits(constructor,Node); + if(!(constructor.prototype instanceof Node)) { + inheritNode(constructor); + } + nodeConstructors[type] = constructor; events.emit("type-registered",type); } diff --git a/test/red/runtime/nodes/registry/registry_spec.js b/test/red/runtime/nodes/registry/registry_spec.js index 02e7b1d95..afe1b46aa 100644 --- a/test/red/runtime/nodes/registry/registry_spec.js +++ b/test/red/runtime/nodes/registry/registry_spec.js @@ -20,8 +20,12 @@ var sinon = require("sinon"); var typeRegistry = require("../../../../../red/runtime/nodes/registry/registry"); +var Node = require("../../../../../red/runtime/nodes/Node"); + var events = require("../../../../../red/runtime/events"); +var inherits = require("util").inherits; + describe("red/nodes/registry/registry",function() { afterEach(function() { @@ -428,9 +432,10 @@ describe("red/nodes/registry/registry",function() { }); describe('#registerNodeConstructor', function() { - function TestNodeConstructor() { - } + var TestNodeConstructor; beforeEach(function() { + TestNodeConstructor = function TestNodeConstructor() { + }; sinon.stub(events,'emit'); }); afterEach(function() { @@ -452,7 +457,27 @@ describe("red/nodes/registry/registry",function() { typeRegistry.registerNodeConstructor('node-type',TestNodeConstructor); }).should.throw("node-type already registered"); events.emit.calledOnce.should.be.true; - }) + }); + it('extends a constructor with the Node constructor', function() { + TestNodeConstructor.prototype.should.not.be.an.instanceOf(Node); + typeRegistry.registerNodeConstructor('node-type',TestNodeConstructor); + TestNodeConstructor.prototype.should.be.an.instanceOf(Node); + }); + it('does not override a constructor\'s prototype', function() { + function Foo(){}; + inherits(TestNodeConstructor,Foo); + TestNodeConstructor.prototype.should.be.an.instanceOf(Foo); + TestNodeConstructor.prototype.should.not.be.an.instanceOf(Node); + + typeRegistry.registerNodeConstructor('node-type',TestNodeConstructor); + + TestNodeConstructor.prototype.should.be.an.instanceOf(Node); + TestNodeConstructor.prototype.should.be.an.instanceOf(Foo); + + typeRegistry.registerNodeConstructor('node-type2',TestNodeConstructor); + TestNodeConstructor.prototype.should.be.an.instanceOf(Node); + TestNodeConstructor.prototype.should.be.an.instanceOf(Foo); + }); }); });