Loading [MathJax]/extensions/TeX/AMSsymbols.js

Saturday, April 11, 2015

JavaScript "classes" my way

I group information about an object in to a "unit" object which holds information about the inputs to make the object, the interface of the object created, the functional constructor for the object, and unit testing for the object. My motivation was to build as much documentation and testing about the object in to the code itself. Validation of the inputs and created objects can also be done easily with some helper functions as long as the same convention is followed. More complicated hierarchies can be created easily with this model, without really introducing any new syntax or dealing with prototypes. I'm assuming something like QUnit is used for unit testing.

var UnitExample = {
required : {
requiredSpecProperty : {type : "property type", like : "example"}
},
options : {
optionalSpecProperty : {type : "property type", like : "example"}
},
interface : {
returnedObjectProperty : "value of same type as returned by make"
},
addsTo: [], // units from which this interface extends
// functional constructor
make : function(spec){
validateSpec(spec, UnitExample.required);
var localValue = spec.requiredSpecProperty;
// return implemented interface
return {
returnedObjectProperty : localValue
};
},
// unit tests for this Unit
unitTest : function(assert){
var testObj = UnitExample.make({
requiredSpecProperty : "input stuff."
});
unitTestInterface(assert, testObj, UnitExample);
assert.equal(testObj.returnedObjectProperty, "input stuff.", "example test");
}
};
view raw unitexample.js hosted with ❤ by GitHub
var UnitExampleExtended = {
required : {
requiredSpecProperty : {type : "property type", like : "example"}
},
options : {},
interface : {
sayHi : function(){}
},
addsTo: [UnitExample], // units from which this interface extends
// functional constructor
make : function(spec){
validateSpec(spec, UnitExampleExtended.required);
// extend from UnitExample
var output = UnitExample.make(spec);
// implement the extension to interface
output.sayHi = function() {
return "hi";
};
return output;
},
// unit tests for this Unit
unitTest : function(assert){
var testObj = UnitExampleExtended.make({
requiredSpecProperty : "input stuff."
});
unitTestInterface(assert, testObj, UnitExampleExtended);
assert.equal(testObj.returnedObjectProperty, "input stuff.", "example test");
assert.equal(testObj.sayHi(), "hi", "example test 2");
}
};
var validateInterface = function(instance, definition) {
if (!definition.hasOwnProperty("interface")){
throw "interface must exist to validate."
}
for(var prop in definition.interface) {
if (!instance.hasOwnProperty(prop)) {
throw "interface property " + prop + " must be implemented."
}
}
if (definition.hasOwnProperty("addsTo")) {
for(var i=0; i < definition.addsTo.length; i++) {
validateInterface(instance, definition.addsTo[i]);
}
}
};
var validateSpec = function (spec, required) {
var errs;
for(var prop in required) {
if (!spec.hasOwnProperty(prop)) {
errs = errs || "Required: ";
errs += "spec." + prop + " {" + required[prop].type + "}, ";
}
}
if (errs)
{
throw errs;
}
}
var unitTestInterface = function(assert, instance, definition) {
assert.ok(definition.hasOwnProperty("interface"), "unit definition has interface.");
for(var prop in definition.interface) {
assert.ok(instance.hasOwnProperty(prop),
"interface property " + prop + " implemented.");
}
if (definition.hasOwnProperty("addsTo")) {
for(var i=0; i < definition.addsTo.length; i++) {
unitTestInterface(assert, instance, definition.addsTo[i]);
}
}
};
view raw unithelpers.js hosted with ❤ by GitHub

No comments:

Post a Comment