Skip to content

Commit

Permalink
Merge pull request #69 from vanlooverenkoen/feature/#51-nullsafety-ki…
Browse files Browse the repository at this point in the history
…wi-generator

Feature/#51 nullsafety kiwi generator
  • Loading branch information
vanlooverenkoen authored Jun 1, 2021
2 parents 1055d9f + 5f994fd commit f4498a5
Show file tree
Hide file tree
Showing 20 changed files with 250 additions and 154 deletions.
10 changes: 5 additions & 5 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ version: 0.1.0
homepage: https://github.com/vanlooverenkoen/kiwi

environment:
sdk: '>=2.0.0 <3.0.0'
sdk: '>=2.12.0 <3.0.0'

dependencies:
kiwi: ^2.1.1
kiwi: ^3.0.0

dev_dependencies:
test: ^1.15.4
build_runner: ^1.10.3
kiwi_generator: ^2.1.1
test: ^1.17.5
build_runner: ^2.0.4
kiwi_generator: ^3.0.0

dependency_overrides:
kiwi:
Expand Down
4 changes: 3 additions & 1 deletion flutter_example/lib/widget/error_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import 'package:flutter/material.dart';
class ErrorContainer extends StatelessWidget {
final VoidCallback error;

const ErrorContainer({@required this.error});
const ErrorContainer({
required this.error,
});

@override
Widget build(BuildContext context) {
Expand Down
8 changes: 4 additions & 4 deletions flutter_example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ description: A Flutter example for kiwi
version: 1.0.0+1

environment:
sdk: ">=2.2.0 <3.0.0"
sdk: ">=2.12.0 <3.0.0"

dependencies:
flutter:
sdk: flutter
kiwi: ^2.1.1
kiwi: ^3.0.0

dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^1.10.3
kiwi_generator: ^2.1.1
build_runner: ^2.0.4
kiwi_generator: ^3.0.0

dependency_overrides:
kiwi:
Expand Down
8 changes: 8 additions & 0 deletions kiwi/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## [3.0.0-nullsafety.1] - 2021-03-19
### Breaking
- resolve should not be nullable.

## [3.0.0-nullsafety.0] - 2021-03-04
### Added
- Nullsafety support in a dev release

## [2.1.1] - 2020-10-24
### Updated
- Description to make the kiwi package easy to find in pub.dev
Expand Down
16 changes: 7 additions & 9 deletions kiwi/lib/src/annotations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ class Register {
this.from,
this.resolvers,
this.constructorName,
}) : assert(type != null),
oneTime = null;
}) : oneTime = null;

/// Create an annotation that will generate a `registerSingleton` method.
const Register.singleton(
Expand All @@ -20,31 +19,30 @@ class Register {
this.from,
this.resolvers,
this.constructorName,
}) : assert(type != null),
oneTime = true;
}) : oneTime = true;

/// The type to register.
final Type type;

/// The type to create when requesting [type].
final Type from;
final Type? from;

/// The name under which the factory will be registered
///
/// You must provide the same name in [KiwiContainer.resolve]
/// to get the same factory.
final String name;
final String? name;

/// A map that give for a type, the name under which it should be resolved
///
/// For example if you have registered a type T under the name
/// 'myType', you have to specify it in this map in order
/// to use it instead of the default value for the type T.
final Map<Type, String> resolvers;
final Map<Type, String>? resolvers;

/// The name of the constructor to use inside the factory.
final String constructorName;
final String? constructorName;

/// Whether the factory has to be created only one time.
final bool oneTime;
final bool? oneTime;
}
64 changes: 37 additions & 27 deletions kiwi/lib/src/kiwi_container.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:kiwi/src/model/exception/kiwi_error.dart';
import 'package:kiwi/src/model/exception/not_registered_error.dart';
import 'package:meta/meta.dart';

/// Signature for a builder which creates an object of type [T].
Expand All @@ -8,14 +9,14 @@ typedef T Factory<T>(KiwiContainer container);
class KiwiContainer {
/// Creates a scoped container.
KiwiContainer.scoped()
: _namedProviders = Map<String, Map<Type, _Provider<Object>>>();
: _namedProviders = Map<String?, Map<Type, _Provider<Object>>>();

static final KiwiContainer _instance = KiwiContainer.scoped();

/// Always returns a singleton representing the only container to be alive.
factory KiwiContainer() => _instance;

final Map<String, Map<Type, _Provider<Object>>> _namedProviders;
final Map<String?, Map<Type, _Provider<Object>>> _namedProviders;

/// Whether ignoring KiwiErrors in the following cases:
/// * if you register the same type under the same name a second time.
Expand All @@ -34,7 +35,7 @@ class KiwiContainer {
/// to [KiwiContainer.resolve].
void registerInstance<S>(
S instance, {
String name,
String? name,
}) {
_setProvider(name, _Provider<S>.instance(instance));
}
Expand All @@ -48,7 +49,7 @@ class KiwiContainer {
/// to [KiwiContainer.resolve].
void registerFactory<S>(
Factory<S> factory, {
String name,
String? name,
}) {
_setProvider(name, _Provider<S>.factory(factory));
}
Expand All @@ -63,15 +64,15 @@ class KiwiContainer {
/// to [KiwiContainer.resolve].
void registerSingleton<S>(
Factory<S> factory, {
String name,
String? name,
}) {
_setProvider(name, _Provider<S>.singleton(factory));
}

/// Removes the entry previously registered for the type [T].
///
/// If [name] is set, removes the one registered for that name.
void unregister<T>([String name]) {
void unregister<T>([String? name]) {
if (!silent && !(_namedProviders[name]?.containsKey(T) ?? false)) {
throw KiwiError(
'Failed to unregister `$T`:\n\nThe type `$T` was not registered${name == null ? '' : ' for the name `$name`'}\n\nMake sure `$T` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.');
Expand All @@ -88,17 +89,21 @@ class KiwiContainer {
///
/// * [KiwiContainer.registerFactory] for register a builder function.
/// * [KiwiContainer.registerInstance] for register an instance.
T resolve<T>([String name]) {
Map<Type, _Provider<Object>> providers = _namedProviders[name];
if (!silent && !(providers?.containsKey(T) ?? false)) {
throw KiwiError(
T resolve<T>([String? name]) {
final providers = _namedProviders[name] ?? Map<Type, _Provider<Object>>();
if (!silent && !(providers.containsKey(T))) {
throw NotRegisteredKiwiError(
'Failed to resolve `$T`:\n\nThe type `$T` was not registered${name == null ? '' : ' for the name `$name`'}\n\nMake sure `$T` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.');
}
if (providers == null) {
return null;
}

return providers[T]?.get(this);
final value = providers[T]?.get(this);
if (value == null) {
throw NotRegisteredKiwiError(
'Failed to resolve `$T`:\n\nThe type `$T` was not registered${name == null ? '' : ' for the name `$name`'}\n\nMake sure `$T` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.');
}
if (value is T) return value as T;
throw NotRegisteredKiwiError(
'Failed to resolve `$T`:\n\nValue was not registered as `$T`\n\nThe type `$T` was not registered${name == null ? '' : ' for the name `$name`'}\n\nMake sure `$T` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.');
}

/// Attemps to resolve the type [S] and tries to cast it to T.
Expand All @@ -114,16 +119,18 @@ class KiwiContainer {
/// * [KiwiContainer.registerFactory] for register a builder function.
/// * [KiwiContainer.registerInstance] for register an instance.
@visibleForTesting
T resolveAs<S, T extends S>([String name]) {
T? resolveAs<S, T extends S>([String? name]) {
final obj = resolve<S>(name);
if (!silent && !(obj is T)) {
throw KiwiError(
'Failed to resolve `$S` as `$T`:\n\nThe type `$S` as `$T` was not registered${name == null ? '' : ' for the name `$name`'}\n\nMake sure `$T` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.');
}
return obj;
if (obj == null) return null;
if (obj is T) return obj as T;
return null;
}

T call<T>([String name]) => resolve<T>(name);
T call<T>([String? name]) => resolve<T>(name);

/// Removes all instances and builders from the container.
///
Expand All @@ -132,32 +139,35 @@ class KiwiContainer {
_namedProviders.clear();
}

void _setProvider<T>(String name, _Provider<T> provider) {
void _setProvider<T>(String? name, _Provider<T> provider) {
final nameProviders = _namedProviders;
if (!silent &&
(_namedProviders.containsKey(name) &&
_namedProviders[name].containsKey(T))) {
(nameProviders.containsKey(name) &&
nameProviders[name]!.containsKey(T))) {
throw KiwiError(
'The type `$T` was already registered${name == null ? '' : ' for the name `$name`'}');
}
_namedProviders.putIfAbsent(name, () => Map<Type, _Provider<Object>>())[T] =
provider;
provider as _Provider<Object>;
}
}

class _Provider<T> {
_Provider.instance(this.object)
: instanceBuilder = null,
: _instanceBuilder = null,
_oneTime = false;

_Provider.factory(this.instanceBuilder) : _oneTime = false;
_Provider.factory(this._instanceBuilder) : _oneTime = false;

_Provider.singleton(this.instanceBuilder) : _oneTime = true;
_Provider.singleton(this._instanceBuilder) : _oneTime = true;

final Factory<T> instanceBuilder;
T object;
final Factory<T>? _instanceBuilder;
T? object;
bool _oneTime = false;

T get(KiwiContainer container) {
T? get(KiwiContainer container) {
final instanceBuilder = _instanceBuilder;

if (_oneTime && instanceBuilder != null) {
object = instanceBuilder(container);
_oneTime = false;
Expand Down
10 changes: 10 additions & 0 deletions kiwi/lib/src/model/exception/not_registered_error.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import 'package:kiwi/src/model/exception/kiwi_error.dart';

class NotRegisteredKiwiError extends KiwiError {
NotRegisteredKiwiError(String message) : super(message);

@override
String toString() {
return 'Not Registered KiwiError:\n\n\n$message\n\n\n';
}
}
8 changes: 4 additions & 4 deletions kiwi/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
name: kiwi
description: A simple yet efficient dependency injection container for Dart and Flutter (can be coupled with the kiwi_generator package).
version: 2.1.1
version: 3.0.0
homepage: https://github.com/vanlooverenkoen/kiwi/tree/master/kiwi

environment:
sdk: '>=2.2.0 <3.0.0'
sdk: ">=2.12.0-0 <3.0.0"

dependencies:
meta: ^1.2.3
meta: ^1.3.0

dev_dependencies:
test: ^1.15.4
test: ^1.17.5
47 changes: 39 additions & 8 deletions kiwi/test/kiwi_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,29 @@ void main() {
expect(container.resolve<int>(), 5);
expect(container.resolve<int>('named'), 6);
expect(container.resolve<num>(), 7);
expect(container.resolve<num>('named'), null);
expect(container.resolve<Character>(), person);

expect(
() => container.resolve<num>('named'),
throwsA(TypeMatcher<KiwiError>().having(
(f) => f.toString(),
'toString()',
'Not Registered KiwiError:\n\n\nFailed to resolve `num`:\n\nThe type `num` was not registered for the name `named`\n\nMake sure `num` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.\n\n\n',
)));
});

test('instances should be resolveAs', () {
final sith = Sith('Anakin', 'Skywalker', 'DartVader');
container.registerSingleton<Character>((c) => sith);

expect(container.resolveAs<Character, Sith>(), sith);
expect(container.resolveAs<Character, Sith>('named'), null);

expect(
() => container.resolveAs<Character, Sith>('named'),
throwsA(TypeMatcher<KiwiError>().having(
(f) => f.toString(),
'toString()',
'Not Registered KiwiError:\n\n\nFailed to resolve `Character`:\n\nThe type `Character` was not registered for the name `named`\n\nMake sure `Character` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.\n\n\n',
)));
});

test('container should resolve when called', () {
Expand All @@ -62,7 +75,13 @@ void main() {
expect(container<int>(), 5);
expect(container<int>('named'), 6);
expect(container<num>(), 7);
expect(container<num>('named'), null);
expect(
() => container.resolve<num>('named'),
throwsA(TypeMatcher<KiwiError>().having(
(f) => f.toString(),
'toString()',
'Not Registered KiwiError:\n\n\nFailed to resolve `num`:\n\nThe type `num` was not registered for the name `named`\n\nMake sure `num` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.\n\n\n',
)));
expect(container<Character>(), person);
});

Expand Down Expand Up @@ -127,10 +146,22 @@ void main() {
expect(container.resolve<int>('named'), 6);

container.unregister<int>();
expect(container.resolve<int>(), null);
expect(
() => container.resolve<int>(),
throwsA(TypeMatcher<KiwiError>().having(
(f) => f.toString(),
'toString()',
'Not Registered KiwiError:\n\n\nFailed to resolve `int`:\n\nThe type `int` was not registered\n\nMake sure `int` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.\n\n\n',
)));

container.unregister<int>('named');
expect(container.resolve<int>('named'), null);
expect(
() => container.resolve<int>('named'),
throwsA(TypeMatcher<KiwiError>().having(
(f) => f.toString(),
'toString()',
'Not Registered KiwiError:\n\n\nFailed to resolve `int`:\n\nThe type `int` was not registered for the name `named`\n\nMake sure `int` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.\n\n\n',
)));
});
});

Expand Down Expand Up @@ -188,15 +219,15 @@ void main() {
throwsA(TypeMatcher<KiwiError>().having(
(f) => f.toString(),
'toString()',
'KiwiError:\n\n\nFailed to resolve `int`:\n\nThe type `int` was not registered\n\nMake sure `int` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.\n\n\n',
'Not Registered KiwiError:\n\n\nFailed to resolve `int`:\n\nThe type `int` was not registered\n\nMake sure `int` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.\n\n\n',
)));

expect(
() => container.resolve<int>('name'),
throwsA(TypeMatcher<KiwiError>().having(
(f) => f.toString(),
'toString()',
'KiwiError:\n\n\nFailed to resolve `int`:\n\nThe type `int` was not registered for the name `name`\n\nMake sure `int` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.\n\n\n',
'Not Registered KiwiError:\n\n\nFailed to resolve `int`:\n\nThe type `int` was not registered for the name `name`\n\nMake sure `int` is added to your KiwiContainer and rerun build_runner build\n(If you are using the kiwi_generator)\n\nWhen using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.\n\n\n',
)));
});
test('values should exist when resolving as', () {
Expand Down
Loading

0 comments on commit f4498a5

Please sign in to comment.