Jean-Yves Didier

component as a true class

This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
{
"name": "arcsjs",
"version": "0.9.5",
"version": "0.9.6",
"description": "Augmented Reality Component System in web browser and node environment",
"homepage": "http://arcs.ibisc.fr",
"repository": {
......@@ -37,6 +37,7 @@
"eslint-webpack-plugin": "^3.0.1",
"jsdoc-webpack-plugin": "^0.3.0",
"mocha": "^9.1.4",
"val-loader": "^4.0.0"
"val-loader": "^4.0.0",
"webpack-cli": "^4.9.2"
}
}
......
......@@ -8,13 +8,75 @@
*
* @class Component
*/
var Component = {
class Component {
/** Error message */
SourceIsNotComponent : {message : "Source is not a component"},
//const SourceIsNotComponent = {message : "Source is not a component"};
/** Error message */
UndefinedSignal : {message : "Signal is not defined"},
//const UndefinedSignal = {message : "Signal is not defined"};
/** Error message */
UndefinedSlot : {message : "Slot is not defined"},
//const UndefinedSlot = {message : "Slot is not defined"};
//slots = [];
//signals = {};
constructor(sltList, sgnList) {
this.slots = [];
this.signals = {};
if (sltList !== undefined) {
this.slot(sltList);
}
if (sgnList !== undefined) {
this.signal(sgnList);
}
}
slotList() {
return this.slots;
}
signalList() {
return Object.keys(this.signals);
}
emit(signal) {
let slt, func, obj;
let args = Array.prototype.slice.call(arguments,1);
for (slt in this.signals[signal]) {
func = this.signals[signal][slt].func;
obj = this.signals[signal][slt].obj;
func.apply(obj, args);
}
}
slot(slot, func) {
let self = this;
if (slot instanceof Array) {
slot.forEach(s => {
if (!self.slots.includes(s))
self.slot(s);
});
} else {
if (!this.slots.includes(slot)) {
this.slots.push(slot);
}
if (func!== undefined)
this[slot]= func;
}
}
signal(signal) {
let self = this;
if (signal instanceof Array) {
signal.forEach(s => {self.signals[s] = [];});
} else {
this.signals[signal] = [];
}
}
/**
* External constructor: give component traits to any constructor.
*
......@@ -32,7 +94,7 @@ var Component = {
* @param sltList {string[]} names of functions designated as slots, may be empty.
* @param sgnList {string[]} names of functions designated as signals, may be empty.
*/
create : function (name, sltList, sgnList) {
static create(name, sltList, sgnList) {
if (name.prototype === undefined) {
console.error("Cannot create such a component");
return 0;
......@@ -191,12 +253,13 @@ var Component = {
name.signal(sgnList);
}
return name;
},
}
/**
* Checks if the given prototype has traits of a component
* @param name {string} name of the prototype
* @TODO we need to check if this is used and refactor this method
*/
check : function (name) {
static check(name) {
if (name.prototype === undefined) {
return false;
}
......@@ -205,7 +268,7 @@ var Component = {
return false;
}
return true;
},
}
/**
* Connects two different components by using their signal and slots
* @param source {object} component sending data
......@@ -213,7 +276,7 @@ var Component = {
* @param destination {object} component receiving data
* @param slt {string} name of the slot to connect
*/
connect : function (source, signal, destination, slt) {
static connect(source, signal, destination, slt) {
var orig, p;
// here we can perform various checks.
if (source.signals === undefined) {
......@@ -240,7 +303,7 @@ var Component = {
}
}
source.signals[signal].push({obj: destination, func: destination[slt]});
},
}
/**
* Diconnects a signal/slot connection between two components
* @param source {object} component sending data
......@@ -248,7 +311,7 @@ var Component = {
* @param destination {object} component receiving data
* @param slt {string} name of the slot to connect
*/
disconnect : function (source, signal, destination, slt) {
static disconnect(source, signal, destination, slt) {
var i;
for (i = 0; i < source.signals[signal].length; i++) {
if (source.signals[signal][i].obj === destination) {
......@@ -258,31 +321,40 @@ var Component = {
}
}
}
},
}
/**
* Invokes a specific slot of a given component
* @param destination {object} component upon which invocation is performed
* @param slt {string} name of the slot to invoke
* @param value {mixed} value to input
*/
invoke : function (destination, slt, value) {
static invoke(destination, slt, value) {
if (destination[slt] === undefined) {
throw Component.UndefinedSlot;
}
var func = destination[slt];
func.apply(destination, value);
},
}
/**
* Specific hook that can be called when initializing a component
* @param component {object} prototype of the component
* @param obj {object} the actual object
*/
config : function (component, obj) {
static config(component, obj) {
if (typeof component.config === 'function') {
component.config(obj);
}
}
};
/** Error message */
Component.SourceIsNotComponent = {message : "Source is not a component"};
/** Error message */
Component.UndefinedSignal = {message : "Signal is not defined"};
/** Error message */
Component.UndefinedSlot = {message : "Slot is not defined"};
export default Component;
......
......@@ -43,7 +43,7 @@ let signalHandler = function(chai, utils) {
let obj = this._obj;
expectedSignals.forEach( function(signal) {
let Handler = new ARCS.Component.create(
let Handler = ARCS.Component.create(
function() {
this.triggered = false;
this.data = [];
......@@ -98,7 +98,7 @@ let signalHandler = function(chai, utils) {
chai.use(signalHandler);
describe('components', function() {
describe('mixin components', function() {
it('should have property slots', function() {
let C = ARCS.Component.create(function() {});
let c = new C();
......@@ -161,6 +161,97 @@ describe('components', function() {
});
describe('class components', function() {
it('should have property slots', function() {
let c = new ARCS.Component();
expect(c).to.have.property('slots');
});
it('should have property signals', function() {
let c = new ARCS.Component();
expect(c).to.have.property('signals');
});
it ('should return correct slot list', function() {
class C extends ARCS.Component {
constructor() { super(['mySlot']) };
mySlot() {}
};
let c = new C();
expect(c.slotList()).to.deep.equal(["mySlot"]);
});
it ('should return correct signal list', function() {
class C extends ARCS.Component {
constructor() { super([],['mySignal']);}
};
let c = new C();
expect(c.signalList()).to.deep.equal(["mySignal"]);
});
it ('should accept creating local slots', function() {
let c = new ARCS.Component();
c.slot('mySlot', () => {});
//expect(c.prototype.slots).to.deep.equal([]);
expect(c.slots).to.deep.equal(['mySlot']);
});
it ('should accept creating local signals', function() {
let c = new ARCS.Component();
c.signal('mySignal');
//expect(C.prototype.signals).to.deep.equal({});
expect(c.signals).to.have.property('mySignal');
});
it ('should be a component', function() {
let c = new ARCS.Component();
expect(c).to.be.a.component;
});
it ('should emit signal', function() {
class A extends ARCS.Component {
constructor() { super(['slotA'], ['signalA']); }
slotA() {
this.emit('signalA');
}
};
let a = new A();
expect(a).to.emit('signalA').when('slotA');
});
});
describe('connection', function() {
it ('should connect', function() {
let A = ARCS.Component.create(function() {
let self = this;
this.mySlot = function() {
self.emit('mySignal');
}
}, [ 'mySlot' ], ['mySignal']
);
class B extends ARCS.Component {
constructor() {
super(['mySlot']);
this.success = false;
}
mySlot() {
this.success = true;
}
};
let a = new A();
let b = new B();
ARCS.Component.connect(a,'mySignal',b,'mySlot');
a.mySlot();
expect(b.success).to.deep.equal(true);
});
});
describe('statemachine', function() {
......