npm.io
2.5.2 • Published 10 years ago

moldy

Licence
MIT
Version
2.5.2
Deps
8
Vulns
0
Weekly
0

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 );
	} );
} );