moldy
v2.5.2
Published
A javascript model API for the browser + server that is actually worth using.
Downloads
41
Readme
TOC
moldy
Create a Moldy.
var personMoldy = Moldy.extend( 'person' )
.$property( 'id' )
.$property( 'name' )
.$property( 'age' )
.create();
personMoldy.should.have.a.property( 'id', undefined );
personMoldy.should.have.a.property( 'name', undefined );
personMoldy.should.have.a.property( 'age', undefined );
Property Attributes
Type & default
Properties can by strongly typed. If a type has been defined, values are cast to that type automatically. If a value cannot be cast to a type then the value will be set to null
or the default
if it has been defined.
var personMoldy = Moldy.extend( 'person', {
properties: {
'age': 'number',
'active': {
type: 'boolean',
default: false
},
'tags': 'array'
}
} ).create();
/**
* When a model's properties have been `typed` the assigned values are cast on the fly
* to ensure the model's data remains sanitized.
*/
/**
* Cast a `string` for `age` to a `number`
*/
personMoldy.age = '13';
personMoldy.age.should.equal( 13 ).and.be.an.instanceOf( Number );
/**
* Cast a truthy `string` for `active` as a `boolean`
*/
personMoldy.active = 'yes';
personMoldy.active.should.equal( true ).and.be.an.instanceOf( Boolean );
/**
* `active` is typed as a `boolean` _and_ a `default` has been defined. When an
* assigned value that cannot be cast as a `boolean` is set then the `default` will
* apply.
*/
personMoldy.active = 'this is not a boolean';
should( personMoldy.active ).equal( false ).and.be.an.instanceOf( Boolean );
/**
* Cast a `string` for `tags` as an `array`
*/
personMoldy.tags = 'lorem';
should( personMoldy.tags ).eql( [ 'lorem' ] ).and.be.an.instanceOf( Array );
Optional
Properties can be optional. By making a property optional, isValid()
and toJson()
will ignore it if is has not been set.
var personMoldy = Moldy.extend( 'person' )
.$property( 'id' )
.$property( 'name' )
.$property( 'age', {
type: 'number',
optional: true
} )
.$property( 'active', {
type: 'boolean',
default: false
} )
.$property( 'tags', {
type: 'array',
optional: true
} )
.create();
/**
* To ensure this `person` is valid we only need to set the `id` and `name` because
* the other keys are either `optional` or have `defaults`.
*/
personMoldy.id = 1;
personMoldy.name = 'David';
personMoldy.$isValid().should.be.ok;
Arrays of a type
A property can be defined as array
of a type like an array
of strings
, or an array
of numbers
.
var personMoldy = Moldy.extend( 'person' )
.$property( 'id' )
.$property( 'tags', {
type: [ 'string' ]
} )
.create();
/**
* When defining an array of a type, the arrays are normal arrays however they have been
* extended to allow hooks into the necessary methods for sanitization.
*/
personMoldy.tags.should.be.an.Array;
personMoldy.tags.should.have.a.property( 'length' ).and.be.a.Number;
personMoldy.tags.should.have.a.property( 'pop' ).and.be.a.Function;
personMoldy.tags.should.have.a.property( 'push' ).and.be.a.Function;
personMoldy.tags.should.have.a.property( 'reverse' ).and.be.a.Function;
personMoldy.tags.should.have.a.property( 'shift' ).and.be.a.Function;
personMoldy.tags.should.have.a.property( 'sort' ).and.be.a.Function;
personMoldy.tags.should.have.a.property( 'splice' ).and.be.a.Function;
personMoldy.tags.should.have.a.property( 'unshift' ).and.be.a.Function;
/**
* Pushing a value - like normal
*/
personMoldy.tags.push( 'yellow' );
/**
* We are pushing a `number` here to show how the value will be cast to a string
*/
personMoldy.tags.push( 1 );
/**
* The value `1` is now a string
*/
personMoldy.tags[ 1 ].should.equal( '1' );
personMoldy.tags.should.have.a.lengthOf( 2 );
personMoldy.tags.should.eql( [ 'yellow', '1' ] );
/**
* A gotcha for using primitive types in this context is that they are not sanitized
* based on the schema if they are changed directly
*/
personMoldy.tags[ 1 ] = 1;
/**
* Technically this should have cast the number `1` to a string but it was a design
* decision not to add getters/setters to each item in an array. A santize method will
* be added in the next version
*/
personMoldy.tags[ 1 ].should.equal( 1 );
Array types can also be model schemas.
var personMoldy = Moldy.extend( 'person' )
.$property( 'cars', {
type: [ {
name: 'car',
properties: {
make: 'string',
model: {
type: 'string',
default: ''
},
year: 'number'
}
} ]
} )
.create();
/**
* Note, we are missing the `model` key and the `year` is a string
*/
personMoldy.cars.push( {
make: 'honda',
year: '2010'
} );
personMoldy.cars[ 0 ].$json().should.eql( {
id: undefined,
make: 'honda',
model: '',
year: 2010
} );
A model's url aka endpoint
A url (endpoint) is automatically generated based on the Moldy
name, key, $url()
and $baseUrl()
.
var Person = Moldy.extend( 'person', {
baseUrl: '/api'
} );
var personMoldy = Person.create();
Person.$url().should.eql( '/api/person' );
/**
* The url can be changed using either `base()` or `url()`
*/
Person.$url( 'v1' );
Person.$url().should.eql( '/api/person/v1' );
find
To find by id
or key
, give an object with appropriate conditions.
var personMoldy = Moldy.extend( 'person', {
key: 'guid',
properties: {
name: ''
}
} );
personMoldy.$findOne( {
guid: '5f55821f-3a28-45c3-b91d-7df927a863d8'
}, function ( _error, _res ) {
if ( _error ) {
return _done( _error );
}
_done();
} );
If an adapter responds with an array the first item will be returned.
var Person = Moldy.extend( 'person', {
key: 'guid',
properties: {
name: ''
}
} );
/**
* In this example the end point GET `http://localhost:3000/api` returns an array of items.
* Moldy will return the first item out of the array. If you need to return an array you can
* use the $collection method.
*/
Person.$findOne( function ( _error, _res ) {
if ( _error ) {
return _done( _error );
}
_res.should.not.be.an.Array;
_done();
} );
find
To find an array of models.
var Person = Moldy.extend( 'person', {
key: 'guid',
properties: {
name: 'string'
}
} );
Person.$find( function ( _error, _people ) {
if ( _error ) {
return _done( _error );
}
_people.should.be.an.Array().with.a.lengthOf( 3 );
_people.forEach( function ( _person ) {
_person.should.be.a.Moldy;
} );
_done();
} );
save
To save the model, call save()
. If the model is dirty
(has not been saved to the server and therefore does not have a valid key
) then the http method will be POST. If the model has been saved, then the http method will be PUT.
var personMoldy = Moldy.extend( 'person', {
properties: {
name: 'string'
}
} ).create();
personMoldy.name = 'David';
personMoldy.$save( function ( _error, _res ) {
if ( _error ) {
return _done( _error );
}
personMoldy.$json().should.eql( _res.$json() );
personMoldy.should.have.a.property( 'id' );
personMoldy.name = 'Mr David';
personMoldy.$save( function ( _error, _res ) {
personMoldy.should.eql( _res );
_done( _error );
} );
} );
destroy
To destroy a model, call destroy()
.
var personMoldy = Moldy.extend( 'person', {
key: 'guid',
properties: {
name: 'string'
}
} ).create();
personMoldy.name = 'David';
personMoldy.$save( function ( _error, _res ) {
if ( _error ) {
return _done( _error );
}
personMoldy.$destroy( function ( _error, _res ) {
personMoldy.$isDirty().should.be.true;
//personMoldy.$isValid().should.be.false; -- DO NOT GET WHY SHOULD BE FALSE
//personMoldy.__destroyed.should.be.true;
_done( _error );
} );
} );