import Store , { FetchOptions , FetchListOptions , Options , ActOptions } from "./Store" ; import Hash from "./Hash" ; import { camelize } from "./helpers/StringHelper" ; export interface IModel { key : string ; } export default class Model implements IModel { keyColumn : string = "id" ; path : string ; private _singular : string ; private static _store : Store < Model > ; private static _reflector : Model ; static storeProperties : Hash < any > = { } ; get self ( ) : typeof Model { return this . constructor as typeof Model ; } static get reflector ( ) : Model { const reflector = this . _reflector ; if ( reflector && reflector instanceof this ) { return reflector ; } return ( this . _reflector = new this ( ) ) ; } get store ( ) : Store < this > { return ( this . constructor as typeof Model ) . store as Store < this > ; } static get hasStore ( ) : boolean { return ! ! this . _store ; } static get store ( ) : Store < Model > { return ( this . _store = this . _store || new Store < Model > ( this , this . storeProperties ) ) ; } static set store ( value ) { this . _store = value ; } get key ( ) : any { return ( this as any ) [ this . keyColumn ] ; } set key ( value ) { ( this as any ) [ this . keyColumn ] = value ; } static key ( value ) { if ( value === null || typeof value === "undefined" ) { throw new Error ( "Unable to resolve key `" + value + "`" ) ; } if ( typeof value === "object" ) { return ( value as IModel ) . key ; } return value ; } get singular ( ) : string { return this . _singular || this . path . substring ( 0 , this . path . length - 1 ) ; } set singular ( value ) { this . _singular = value ; } get foreignName ( ) : string { return `${ this . singular }_${ this . keyColumn }` ; } foreignKey ( foreignModel ) { return ( this as any ) [ foreignModel . reflector . foreignName ] ; } get map ( ) : Hash < any > { const result = { } ; Object . keys ( this ) . forEach ( attribute => { const mapping = this . mappingFor ( attribute ) ; if ( mapping !== null ) { result [ attribute ] = mapping ; } } ) ; return result ; } private mappingFor ( attribute ) { let value = ( this as any ) [ attribute ] ; if ( typeof value === "object" || typeof value === "function" || this . self . reflector . hasOwnProperty ( attribute ) ) { return null ; } return this . denormalized ( attribute , value ) ; } private transformer ( attribute , type ) { const underscored = `${ type }_${ attribute }` ; if ( ( this as any ) [ underscored ] ) { return underscored ; } const camel = type + camelize ( attribute ) ; if ( ( this as any ) [ camel ] ) { return camel ; } } private transform ( attribute , value , type ) { const transformer = this . transformer ( attribute , type ) ; return transformer ? ( this as any ) [ transformer ] ( value ) : value ; } normalized ( attribute , value ) { return this . transform ( attribute , value , "normalize" ) ; } denormalized ( attribute , value ) { return this . transform ( attribute , value , "denormalize" ) ; } set ( values ) { Object . keys ( values ) . forEach ( key => { ( this as any ) [ key ] = this . normalized ( key , values [ key ] ) ; } ) ; return this ; } duplicate ( ) { const result = Object . assign ( new this . self ( ) , this ) ; delete result [ this . keyColumn ] ; return result ; } static make ( values ) { return new this ( ) . set ( values ) ; } static makeArray ( valuesArray ) { return valuesArray . map ( ( values ) => this . make ( values ) ) as Model [ ] ; } reload ( ) { return this . self . find ( this . key ) as Promise < this > ; } static find ( key , options ? ) { return this . store . get ( key , options ) ; } static all ( options ? ) { return this . store . index ( options ) ; } create ( options ? ) { return this . self . create ( this . map , options ) as Promise < this > ; } static create ( values , options ? ) { return this . store . create ( values , options ) ; } update ( options ? ) { return this . self . update ( this . key , this . map , options ) as Promise < this > ; } static update ( key , values , options ? ) { return this . store . update ( key , values , options ) ; } save ( options ? ) { return this . self . save ( this . map , options ) as Promise < this > ; } static save ( values , options ? ) { if ( values [ this . reflector . keyColumn ] ) { return this . update ( values [ this . reflector . keyColumn ] , values , options ) ; } return this . create ( values , options ) ; } destroy ( options ? ) { return this . self . destroy ( this . key , options ) as Promise < any > ; } static destroy ( key , options ? ) { return this . store . destroy ( key , options ) as Promise < any > ; } belongsTo ( foreignModel , options ? ) { return foreignModel . find ( this . foreignKey ( foreignModel ) , options ) ; } belongsToMany ( foreignModel , options ? ) { return this . self . belongsToMany ( foreignModel , this . foreignKey ( foreignModel ) , options ) ; } static belongsToMany ( foreignModel , value , options ? ) { return foreignModel . hasMany ( this , value , options ) ; } hasOne ( foreignModel , options ? ) { return this . self . hasOne ( foreignModel , this . key , options ) ; } static async hasOne ( foreignModel , key , options ? ) { const items = await this . hasMany ( foreignModel , key , options ) ; return items . length ? items [ 0 ] : null ; } hasMany ( foreignModel , options ? ) { return this . self . hasMany ( foreignModel , this . key , options ) ; } static hasMany ( foreignModel , key , options ? ) { return this . store . foreign ( foreignModel , key , options ) ; } act ( options ? ) { if ( ! options ) { options = { } ; } options . url = options . url || this . key ; return this . self . act ( options ) ; } static act ( options ? ) { return this . store . act ( options ) ; } }