Skip to content

Instantly share code, notes, and snippets.

@JoeRobich
Created October 7, 2012 05:01
Show Gist options
  • Save JoeRobich/3847162 to your computer and use it in GitHub Desktop.
Save JoeRobich/3847162 to your computer and use it in GitHub Desktop.
Port of AsFac ( https://github.com/thedevstop/asfac ) to TypeScript. Greatly simplified since there is no reflection in TypeScript. Passes no parameters when invoking a constructor and does not perform property injection.
interface ICallback {
(factory:JsFactory, scopeName:string):any;
}
interface ICallbackDictionary {
[name:string]:ICallback;
}
class JsFactory {
static DefaultScopeName = '';
private _registrations:ICallbackDictionary = { };
private _registerCallback (callback:ICallback, typeName:string, scopeName:string, asSingleton:bool=false):void {
var qualifiedTypeName = this._getQualifiedTypeName(scopeName, typeName);
if (asSingleton)
this._registrations[qualifiedTypeName] = (function(callback:ICallback, scopeName:string) {
var instance:any = undefined;
return function(factory:JsFactory, scopeName:string) {
if (typeof(instance) === 'undefined')
instance = callback(factory, scopeName);
return instance;
};
})(callback, scopeName);
else
this._registrations[qualifiedTypeName] = callback;
}
private _registerInstance (instance:any, typeName:string, scopeName:string):void {
this._registerCallback(function():any { return instance; }, typeName, scopeName);
}
private _registerType (type:any, typeName:string, scopeName:string, asSingleton:bool):void {
this._registerCallback(function():any { return new type(); }, typeName, scopeName, asSingleton);
}
private _isFunction (obj:any):bool {
var typeString:string = Object.prototype.toString.call(obj);
return (typeString === '[object Function]' && obj.length > 0);
}
private _isClass (obj:any):bool {
var typeString:string = Object.prototype.toString.call(obj);
return (typeString === '[object Function]' && obj.length == 0);
}
private _getQualifiedTypeName (scopeName:string, typeName:string):string {
return scopeName + ':' + typeName;
}
register (instance:any, typeName:string, scopeName:string=JsFactory.DefaultScopeName, asSingleton:bool=false):void {
if (this._isFunction(instance))
this._registerCallback(instance, typeName, scopeName, asSingleton);
else if (this._isClass(instance))
this._registerType(instance, typeName, scopeName, asSingleton);
else
this._registerInstance(instance, typeName, scopeName);
}
resolve (typeName:string, scopeName:string=JsFactory.DefaultScopeName):any {
var qualifiedTypeName = this._getQualifiedTypeName(scopeName, typeName);
var callback:any = this._registrations[qualifiedTypeName];
return callback ? callback(this, scopeName) : undefined;
}
}
module JsFactoryLocator {
var _instance:JsFactory = null;
export var getFactory = function() {
if (_instance === null)
_instance = new JsFactory();
return _instance;
}
}
interface IDictionary {
[name:string]:any;
}
class Dictionary implements IDictionary {
}
function logMessage(message:string):void {
console.log(message);
document.writeln(message + '<br />');
}
function assertTrue(assertion:string, check:bool):void {
var message:string = assertion + ': ';
message += check ? 'pass' : 'fail';
logMessage(message);
}
function assertSame(assertion:string, obj1:any, obj2:any):void {
assertTrue(assertion, obj1 === obj2);
}
function assertNotSame(assertion:string, obj1:any, obj2:any):void {
assertTrue(assertion, obj1 !== obj2);
}
function assertNull(assertion:string, obj:any):void {
assertSame(assertion, obj, null);
}
var factory:JsFactory;
var instance:any;
var instance2:any;
factory = new JsFactory();
instance = { foo:"bar" };
factory.register(instance, "Object");
instance2 = factory.resolve("Object");
assertSame("test_should_allow_register_concrete_instance", instance, instance2);
factory = new JsFactory();
factory.register(Dictionary, "Dictionary");
instance = factory.resolve("Dictionary");
assertSame("test_should_allow_register_type", instance.constructor, Dictionary);
factory = new JsFactory();
instance = [1, 2, 3];
factory.register(function():Array { return instance; }, "Array");
instance2 = factory.resolve("Array");
assertSame("test_should_allow_register_callback", instance, instance2);
factory = new JsFactory();
factory.register(function():Dictionary { return new Dictionary(); }, "Dictionary", JsFactory.DefaultScopeName, true);
instance = factory.resolve("Dictionary");
instance2 = factory.resolve("Dictionary");
assertSame("test_should_allow_register_callback_as_singleton", instance, instance2);
factory = new JsFactory();
factory.register(function():Dictionary { return new Dictionary(); }, "Dictionary", JsFactory.DefaultScopeName, false);
instance = factory.resolve("Dictionary");
instance2 = factory.resolve("Dictionary");
assertNotSame("test_should_return_new_instances_when_callback_not_registered_as_singleton", instance, instance2);
factory = new JsFactory();
factory.register(Dictionary, "Dictionary", JsFactory.DefaultScopeName, true);
instance = factory.resolve("Dictionary");
instance2 = factory.resolve("Dictionary");
assertSame("test_should_allow_register_type_as_singleton", instance, instance2);
factory = new JsFactory();
factory.register(Dictionary, "Dictionary", JsFactory.DefaultScopeName, false);
instance = factory.resolve("Dictionary");
instance2 = factory.resolve("Dictionary");
assertNotSame("test_should_return_new_instances_when_type_not_registered_as_singleton", instance, instance2);
factory = new JsFactory();
factory.register(null, "Dictionary");
instance = factory.resolve("Dictionary");
assertNull("test_should_be_able_register_null_as_an_instance_value", instance);
factory = new JsFactory();
instance = { numbers:[1, 2, 3] };
factory.register(instance, "Object");
instance2 = factory.resolve("Object");
assertSame("test_should_resolve_default_scope_if_registered", instance, instance2);
factory = new JsFactory();
instance = { numbers:[1, 2, 3] };
factory.register(instance, "Object", "nonDefaultScope");
instance2 = factory.resolve("Object", "nonDefaultScope");
assertSame("test_should_resolve_type_with_specified_scope", instance, instance2);
var resolveFunction:Function;
factory = new JsFactory();
resolveFunction = function (jsFactory:JsFactory, scopeName:string):any {
assertSame("test_should_pass_factory_into_callback_during_resolution", factory, jsFactory);
return new Dictionary();
};
factory.register(resolveFunction, "Dictionary");
instance = factory.resolve("Dictionary");
factory = new JsFactory();
var scope:string = "nonDefaultScope";
resolveFunction = function (jsFactory:JsFactory, scopeName:string):any {
assertSame("test_should_pass_scope_name_into_callback_during_resolution", scope, scopeName);
return new Dictionary();
};
factory.register(resolveFunction, "Dictionary", scope);
instance = factory.resolve("Dictionary", scope);
instance = new Dictionary();
JsFactoryLocator.getFactory().register(instance, "Dictionary");
factory = new JsFactory();
factory.register(new Dictionary(), "Dictionary");
instance2 = JsFactoryLocator.getFactory().resolve("Dictionary");
assertSame("test_should_resolve_from_singleton_registration", instance, instance2);
instance = new Dictionary();
JsFactoryLocator.getFactory().register(new Dictionary(), "Dictionary");
factory = new JsFactory();
factory.register(instance, "Dictionary");
instance2 = factory.resolve("Dictionary");
assertSame("test_should_resolve_from_instance_registration", instance, instance2);
@JoeRobich
Copy link
Author

Constructor functions are separated from callback functions since we expect callback functions to take 2 parameters (JsFactory and ScopeName). Ported over the applicable AsFac tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment