UNPKG

rethinkts

Version:

A model system for RethinkDB, written in and for TypeScript.

130 lines (103 loc) 3.84 kB
import { Term } from 'rethinkdbdash'; import { ColumnInfo } from './column'; import { assignWithArrays, pick as _pick } from './utils'; import { RethinkAdapter } from './adapter'; import { Hooks } from './hooks'; import { RelationshipInfo } from './relationships'; export const modelInfoKey = Symbol('rethink model info'); export interface ModelCtor<T> { new(): T; } export interface IndexInfo { name: string; keys: string[] | ((r: Term<any>) => any); options?: { multi?: boolean; geo?: boolean; } } export interface ModelInfo { database: string; table: string; columns: ColumnInfo[]; indexes: IndexInfo[]; primaryKey: string; tags: Map<string, Set<string>>; relationships: RelationshipInfo[]; } export function createModelInfo(target: ModelCtor<any>, ...objs: Partial<ModelInfo>[]): ModelInfo { return target[modelInfoKey] = assignWithArrays(<Partial<ModelInfo>> { columns: [], indexes: [], relationships: [], primaryKey: 'id', tags: new Map<string, Set<string>>(), }, target[modelInfoKey], ...objs); } /** * Model decorator */ export function Model(info: Partial<ModelInfo>): ClassDecorator { return (target: ModelCtor<any>) => { const modelInfo = createModelInfo(target, info); target.prototype.toJSON = function () { return [...modelInfo.columns, ...modelInfo.relationships] .filter(column => this[column.key] !== undefined) .reduce((t, column) => ({ ...t, [column.key]: this[column.key] }), {}); }; }; } function getKeys(ctor: ModelCtor<any>, tagsOrKeys: string[] | string): string[] { const modelTags = Model.getInfo(ctor).tags; let keys: string[]; if (Array.isArray(tagsOrKeys)) { keys = tagsOrKeys; } else { keys = [tagsOrKeys]; } return keys.reduce((keys, tagOrKey) => { if (modelTags.has(tagOrKey)) { return [...keys, ...modelTags.get(tagOrKey)]; } return [...keys, tagOrKey]; }, []); } export namespace Model { export function construct<M>(ctor: ModelCtor<M>, data: Partial<M>): M { return Model.assign(new ctor(), data); } export function assign<M>(model: M, ...sources: Partial<M>[]): M { return Object.assign(model, ...sources); } export function pickAssign<M>(model: M, tagsOrKeys: string[] | string, ...sources: Partial<M>[]): M { const ctor = <ModelCtor<M>> model.constructor; const keys = getKeys(ctor, tagsOrKeys) as Array<keyof M>; return Model.assign(model, ...sources.map(source => _pick(source, ...keys))); } export function pick<M>(model: M, ...tagsOrKeys: string[]): Partial<M> { const ctor = <ModelCtor<M>> model.constructor; const modelTags = Model.getInfo(ctor).tags; const data = tagsOrKeys.reduce((target, tagOrKey) => { if (modelTags.has(tagOrKey)) { return { ...target, ..._pick<M, any>(model, ...modelTags.get(tagOrKey)) }; } return { ...target, [tagOrKey]: model[tagOrKey] }; }, {}); return Model.construct(ctor, data); } export function without<M>(model: M, ...tagsOrKeys: string[]): Partial<M> { const ctor = <ModelCtor<M>> model.constructor; const keys = getKeys(ctor, tagsOrKeys); const pickKeys = Object.keys(model) .filter(key => !keys.includes(key)); return Model.construct(ctor, _pick<M, any>(model, ...pickKeys)); } export function getInfo(ctor: ModelCtor<any>): ModelInfo { return ctor[modelInfoKey]; } export function notify(model: any, hook: Hooks, ...args: any[]): any { if (model[hook]) { return model[hook](...args); } } }