Created
June 19, 2015 05:38
-
-
Save s-panferov/29c164cafd03d4871685 to your computer and use it in GitHub Desktop.
Immutable models in TS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import invariant from '../lib/invariant'; | |
import * as models from './index'; | |
import { Record, Map, Set, List } from 'immutable'; | |
import { Option, Some, None } from 'opty'; | |
export enum ChatType { | |
Dialog = <any>'dialog', | |
Room = <any>'room' | |
} | |
/* | |
* INTERFACES | |
*/ | |
export interface IChatRef extends models.IRef { | |
__IChatRef: IChatRef | |
} | |
export interface IChat extends IChatAttributes { | |
__IChat: IChat; | |
getId(): string; | |
getRef(): IChatRef; | |
getAttributes(): IChatAttributes; | |
getTags(): List<models.IRef> | |
} | |
export interface IChatAttributes extends Map<any, void> { | |
__IChatAttributes: IChatAttributes; | |
getName(): string; | |
getChatType(): ChatType; | |
getNewCount(): number; | |
} | |
export interface IChatRawAttributes { | |
name: string; | |
chatType?: ChatType; | |
nameCount?: number; | |
} | |
export interface IChatRawReferences { | |
tags?: { | |
data: { | |
id: string; | |
type: string | |
}[] | |
} | |
} | |
interface IChatRaw extends models.ApiRawDoc<IChatRawAttributes, IChatRawReferences> { } | |
/* | |
* RAW RECORDS | |
*/ | |
export const ChatRef = Record({ | |
id: '', | |
type: 'chats' | |
}); | |
models.defineRef(ChatRef.prototype); | |
export const ChatAttributes = Record({ | |
chatType: ChatType.Room, | |
name: '', | |
newCount: 0 | |
}); | |
models.defineGetter(ChatAttributes.prototype, 'chatType'); | |
models.defineGetter(ChatAttributes.prototype, 'name'); | |
models.defineGetter(ChatAttributes.prototype, 'newCount'); | |
export const ChatRelationships = Record({ | |
tags: new (Record({ data : models.RecordList(models.Ref)() })) | |
}); | |
export const ChatRecord = Record({ | |
id: '', | |
type: 'chats', | |
attributes: new ChatAttributes(), | |
relationships: new ChatRelationships() | |
}); | |
models.defineApiDoc(ChatRecord.prototype); | |
models.defineRelationship(ChatRecord.prototype, 'tags'); | |
models.defineAttr(ChatRecord.prototype, 'chatType'); | |
models.defineAttr(ChatRecord.prototype, 'name'); | |
models.defineAttr(ChatRecord.prototype, 'newCount'); | |
var __chat = new ChatRecord(); | |
export function Chat(raw: IChatRaw): IChat { | |
return <IChat>models.parseRecord(ChatRecord, raw); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Record, Map, fromJS, Seq, List } from 'immutable'; | |
import { Some, None, Option } from 'opty'; | |
import invariant from '../lib/invariant'; | |
import * as _s from 'underscore.string'; | |
import * as _ from 'lodash'; | |
export const Ref = Record({ | |
id: 'unknown', | |
type: 'unknown', | |
}); | |
defineId(Ref.prototype); | |
defineType(Ref.prototype); | |
defineRef(Ref.prototype); | |
export interface ApiRawDoc<Attr, Rel> { | |
id: string; | |
type?: string; | |
attributes: Attr; | |
relationships: Rel; | |
} | |
export interface TypedNoneConstructor<R, T> { | |
new (): None<T> | |
wrapNull(val: R): Option<T> | |
} | |
export interface TypedSomeConstructor<R, T> { | |
new (val: T): Some<T> | |
wrapNull(val: R): Option<T> | |
} | |
export function OptionalRecord<R, T>(record: { new (val: R): T }): [TypedNoneConstructor<R, T>, TypedSomeConstructor<R, T>] { | |
let _TypedNone: TypedNoneConstructor<R, T> = <any>function _TypedNone() { | |
None.call(this); | |
}; | |
_TypedNone.prototype = Object.create(None.prototype); | |
(<any>_TypedNone).__constructor = record; | |
let _TypedSome: TypedSomeConstructor<R, T> = <any>function _TypedSome(val) { | |
Some.call(this, val); | |
}; | |
_TypedSome.prototype = Object.create(Some.prototype); | |
(<any>_TypedSome).__constructor = record; | |
let wrapNull = function (value: R): Option<T> { | |
if (value == null) { | |
return new _TypedNone(); | |
} | |
else { | |
return new _TypedSome(parseRecord(record, value)); | |
} | |
}; | |
_TypedNone.wrapNull = wrapNull; | |
_TypedSome.wrapNull = wrapNull; | |
return [_TypedNone, _TypedSome] | |
} | |
export function RecordList(record): typeof List { | |
let _List: typeof List = <any>function _List() { | |
var list = List.call(this, arguments); | |
list.__constructor = (values) => List(values.map((value) => parseRecord(record, value))); | |
return list; | |
}; | |
_List.isList = List.isList; | |
return _List; | |
} | |
export interface IRef extends Map<any, void> { | |
__refMarker: IRef, | |
getRef(): IRef; | |
getId(): string; | |
getType(): string; | |
} | |
export interface IRawRef { | |
id: string; | |
type: string; | |
} | |
export interface IRawData<T> { | |
data: T | |
} | |
export function defineAttr(proto, name) { | |
proto['get' + _s.capitalize(name)] = function() { | |
return this.get('attributes').get(name) | |
} | |
} | |
export function defineAttributes(proto) { | |
proto['getAttributes'] = function() { | |
return this.get('attributes') | |
} | |
} | |
export function defineGetter(proto, name) { | |
proto['get' + _s.capitalize(name)] = function() { | |
return this.get(name) | |
} | |
} | |
export function defineRef(proto) { | |
defineId(proto); | |
defineType(proto); | |
proto['getRef'] = function() { | |
var id = this.get("id"); | |
var type = this.get("type"); | |
invariant(id, "id is not set"); | |
invariant(type, "type is not set"); | |
return Map({ id, type }) | |
} | |
} | |
export function defineId(proto) { | |
proto['getId'] = function() { | |
return this.get("id"); | |
} | |
} | |
export function defineType(proto) { | |
proto['getType'] = function() { | |
return this.get("type"); | |
} | |
} | |
export function defineRelationship(proto, name) { | |
proto['get' + _s.capitalize(name)] = function () { | |
return this | |
.get('relationships') | |
.get(name) | |
.get('data'); | |
} | |
} | |
export function defineApiDoc(proto) { | |
defineRef(proto); | |
defineAttributes(proto); | |
} | |
export function parseRecord<T>(record: { new (val: any): T; prototype: {[key: string]: any} }, value: any): T { | |
var recordWalker = function(value, key) { | |
if (record.prototype[key]) { | |
var recordVal = record.prototype[key]; | |
if (recordVal instanceof Record) { | |
return parseRecord(recordVal.constructor, value); | |
} | |
else if (recordVal instanceof Some || recordVal instanceof None) { | |
(<any>recordVal.constructor).wrapNull(value); | |
} | |
else if (recordVal.__constructor) { | |
console.log('using constructor', value) | |
return recordVal.__constructor(value); | |
} | |
else { | |
return fromJSDefault(value); | |
} | |
} else { | |
return fromJSDefault(value); | |
} | |
}; | |
var result = {}; | |
_.forEach(value, (v, k) => { | |
result[k] = recordWalker(v, k); | |
}); | |
return new record(result); | |
} | |
function fromJSDefault(json) { | |
if (Array.isArray(json)) { | |
return (<any>Seq).Indexed(json).map(fromJSDefault).toList(); | |
} | |
if (isPlainObj(json)) { | |
return (<any>Seq).Keyed(json).map(fromJSDefault).toMap(); | |
} | |
return json; | |
} | |
function isPlainObj(value) { | |
return value && (value.constructor === Object || value.constructor === undefined); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment