class-o-mat
v0.6.1
Published
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
Downloads
21
Maintainers
Readme
ClassOMat ( the Minion generator )
Less code, more precision
IMPORTANT: Users of the DataObject Plugin may need to update to v0.0.2 for that plugin.
Version 0.6.1 (Alpha): Strongly recommend using "~0.x.x" in your package.json instead of "^0.x.x"
Should work, but use at own risk.
Release Notes:
Updates are frequent for both new features and bug fixes.
ClassOMat allows you to quickly build classes in JavaScript around a data API with less code than little code.
ClassOMat is a Class Factory System. It creates classes dynamically that are as complex as needed, but no more.
QuickStart:
QuickTour:
NEW ClassOMat Plugins List
ClassOMat Reference:
Some reasons to use ClassOMat:
Future development plans for ClassOMat:
NEW ClassOMat Plugin System:
Before we continue...
A Minion Class is a class created by a ClassOMat.
A minion is an instance of a Minion Class
ClassOMat combines aspects of inheritence with mixins to create new classes:
- ClassOMat can add functions at will to a class.
- ClassOMat can also be set up to provide some inherited traits.
ClassOMat-generated classes can accept new methods dynamically:
- An instance of a class (minion) generated by ClassOMat can have new functions added easily to its API.
ClassOmat can created hierachies of nested class instances automatically:
- A parent minion can copy specified parameters to a child minion and can rename those parameters if needed
- A minion can safely create a child Minion as an instance of its own class.
Quick Start:
0: Install ClassOMat: npm install 'class-o-mat';
1: Import ClassOMat and create a ClassOMat instance:
import ClassOMat from 'class-o-mat';
const classOMat = new ClassOMat('top');
2: Create a Base Class: If you do NOT create a BaseClass, an empty base class called ClassOMatDefaultBaseClass will be used.
class BaseClass {}
3: Add some parameter methods ( fields ) to the ClassOMat instance:
const nameFieldSpec = classOMat.field('name');
const ageFieldSpec = classOMat.field('age');
4: Add a the Base Class to the ClassOMat:
classOMat.BaseClass(BaseClass);
5: Create a Minion Class from the ClassOMat:
// the $() is the 'execute' function.
const MinionClass = classOMat.$()
6: Create an instance ( minion ) of the Minion Class and use it.
const minion = new MinionClass();
minion.name('Dorothy Gale');
minion.age(16);
const name = minion.name(); // name = Dorothy Gale
const age = minion.age(); // age = 16
Same thing using chaining syntax:
import ClassOMat from 'class-o-mat';
const classOMat = new ClassOMat('top');
class BaseClass {};
classOMat.BaseClass(BaseClass).field('name').field('age');
const MinionClass = classOMat.$();
const minion = new MinionClass().name('Dorothy Gale').age(16);
const name = minion.name(); // Dorothy Gale
const age = minion.age(); // 16
What just happened?
- We added two API field methods to the classOMat.
- These fields access elements of the newBaseClass._params object.
- Since we used no other parameters for the api fields except for their name, the default Getter/Setter hybrid method was used ( there are many parameter methods available ). The default Getter/Setter methods does:
If a parameter is passed, the method will set a value into the ._params object. So, minion.age(16) did this: : minion._params.age = 16.
- After the parameter is passed, the minion instance is returned to allow for chaining.
If NO parameter is passed the method will get the value from the ._params object. (minion.age() is the same as minion._params.age;
Why use ClassOMat to create classes.
If you want a class with only a few parameter accessor methods, and if you knew ahead of time what those parameter methods would be, then creating classes by hand may be easier.
However: You may wish to consider ClassOMat if you want any ONE of the following:
- multiple parameter methods of varying types to add to a class.
- to be able to use the same ( or similar ) sets of parameters for multiple classes without using direct inheritence.
- to dynamically set parameter methods you want to attach to a Class.
- to be able to easily set validations to your parameters.
- to be able to adapt itself in midstream.
- to be able to adapt other classes in midstream.
- an easy way to generate a chainable syntax.
- an easy way to generate child classes without worrying about infinite recursions.
ClassOMat-generated classes are more concise:
This:
import ClassOMat from 'class-o-mat';
const classOMat = new ClassOMat('top');
class BaseClass {};
classOMat.BaseClass(BaseClass).field('name').field('age');
const MinionClass = classOMat.$();
const minion = new MinionClass().name('Dorothy Gale').age(16);
const name = minion.name(); // Dorothy Gale
const age = minion.age(); // 16
Could have been This;
const NONE = Symbol('NONE');
class BaseClass {
constructor() {
this._params = {};
}
name(name=NONE) {
if (name === NONE) return this._params.name;
this._params.name = name;
return this;
}
age(age=NONE) {
if (age === NONE) return this._params.age;
this._params.age = age;
return this;
}
}
const minion = new BaseClass();
minion.name('Dorothy Gale').age(16);
const name = minion.name(); // Dorothy Gale;
const age = minion.age(); 16
In particular, note how setting up two Getter/Setter param methods goes from ~10-13 lines of code ( depending upon your style ) to 1 line
ClassOMat specifying two Getter Setter fields:
classOMat.field('name').field('age);
Standard Class code specifying same fields:
....
name(name=NONE) {
if (name === NONE) return this._params.name;
this._params.name = name;
return this;
}
age(age=NONE) {
if (age === NONE) return this._params.age;
this._params.age = age;
return this;
}
If you wanted to set 6 Getter/Setter fields:
ClassOMat:
classOMat.field('name').field('age').field('gender')
.field('eyeColor').field('height').field('weight');
// OR... if you wanted to break it up a bit....
classOMat.field('name').field('age').field('gender');
classOMat.field('eyeColor').field('height').field('weight');
Done in standard class format specifying these parameters code would have required 30-40 lines
Even if there is a single getter/setter function and simply adding wrappers to it in the class, perhaps getting 30-40 lines down to 10-20.
But it STILL would require repetition and more chances for bugs. Also, it would not allow for different field accessor methods with different validators, and it would not allow for class nesting or other introspection methods.
More Examples Coming Soon...
List of Current and Pending Plugins:
At the moment, ClassOMat is a very new system, but there are now plugins already developed and being published.
In the order of expected publication:
ClassOMat DataObject plugin (Published): Converts the internal params created by a ClassOMat minion into standard javascript object or json.
ClassOMat Help plugin (Published): Allows adding of help text for each field.
ClassOMat IO plugin: Will allow a minion to save its data in json format.
ClassOMat CLI plugin: Will provide a command-line interface for working with minions, including saving data.
PathOMat: Will create an interactive system for validating file paths in a command line.
MongooseOMat plugin: Will create a Mongoose Model from a Minion.
Plugins that are not being worked on but it would be nice:
- SQLOMat plugin: To create SQL schema from a Minion.
ClassOMat Reference:
Table of Contents:
- ClassOMat:
- Field Methods:
- API Field Methods:
- Required Field Methods:
- API Method Methods:
- Nesting ( addChild ) Methods:
- Types of Parameter Accessor Functions:
- Using minions.. ( instances of classes generated by ClassOMat ):
ClassOMat Methods
$(): The executor function. Use this to create a class from the ClassOMat instance.
const classOMat = new ClassOMat('classOMat');
class BaseMinionClass {}
classOMat.BaseClass(BaseMinionClass).field('name').$();
const MinionClass = classOMat.$();
const minionInstance = new MinionClass();
BaseClass: Sets the BaseClass that ClassOMat will modify into a new generated class.
If no BaseClass is set, an empty base class will be used.
class BaseClass {}
classOMat.BaseClass(BaseClass);
field: Specify a field. Once this is called, use the field methods listed below
const fieldOMat = classOMat.field([name of field]);
radio: specify a series of fields that function as a multiple choice option such as a radio button on an html form... This is basically a wrapper for multiple fields and the staticSetter function ... a bit more convenient this way.
const classOMat = new ClassOMat('radioTest');
classOMat.base('drink').default('Martini').$()
.option('martini').value('Martini')
.option('vodkaGimlet').value('Vodka Gimlet')
.option('tomCollins').value('Tom Collins')
.option('shirleyTemple').value$('Shirley Temple');
const minion = new (classOMat.$())();
minion.drink(); // Martini
minion.vodkaGimlet();
minion.drink(); // Vodka Gimlet.
minion.tomCollins();
minion.drink(); // Tom Collins
paramObjectKey: Specify the name of the parameters object in a MinionClass created by ClassOMat. By default, this parameters object is minion._params.
const classOMat = new ClassOMat('paramKeyTest')
.BaseClass(class Coffee {})
.paramObjectKey('_properties');
const minion = new (classOMat.$())();
// parameters are now at minion._properties
// instead of the default minion._params
If paramObjectKey is set to false, NO internal params object will be used.
Instead, each parameter to be set will have '___' prepended to its key. So, for example, minion.id(...) will set minion.___id. This may be useful for minions that are to have a limited number of parameters to set and require more performance. The higher performance will come from the getter/setter function internally using this.___id instead of this._params.id;
Likewise, the using author may use this.___id ( or whatever the paremeter key is without invoking the accessor function.
I recommend using this[___parameter key] for use by other functions within the class as a sort of private variable but using the minionparameter key for external API type calls.
clone: Create a clone of this ClassOMat.
subOMat: Create a clone of this ClassOMat that can be modified without affecting the original.
cacheOMats: When this is called, any Minion Classes created with a ClassOMat will contain references to the ClassOMat that created them and to the ClassOMats that are attached with addChild Command.
This allows mixins to be added or removed during the run of the program.
class BaseClass {}
const classOMat = new ClassOMat('COM');
classOMat.field('name').field('age');
const subClassOMat = classOMat.subOMat('SUBCOM');
subClassOMat.field('gender');
const Minion = classOMat.$();
const minion = new Minion();
minion.name('Clara').age(24); // OK.. sets name and age.
minion.gender('female'); // throws error because gender is not specified as a method.
const SubMinion = subClassOMat.$();
const subMinion = new SubMinion();
subMinion.name('Pete').age(31); // OK sets name and age.
subMinion.gender('male'); // OK sets gender.
Field Methods
Setting a parameter access type: Multiple methods are available and outlined in the Parameter Accessor Functions section.
abstract: signifies that a field cannot be used unless via a 'subOMat'. See the subOMat command below.
classOMat.field('name').abstract();
const aSubOMat = classOMat.subOMat('sub');
const minion = new (classOMat.$())();
const subMinion = new (aSubOMat.$())();
minion.name('hello'); // throws abstract field error;
subMinion.name('hello'); // sets name = 'hello';
cast: When a value is being set, will cast that value into a specific type.
TypeCasting Documentation:
default: Sets a default value for a parameter.
classOMat.field('name').default('Ralphy');
const minion = new (classOMat.$())();
minion.name(); // Ralphy;
field: Attaches a new field to the parent classOMat. Proxies classOmat.field function.
classOMat.field('name').field('age');
// same as :
classOMat.field('name');
classOMat.field('age');
fallback: Provides a fallback field for when a get attempted on this field returns null.
classOMat.field('imageFile').
.field('imageFile2x').fallback('imageFile');
const minion = new (classOMat.$())();
minion.imageFile('k.png');
minion.imageFile(); // k.png
minion.imageFile2x(); // k.png
internalKey: allows a field to set a parameter with a different name than the name of the field.
classOMat.field('name').field('who').internalKey('name');
const minion = new (classOMat.$())();
minion.name('Sylvie Vartan'); // same as minion._params.name = 'Sylvie Vartan';
minion.name(); // 'Sylvie Vartian'
minion.who('Roy'); // same as minion._params.name = 'Roy';
minion.name(); // 'Roy'
minion.who(); // 'Roy'
key: getterSetter for the classOMat name of this field.. The key is auomatically set with classOMat.field([key])
const field = classOMat.field('name');
field.key(); // name;
field.key('who');
field.key(); // who;
required: specifies that this field is required. See
const field = classOMat.field('name').required();
protected: Specifies that this field cannot be overridden. Useful for writing plugins where you don't want the using author to override your fields.
const field = classOMat.field('name').protected();
onGet: allows a callback event to be added that will be run when a field value is gotten. Multiple callbacks can be added. They will be run in the order they were added to the field.
**Each callback has three parameters:
- self: the minion instance that the field belongs to)
- eventKey: [fieldname]
- value: The value that was set.
const callback1 = (self, eventKey, value)=>...
const callback2 = (self, eventKey, value)=>...
classOMat.field('name').onSet(callback1).onSet(callback2);
/// OR...
classOMat.field('name').onSet(callback1, callback2);
// in either case,
// calling minion.name('Hilda') will run :
// callback1(minionInstance, ON_SET_NAME, 'Hilda');
// callback2(minionInstance, ON_SET_NAME, 'Hilda');
onSet: allows a callback event to be added that will be run when a field value is set. (This will not be run when the default value is set an initialization time.) Multiple callbacks can be added. They will be run in the order they were added to the field.
**Each callback has three parameters:
- self: the minion instance that the field belongs to)
- eventKey: [fieldname]
- value: The value that was set.
const callback1 = (self, eventKey, value)=>...
const callback2 = (self, eventKey, value)=>...
classOMat.field('name').onSet(callback1).onSet(callback2);
/// OR...
classOMat.field('name').onSet(callback1, callback2);
// in either case,
// calling minion.name('Hilda') will run :
// callback1(minionInstance, ON_SET_NAME, 'Hilda');
// callback2(minionInstance, ON_SET_NAME, 'Hilda');
addChild: This command creates an instance of a childAdder with its own params... Proxies the same method in the classOMat.
classOMat.field('name').addChild()....
// same as :
classOMat.field('name');
classOMat.addChild()...
again: creates a clone of this field.
// THIS
classOMat.field('name').validate().string().$().again('title').$();
// IS THE SAME AS
classOMat.field('name').validate().string().$()
.field('title').validate().string().$();
validate sets up a validator for the field. This command creates an instance of a field validator. After you finish specifying the validator, you must use the .$() command. This will return the field that called the validator in the continuation. Click here to see all validator methods
classOMat.field('name').validate().string().$();
reducer Allows a get or set reducer to be established for a field. A get reducer occurs as the last element of the field access function while a set reducer is the first.
The reducer section defaults to an onSet reducer. You can change that withreducer.onSet(false)
.
To make the reducer onGet: reducer.onSet(false).onGet(true);
To make the reducer both: reducer.onSet(true).onGet(true)
You can call reducer multiple times to combine these reducers.
You MUST finish a reducer definition with the $() executor command.;
const onSetReducer =(value)=>value+100;
const onGetReducer =(value)=>value+1000;
const onGetSetReducer =(value)=>value * 10;
// onGet reducer
classOMat.field('onGetTest').reducer().onGet(true).onSet(false).method(onGetReducer).$();
// onSet reducer
classOMat.field('onSetTest').reducer().method(onSetReducer).$();
// onSet AND onGet reducer;
classOMat.field('onGetSet').reducer().onGet(true).method(onGetSetReducer).$();
const minion = new (classOMat.$())();
minion.onGetTest(100); // stores 100 in the field onGetTest;
minion.params().onGetTest; // gets 100;
minion.onGetTest(); // gets 1100 but does not change the internal value;
minion.onSetTest(100); // stores 1100 in the field onSetTest;
minion.params().onSetTest; // 1100;
minion.onSetTest(); // 1100;
minion.onGetSet(100); // stores 1000 in the field onGetSet;
minion.params().onGetSet; // 1000;
minion.onGetSet(); // returns 10,000 but the internal value is still 1,000
action Allows a get or set action to be established for a field. A get reducer occurs as the last element of the field access function while a set reducer is the first.
THIS IS TESTED BUT UNSUPPORTED. It may be removed since it is similar to onSet and onGet methods.
The action section defaults to an onSet reducer. You can change that withaction.onSet(false)
.
To make the actino onGet: action.onSet(false).onGet(true);
To make the action both: action.onSet(true).onGet(true)
You can call reducer multiple times to combine these reducers.
You MUST finish a reducer definition with the $() executor command.;
let borky = 0;
const onSetReducer =(minion, value)=>borky+=100;
const onGetReducer =(minion, value)=>borky+=1000;
const onGetSetReducer =(minion, value)=>borky * 10;
// onGet action
classOMat.field('onGetTest').action().onGet(true).onSet(false).method(onGetReducer).$();
// onSet ction
classOMat.field('onSetTest').action().method(onSetReducer).$();
// onSet AND onGet action;
classOMat.field('onGetSet').action().onGet(true).method(onGetSetReducer).$();
const minion = new (classOMat.$())();
minion.onGetTest(100); // stores 100 in the field onGetTest;
minion.params().onGetTest; // gets 100;
minion.onGetTest(); // gets 100;
console.log(borky); // 1000;
minion.onSetTest(100); // stores 100 in the field onSetTest;
minion.params().onSetTest; // 100;
minion.onSetTest(); // 100;
console.log(borky); // 1100;
minion.onGetSet(100); // stores 1000 in the field onGetSet;
minion.params().onGetSet; // 1000;
minion.onGetSet(); // returns 1,000
console.log(borky); // 11,000
REQUIRED FIELDS
A field can be required. When there are one or more required fields, the generated minion class instance has a method minion.___ready() which indicates that all required fields have their data.
If a required field also has a default value, the field is considered to need a value to be set over the default.
to require a field:
classOMat.field('name').required().$();
to see that all required fields have been set:
minion.___ready(); // true if all entered, false otherwise.
to mark a field as ready manually:
minion.___markReady('fieldName');
to access the underlying NamedDepCounter that handles required fields:
When there are required fields, a NamedDepCounter is added to the Minion class to track the status of the required fields. All methods below are NamedDepCounter methods.
minion.___required(); // gets the NamedDepCounter;
to add an event that fires when a specific required field has been set:
minion.___required().onMark((minionInstance, fieldKey)=>{...});
to add an event that fires when all the required fields have been set.
minion.___required().onComplete((minionInstance)=>{...})
to reset the required fields (this will required them all to be set again for the minion instance to be considered ready:
minion.___required().reset();
Parameter Accessor Types:
getter: A read-only function:
classOMat.field('name').getter().default('The Doctor');
const minion = new (classOMat.$())();
minion.name(); // The Doctor
minion.name('Sam'); // The Doctor ( does not set anything )
setter: A write-only function:
classOMat.field('name').setter().default('The Doctor');
const minion = new (classOMat.$())();
minion.name(); // sets name = null and returns minion.
minion.name('Sam'); // sets name = Sam and returns minion.
getterSetter: Default field type. If no argument, retruns the value, if an argument sets the value and returns the instance for chaining.
classOMat.field('name').getterSetter().default('The Doctor');
// OR
classOMat.field('name').default('The Doctor');
const minion = new (classOMat.$())();
minion.name(); // returns 'The Doctor'
minion.name('Sam'); // sets name = Sam and returns minion.
getterSetterSafe: Like getterSetter but has a second option to use while setting the field. With a normal getterSetter, setting a value to a field that is undefined will return the existing value of the field instead of the minion instance.
This behavior breaks chaining. For fields where you might want to check for undefined for chaining and where performance is not crucial, getterSetterSafe is an option.
classOMat.field('name').getterSetter().field('other').getterSetterSafe();
const minion = new (classOMat.$())();
const aName = undefined;
const anOther = undefined;
// below results is an error because minion.name did not return the minion instance // and therefore there was no 'other' field to operate.
minion.name(aName).other('other');
// below will work, but will NOT set minion.other because anOther = undefined.
minion.other(anOther, false).name('doris');
// below will throw an error specifying exactly where in the chain the error was.
minion.other(anOther, true).name('day');
// below will work just like regular getterSetter. It will throw an error but
// the error will be less specific about what value caused the fail.
minion.other(anOther).name('Ralph');
staticSetter: When this argument is called... a pre-specified value is set. In combination with internalKey, this can be used for a multiple choice set of parameters.
classOMat.field('continent').default('Antarctica')
.field('antarctica').staticSetter('Antarctica').internalKey('contintent')
.field('northAmerica').staticSetter('North America').internalKey('contintent')
.field('southAmerica').staticSetter('South America').internalKey('contintent')
.field('europe').staticSetter('Europe').internalKey('contintent')
.field('asia').staticSetter('Asia').internalKey('contintent')
.field('australia').staticSetter('Australia').internalKey('contintent')
const minion = new (classOMat.$())();
minion.continent(); // Antarctica
minion.asia(); // sets continent to Asia, returns the minion.
minion.continent(); // Asia.
minion.australia(); //
minion.continent(); // Australia
swapper: If no argument is passed, gets the value of the field. If an argument is passed in, sets a new value to the field and returns the existing one.
classOMat.field('name').swapper().default('Kelly Johnson');
const minion = new (classOMat.$())();
minion.name(); // returns 'Kelly Johnson'
minion.name('Sam'); // sets name and returns 'Kelly Johnson'
toggler: Switches between two Boolean arguments and returns the minion
classOMat.field('isCool').toggler().default(false);
const minion = new (classOMat.$())();
minion.isCool(); // sets isCool = true and returns the minion.
queue: Simulates a Queue with an array in the minion._params:
note: this can also be done in the same way the Stack is represented below:
classOMat.field('ages').queue().$(); // Must use $() after specifying a queue
const minion = new (classOMat.$())();
minion.queue().add(10).add(20).add(30);
const values = minion.queue().values(); // [30, 20, 10];
const first = minion.queue().next(); // 10;
const remainingValues = minion.queue().values(); // [30, 20];
stack: Simulates a Stack with an array in the minion._params;
note: this can also be done in the same way the Queue is represented below:
classOMat.field('ages').stack().$(); // Must use $() after specifying a queue
const minion = new (classOMat.$())();
const stack = minion.stack();
stack.add(10).add(20).add(30);
const values = stack.values(); // [10, 20, 30];
const first = stack.next(); // 30;
const remainingValues = stack.values(); // [10, 20];
TypeCasting Methods:
You can have a field cast a value into a specific type. You can combine type casting methods with validations as well.
The following type casts are available:
- string
- number
- float
- int
- bool (boolean)
Example of using type casting in a classOMat with fields.
classOMat.field('age').cast().int()
.field('name').cast().string()
.field('name').cast().number()
.field('name').cast().float()
.field('name').cast().int()
.field('name').cast().boolean()
Validator Methods:
A validator can contain multiple validations chained using AND or OR logic. By default, chaining validations in a validator is assumed to be by AND logic. You can combine validator methods with typeCasting.
ARBITRARY FUNCTION
with: Validates with the passed in function. Also will warn if the value passed in is NOT a function.
const testIsSquare =(dimensions)=>dimensions.width === dimensions.height;
classOMat.field('isSquare').validate().with( testIsSquare ).$()
withFunction: Same as with but will not warn if an improper function is passed in.
TYPES
bool: Boolean. classOMat.field('isAuthorized').validate().bool().$()
int: Integer classOMat.field('age').validate().int().$()
method: Method only
classOMat.field('test').validate().method().$()
const minion = new (classOMat.$())();
minion.test( ()=>true );
number: Number classOMat.field('price').validate().number().$()
string: String classOMat.field('name').validate().string().$()
object: Object classOMat.field('info').validate().object().$();
EQUALITY & RANGES
equal: Same as value===comparatorclassOMat.field('age').validate().equal(21).$()
equalish: Same as value == comparatorclassOMat.field('age').validate().equalish('21').$()
greaterThan: classOMat.field('age').validate().greaterThan(21).$()
gt: also greaterThan
greaterThanOrEqual:classOMat.field('age').validate().greaterThanOrEqual(21).$()
gte: also greaterThanOrEqual
lessThan: classOMat.field('age').validate().lessThan(10).$()
lt: also lessThan
lessThanOrEqual:classOMat.field('age').validate().lessThanOrEqual(10).$()
lte: also lessThanOrEqual
MULTIPLE CHOICE:
selectList: Must be one of the list.classOMat.field('authLevel').validate().selectList(['admin', 'author', 'reader']).$()
CHAINING LOGIC
and: This is the default logic but can still be written out for explicitness.
classOMat.field('name').validate().string().and().gte('a').$();
// since this is default setting: could also be:
classOMat.field('name').validate().string().gte('a').$();
or:
classOMat.field('name').validate().equal('Frank').or('Bill').$();
INVALID PARAMETER HANDLING:
message: The message to throw or warn with.classOMat.field('name').validate().string().message('Very Bad Name Parameter').$()
warn: Will only warn instead of throwing an error. Default is to throw an error.classOMat.field('name').validate().string().message('Very Bad').warn().$()
VALIDATION CALLBACK:
You can add a callback method to a validator.
const onAgeValidation =(isValid, key, value)=>console.log(isValid);
classOMat.field('age').validate().uint().callback(onAgeValidation).$();
const minion = new (classOMat.$())();
minion.age(100); // true
minion.age(-100); // false
Nesting (addChild) methods.
Using addChild will expose the ChildAdder API. This allows a minion to create a child minion. A child minion can be an instance of another classOMat ( and associated base class ) OR another minion created by the parent classOMat:
The following example should be seen only as pseudo code until further testing is done.
This is a pretend example of how one might go about setting up a BTree using ClassOMat. Clearly more code would need to be placed in the classes, but here it is.
After an addChild sequence, you must use the $() operator OR the as$() convenience function
class BaseBTree {}
class BaseBTreeNode {}
const nodeOMat = new ClassOMat('BTreeNode');
nodeOMat.BaseClass(BaseBTreeNode)
.field('values').stack().validate().string().$()
.addChild().as('spawn').$();
const bTreeOMat = new ClassOMat('BTree');
bTreeOMat.BaseClass(BaseBTree)
.field('name').validate().string().$()
.addChild(nodeOMat).as$('start');
const BTree = bTreeOMat.$();
const bTree = new BTree();
const bTreeNode = bTree.start();
const stillBTree = bTreeNode.___parent();
const stillBTreeNode = bTree.___child('start');
const anotherBTreeNode = bTree.start();
const bTreeNodes = bTree.___child('start'); // array of the nodes.
comments on relevant parts:
Set up the ClassOMat for the node class.
const nodeOMat = new ClassOMat('BTreeNode');
nodeOMat.BaseClass(BaseBTreeNode);
Allow the BTreeNode to create a child BTreeNode by using the 'spawn' command. Leaving the addChild parameter blank, specifies that the child is an instance of the same class.
nodeOMat.addChild().as('spawn');
Allow the BTree no create a child BTreeNode by using the 'start' command.
bTreeOMat.addChild(nodeOMat).as$('start');
To create the minion classes, run $() on the top level ClassOMat. the child minion classes will automatically be created and nested within the top level instances.
const BTree = bTreeOMat.$();
const bTree = new BTree;
// the BTreeNode class is embedded within bTree
// and will be instantiated using the appropriate
// bTree.start() command.
To get a specific child created by a parent minion:
const bTreeNode = bTree.start();
const sameBTreeNode = bTree.___child('start');
- If there is only one child of a given name, that child is returned.
- If there is more than one child of a given name and type, an array of those children is returned...
- UNLESS...
To access multiple children of the same type by a named object instead of an array:
bTreeOMat.addChild(nodeOMat).namedChildren(true).as$('start')
To get ALL children created by a parent minion:
The ___children method will return an associative data object containing all the children
cached by the parent minion.
bTree.___children() ;
/* RETURNS a data object like:
{
childOne: [childOneMinionInstance],
childTwo....
}
*/
To add a specific accessor for getting a child created by a parent minion:
This provides a wrapper for the above ___child('...') function.
classOMat.addChild(childClassOMat).accessChildAs('nemo').as$('makeChild');
const minion = new (classOMat.$())();
const childMinion = minion.makeChild();
// to get childMinion from minion
minion.___child('makeChild');
// OR
minion.nemo();
TO NOT STORE CHILD INSTANCES IN THEIR PARENT:
In some applications, it may not be desirable for the parent minion to have references to the child minions created from it. This is especially true if a lot of child minions of one type are to be created and / or the child minions are only needed for a brief purpose.
classOMat.addChild(childClassOMat).cacheChild(false).as$('makeChild');
To have only ONE child created by an addChild option (SINGLETON): The first time the child accessor is called, a child is instantiated. The next times the child accessor is called returns the singleton
classOMat.addChild(childClassOMat).singleton().as$('henry');
To delete child minions from the parent manually:
You may need to do this from time to time.
minion.___deleteChild(childMinionKey);
**To delete a child when you have set accessChildAs: **
classOMat.addChild(childOMat).accessChildAs('nemo').as$('makeChild');
const minion = new (classOMat.$())();
const childMinion = minion.makeChild();
// to delete:
minion.delete_nemo();
To get a parent minion:
const theParentBTree = bTreeNode.___parent();
To run as instantiator function on a child:
For most instantiations, you can simply rely upon copyField to copy fields
from the parent to the child. When you find a need to run a constructor-type function,
you can use addChild.initializer([function]);
You need to pass a function into addChildInitializer:
The first two arguments in this function must be: child and parent After that, you can specify any arguments you wish.
const parentOMat = new ClassOMat('parent');
parentOMat.BaseClass(class B {}).field('somethingNew');
const childOMat = new ClassOMat('child').field('somethingBorrowed');
class ChildBaseClass {
init(somethingNew) {
this.somethingBorrowed(somethingNew);
return this;
}
}
childOMat.BaseClass(ChildBaseClass);
const childInit =(child, parent, value)=>child.init(value);
parentOMat.addChild(childOMat).as('child').initializer(childInit).$();
ApiField Methods:
Api Fields allow a minion to add new fields to itself AFTER it is instantiated:
Standard ApiField: All fields created from the ApiField are created in the same way and with the same validator if there is one.
Dynamic ApiField: All fields are created and can be altered upon creation.
ApiField:
class BaseMinion {}
const classOMat = new ClassOMat('COM');
classOMat.BaseClass(BaseMinion);
// set apiField, which is similar to the way you set a field...
classOMat.apiField('title').field().validate().string().$().$();
const Minion = classOMat.$();
const minion = new Minion();
// create the method jobTitle that follows
// the validation ruls of the apiField.
minion.title().as('jobTitle').$();
minion.title().as('bookTitle').$();
// now you can do this.
minion.jobTitle('Supervisor');
minion.bookTitle('Fathers and Sons');
minion.jobTitle(); // Supervisor;
minion.bookTitle(); // Fathers and Sons
DynamicApiField:
...
classOMat.apiField('wildcard').as$('anything');
const minion = new (classOMat.$())();
minion.anything().validate().string().$().as$('name');
minion.anything().validate().uint().$().as$('age');
minion.name('John'); // set name to John
minion.age(23); // set age to 23;
minion.name(23); // throws error
minion.age('Howard'); // throws error.
ApiMethod Methods ( UNTESTED):
ApiMethods allow a minion instance to add an arbitrary function:
class BaseMinion {}
const classOMat = new ClassOMat('COM');
classOMat.BaseClass(BaseMinion);
function randomFunction() {
console.log(' Here is a random function! ');
}
classOMat.apiMethod('addFunction').$();
const Minion = classOMat.$();
const minion = new Minion();
minion.addFunction(randomFunction);
minion.randomFunction() // logs 'Here is a random function!'
Using minions ( Instances of classes created by ClassOMat )
For the most part, using minions is just like using any other class
ONE EXCEPTION:
ClassOMat writes its own constructor method that sets the parameters object and fills it with any required defaults.
I recommend writing a separate init function and calling it explicitly in order to avoid colliding with ClassOMat's constructor.
Methods added to ClassOMat minions:
keys: This shows a list of all the field names set by classOMat.
class BaseMinion {}
const classOMat = new ClassOMat().BaseClass(BaseMinion).field('name').field('age');
const Minion = classOMat.$();
const minion = new Minion();
minion.keys() // returns array : ['name', 'age']
___child: If this minion created a child, show that child by key. ( see below sample ) If more than one child is created for a particular key, returns an array of children.
___children: Shows all the children as an object.
___parent: If this minion was instantiated by another minion specified in ClassOMat, shows the parent class. ( see below sample )
___classOMats: Only available if ClassOMat.cacheOMats(true)
is set. Returns a group of the Classes generated by the ClassOMat. This includes the classes that are child ( nested classes ).
___classGroup: Returns the group of classes available to a Minion Class instance. These include the Minion Class itself and any other classes generated by ClassOMat.addChild()
___isClassOMat: returns true if this is a classOMat generated minion.
class BaseMinionParent {}
class BaseMinionChild {}
const childOMat = new ClassOMat('child').BaseClass(BaseMinionChild);
const parentOMat = new ClassOMat('parent').BaseClass(BaseMinionParent);
parentOMat.addChild(childOMat).as$('child');
const MinionParent = parentOMat.$();
const parent = new MinionParent();
const child = parent.child();
child.___parent(); // returns parent.
parent.___child('child'); // returns child
parent.___children(); // returns { child: child }
const sister = parent.child();
sister.___parent();// parent
parent.___child('child'); // [child, sister];
parent.___children(); // { child: [child, sister];
parent.___isClassOMat(); // true;
child.___isClassOMat(); // tue;
Overriding the names of ___child, ___children and ___parent methods If you wish to use different function names for these methods....
override ___parent method name:
classOMat.addChild(childOMat).parentKey('parent').as$('child');
const parent = new (classOMat.$())();
const child = parent.child();
child.parent(); // parent
override ___child method name: he he.. forgot to code this one! I'll get to it when I get to it.
override ___children method name: wow.. I am a slacker.. .
Plugin System
ClassOMat now has a plugin development system to allow extending ClassOMat's abilities. Plugins should come with their own documentation for using authors.
Click here to learn how to Develop Plugins.
Future Plans:
Below is the todo list for future ClassOMat development. Any takers, contributors, testers are welcome. These are listed in roughly the order in which they can be released.
Mechanism to ensure required fields are completed.
Currently require is just a placeholder command.
Allow continuation syntax for addChild command from Minion.
Asynchronous validations
Asynchronous data connections to remote services
MongooseOMat:
A version of ClassOMat with the option to make Mongoose models automatically from the classOMat specification.
Testing by use in the AeneaDb Project (ongoing)
The initial reason for developing ClassOMat was to aid in the development of AeneaDB, a graph-native database in JavaScript, which is heavily dependent upon Class Factories.
Most of the features now in ClassOMat fill needs of AeneaDB. Hence, AeneaDB testing/coding is helping to drive debugging ClassOMat in addition to the ClassOMat test routines.
Debugging Tools
Once the Getter/Setter system is tested, proper logging and introspection tools can be developed. These tools will make it easier to debug complex ClassOMat chaining sequences.
Commenting Tool
Something like a jsdoc command line tool for ClassOMat, or simply testing to see how jsdoc might be used safely with ClassOMat.
Performance Testing
ClassOMat is designed to sacrifice some performance in the initialization of classes with the idea that the more concise classes that are created should peform more efficiently.
Performance testing will be done soon in order to validate that idea and correct any deficiencies. Also performance testing will help to validate the most efficient ways of using Minion Classes.
UI Tools
UI tools could simply be text editor or IDE plugins. UI tools could also be a proper GUI for developing easily and intuitively with ClassOMat and with projects created by ClassOMat.
Plans Implemented in part or whole:
Plugin Dev System: partly implemented.
A system to develop plugins for ClassOMat. It is partly developed, now supporting appending constructors, adding events and adding new functions to the generated Minion class.
Field Fallback Option: implemented
A field option that will allow a field to get the contents of another field when it has no set value.
Radio Group: implemented
A multiple choice set of fields that combine to set a single parameter value. This is currently handled by the staticSetter option with internalKey. Radio Group would
ApiMethods
ApiMethods are now implemented.
Events System
A simple onSet system is implemented and tested. Looking into need for Getter System.
accessChildAs Option for addChild
Provide an easier way for a parent minion to access a child minion than using ___children().. This option provides a wrapper for it.
childCache Option for addChild
Currently the addChild command creates an accessor that creates an instance of a child class and allows it to be accessed from the parent. The childCache(false) option of addChild allows a child class to be created without storing it in the parent. For some applications this might be a better and more efficient behavior.
Release Notes:
Version 0.6.0 & 0.6.1
Added VERSION static and instance vars to ClassOMat and Plugin to better match plugins and atoms
Version 0.5.5
- Added dynamic api fields.
- allowed override of ValidatorOMat in specific instances ( not for general use )
- removed unused code.
Version 0.6.0
Added VERSION static and instance var to ClassOMat and Plugin classes.
This will be used to validate future plugins or atoms
Version 0.5.2 & 0.5.3
Bugfix for apiField.
Also fixed a readme error for apiField
Version 0.5.1
Bugfix with package.json "main" setting
Version 0.5.0
BREAKING CHANGE.. Only for users of the ClassOMat DataObject Plugin You can remedy this by updating the ClassOMat DataObject Plugin to v0.0.2
Reworked plugin system by writing it as a ClassOMat app within ClassOMat.
Added singleton child option.
Bugfix for accessChildAs option for addChild command.
Reworked field building system. This should allow future code reduction by reusinf some of the ClassOMat API code to build the ClassOMat system itself.
Version 0.4.7
Added action functionality. This is UNSUPPORTED as it may be similar enough to onGet and onSet feature to be deemed redundant.
Version 0.4.6
Added reducer functionality
Version 0.4.4
Bug fixed named children
Version 0.4.3
Added TypeCasting.
Version 0.4.2
Added Validation Callbacks.
Version 0.4.1
Added system to handle required fields.
Version 0.4.0
Added 'object' type into validations. UNTESTED Added 'protected' option for field. UNTESTED Bugfixes
Version 0.3.94
Added radio method to plugin class... UNTESTED..
Version 0.3.93 Alpha
Added field methods to the plugin system.
Version 0.3.90 Alpha
First tests of Plugin System. Now tested with overriding constructor, adding new methods to the Minion class and onGet and onSet methods.
Version 0.3.85 Alpha
Separated buildClass methods from class-o-mat/class-o-mat.js file in order to make system more modular and make it easier to write plugins for ClassOMat.
Version 0.3.80 Alpha
Added option to access multiple child minions of the same type via a named object instead of by an array. This is tested, but not extensively
Version 0.3.73 Alpha
- Added radio option for fields. This allows setting up of a multiple choice series of fierld that function on the same parameter object.
- :-( Also... fixed embarrassing documentation error where the staticSetter function was referred to as setterStatic...
Version 0.3.72 Alpha
Added fallback option for fields. A field with this option provides another field to get from if the first field get returns null.
Version 0.3.71 Alpha
- added accessChildAs option for addChild system.
- added cacheChild option for addChild system.
- provided functions to delete a child or children of the same childKey
Version 0.3.70 Alpha
Added and tested onSet callback events for fields.
Version 0.3.65 Alpha
Added staticSetter accessor method. When used in conjunction with internalKey option can be used for multiple choice.
Version 0.3.63, 0.3.64 Alpha
Made fix to issue where minions could not access children where paramObjectKey = false
Verison 0.3.61 Alpha
Also this version adds the ability to set parameterObjectKey to false. When this is set, there will be no internal params object in each minion. Instead, each parameter to be set will have '___' prepended to its key. So, for example, minion.id(...) will set minion.___id. This may be useful for minions that are to have a limited number of parameters to set and require more performance. The higher performance will come from the getter/setter function internally using this.___id instead of this._params.id;
Likewise, the using author may use this.___id ( or whatever the paremeter key is without invoking the accessor function.
I recommend using this[___parameter key] for use by other functions within the class as a sort of private variable but using the minionparameter key for external API type calls.
Version 0.3.6... Alpha
This version allows adding an initializer to addChild command. It also fixes duplication and other issues with Class creation from ClassOMats.
Version 0.3.54 .. Alpha
Added ability to cache the ClassOMat instance within a minion class and then allowed the minion class to re-execute the ClassOMat to update the underlying classes of the minion and any of the child classes.
Version 0.3.5... Alpha
All tests currently pass, and some elements of this code are being tested in the creation of the Aenea Database as well as in the tests in this repo.
Currently 3/43 tests are failing all in the parameter validation sections. This will be resolved in next published version
This version has UNTESTED start of adding apiMethod feature. This feature will allow arbitrary methods to be added to a class on the fly as parameters are now added using the apiField feature.
UNTESTED bool parameter validator
This version also corrects many
Version 0.2.0 Alpha
Adds two new field types, queues and stacks.
Adds validators
Adds apiFields
Version 0.1.2 Alpha
Switches addChild to use more standard continuation format rather than multiple arguments. Adds internalKey to addChild.