Created
February 4, 2011 22:09
-
-
Save jtacoma/811874 to your computer and use it in GitHub Desktop.
One way to translate a spec(ification), that is a plain old object, into a proper class. The resulting class is to be instantiated from an object with a fixed set of properties (e.g. a table row) and to calculate other properties based on those received.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Given a prototype-like object, create a class based on that object's | |
// properties. | |
function buildClass(spec) { | |
// Throw an exception of the argument is inappropriate: | |
if (typeof(spec) != 'object' || spec === null) | |
throw 'expected an object.'; | |
var empty = true; | |
for (var name in spec) { empty = false; break; } | |
if (empty) | |
throw 'expected a non-empty object.'; | |
// Define a constructor that will set all owned properties based on the | |
// spec (as a prototype-like object) and the arguments passed in to the | |
// constructor: | |
var constructor = function (args) { | |
for (var name in spec) | |
if (typeof(spec[name]) != 'undefined' && spec[name].hasOwnProperty('prototype')) | |
// A function on the spec is evaluated over the object thus far | |
// defined, and the return value is stored on the object: | |
this[name] = spec[name](this); | |
else if (typeof(spec[name]) != 'undefined') | |
// All other defined properties on the spec are 'static' fields: | |
continue; | |
else if (!(name in args) || typeof(args[name]) == 'undefined') | |
// An undefined property on the spec indicates a required argument | |
// to the constructor: | |
throw 'argument contains no such property: ' + name; | |
else | |
// Copy supplied properties matching spec into the new object: | |
this[name] = args[name]; | |
}; | |
// Set constant 'static' fields on the prototype: | |
for (var name in spec) | |
if (typeof(spec[name]) != 'undefined') | |
constructor.prototype[name] = spec[name]; | |
// Finally, we're done: | |
return constructor; | |
} | |
exports.buildClass = buildClass; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var vows = require('vows'), | |
assert = require('assert'); | |
var lib = require('./json2class.js'); | |
vows.describe('json2class').addBatch({ | |
'Attempting to build a class from...': { | |
'undefined must throw an exception.': function() { | |
assert.throws (function () { lib.buildClass(); }); | |
assert.throws (function () { lib.buildClass(undefined); }); | |
}, | |
'null must throw an exception.': function() { | |
assert.throws (function () { lib.buildClass(null); }); | |
}, | |
'number must throw an exception.': function() { | |
assert.throws (function () { lib.buildClass(42); }); | |
}, | |
'string must throw an exception.': function() { | |
assert.throws (function () { lib.buildClass('a string'); }); | |
}, | |
'an empty object must throw an exception.': function() { | |
assert.throws (function () { lib.buildClass({}); }); | |
}, | |
}, | |
'A class built from an object with an undefined property, a defined property, and a function...': { | |
topic: function() { | |
var cls = lib.buildClass({ | |
a: undefined, | |
b: 'defined', | |
c: function(obj) { return '(' + obj.a + ',' + obj.b + ')' }, | |
}); | |
return cls; | |
}, | |
'must be callable.': function(topic) { | |
assert.ok (topic); | |
assert.ok ('call' in topic); | |
}, | |
'instantiated with no arguments, must throw an exception.': function(topic) { | |
assert.throws (function () { new topic(); }); | |
}, | |
'instantiated with an argument, must copy a value for the spec\'s undefined property from that argument.': function(topic) { | |
assert.equal (new topic({a:1}).a, 1); | |
}, | |
'instantiated with an argument that has no such property, must throw an exception.': function(topic) { | |
assert.throws (function() { new topic({}); }); | |
}, | |
'instantiated with an argument that has a value for the defined property, must ignore that value.': function(topic) { | |
assert.equal (new topic({a:1,b:2}).b, 'defined'); | |
}, | |
'instantiated, must call the spec\'s function to determine a value for the associated property.': function(topic) { | |
assert.equal (new topic({a:1,b:2}).c, '(1,defined)'); | |
}, | |
} | |
}).run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment