Skip to content
This repository was archived by the owner on Dec 6, 2017. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/errors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ class NoProviderError extends ArgumentError {
class CircularDependencyError extends ArgumentError {
CircularDependencyError(message) : super(message);
}

class OverrideError extends ArgumentError {
OverrideError(message) : super(message);
}
47 changes: 36 additions & 11 deletions lib/module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ class Module {
*
* The [value] is what actually will be injected.
*/
void value(Type id, value,
{CreationStrategy creation, Visibility visibility}) {
void value(Type id, value, {CreationStrategy creation, Visibility visibility,
bool allowOverride: false}) {
_dirty();
_checkForExistingId(id, allowOverride);
_providers[id] = new _ValueProvider(value, creation, visibility);
}

Expand All @@ -66,8 +67,9 @@ class Module {
* implied that [id] should be instantiated.
*/
void type(Type id, {Type implementedBy, CreationStrategy creation,
Visibility visibility}) {
Visibility visibility, bool allowOverride: false}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allowOverride should probably just be override. allowOverride makes me think that while defining this type binding I would like to [dis]allow others to override this binding, rather than I might be overriding an existing binding with this one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

_dirty();
_checkForExistingId(id, allowOverride);
_providers[id] = new _TypeProvider(
implementedBy == null ? id : implementedBy, creation, visibility);
}
Expand All @@ -78,9 +80,10 @@ class Module {
* The [factoryFn] will be called and all its arguments will get injected.
* The result of that function is the value that will be injected.
*/
void factory(Type id, FactoryFn factoryFn,
{CreationStrategy creation, Visibility visibility}) {
void factory(Type id, FactoryFn factoryFn, {CreationStrategy creation,
Visibility visibility, bool allowOverride: false}) {
_dirty();
_checkForExistingId(id, allowOverride);
_providers[id] = new _FactoryProvider(factoryFn, creation, visibility);
}

Expand All @@ -93,12 +96,31 @@ class Module {
_dirty();
}

_dirty() {
void _dirty() {
_providersCache = null;
}

bool get _isDirty =>
_providersCache == null || _childModules.any((m) => m._isDirty);

void _checkForExistingId(Type id, bool allowOverride) {
if (allowOverride) return;
if (_providers.containsKey(id)) {
var binding = _providers[id];
var type;
if (binding is _ValueProvider) {
type = "value";
} else if (binding is _TypeProvider) {
type = "type";
} else if (binding is _FactoryProvider) {
type = "factory";
}
assert(type != null);

throw new OverrideError("'$id' is already registered as a $type "
"provider.");
}
}
}

/** Deafault creation strategy is to instantiate on the defining injector. */
Expand All @@ -117,7 +139,8 @@ abstract class _Provider {

_Provider(this.creationStrategy, this.visibility);

dynamic get(Injector injector, Injector requestor, ObjectFactory getInstanceByType, error);
dynamic get(Injector injector, Injector requestor,
ObjectFactory getInstanceByType, error);
}

class _ValueProvider extends _Provider {
Expand All @@ -127,7 +150,8 @@ class _ValueProvider extends _Provider {
Visibility visibility])
: super(creationStrategy, visibility);

dynamic get(Injector injector, Injector requestor, ObjectFactory getInstanceByType, error) =>
dynamic get(Injector injector, Injector requestor,
ObjectFactory getInstanceByType, error) =>
value;
}

Expand All @@ -138,7 +162,8 @@ class _TypeProvider extends _Provider {
Visibility visibility])
: super(creationStrategy, visibility);

dynamic get(Injector injector, Injector requestor, ObjectFactory getInstanceByType, error) =>
dynamic get(Injector injector, Injector requestor,
ObjectFactory getInstanceByType, error) =>
injector.newInstanceOf(type, getInstanceByType, requestor, error);

}
Expand All @@ -150,6 +175,6 @@ class _FactoryProvider extends _Provider {
Visibility visibility])
: super(creationStrategy, visibility);

dynamic get(Injector injector, Injector requestor, getInstanceByType, error) =>
factoryFn(injector);
dynamic get(Injector injector, Injector requestor, getInstanceByType,
error) => factoryFn(injector);
}
115 changes: 79 additions & 36 deletions test/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ void main() {
typedef Injector InjectorFactory(List<Module> modules, [String name]);

createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {

describe(injectorName, () {

it('should instantiate a type', () {
Expand Down Expand Up @@ -183,8 +182,8 @@ createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {

it('should inject generic parameterized types', () {
var injector = injectorFactory([new Module()
..type(ParameterizedType)
..type(GenericParameterizedDependency)
..type(ParameterizedType)
..type(GenericParameterizedDependency)
]);
expect(injector.get(GenericParameterizedDependency),
new isInstanceOf<GenericParameterizedDependency>());
Expand All @@ -193,8 +192,8 @@ createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {

xit('should error while resolving parameterized types', () {
var injector = injectorFactory([new Module()
..type(ParameterizedType)
..type(ParameterizedDependency)
..type(ParameterizedType)
..type(ParameterizedDependency)
]);
expect(() => injector.get(ParameterizedDependency), throws);
});
Expand Down Expand Up @@ -224,8 +223,8 @@ createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {

it('should allow providing values', () {
var module = new Module()
..value(Engine, 'str value')
..value(Car, 123);
..value(Engine, 'str value')
..value(Car, 123);

var injector = injectorFactory([module]);
var abcInstance = injector.get(Engine);
Expand Down Expand Up @@ -261,10 +260,9 @@ createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {

it('should inject injector into factory function', () {
var module = new Module()
..type(Engine)
..factory(Car, (Injector injector) {
return new Car(injector.get(Engine), injector);
});
..type(Engine)
..factory(Car, (Injector injector) =>
new Car(injector.get(Engine), injector));

var injector = injectorFactory([module]);
var instance = injector.get(Car);
Expand All @@ -277,11 +275,11 @@ createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {
it('should throw an exception when injecting a primitive type', () {
var injector = injectorFactory([
new Module()
..type(NumDependency)
..type(IntDependency)
..type(DoubleDependency)
..type(BoolDependency)
..type(StringDependency)
..type(NumDependency)
..type(IntDependency)
..type(DoubleDependency)
..type(BoolDependency)
..type(StringDependency)
]);

expect(() {
Expand Down Expand Up @@ -312,13 +310,13 @@ createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {


it('should throw an exception when circular dependency', () {
var injector = injectorFactory([new Module()..type(CircularA)..type(CircularB)]);
var injector = injectorFactory([new Module()..type(CircularA)
..type(CircularB)]);

expect(() {
injector.get(CircularA);
}, toThrow(CircularDependencyError, 'Cannot resolve a circular dependency! '
'(resolving CircularA -> '
'CircularB -> CircularA)'));
}, toThrow(CircularDependencyError, 'Cannot resolve a circular '
'dependency! (resolving CircularA -> CircularB -> CircularA)'));
});


Expand Down Expand Up @@ -462,9 +460,9 @@ createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {
it('should instantiate class only once (Issue #18)', () {
var injector = injectorFactory([
new Module()
..type(Log)
..type(ClassOne)
..factory(InterfaceOne, (i) => i.get(ClassOne))
..type(Log)
..type(ClassOne)
..factory(InterfaceOne, (i) => i.get(ClassOne))
]);

expect(injector.get(InterfaceOne), same(injector.get(ClassOne)));
Expand All @@ -483,8 +481,8 @@ createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {
}

var parentModule = new Module()
..type(Engine, implementedBy: MockEngine, creation: creation)
..type(Car, creation: creation);
..type(Engine, implementedBy: MockEngine, creation: creation)
..type(Car, creation: creation);

var parentInjector = injectorFactory([parentModule]);
var childInjector = parentInjector.createChild([]);
Expand All @@ -503,7 +501,7 @@ createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {
}

var module = new Module()
..type(Engine, implementedBy: MockEngine, creation: creation);
..type(Engine, implementedBy: MockEngine, creation: creation);
var injector = injectorFactory([module]);
expect(() {
injector.get(Engine);
Expand All @@ -515,38 +513,83 @@ createInjectorSpec(String injectorName, InjectorFactory injectorFactory) {
describe('visiblity', () {

it('should hide instances', () {

var rootMock = new MockEngine();
var childMock = new MockEngine();

var parentModule = new Module()
..value(Engine, rootMock);
var parentModule = new Module()..value(Engine, rootMock);
var childModule = new Module()
..value(Engine, childMock, visibility: (_, __) => false);
..value(Engine, childMock, visibility: (_, __) => false);

var parentInjector = injectorFactory([parentModule]);
var childInjector = parentInjector.createChild([childModule]);

var val = childInjector.get(Engine);
expect(val, same(rootMock));
expect(childInjector.get(Engine), same(rootMock));
});

it('should throw when an instance in not visible in the root injector', () {
var module = new Module()
..value(Car, 'Invisible', visibility: (_, __) => false);
..value(Car, 'Invisible', visibility: (_, __) => false);

var injector = injectorFactory([module]);

expect(() {
injector.get(Car);
}, toThrow(
NoProviderError,
'No provider found for Car! (resolving Car)'
NoProviderError, 'No provider found for Car! (resolving Car)'
));
});

});
describe('override', () {

it('should prevent overriding a provider by default', () {
expect(() {
new Module()
..value(Engine, null)
..value(Engine, new MockEngine());
}, toThrow(
OverrideError, "'Engine' is already registered as a value provider"
));

expect(() {
new Module()
..value(Engine, null)
..type(Engine);
}, toThrow(
OverrideError, "'Engine' is already registered as a value provider"
));

expect(() {
new Module()
..value(Engine, null)
..factory(Engine, (_) => null);
}, toThrow(
OverrideError, "'Engine' is already registered as a value provider"
));
});
});

it('should allow overriding a provider', () {
var module;
var injector;

var value = new MockEngine2();
module = new Module()
..value(Engine, null)
..value(Engine, value, allowOverride: true);
expect(injectorFactory([module]).get(Engine), same(value));

module = new Module()
..value(Engine, null)
..type(Engine, allowOverride: true);
expect(injectorFactory([module]).get(Engine), instanceOf(Engine));

module = new Module()
..value(Engine, null)
..factory(Engine, (_) => new Engine(), allowOverride: true);
expect(injectorFactory([module]).get(Engine), instanceOf(Engine));
});

});
});

}