Skip to content

Instantly share code, notes, and snippets.

@janbiasi
Last active January 6, 2016 10:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save janbiasi/6980bf0a3a3ca2d95bd3 to your computer and use it in GitHub Desktop.
Save janbiasi/6980bf0a3a3ca2d95bd3 to your computer and use it in GitHub Desktop.
Node Type let you easily work with types and its core possibilities such as enumerability, writability and readonly settings. Properties can be declared with a specific type and a livetime validator.
var out = function(val) { console.log(val); };
var User = Type.create({
properties: {
name: String,
id: {
type: Number,
enumerable: false
},
class: {
type: String,
value: 'USER',
readonly: true,
wrtiable: false,
enumerable: false
}
}
});
// Testing User.name
User.name = 'John Doe';
User.name = 2048; // TypeError: String needed, got number ...
out(User.name); // "John Doe"
// Testing User.class
User.class = 'HUMAN'; // ReadOnly Error ...
// Enumerability of properties
for(var key in User) {
out(key, User[key]);
}
// { name: John Doe, __store__: {...}, __config__: {...} }
/**!
* Node Type let you easily work with types and its core possibilities
* such as enumerability, writability and readonly settings. Properties
* can be declared with a specific type and a livetime validator.
*
* @name node-type
* @author Jan Biasi <biasijan@gmail.com>
* @license MIT License
*
* YOU CAN INSTALL TYPE.JS WITH NPM, USE THE FOLLOWING COMMAND
* NPM INSTALL --SAVE NODE-TYPE
*/
(function() {
'use strict';
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
/**
* Main Type class, do not instanciate manually, use
* the create method instead.
*
* @see Type.create (line 216)
* @param {Object} definition Definition for typeset
*/
function Type(definition) {
var self = this;
definition = definition || null;
/**
* Default settings for all Type instances, can be overwritten in the
* decleration of each individual type. These settings are the default
* settings for each instances.
* @type {Object}
*
* errorOnReadOnlyAccess turn on to throw errors on ReadOnly access
* defaultWritable if the defined properties should be writeable
* defaultEnumerable if the defined properties should be enumerable
*/
self.__config__ = {
errorOnReadOnlyAccess: false,
defaultWritable: true,
defaultEnumerable: true
};
/**
* Storage for variables, because values can't be stored in the Type itself
* causing a endless loop and stack size exceeded error.
* @type {Object}
*/
self.__store__ = {};
if(typeof definition === 'object') {
/**
* You can define properties within the properties object,
* like this:
*
* var myType = Type.create({
* properties: {
* code: Number,
* address: {
* type: String,
* enumerable: false
* }
* }
* });
*
*/
var props = definition.properties;
if(typeof props === 'object') {
for(var field in props) {
self._addProperty(props[field], field);
}
} else if(Array.isArray(props)) {
/**
* You can define properties within the properties array,
* like this:
*
* var myType = Type.create({
* properties: [
* {
* name: 'buylist',
* type: Array
* },
* {
* name: 'due-until',
* type: String,
* writable: false
* }
* ]
* });
*
*/
props.forEach(function(propertie) {
self._addProperty(propertie);
});
} else {
throw new Error('No properites found to define, please add some.');
}
}
};
/**
* Validates a value with an expected type which is needed.
*
* @param {*} expect Expected class
* @param {*} got Input value
* @return {Boolean}
*/
Type.prototype._validateTypes = function(expect, got) {
switch(expect.name.toLowerCase()) {
case 'number':
return (typeof got === 'number');
case 'string':
return (typeof got === 'string');
case 'function':
var gt = {};
if(got && gt.toString.call(got) === '[object Function]') {
return true;
}
case 'array':
return Array.isArray(got);
case 'object':
return (typeof got === 'object');
default:
return false;
}
};
/**
* Wrapper for adding a new property to the current Type instance,
* generating the configuration stub and defining the property with its
* settings onto the Type wrapper.
*
* @param {Object} propset Property settings
* @param {String?} altName Direct assigned name
*/
Type.prototype._addProperty = function(propset, altName) {
var configuration = this._generateConfiguration(propset, altName);
var property = altName ? altName : obj.name;
Object.defineProperty(this, property, configuration);
};
/**
* Generates the defineProperty valid configuration sets, including
* the getter and setter (with their validators inside).
*
* @param {Object} conf Main configuration object for property
* @param {String?} altName Alternative name, if used direct object assignments
* @return {Object} Configuration stub
*/
Type.prototype._generateConfiguration = function(conf, altName) {
var self = this;
if(typeof conf === 'object') {
conf.name = conf.name || altName;
} else {
var typesave = conf;
conf = {
type: typesave,
name: altName
};
}
var getter = function(value) {
return self.__store__[conf.name];
};
var setter = function(value) {
if(typeof conf.type !== 'undefined' && conf.type !== null) {
if(!self._validateTypes(conf.type, value)) {
throw new TypeError(
'Type expected ' + conf.type.name + ' but received ' +
typeof value + ' instead. Please use the right type.'
);
}
if(conf.readonly) {
if(self.__config__.errorOnReadOnlyAccess === true) {
throw new Error(
'Property ' + conf.name + ' is set to readonly ' +
'and can\'t be modified.'
);
} else {
return;
}
}
}
self.__store__[conf.name] = value;
};
if(conf.value !== undefined) {
// Setting initial values if is defined in propset
if(self._validateTypes(conf.type, conf.value)) {
self.__store__[conf.name] = conf.value;
}
}
return {
enumerable: conf.enumerable || self.__config__.defaultEnumerable,
configurable: conf.configurable || self.__config__.defaultWritable,
get: getter,
set: setter
};
};
/**
* Update a default configuration for all types
*
* @param {String} key Configuration key
* @param {*} value New value for configuration
*/
Type.prototype.configure = function(key, value) {
var setting = this.__config__[key];
if(setting !== undefined) {
this.__config__[key] = value;
}
};
/**
* Wrapper for instanciating new Types
*
* @param {Object} definition Typeset definition
* @return {Type} Type-instance
*/
Type.create = function(definition) {
return new Type(definition);
};
if(typeof module !== 'undefined' && module.exports) {
// Export the Type module for Node.js
exports = module.exports = Type;
} else {
// Make the Type public in Browser windows
window.Type = Type;
}
})();
/**!
* Node Type let you easily work with types and its core possibilities
* such as enumerability, writability and readonly settings. Properties
* can be declared with a specific type and a livetime validator.
*
* @name node-type
* @author Jan Biasi <biasijan@gmail.com>
* @license MIT License
*
* YOU CAN INSTALL TYPE.JS WITH NPM, USE THE FOLLOWING COMMAND
* NPM INSTALL --SAVE NODE-TYPE
*/
!function(){"use strict";function e(e){var t=this;if(e=e||null,t.__config__={errorOnReadOnlyAccess:!1,defaultWritable:!0,defaultEnumerable:!0},t.__store__={},"object"==typeof e){var r=e.properties;if("object"==typeof r)for(var n in r)t._addProperty(r[n],n);else{if(!Array.isArray(r))throw new Error("No properites found to define, please add some.");r.forEach(function(e){t._addProperty(e)})}}}e.prototype._validateTypes=function(e,t){switch(e.name.toLowerCase()){case"number":return"number"==typeof t;case"string":return"string"==typeof t;case"function":var r={};if(t&&"[object Function]"===r.toString.call(t))return!0;case"array":return Array.isArray(t);case"object":return"object"==typeof t;default:return!1}},e.prototype._addProperty=function(e,t){var r=this._generateConfiguration(e,t),n=t?t:obj.name;Object.defineProperty(this,n,r)},e.prototype._generateConfiguration=function(e,t){var r=this;if("object"==typeof e)e.name=e.name||t;else{var n=e;e={type:n,name:t}}var o=function(t){return r.__store__[e.name]},a=function(t){if("undefined"!=typeof e.type&&null!==e.type){if(!r._validateTypes(e.type,t))throw new TypeError("Type expected "+e.type.name+" but received "+typeof t+" instead. Please use the right type.");if(e.readonly){if(r.__config__.errorOnReadOnlyAccess===!0)throw new Error("Property "+e.name+" is set to readonly and can't be modified.");return}}r.__store__[e.name]=t};return void 0!==e.value&&r._validateTypes(e.type,e.value)&&(r.__store__[e.name]=e.value),{enumerable:e.enumerable||r.__config__.defaultEnumerable,configurable:e.configurable||r.__config__.defaultWritable,get:o,set:a}},e.prototype.configure=function(e,t){var r=this.__config__[e];void 0!==r&&(this.__config__[e]=t)},e.create=function(t){return new e(t)},"undefined"!=typeof module&&module.exports?exports=module.exports=e:window.Type=e}();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment