Skip to content

Instantly share code, notes, and snippets.

@jamiewinder
Created March 2, 2017 13:56
Show Gist options
  • Save jamiewinder/d148f7289ee980ce4275d5e75d470304 to your computer and use it in GitHub Desktop.
Save jamiewinder/d148f7289ee980ce4275d5e75d470304 to your computer and use it in GitHub Desktop.
Example Repository
class DataRecord<T> {
// Constructor
constructor(value: T, initialRef: any) {
this.value = value;
this.refs = new Set([initialRef]);
}
// Properties
public readonly value: T;
public readonly refs: Set<any>;
}
export class KeyedRepository<T> {
// Fields
private readonly _records = new Map<any, DataRecord<T>>();
// Methods
/**
* Gets a record value from the repository, or `undefined` if it cannot be found. The record
* will be updated with the given reference.
*/
public get(key: any, ref: any): T | undefined {
const record = this._records.get(key);
if (record === undefined) {
return undefined;
}
if (ref != null) {
record.refs.add(ref);
}
return record.value;
}
/**
* Adds a value to the repository using the given key, and initial reference. This
* function will throw if a record with this key already exists.
*/
public add(key: any, ref: any, value: T): void {
let record = this._records.get(key);
if (record !== undefined) {
throw new Error('Cannot add because a record with this key already exists');
}
record = new DataRecord<T>(value, ref);
this._records.set(key, record);
}
/**
* Attempts to get the value for the given key. If this fails, the
* record will be added by invoking the given value function.
*/
public getOrAdd(key: any, ref: any, createValue: () => T): T {
const existing = this.get(key, ref);
if (existing === undefined) {
const value = createValue();
if (value === undefined) {
throw new Error('Cannot add the value of `undefined`');
}
this.add(key, ref, value);
return value;
} else {
return existing;
}
}
/**
* Gets all values
*/
public getAll(ref: any): Array<T> {
const values = [];
for (const record of this._records.values()) {
values.push(record.value);
if (ref != null) {
record.refs.add(ref);
}
}
return values;
}
/**
* Unreferences all records with the given reference. Any unreferenced data
* will be removed.
*/
public unref(ref: any): void {
for (const [key, record] of this._records.entries()) {
record.refs.delete(ref);
if (record.refs.size === 0) {
this._records.delete(key);
}
}
}
// Properties
public get size() {
return this._records.size;
}
}
export type Constructor<T> = { new(): T };
export class TypedRepository {
// Fields
private readonly _typeRepositories = new Map<object, KeyedRepository<any>>();
// Methods
/**
* Gets a value from the repository, or `undefined` if it cannot be found. The record
* will be updated with the given reference.
*/
public get<T extends object>(type: Constructor<T>, key: any, ref: any): T | undefined {
const typeRepository = this._typeRepositories.get(type);
if (!typeRepository) {
return undefined;
}
return typeRepository.get(key, ref);
}
/**
* Adds a value using the given type and key, and initial reference. This
* function will throw if a record with the type and key already exists.
*/
public add<T extends object>(type: Constructor<T>, key: any, ref: any, value: T): void {
let typeRepository = this._typeRepositories.get(type);
if (!typeRepository) {
typeRepository = new KeyedRepository<any>();
this._typeRepositories.set(type, typeRepository);
}
return typeRepository.add(key, ref, value);
}
/**
* Attempts to get the value with the given type and key. If this fails, the
* record will be added by invoking the given value function.
*/
public getOrAdd<T extends object>(type: Constructor<T>, key: any, ref: any, createValue: () => T): T {
const existing = this.get(type, key, ref);
if (existing === undefined) {
const value = createValue();
if (value === undefined) {
throw new Error('Cannot add the value of undefined');
}
this.add(type, key, ref, value);
return value;
} else {
return existing;
}
}
/**
* Gets all values for the given type
*/
public getAllOfType<T extends object>(type: Constructor<T>, ref: any): Array<T> {
const typeMap = this._typeRepositories.get(type);
if (!typeMap) {
return [];
}
return typeMap.getAll(ref);
}
/**
* Unreferences records in the repository across all types and keys.
* Any unreferenced records will be removed.
*/
public unref(ref: any): void {
for (const [type, typeRepository] of this._typeRepositories.entries()) {
typeRepository.unref(ref);
if (typeRepository.size === 0) {
this._typeRepositories.delete(type);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment