feat(injector): implement InjectLazy
This commit is contained in:
parent
e02cdfe733
commit
a0176273c5
@ -12,4 +12,11 @@ export class InjectFuture {
|
|||||||
constructor(token){
|
constructor(token){
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InjectLazy {
|
||||||
|
@CONST()
|
||||||
|
constructor(token){
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
}
|
}
|
@ -59,6 +59,6 @@ export class BindingBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_constructDependencies(deps:List) {
|
_constructDependencies(deps:List) {
|
||||||
return ListWrapper.map(deps, (t) => new Dependency(Key.get(t), false));
|
return ListWrapper.map(deps, (t) => new Dependency(Key.get(t), false, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -37,11 +37,11 @@ export class Injector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getByKey(key:Key) {
|
getByKey(key:Key) {
|
||||||
return this._getByKey(key, false);
|
return this._getByKey(key, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
asyncGetByKey(key:Key) {
|
asyncGetByKey(key:Key) {
|
||||||
return this._getByKey(key, true);
|
return this._getByKey(key, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
createChild(bindings:List):Injector {
|
createChild(bindings:List):Injector {
|
||||||
@ -61,7 +61,11 @@ export class Injector {
|
|||||||
return ListWrapper.createFixedSize(Key.numberOfKeys() + 1);
|
return ListWrapper.createFixedSize(Key.numberOfKeys() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getByKey(key:Key, returnFuture) {
|
_getByKey(key:Key, returnFuture, returnLazy) {
|
||||||
|
if (returnLazy) {
|
||||||
|
return () => this._getByKey(key, returnFuture, false);
|
||||||
|
}
|
||||||
|
|
||||||
var strategy = returnFuture ? this._asyncStrategy : this._syncStrategy;
|
var strategy = returnFuture ? this._asyncStrategy : this._syncStrategy;
|
||||||
|
|
||||||
var instance = strategy.readFromCache(key);
|
var instance = strategy.readFromCache(key);
|
||||||
@ -71,7 +75,7 @@ export class Injector {
|
|||||||
if (isPresent(instance)) return instance;
|
if (isPresent(instance)) return instance;
|
||||||
|
|
||||||
if (isPresent(this._parent)) {
|
if (isPresent(this._parent)) {
|
||||||
return this._parent._getByKey(key, returnFuture);
|
return this._parent._getByKey(key, returnFuture, returnLazy);
|
||||||
}
|
}
|
||||||
throw new NoProviderError(key);
|
throw new NoProviderError(key);
|
||||||
}
|
}
|
||||||
@ -129,7 +133,7 @@ class _SyncInjectorStrategy {
|
|||||||
|
|
||||||
_resolveDependencies(key:Key, binding:Binding) {
|
_resolveDependencies(key:Key, binding:Binding) {
|
||||||
try {
|
try {
|
||||||
var getDependency = d => this.injector._getByKey(d.key, d.asFuture);
|
var getDependency = d => this.injector._getByKey(d.key, d.asFuture, d.lazy);
|
||||||
return ListWrapper.map(binding.dependencies, getDependency);
|
return ListWrapper.map(binding.dependencies, getDependency);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ProviderError) e.addKey(key);
|
if (e instanceof ProviderError) e.addKey(key);
|
||||||
@ -184,7 +188,7 @@ class _AsyncInjectorStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_resolveDependencies(key:Key, binding:Binding):List {
|
_resolveDependencies(key:Key, binding:Binding):List {
|
||||||
var getDependency = d => this.injector._getByKey(d.key, true);
|
var getDependency = d => this.injector._getByKey(d.key, true, d.lazy);
|
||||||
return ListWrapper.map(binding.dependencies, getDependency);
|
return ListWrapper.map(binding.dependencies, getDependency);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,9 +5,10 @@ var _id = 0;
|
|||||||
|
|
||||||
//TODO: vsavkin: move to binding once cyclic deps are supported
|
//TODO: vsavkin: move to binding once cyclic deps are supported
|
||||||
export class Dependency {
|
export class Dependency {
|
||||||
constructor(key:Key, asFuture){
|
constructor(key:Key, asFuture, lazy){
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.asFuture = asFuture;
|
this.asFuture = asFuture;
|
||||||
|
this.lazy = lazy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
library facade.di.reflector;
|
library facade.di.reflector;
|
||||||
|
|
||||||
import 'dart:mirrors';
|
import 'dart:mirrors';
|
||||||
import 'annotations.dart' show Inject, InjectFuture;
|
import 'annotations.dart' show Inject, InjectFuture, InjectLazy;
|
||||||
import 'key.dart' show Key, Dependency;
|
import 'key.dart' show Key, Dependency;
|
||||||
import 'exceptions.dart' show NoAnnotationError;
|
import 'exceptions.dart' show NoAnnotationError;
|
||||||
|
|
||||||
@ -31,15 +31,18 @@ class Reflector {
|
|||||||
|
|
||||||
final metadata = p.metadata.map((m) => m.reflectee);
|
final metadata = p.metadata.map((m) => m.reflectee);
|
||||||
|
|
||||||
var inject = metadata.where((m) => m is Inject);
|
var inject = metadata.firstWhere((m) => m is Inject, orElse: () => null);
|
||||||
var injectFuture = metadata.where((m) => m is InjectFuture);
|
var injectFuture = metadata.firstWhere((m) => m is InjectFuture, orElse: () => null);
|
||||||
|
var injectLazy = metadata.firstWhere((m) => m is InjectLazy, orElse: () => null);
|
||||||
|
|
||||||
if (inject.isNotEmpty) {
|
if (inject != null) {
|
||||||
return new Dependency(Key.get(inject.first.token), false);
|
return new Dependency(Key.get(inject.token), false, false);
|
||||||
} else if (injectFuture.isNotEmpty) {
|
} else if (injectFuture != null) {
|
||||||
return new Dependency(Key.get(injectFuture.first.token), true);
|
return new Dependency(Key.get(injectFuture.token), true, false);
|
||||||
|
} else if (injectLazy != null) {
|
||||||
|
return new Dependency(Key.get(injectLazy.token), false, true);
|
||||||
} else if (p.type.qualifiedName != #dynamic) {
|
} else if (p.type.qualifiedName != #dynamic) {
|
||||||
return new Dependency(Key.get(p.type.reflectedType), false);
|
return new Dependency(Key.get(p.type.reflectedType), false, false);
|
||||||
} else {
|
} else {
|
||||||
throw new NoAnnotationError(type);
|
throw new NoAnnotationError(type);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {Type, isPresent} from 'facade/lang';
|
import {Type, isPresent} from 'facade/lang';
|
||||||
import {Inject, InjectFuture} from './annotations';
|
import {Inject, InjectFuture, InjectLazy} from './annotations';
|
||||||
import {Dependency, Key} from './key';
|
import {Dependency, Key} from './key';
|
||||||
import {NoAnnotationError} from './exceptions';
|
import {NoAnnotationError} from './exceptions';
|
||||||
|
|
||||||
@ -27,10 +27,13 @@ export class Reflector {
|
|||||||
type = paramAnnotation;
|
type = paramAnnotation;
|
||||||
|
|
||||||
} else if (paramAnnotation instanceof Inject) {
|
} else if (paramAnnotation instanceof Inject) {
|
||||||
return this._createDependency(paramAnnotation.token, false);
|
return this._createDependency(paramAnnotation.token, false, false);
|
||||||
|
|
||||||
} else if (paramAnnotation instanceof InjectFuture) {
|
} else if (paramAnnotation instanceof InjectFuture) {
|
||||||
return this._createDependency(paramAnnotation.token, true);
|
return this._createDependency(paramAnnotation.token, true, false);
|
||||||
|
|
||||||
|
} else if (paramAnnotation instanceof InjectLazy) {
|
||||||
|
return this._createDependency(paramAnnotation.token, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +44,7 @@ export class Reflector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_createDependency(token, asFuture):Dependency {
|
_createDependency(token, asFuture, lazy):Dependency {
|
||||||
return new Dependency(Key.get(token), asFuture);
|
return new Dependency(Key.get(token), asFuture, lazy);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import {describe, it, iit, expect, beforeEach} from 'test_lib/test_lib';
|
import {describe, ddescribe, it, iit, expect, beforeEach} from 'test_lib/test_lib';
|
||||||
import {Injector, Inject, bind} from 'di/di';
|
import {Injector, Inject, InjectLazy, bind} from 'di/di';
|
||||||
|
|
||||||
class Engine {}
|
class Engine {}
|
||||||
class BrokenEngine {
|
class BrokenEngine {
|
||||||
@ -19,6 +19,12 @@ class Car {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CarWithLazyEngine {
|
||||||
|
constructor(@InjectLazy(Engine) engineFactory) {
|
||||||
|
this.engineFactory = engineFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CarWithDashboard {
|
class CarWithDashboard {
|
||||||
constructor(engine:Engine, dashboard:Dashboard) {
|
constructor(engine:Engine, dashboard:Dashboard) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
@ -118,31 +124,6 @@ export function main() {
|
|||||||
expect(() => new Injector([bind("blah")])).toThrowError('Invalid binding blah');
|
expect(() => new Injector([bind("blah")])).toThrowError('Invalid binding blah');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("child", function () {
|
|
||||||
it('should load instances from parent injector', function() {
|
|
||||||
var parent = new Injector([Engine]);
|
|
||||||
var child = parent.createChild([]);
|
|
||||||
|
|
||||||
var engineFromParent = parent.get(Engine);
|
|
||||||
var engineFromChild = child.get(Engine);
|
|
||||||
|
|
||||||
expect(engineFromChild).toBe(engineFromParent);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create new instance in a child injector', function() {
|
|
||||||
var parent = new Injector([Engine]);
|
|
||||||
var child = parent.createChild([
|
|
||||||
bind(Engine).toClass(TurboEngine)
|
|
||||||
]);
|
|
||||||
|
|
||||||
var engineFromParent = parent.get(Engine);
|
|
||||||
var engineFromChild = child.get(Engine);
|
|
||||||
|
|
||||||
expect(engineFromParent).not.toBe(engineFromChild);
|
|
||||||
expect(engineFromChild).toBeAnInstanceOf(TurboEngine);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should provide itself', function() {
|
it('should provide itself', function() {
|
||||||
var parent = new Injector([]);
|
var parent = new Injector([]);
|
||||||
var child = parent.createChild([]);
|
var child = parent.createChild([]);
|
||||||
@ -184,5 +165,56 @@ export function main() {
|
|||||||
expect(e.message).toContain("Error during instantiation of Engine! (Car -> Engine)");
|
expect(e.message).toContain("Error during instantiation of Engine! (Car -> Engine)");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe("child", function () {
|
||||||
|
it('should load instances from parent injector', function() {
|
||||||
|
var parent = new Injector([Engine]);
|
||||||
|
var child = parent.createChild([]);
|
||||||
|
|
||||||
|
var engineFromParent = parent.get(Engine);
|
||||||
|
var engineFromChild = child.get(Engine);
|
||||||
|
|
||||||
|
expect(engineFromChild).toBe(engineFromParent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create new instance in a child injector', function() {
|
||||||
|
var parent = new Injector([Engine]);
|
||||||
|
var child = parent.createChild([
|
||||||
|
bind(Engine).toClass(TurboEngine)
|
||||||
|
]);
|
||||||
|
|
||||||
|
var engineFromParent = parent.get(Engine);
|
||||||
|
var engineFromChild = child.get(Engine);
|
||||||
|
|
||||||
|
expect(engineFromParent).not.toBe(engineFromChild);
|
||||||
|
expect(engineFromChild).toBeAnInstanceOf(TurboEngine);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("lazy", function () {
|
||||||
|
it("should create dependencies lazily", function () {
|
||||||
|
var injector = new Injector([
|
||||||
|
Engine,
|
||||||
|
CarWithLazyEngine
|
||||||
|
]);
|
||||||
|
|
||||||
|
var car = injector.get(CarWithLazyEngine);
|
||||||
|
expect(car.engineFactory()).toBeAnInstanceOf(Engine);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cache instance created lazily", function () {
|
||||||
|
var injector = new Injector([
|
||||||
|
Engine,
|
||||||
|
CarWithLazyEngine
|
||||||
|
]);
|
||||||
|
|
||||||
|
var car = injector.get(CarWithLazyEngine);
|
||||||
|
var e1 = car.engineFactory();
|
||||||
|
var e2 = car.engineFactory();
|
||||||
|
|
||||||
|
expect(e1).toBe(e2);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user