Skip to content

Instantly share code, notes, and snippets.

@s-panferov
Created June 19, 2015 05:38
Show Gist options
  • Save s-panferov/29c164cafd03d4871685 to your computer and use it in GitHub Desktop.
Save s-panferov/29c164cafd03d4871685 to your computer and use it in GitHub Desktop.
Immutable models in TS
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);
}
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