Schema-Llama
npm install --save schema-llama@0.0.8
TOC
Preamble
We have not written this project yet. It's currently in its conception and is going down the experimental development mode. Star the repo and see when we get it done.
v0.0.8: We just finished the initial alpha build of this package. It's pushed to NPM, but things could change some more.
Thanks!
Introduction
This project is spawned out of a desire for pure javascript libraries that save time in long term projects. The basic concept behind this library is to take a "logically described schema" and transform it into an es6 class you can play with like any other es6 class you love. Love ES6 More!
So...take my schema:
const llamaSchema = name: String dob: Date age: Number
And use powerful llama powers to create this class with baked-in validation:
name: String; dob: Date; age: Number; { thisname = name; thisdob = dob; thisage = age; } { ifvalueconstructor !== String throw TypeError'Invalid name provided.'; thisname = value; } { ifvalueconstructor !== String || valueconstructor !== Date throw TypeError'You must provide a valid date value.'; ifvalueconstructor === String value = value; return value; } { ifvalueconstructor !== String || valueconstructor !== Number throw TypeError'You must provide a valid number'; ifvalueconstructor === String value = value; return value; }
Rational
WHY DO THIS!?
Good question. Because we can. No, just kidding: because I had a difficult time finding a library that treats schemas like classes. Commonly other libraries manage their schema methods and statics like this:
const Llama = name: String dob: Date age: Number Llamamethods { /**Implement eating grass?**/}Llamastatics = { /** Implement static method to create a new lama. **/}
This is OK, I guess; but I want the clean feeling of ES6 classes with my method declarations:
name: String dob: Date age: Number/** we can pass in other settings here.**/ { return `Llama ate some grass.`; } static { return name dob: Date age: 0 ; }
Essentially, the backbone of this project is a single factory function. The implementation of this class creator (Class Factory) function is as follows (Simplified):
const convertSchemaToClass = { return { ifParentClass && ParentClassconstructor !== Function throw TypeError'You can only schemify classes.'; const primitives = String Date Number Symbol ; const classer = ParentClass ? { superprops; } : class { superprops; } ; const keys = Object; /** Do some magic to turn schema into class accessor methods. **/ forconst key of keys const keySymbol = Symbolkey; Object; return classer; }}
Usage
You don't really need to know how it works for you to use it:
Steps
- Import the class factory.
;
- Create a schema object.
Objects contain properties with constructors that define what kind of item can be stored in that property in your class. You can set a schema property to either 1) a class constructor or 2) a validator function.
const schemaObject = name: String dob: Date vehicle: Vehicle //Imaginary constructor email: EmailValidator //Imaginary validator in the form (value) => { return value; }
- Create the es6 class with options.
Schema(schemaObject)(options [, ParentClass ])
const schemaOptions = attemptCast: false mapNullToEmptyArray: false const SchemaClass = schemaOptions; /** Add methods and such. **/
Schema Options:
- attemptCast: Boolean - default: false
- required: ?Array - Array of property names that are required.
- (optional) You can put all of this together into one sleek call:
/** Parent class methods and props. **/ name: String dob: Date vehicle: Vehicle //Imaginary constructor email: EmailValidator //Imaginary validator in the form (value) => { return value; } attemptCast: false Parent /** Add class methods and such. **/
Examples
Simple Example
; const Llama = ; //Class that becomes a subclass of llama schema. { superprops; //Do annoying magic. } { return thisage + 1; }
Embedded Schemas Example
; name: String dob: Date age: Number panchos: price: Number { superprops; } { return panchos; }
Embedded ES6 Class Example:
This is really where the rubber hits the road with this idea. This schema would intuitively allow you to pass class definitions as "Types" for your property definitions.
Let's say that we want to handle pancho operations instead of having them act like pure objects?
price: Number { superprops; } { return thisprice * tax + 013; } //NOTE: You do not have to use Schema! You can just as easily use a pure JS class. name: String dob: Date age: Number panchos: Pancho email: { superprops; } { ifwithTax return panchos; return panchos; }
Embedded Validator Example:
So, let's say I want to create a String validator called EmailAddress?
; const SimpleEmailAddress = { //Throw an error if the email address is invalid. return Stringvalue;} //We can even be smart and do settings with a factoryconst EmailAddress = { return { //Do validation logic... return value }} name: String dob: Date age: Number panchos: price: Number email: { superprops; } { return panchos; }
Validator Helper Library
;
Enum([ String ])
Number(settings: { min: Number, max: Number, type: ['int', 'double'] })
String(settings: { min: Number, max: Number, match: RegExp })
Custom Error Handling
Before using the schema code anywhere, use this to override the error classes.
; //Override Schema ErrorHandlingSchemaError = MyErrorClass;SchemaTypeError = MyTypeErrorClass;SchemaValidationError = MyValidationErrorClass;
Get/Set Hooks
This is probably most useful to me, but I could see it being useful for some architectures.
For example, we may want to store a vanilla JS Date in the entity, but would like to have a Moment object when we access the data.
name: String dob: Date age: Number { ifTypeClass === Date return ; } { ifTypeClass === Date return value } { superprops; } const llamaface = ;llamafacename = 'JerryLlama';llamafacedob = ; //Stores pure JS date.llamafaceage = 10;
API
All object instantiated using schema-llama will have the following methods built in:
toJSON()
: Returns a pure object minus any schema-llama methods and hidden properties.