UNPKG

class-o-mat

Version:

ALPHA VERSION ONLY: class-o-mats will help to create getter-setter functionality with validations that can be attached to a container class for easier coding. Some basic validations are included. class-o-mats also has the ability to store params as bloc

653 lines (535 loc) 18.5 kB
import Bunyan from 'bunyan'; const log = Bunyan.createLogger({name: "ClassOMat"}); import mixin from 'mixin-decorator'; import clonedeep from 'lodash.clonedeep'; import uuid from 'uuid/v4'; import _eventMethods from './_event-methods-o-mat'; import _apiMethods from './_api-methods-o-mat'; import { getterSetter as gs, toggler as tg } from './class-o-mat-field-functions'; import { getterSetter } from '../methods/field-functions'; import { NONE, CLASS_O_MAT, TO_SET } from '../constants/types'; import ClassOMatGroup from './class-o-mat-group'; import FieldOMat from './field-o-mat'; import ApiFieldOMat from './api-field-o-mat'; import ApiMethodOMat from './api-method-o-mat'; import ChildOMatAdder from './child-o-mat-adder'; import RadioOMat from './radio-o-mat'; import buildClassMethods from '../methods/build-class'; @mixin(_eventMethods, _apiMethods) class ClassOMat { static TYPE = CLASS_O_MAT; TYPE = CLASS_O_MAT; static VERSION = '0.6.1'; VERSION = '0.6.1'; constructor(key='classOMat that needs a key', parent=NONE) { const group = ( parent === NONE) ? new ClassOMatGroup() : parent.group(); class ClassOMatDefaultBaseClass {}; this._params = { key, id : NONE, uid : uuid(), pluginKeys : [], cacheOMats : false, accessorGetter : getterSetter, FieldOMat, fieldOMats : [], fieldOMatsIndex : {}, apiFields : [], paramObjectKey : '_params', paramKeyPrefix : '___', createParamsAccessor : true, createKeysAccessor : true, group, childOMats : {}, childOMatInfos : {}, copyToChildFields : [], parentKey : '___parent', needsParentAccessor : false, parentId : parent === NONE ? NONE : parent.id(), BaseClass : ClassOMatDefaultBaseClass, BuiltClass : NONE, defaults : {___pending:[]}, mixins : { ___isClassOMat: ()=>true, }, buildMethods : buildClassMethods, ON_GET : NONE, ON_SET : NONE, ON_DELETE : NONE, } const [isNew, id] = group.add(this); this._params = { ...this._params, id, group } } // API TO BUILD THE CLASS TO BE GENERATED /** * builds the class for this param and all the * child params .. returns all as an object. * @return {[type]} [description] */ $() { const classGroup = this._params.group.$(); const Class = classGroup.get(this.id()); const test = new Class(); return classGroup.get(this.id()); } /** * Build the class with the FieldSets in the ClassOMat * This should be used by Group but it available in case. * @param {Class} BaseClass The base class to have the methods added. * @return {Class} The base class with the methods added. */ buildClass(classGroup=NONE, classOMatGroup=NONE) { const { _params } = this; let { buildMethods, defaults, mixins } = _params; let workingArguments = {classOMat: this, mixins, defaults, params: _params} let WorkingClass; [WorkingClass, buildMethods] = buildMethods.build(workingArguments, buildMethods); const BuiltClass = WorkingClass; this.BuiltClass(BuiltClass); return BuiltClass; } // API FOR DEFINING THIS CLASS_O_MAT /** * getter setter for id * @param {number} id * @return {number|ClassOMat} key, */ id(...p) { return gs(this, 'id', ...p) }; uid(...p) { return gs(this, 'uid', ...p) }; /** * If true, this class will have a reference * to the ClassOMatGroup that this ClassOMat belongs to. * This is needed to modify * @param {[type]} cacheChildOMats [description] * @return {[type]} [description] */ cacheOMats(cacheOMats=NONE) { if (cacheOMats === NONE) return this._params.cacheOMats; this._params.cacheOMats = cacheOMats === true ? true : false; return this; } // API TO SPECIFY THE CLASS TO BE GENERATED /** * Set the BaseClass for this ClassOMat * @param {Class} BaseClass */ BaseClass(BaseClass=NONE) { return gs(this, 'BaseClass', BaseClass); } /** * Set the BuiltClass for this ClassOMat * @param {Class} BuiltClass */ BuiltClass(BuiltClass=NONE) { return gs(this, 'BuiltClass', BuiltClass); } defaultAccessor(accessorGetter) { return gs(this, 'accessor', accessorGetter) } /** * toggler for createParamsAccessor * This will create a parameters accessor in the BuiltClass * default = true * under the function name __params * @return {object|ClassOMat} */ createParamsAccessor() { return tg(this, 'createParamsAccessor') }; /** * toggler for createKeyAccesor * This will create a parameter keys accessor in the BuiltClass * defalut = true * @return {object|ClassOMat} */ createKeyAccesor() { return tg(this, 'createKeyAccesor', ); }; /** * Lets you attach a field to a Minion instance * AFTER it is created. It otherwise works * just like classOMat.field * @param {[type]} key [description] * @return {[type]} [description] */ apiField(key) { const apiFieldOMat = new ApiFieldOMat(this, key) this._params.fieldOMats.push(apiFieldOMat); return apiFieldOMat; } /** * lets you attach a method to the classOMat * that will add that method to the minion class api. * @return {ApiMethod} [description] */ apiMethod(method) { const apiMethodOMat = new ApiMethodOMat(this, method); this._params.fields.push(apiMethodOMat); return apiMethodOMat; } deleteField(key) { const { fieldsIndex, fields } = this._params; if (key in fieldsIndex === false) { throw new Error( `Tried to delete non-existent field ${key} from classOMat ${this._params.key}.`); } const fieldIndex = fieldsIndex[key]; fields.splice(fieldIndex, 1); delete fieldIndex[key]; this._params.fieldsIndex = fieldsIndex; this._params.fields = fields; return this; } /** * Get a FieldOMat. * @param {string} key the key for this field. * This will be the function name of the class instance. * @return {FieldOMat} The field param builder */ field(key, params = NONE) { if (this._params.fieldOMatsIndex[key]) { return this._params.fieldOMats[this._params.fieldOMatsIndex[key]] } const field = new this._params.FieldOMat().init(this, key); if (params !== NONE) { const { parent, ...transferParams } = params; field.params(transferParams, true); } this._params.fieldOMats.push(field); this._params.fieldOMatsIndex[key] = this._params.fieldOMats.length -1; return field; } /** * get or set all the fields to be defined in this classOMat * @param {Array} fields * @return {Array} fields */ fields(fieldOMats = NONE) { if (fieldOMats === NONE) return this._params.fieldOMats; this._params.fieldOMats = fieldOMats; return this; } fieldNames() { return this._params.fieldOMats.map((f)=>f.key()) } /** * getter setter for group * @param {Group} group * @return {Group|ClassOMat} */ group(...p) { return gs(this, 'group', ...p); }; parentKey(...p) { return gs(this, 'parentKey', ...p)}; pluginKeys() { return this._params.pluginKeys; } /** * getter setter for paramObjectKey * @param {object} p * @return {object|ClassOMat} */ paramObjectKey(paramObjectKey=NONE) { if (paramObjectKey === false) return gs(this, 'paramObjectKey', paramObjectKey); if (paramObjectKey !== NONE) { if (typeof paramObjectKey !== 'string') { throw new Error('ClassOMat: Invalid paramObjectKey'); } if (paramObjectKey.slice(0, 1) !== '_') { paramObjectKey = '_' + paramObjectKey; } } return gs(this, 'paramObjectKey', paramObjectKey); }; radio(key) { return new RadioOMat(this, key); } // FOR NEW CLASS-O-MATS. /** * create a copy of this paramset but with the new key * @param {string} key * @return {ClassOMat} the cloned set. */ clone(key = this.key()) { const clone = new ClassOMat(key, this); const cloneParams = clonedeep(this.params()); clone.params(cloneParams); clone.key(key); clone.uid(uuid()); return clone; } subOMat(key) { const subOMat = new ClassOMat(key); const subOMatParams = clonedeep(this._params); delete subOMatParams.group; delete subOMatParams.key; delete subOMatParams.uid; delete subOMatParams.childOMats; delete subOMatParams.childInfoOMats; subOMat.params(subOMatParams, true); const { childOMats, childOMatInfos } = this._params; const childOMatInfoKeys = Object.keys(childOMatInfos); childOMatInfoKeys.forEach((childInfoKey)=>{ const childOMatInfo = childOMatInfos[childInfoKey]; const { childOMatKey, ...childOMatInfoToAdder } = childOMatInfo; const childOMat = childOMats[childOMatInfo.childOMatUid]; subOMat._addChildFromAdder({ childOMat, ...childOMatInfoToAdder}); }); return subOMat; } // API FOR NESTING SETS and NAVIGATING SAME /** * add a set as a child of this one. * @param {ClassOMat} set default is NONE which means "self" * @return {ClassOMat} this; */ addChild(childOMat = NONE) { return new ChildOMatAdder().init(this, childOMat); } /** * getterSetter for all the childOMats of this classOMat * @param {Object|NONE} p * @return {Object|ClassOMat} the Object of childOMats or this */ childOMats(...p) { return gs(this, 'childOMats', ...p)}; /** * get a classOMat nested within this one. * @param {string} key * @return {ClassOMat} the child set */ getChild(key) { const { childOMats, childOMatInfos } = this._params; if (key in childOMatInfos === false) { throw new Error(`Invalid Set Name: ${key}`); } return childOMats[childOMatInfos[key].childOMatUid]; } /** * If this ClassOMat is nested within a parent, get that parent * or set it.. this is a getter/setter function * get the parent. * * adding a parent forces adding the parent accessor to the child class * if one is needed. * @param {ClassOMat|NONE} parent * @return {ClassOMat} */ parent(parent=NONE) { if (parent===NONE) return this._params.parent; this._params.parent = parent; return this; } needsParentAccessor() { this._params.needsParentAccessor = true; return this; } reGroupChildren() { const { childOMats, childOMatInfos, group } = this._params; const childKeys = Object.keys(childOMatInfos); if (childKeys.length === 0) return; childKeys.forEach((childKey)=>{ const childOMatInfo = childOMatInfos[childKey]; const { childOMatUid } = childOMatInfo; const childOMat = childOMats[childOMatUid]; const childOMatGroup = childOMat.group(); if (childOMatGroup.uid()===group.uid()) return; const [isNew, id] = group.add(childOMat); if (isNew === false) return; childOMat.id(id); childOMat.reGroupChildren(); }); } addChildAccessors(classGroup) { const { childOMats, childOMatInfos, BuiltClass } = this._params; const childKeys = Object.keys(childOMatInfos); if (childKeys.length === 0) return BuiltClass; const childAccessorsMixins = {}; childKeys.forEach((childKey)=>{ const childOMatInfo = childOMatInfos[childKey]; const childOMat = childOMats[childOMatInfo.childOMatUid]; const childClassId = childOMat.id(); const ChildClass = classGroup.get(childClassId); childAccessorsMixins[childKey] = this._childAccessorFunction( { childKey, classGroup, childClassId, ...childOMatInfo }); const {accessChildAs, namedChildren, singleton} = childOMatInfo; const _defaultChild = (singleton === true) ? NONE : (namedChildren === true) ? {} : []; if (accessChildAs !== NONE) { childAccessorsMixins[accessChildAs] = function() { return this.___child(childKey)||_defaultChild; } childAccessorsMixins[`delete_${accessChildAs}`] = function() { return this.___deleteChild(childKey); } } }); @mixin(childAccessorsMixins) class WorkingClass extends BuiltClass {}; this.BuiltClass(WorkingClass); return WorkingClass; } _childAccessorFunction({ accessChildAs, cacheChild, classGroup, childClassId, childUid, copyToChildFields, initializer, internalKey, childKey, namedChildren, parentKey, singleton }) { const {fields, paramObjectKey } = this._params; const getStoredChildObject = (namedChildren === false) ? (___children, childKey, childInstance)=> { const storedChild = ___children[childKey]; if (storedChild === undefined) return childInstance; if (Array.isArray(storedChild) === true) return storedChild.concat([childInstance]); return [storedChild, childInstance]; } : (___children, childKey, childInstance, instanceKey)=> { const storedChild = ___children[childKey]; if (storedChild === undefined) return { [instanceKey]: childInstance } return { ...storedChild, [instanceKey]: childInstance } } const nonInitializerWrapper =(childInstance, self, ...p)=>{ return childInstance[parentKey](self, ...p); } const initializerWrapper =(childInstance, self, ...p)=>{ const minion = childInstance[parentKey](self, ...p); initializer(minion, self, ...p); return minion; } const copyFieldsWrapper =(childInstance, self)=>{ const parentParams = self.params(); const childParams = childInstance.params(); copyToChildFields.forEach(({key, as})=>{ childParams[as] = parentParams[key]; }); return childInstance; } const copyToChildFieldsCount = copyToChildFields.length; let initWrapper; if (initializer === NONE) { if (copyToChildFieldsCount === 0) { initWrapper = nonInitializerWrapper; } else { initWrapper = copyFieldsWrapper; } } else { if (copyToChildFieldsCount === 0) { initWrapper = initializerWrapper; } else { initWrapper =(childInstance, self, ...p)=>{ childInstance = copyFieldsWrapper(childInstance, self); return initializerWrapper(childInstance, self, ...p); } } } if (cacheChild === false) { return function(...p) { return initWrapper( new (classGroup.get(childClassId))()[parentKey](this), this, ...p ) } } const getChildren = paramObjectKey === false ? (self)=>self.___children : (self)=>self[paramObjectKey].___children; if (singleton === true) { const workingInitWrapper = initWrapper; initWrapper = (childInstance, self, ...p)=>{ const result = workingInitWrapper(childInstance, self, ...p); self[childKey] = function() { return childInstance }; return childInstance; } } if (namedChildren === false) { return function(...p) { const childInstance = new (classGroup.get(childClassId))()[parentKey](this); const ___children = getChildren(this); ___children[childKey] = getStoredChildObject(___children, childKey, childInstance); return initWrapper(childInstance, this, ...p); } } return function(...p) { const [instanceKey, ...params ] = p; const childInstance = new (classGroup.get(childClassId))()[parentKey](this); const ___children = getChildren(this); ___children[childKey] = getStoredChildObject(___children, childKey, childInstance, instanceKey); return initWrapper(childInstance, this, ...params); } } // INTERNAL COMMANDS FOR ADDING CHILDREN AND THEIR FEATURES _addChildFromAdder(adderParams) { const { childOMats, group } = this._params; const { accessChildAs, cacheChild, childOMat, childIsSelf, copyToChildFields, internalKey, initializer, childKey, namedChildren, parentKey, singleton } = adderParams; const newChildOMat = ( childIsSelf === true ) ? this : childOMat; newChildOMat.needsParentAccessor(); newChildOMat.parent(this); newChildOMat.parentKey(parentKey); const childOMatUid = newChildOMat.uid(); this._params.childOMats[childOMatUid] = newChildOMat; this._params.childOMatInfos[childKey] = { accessChildAs, cacheChild, childOMatUid, childIsSelf, copyToChildFields, internalKey, initializer, childKey, namedChildren, parentKey, singleton } if (childIsSelf===true) { newChildOMat.childOMats(this.childOMats()); this.field(parentKey); } this.reGroupChildren(); newChildOMat.needsParentAccessor(); return this; } _addFieldCopier(copy) { const { key, as } = copy._params; this._params.copyToChildFields.push([key, as]); } _acceptFieldOMat(fieldOMat) { this._params.fieldOMats.push(fieldOMat); } } export default ClassOMat;