Added a registry to hold all of the factories and adapters

This commit is contained in:
Christopher Allford 2020-07-01 11:07:57 -07:00
parent 627bd5a99a
commit e6e764320b
3 changed files with 132 additions and 0 deletions

View File

@ -1,5 +1,8 @@
import { Model } from '../models/model';
/**
* An interface for implementing adapters to create models.
*/
export interface Adapter<T extends Model> {
/**
* Creates a model or array of models using a service..

View File

@ -0,0 +1,42 @@
import { AdapterTypes, FactoryRegistry } from './factory-registry';
import { ModelFactory } from './model-factory';
import { Product } from '../models/product';
import { APIAdapter } from './api-adapter';
describe( 'FactoryRegistry', () => {
let factoryRegistry: FactoryRegistry;
beforeEach( () => {
factoryRegistry = new FactoryRegistry();
} );
it( 'should register factories once', () => {
const factory = ModelFactory.define<Product, any, ModelFactory<Product>>( ( { params } ) => {
return new Product( params );
} );
expect( factoryRegistry.getFactory( Product ) ).toBeNull();
factoryRegistry.registerFactory( Product, factory );
expect( () => factoryRegistry.registerFactory( Product, factory ) ).toThrowError( /already been registered/ );
const loaded = factoryRegistry.getFactory( Product );
expect( loaded ).toBe( factory );
} );
it( 'should register adapters once', () => {
const adapter = new APIAdapter<Product>( '', ( model ) => model );
expect( factoryRegistry.getAdapter( Product, AdapterTypes.API ) ).toBeNull();
factoryRegistry.registerAdapter( Product, AdapterTypes.API, adapter );
expect( () => factoryRegistry.registerAdapter( Product, AdapterTypes.API, adapter ) ).toThrowError( /already been registered/ );
const loaded = factoryRegistry.getAdapter( Product, AdapterTypes.API );
expect( loaded ).toBe( adapter );
} );
} );

View File

@ -0,0 +1,87 @@
import { Adapter } from './adapter';
import { Model } from '../models/model';
import { ModelFactory } from './model-factory';
type Registry<T> = { [key: string ]: T };
/**
* The types of adapters that can be stored in the registry.
*
* @typedef AdapterTypes
* @property {string} API "api"
* @property {string} Database "database"
* @property {string} Custom "custom"
*/
export enum AdapterTypes {
API = 'api',
Database = 'database',
Custom = 'custom'
}
/**
* A registry that allows for us to easily manage all of our factories and related state.
*/
export class FactoryRegistry {
private readonly registry: Registry<ModelFactory<any>> = {};
private readonly adapters: { [key in AdapterTypes]: Registry<Adapter<any>> } = {
api: {},
database: {},
custom: {},
};
/**
* Registers a factory for the class.
*
* @param {Function} modelClass The class of model we're registering the factory for.
* @param {ModelFactory} factory The factory that we're registering.
*/
public registerFactory<T extends Model>( modelClass: new () => T, factory: ModelFactory<T> ): void {
if ( this.registry.hasOwnProperty( modelClass.name ) ) {
throw new Error( 'A factory of this type has already been registered for the model class.' );
}
this.registry[ modelClass.name ] = factory;
}
/**
* Fetches a factory that was registered for the class.
*
* @param {Function} modelClass The class of model for the factory we're fetching.
*/
public getFactory<T extends Model>( modelClass: new () => T ): ModelFactory<T> | null {
if ( this.registry.hasOwnProperty( modelClass.name ) ) {
return this.registry[ modelClass.name ];
}
return null;
}
/**
* Registers an adapter for the class.
*
* @param {Function} modelClass The class of model that we're registering the adapter for.
* @param {AdapterTypes} type The type of adapter that we're registering.
* @param {Adapter} adapter The adapter that we're registering.
*/
public registerAdapter<T extends Model>( modelClass: new () => T, type: AdapterTypes, adapter: Adapter<T> ): void {
if ( this.adapters[ type ].hasOwnProperty( modelClass.name ) ) {
throw new Error( 'An adapter of this type has already been registered for the model class.' );
}
this.adapters[ type ][ modelClass.name ] = adapter;
}
/**
* Fetches an adapter registered for the class.
*
* @param {Function} modelClass The class of the model for the adapter we're fetching.
* @param {AdapterTypes} type The type of adapter we're fetching.
*/
public getAdapter<T extends Model>( modelClass: new () => T, type: AdapterTypes ): Adapter<T> | null {
if ( this.adapters[ type ].hasOwnProperty( modelClass.name ) ) {
return this.adapters[ type ][ modelClass.name ];
}
return null;
}
}