Permalink
Browse files

feature: ioc-Container is more typesafe

  • Loading branch information...
MikeMitterer committed Dec 19, 2018
1 parent bc9b95f commit a653946edd8df74340f47fc5a5ba925d666f8347
@@ -22,7 +22,7 @@ part of m4d_core;
abstract class MdlComponent extends Object with MdlEventListener {
final Logger _logger = new Logger('mdlcore.MdlComponent');

final ioc.IOCContainer injector;
final ioc.Container injector;

/// This is the element witch has the mdl-js- class
final dom.Element element;
@@ -90,7 +90,7 @@ class MdlComponentHandler {

//final List<Module> _modules = new List<Module>();

final _iocContainer = ioc.IOCContainer();
final _iocContainer = ioc.Container();

/// If set to true it
bool _enableVisualDebugging = false;
@@ -21,7 +21,7 @@ part of m4d_core;

typedef void MdlCallback(final dom.HtmlElement element);

typedef MdlComponent MdlComponentFactory(final dom.HtmlElement element,final ioc.IOCContainer iocContainer);
typedef MdlComponent MdlComponentFactory(final dom.HtmlElement element,final ioc.Container iocContainer);

enum SelectorType { CLASS, TAG, ATTRIBUTE }

@@ -96,7 +96,7 @@ class MdlConfig<T extends MdlComponent> {
/// Avoids problems with Components and Helpers like MaterialRipple
final bool isWidget;

MdlConfig(this._selector, T componentFactory(final dom.HtmlElement element,final ioc.IOCContainer iocContainer),
MdlConfig(this._selector, T componentFactory(final dom.HtmlElement element,final ioc.Container iocContainer),
{ final bool isWidget: false }) : _componentFactory = componentFactory, this.isWidget = isWidget {

Validate.isTrue(T != "dynamic", "Add a type-information to your MdlConfig like new MdlConfig<MaterialButton>()");
@@ -108,7 +108,7 @@ class MdlConfig<T extends MdlComponent> {

Type get type => T;

MdlComponent newComponent(final dom.HtmlElement element,final ioc.IOCContainer iocContainer) {
MdlComponent newComponent(final dom.HtmlElement element,final ioc.Container iocContainer) {
return _componentFactory(element,iocContainer);
}

@@ -123,7 +123,7 @@ class MdlConfig<T extends MdlComponent> {
/// (MdlComponent) properties!
class MdlWidgetConfig<T extends MdlComponent> extends MdlConfig<T> {
MdlWidgetConfig(final String selector,
T componentFactory(final dom.HtmlElement element,final ioc.IOCContainer iocContainer)) :
T componentFactory(final dom.HtmlElement element,final ioc.Container iocContainer)) :
super(selector, componentFactory, isWidget: true) {

priority = RegistrationPriority.WIDGET;
@@ -23,6 +23,9 @@ import 'package:validate/validate.dart';

typedef Map<String, dynamic> ToJson();
typedef Map<String, Function> ToEvents();
typedef String AsString();

Container _container = Container();

abstract class Binder {
void bind();
@@ -31,25 +34,26 @@ abstract class Binder {
enum ServiceType {
Instance,
Function,
Json
Json,
Provider
}

abstract class IOCModule {
BindingSyntax bind(final Service service) => IOCContainer().bind(service);
abstract class Module {
BindingSyntax bind(final Service service) => _container.bind(service);

configure();

List<IOCModule> get dependsOn => <IOCModule>[];
List<Module> get dependsOn => <Module>[];

void _resolveDependencies() {
dependsOn.forEach((final IOCModule module) {
dependsOn.forEach((final Module module) {
module?._resolveDependencies();
});
configure();
}
}

class Service {
class Service<R> {
/// Service name e.g. 'm4d_formatter.Formatters'
final String name;

@@ -70,38 +74,79 @@ class Service {
int get hashCode =>
name.hashCode ^
type.hashCode;

R resolve() => ServiceResolveSyntax<R>(_container.raw(this))._as();
ServiceResolveSyntax<R> get to => ServiceResolveSyntax<R>(_container.raw(this));
}

class ServiceResolveSyntax<R> {
final R _data;

// Data can be null!
ServiceResolveSyntax(this._data);

/// To convert R to one of it's subclasses
///
/// class TestClass implements JsonSerializer {
/// ...
/// }
///
/// var object = TestService.resolveTo().subclass<JsonSerializer>();
///
S subclass<S>() => _data as S;

/// Map from one type into another
///
/// final tc = TestProvider.map((ServiceProvider<_TestClass> sp)
/// => "${sp.get().firstname} ${sp.get().lastname}");
///
M map<M>(M mapper(R value)) => mapper(_as());

R _as() => _data;
}

class InstanceService extends Service {
InstanceService(final String name) : super(name, ServiceType.Instance);
}

/// [IOCContainer] is a singleton
class IOCContainer {
static IOCContainer _singleton;
class ServiceProvider<T> {
final T _instance;
ServiceProvider(this._instance);

T get() => _instance;
}

/// [Container] is a singleton
class Container {
static Container _singleton;

final _services = Map<Service, dynamic>();

factory IOCContainer.bindModules(final List<IOCModule> modules) {
factory Container.bindModules(final List<Module> modules) {
Validate.notNull(modules);

if (_singleton == null) {
_singleton = IOCContainer._private();
_singleton = Container._private();
}

modules.forEach((final IOCModule module) => module?._resolveDependencies());
modules.forEach((final Module module) => module?._resolveDependencies());

return _singleton;
}

factory IOCContainer() => IOCContainer.bindModules(<IOCModule>[]);
factory Container() => Container.bindModules(<Module>[]);

Container._private();

/// Binds a Service-ID ([service]) to its implementation
BindingSyntax bind(final Service service) {
Validate.notNull(service);

return BindingSyntax._private(service);
}

ResolveSyntax resolve(final Service service) => ResolveSyntax(_services[service]);
ContainerResolveSyntax resolve(final Service service) => ContainerResolveSyntax(raw(service));

IOCContainer._private();
dynamic raw(final Service service) => _services[service];

void unregister(final Service service) => _services.remove(service);

@@ -110,10 +155,19 @@ class IOCContainer {
int get nrOfServices => _services.length;
}

class BindingSyntax {
class BindingSyntax<T> {
Service _service;

void to(final Object implementation) => _InstanceBinder(_service, implementation).bind();
void to(final Object implementation) {
// if(_service.rtType != null) {
// Validate.isTrue(_service.rtType is implementation.runtimeType)
// }
if(_service.type == ServiceType.Instance) {
_InstanceBinder(_service, implementation).bind();
} else {
_ProviderBinder(_service, implementation).bind();
}
}

void toFunction<R>(R callback()) => _FunctionBinder<R>(_service, callback).bind();

@@ -124,13 +178,27 @@ class BindingSyntax {
BindingSyntax._private(this._service);
}

class ResolveSyntax {
class ContainerResolveSyntax {
final _data;

// Data can be null!
ResolveSyntax(this._data);
ContainerResolveSyntax(this._data);

T as<T>([ T converter(final data) ]) => converter == null ? _data as T : converter(_data);
R as<R>() {
if(_data is ServiceProvider<R>) {
return (_data as ServiceProvider<R>).get();
} else {
return _data;
}
}

ServiceProvider<R> asProvider<R>() {
if(_data is ServiceProvider<R>) {
return _data as ServiceProvider<R>;
} else {
return ServiceProvider<R>(_data);
}
}

dynamic get untyped => _data;
}
@@ -145,12 +213,38 @@ class _InstanceBinder extends Binder {
void bind() {
Validate.notNull(_service);
Validate.notNull(_implementation);
Validate.isTrue(_service.type == ServiceType.Instance);
Validate.isTrue(_service.type == ServiceType.Instance,
"You can bind ${_service.name} only to ${_service.type}");

Validate.isTrue(_implementation is! Type,
"You must bind a concrete class to '${_service.name}', "
"not a type! ($_implementation)");

IOCContainer()._services[_service] = _implementation;
_container._services[_service] = _implementation;
}
}

class _ProviderBinder extends Binder {
final Service _service;
final ServiceProvider _implementation;

_ProviderBinder(this._service, this._implementation);

@override
void bind() {
Validate.notNull(_service);
Validate.notNull(_implementation);
Validate.isTrue(_service.type == ServiceType.Provider,
"You can bind ${_service.name} only to ${_service.type}");

Validate.isTrue(_implementation is ServiceProvider,
"${_service.name} must be of type ${_service.type}");

Validate.isTrue(_implementation is! Type,
"You must bind a concrete class to '${_service.name}', "
"not a type! ($_implementation)");

_container._services[_service] = _implementation;
}
}

@@ -164,10 +258,12 @@ class _FunctionBinder<R> extends Binder {
void bind() {
Validate.notNull(_service);
Validate.notNull(_callback);
Validate.isTrue(_service.type == ServiceType.Function);
Validate.isTrue(_service.type == ServiceType.Function,
"${_service.name} must be a '${_service.type}' but was '${ServiceType.Function}'!");

Validate.isTrue(_callback is R Function());

IOCContainer()._services[_service] = _callback;
_container._services[_service] = _callback;
}
}

@@ -184,7 +280,7 @@ class _JsonBinder extends Binder {
Validate.isTrue(_service.type == ServiceType.Json);
Validate.isTrue(_callback is ToJson);

IOCContainer()._services[_service] = _callback;
_container._services[_service] = _callback;
}
}

@@ -201,7 +297,7 @@ class _EventsBinder extends Binder {
Validate.isTrue(_service.type == ServiceType.Function);
Validate.isTrue(_callback is ToEvents);

IOCContainer()._services[_service] = _callback;
_container._services[_service] = _callback;
}
}

@@ -46,7 +46,7 @@ class SlowComponent extends MdlComponent {

static const _SlowComponentCssClasses _cssClasses = const _SlowComponentCssClasses();

SlowComponent.fromElement(final dom.HtmlElement element,final ioc.IOCContainer iocContainer)
SlowComponent.fromElement(final dom.HtmlElement element,final ioc.Container iocContainer)
: super(element,iocContainer) {
_init();
}
@@ -93,7 +93,7 @@ class SlowComponent extends MdlComponent {
void registerSlowComponent() {
final MdlConfig config = new MdlWidgetConfig<SlowComponent>(
_SlowComponentConstant.WIDGET_SELECTOR,
(final dom.HtmlElement element,final ioc.IOCContainer iocContainer)
(final dom.HtmlElement element,final ioc.Container iocContainer)
=> new SlowComponent.fromElement(element,iocContainer)
);

Oops, something went wrong.

0 comments on commit a653946

Please sign in to comment.