add(angel3): adding re-branded angel3 container package
This commit is contained in:
parent
1b2983d02b
commit
be65590d2e
39 changed files with 16346 additions and 0 deletions
71
packages/container/container/.gitignore
vendored
Normal file
71
packages/container/container/.gitignore
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.dart_tool
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
|
||||
### Dart template
|
||||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
|
||||
# SDK 1.20 and later (no longer creates packages directories)
|
||||
|
||||
# Older SDK versions
|
||||
# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
|
||||
.project
|
||||
.buildlog
|
||||
**/packages/
|
||||
|
||||
|
||||
# Files created by dart2js
|
||||
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||
# rules if you intend to use dart2js directly
|
||||
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
|
||||
# differentiate from explicit Javascript files)
|
||||
*.dart.js
|
||||
*.part.js
|
||||
*.js.deps
|
||||
*.js.map
|
||||
*.info.json
|
||||
|
||||
# Directory created by dartdoc
|
||||
|
||||
# Don't commit pubspec lock file
|
||||
# (Library packages only! Remove pattern if developing an application package)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
|
||||
## VsCode
|
||||
.vscode/
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
/out/
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
12
packages/container/container/AUTHORS.md
Normal file
12
packages/container/container/AUTHORS.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
Primary Authors
|
||||
===============
|
||||
|
||||
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
|
||||
|
||||
Thomas is the current maintainer of the code base. He has refactored and migrated the
|
||||
code base to support NNBD.
|
||||
|
||||
* __[Tobe O](thosakwe@gmail.com)__
|
||||
|
||||
Tobe has written much of the original code prior to NNBD migration. He has moved on and
|
||||
is no longer involved with the project.
|
151
packages/container/container/CHANGELOG.md
Normal file
151
packages/container/container/CHANGELOG.md
Normal file
|
@ -0,0 +1,151 @@
|
|||
# Change Log
|
||||
|
||||
## 8.1.1
|
||||
|
||||
* Updated repository link
|
||||
|
||||
## 8.1.0
|
||||
|
||||
* Updated `lints` to 3.0.0
|
||||
* Fixed analyser warnings
|
||||
|
||||
## 8.0.0
|
||||
|
||||
* Require Dart >= 3.0
|
||||
|
||||
## 7.1.0-beta.2
|
||||
|
||||
* Require Dart >= 2.19
|
||||
* Refactored `EmptyReflector`
|
||||
|
||||
## 7.1.0-beta.1
|
||||
|
||||
* Require Dart >= 2.18
|
||||
* Moved `defaultErrorMessage` to `ContainerConst` class to resolve reflectatable issue.
|
||||
* Added `hashCode`
|
||||
|
||||
## 7.0.0
|
||||
|
||||
* Require Dart >= 2.17
|
||||
|
||||
## 6.0.0
|
||||
|
||||
* Require Dart >= 2.16
|
||||
* Removed `error`
|
||||
|
||||
## 5.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 3.1.1
|
||||
|
||||
* Updated `_ReflectedMethodMirror` to have optional `returnType` parameter
|
||||
* Updated `Container` to handle non nullable type
|
||||
|
||||
## 3.1.0
|
||||
|
||||
* Updated linter to `package:lints`
|
||||
|
||||
## 3.0.2
|
||||
|
||||
* Resolved static analysis warnings
|
||||
|
||||
## 3.0.1
|
||||
|
||||
* Updated README
|
||||
|
||||
## 3.0.0
|
||||
|
||||
* Migrated to support Dart >= 2.12 NNBD
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Migrated to work with Dart >= 2.12 Non NNBD
|
||||
|
||||
## 1.1.0
|
||||
|
||||
* `pedantic` lints.
|
||||
* Add `ThrowingReflector`, which throws on all operations.
|
||||
* `EmptyReflector` uses `Object` instead of `dynamic` as its returned
|
||||
type, as the `dynamic` type is (apparently?) no longer a valid constant value.
|
||||
* `registerSingleton` now returns the provided `object`.
|
||||
* `registerFactory` and `registerLazySingleton` now return the provided function `f`.
|
||||
|
||||
## 1.0.4
|
||||
|
||||
* Slight patch to prevent annoying segfault.
|
||||
|
||||
## 1.0.3
|
||||
|
||||
* Added `Future` support to `Reflector`.
|
||||
|
||||
## 1.0.2
|
||||
|
||||
* Added `makeAsync<T>`.
|
||||
|
||||
## 1.0.1
|
||||
|
||||
* Added `hasNamed`.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
* Removed `@GenerateReflector`.
|
||||
|
||||
## 1.0.0-alpha.12
|
||||
|
||||
* `StaticReflector` now defaults to empty arguments.
|
||||
|
||||
## 1.0.0-alpha.11
|
||||
|
||||
* Added `StaticReflector`.
|
||||
|
||||
## 1.0.0-alpha.10
|
||||
|
||||
* Added `Container.registerLazySingleton<T>`.
|
||||
* Added named singleton support.
|
||||
|
||||
## 1.0.0-alpha.9
|
||||
|
||||
* Added `Container.has<T>`.
|
||||
|
||||
## 1.0.0-alpha.8
|
||||
|
||||
* Fixed a bug where `_ReflectedTypeInstance.isAssignableTo` always failed.
|
||||
* Added `@GenerateReflector` annotation.
|
||||
|
||||
## 1.0.0-alpha.7
|
||||
|
||||
* Add `EmptyReflector`.
|
||||
* `ReflectedType.newInstance` now returns a `ReflectedInstance`.
|
||||
* Moved `ReflectedInstance.invoke` to `ReflectedFunction.invoke`.
|
||||
|
||||
## 1.0.0-alpha.6
|
||||
|
||||
* Add `getField` to `ReflectedInstance`.
|
||||
|
||||
## 1.0.0-alpha.5
|
||||
|
||||
* Remove concrete type from `ReflectedTypeParameter`.
|
||||
|
||||
## 1.0.0-alpha.4
|
||||
|
||||
* Safely handle `void` return types of methods.
|
||||
|
||||
## 1.0.0-alpha.3
|
||||
|
||||
* Reflecting `void` in `MirrorsReflector` now forwards to `dynamic`.
|
||||
|
||||
## 1.0.0-alpha.2
|
||||
|
||||
* Added `ReflectedInstance.reflectee`.
|
||||
|
||||
## 1.0.0-alpha.1
|
||||
|
||||
* Allow omission of the first argument of `Container.make`, to use
|
||||
a generic type argument instead.
|
||||
* `singleton` -> `registerSingleton`
|
||||
* Add `createChild`, and support hierarchical containers.
|
29
packages/container/container/LICENSE
Normal file
29
packages/container/container/LICENSE
Normal file
|
@ -0,0 +1,29 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
45
packages/container/container/README.md
Normal file
45
packages/container/container/README.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Angel3 Container
|
||||
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_container?include_prereleases)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/container/angel_container/LICENSE)
|
||||
|
||||
A better IoC container for Angel3, ultimately allowing Angel3 to be used with or without `dart:mirrors` package.
|
||||
|
||||
```dart
|
||||
import 'package:angel3_container/mirrors.dart';
|
||||
import 'package:angel3_framework/angel3_framework.dart';
|
||||
import 'package:angel3_framework/http.dart';
|
||||
|
||||
@Expose('/sales', middleware: [process1])
|
||||
class SalesController extends Controller {
|
||||
@Expose('/', middleware: [process2])
|
||||
Future<String> route1(RequestContext req, ResponseContext res) async {
|
||||
return "Sales route";
|
||||
}
|
||||
}
|
||||
|
||||
bool process1(RequestContext req, ResponseContext res) {
|
||||
res.write('Hello, ');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool process2(RequestContext req, ResponseContext res) {
|
||||
res.write('From Sales, ');
|
||||
return true;
|
||||
}
|
||||
|
||||
void main() async {
|
||||
// Using Mirror Reflector
|
||||
var app = Angel(reflector: MirrorsReflector());
|
||||
|
||||
// Sales Controller
|
||||
app.container.registerSingleton<SalesController>(SalesController());
|
||||
await app.mountController<SalesController>();
|
||||
|
||||
var http = AngelHttp(app);
|
||||
var server = await http.startServer('localhost', 3000);
|
||||
print("Angel3 server listening at ${http.uri}");
|
||||
}
|
||||
```
|
1
packages/container/container/analysis_options.yaml
Normal file
1
packages/container/container/analysis_options.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
include: package:lints/recommended.yaml
|
75
packages/container/container/example/main.dart
Normal file
75
packages/container/container/example/main.dart
Normal file
|
@ -0,0 +1,75 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:platform_container/mirrors.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
// Create a container instance.
|
||||
var container = Container(const MirrorsReflector());
|
||||
|
||||
// Register a singleton.
|
||||
container.registerSingleton<Engine>(Engine(40));
|
||||
|
||||
// You can also omit the type annotation, in which the object's runtime type will be used.
|
||||
// If you're injecting an abstract class, prefer the type annotation.
|
||||
//
|
||||
// container.registerSingleton(Engine(40));
|
||||
|
||||
// Register a factory that creates a truck.
|
||||
container.registerFactory<Truck>((container) {
|
||||
return _TruckImpl(container.make<Engine>());
|
||||
});
|
||||
|
||||
// Use `make` to create an instance.
|
||||
var truck = container.make<Truck>();
|
||||
|
||||
// You can also resolve injections asynchronously.
|
||||
container.registerFactory<Future<int>>((_) async => 24);
|
||||
print(await container.makeAsync<int>());
|
||||
|
||||
// Asynchronous resolution also works for plain objects.
|
||||
await container.makeAsync<Truck>().then((t) => t.drive());
|
||||
|
||||
// Register a named singleton.
|
||||
container.registerNamedSingleton('the_truck', truck);
|
||||
|
||||
// Should print: 'Vroom! I have 40 horsepower in my engine.'
|
||||
truck.drive();
|
||||
|
||||
// Should print the same.
|
||||
container.findByName<Truck>('the_truck').drive();
|
||||
|
||||
// We can make a child container with its own factory.
|
||||
var childContainer = container.createChild();
|
||||
|
||||
childContainer.registerFactory<Truck>((container) {
|
||||
return _TruckImpl(Engine(5666));
|
||||
});
|
||||
|
||||
// Make a truck with 5666 HP.
|
||||
childContainer.make<Truck>().drive();
|
||||
|
||||
// However, calling `make<Engine>` will return the Engine singleton we created above.
|
||||
print(childContainer.make<Engine>().horsePower);
|
||||
}
|
||||
|
||||
abstract class Truck {
|
||||
void drive();
|
||||
}
|
||||
|
||||
class Engine {
|
||||
final int horsePower;
|
||||
|
||||
Engine(this.horsePower);
|
||||
}
|
||||
|
||||
class _TruckImpl implements Truck {
|
||||
final Engine engine;
|
||||
|
||||
_TruckImpl(this.engine);
|
||||
|
||||
@override
|
||||
void drive() {
|
||||
print('Vroom! I have ${engine.horsePower} horsepower in my engine.');
|
||||
}
|
||||
}
|
6
packages/container/container/example/throwing.dart
Normal file
6
packages/container/container/example/throwing.dart
Normal file
|
@ -0,0 +1,6 @@
|
|||
import 'package:platform_container/platform_container.dart';
|
||||
|
||||
void main() {
|
||||
var reflector = const ThrowingReflector();
|
||||
reflector.reflectClass(StringBuffer);
|
||||
}
|
1
packages/container/container/lib/mirrors.dart
Normal file
1
packages/container/container/lib/mirrors.dart
Normal file
|
@ -0,0 +1 @@
|
|||
export 'src/mirrors/mirrors.dart';
|
9
packages/container/container/lib/platform_container.dart
Normal file
9
packages/container/container/lib/platform_container.dart
Normal file
|
@ -0,0 +1,9 @@
|
|||
library angel3_container;
|
||||
|
||||
export 'src/container.dart';
|
||||
export 'src/empty/empty.dart';
|
||||
export 'src/static/static.dart';
|
||||
export 'src/exception.dart';
|
||||
export 'src/reflector.dart';
|
||||
export 'src/throwing.dart';
|
||||
export 'src/container_const.dart';
|
239
packages/container/container/lib/src/container.dart
Normal file
239
packages/container/container/lib/src/container.dart
Normal file
|
@ -0,0 +1,239 @@
|
|||
import 'dart:async';
|
||||
import 'exception.dart';
|
||||
import 'reflector.dart';
|
||||
|
||||
class Container {
|
||||
final Reflector reflector;
|
||||
final Map<Type, dynamic> _singletons = {};
|
||||
final Map<Type, dynamic Function(Container)> _factories = {};
|
||||
final Map<String, dynamic> _namedSingletons = {};
|
||||
final Container? _parent;
|
||||
|
||||
Container(this.reflector) : _parent = null;
|
||||
|
||||
Container._child(Container this._parent) : reflector = _parent.reflector;
|
||||
|
||||
bool get isRoot => _parent == null;
|
||||
|
||||
/// Creates a child [Container] that can define its own singletons and factories.
|
||||
///
|
||||
/// Use this to create children of a global "scope."
|
||||
Container createChild() {
|
||||
return Container._child(this);
|
||||
}
|
||||
|
||||
/// Determines if the container has an injection of the given type.
|
||||
bool has<T>([Type? t]) {
|
||||
var t2 = T;
|
||||
if (t != null) {
|
||||
t2 = t;
|
||||
} else if (T == dynamic && t == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Container? search = this;
|
||||
while (search != null) {
|
||||
if (search._singletons.containsKey(t2)) {
|
||||
return true;
|
||||
} else if (search._factories.containsKey(t2)) {
|
||||
return true;
|
||||
} else {
|
||||
search = search._parent;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Determines if the container has a named singleton with the given [name].
|
||||
bool hasNamed(String name) {
|
||||
Container? search = this;
|
||||
|
||||
while (search != null) {
|
||||
if (search._namedSingletons.containsKey(name)) {
|
||||
return true;
|
||||
} else {
|
||||
search = search._parent;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Instantiates an instance of [T], asynchronously.
|
||||
///
|
||||
/// It is similar to [make], but resolves an injection of either
|
||||
/// `Future<T>` or `T`.
|
||||
Future<T> makeAsync<T>([Type? type]) {
|
||||
var t2 = T;
|
||||
if (type != null) {
|
||||
t2 = type;
|
||||
}
|
||||
|
||||
Type? futureType; //.Future<T>.value(null).runtimeType;
|
||||
|
||||
if (T == dynamic) {
|
||||
try {
|
||||
futureType = reflector.reflectFutureOf(t2).reflectedType;
|
||||
} on UnsupportedError {
|
||||
// Ignore this.
|
||||
}
|
||||
}
|
||||
|
||||
if (has<T>(t2)) {
|
||||
return Future<T>.value(make(t2));
|
||||
} else if (has<Future<T>>()) {
|
||||
return make<Future<T>>();
|
||||
} else if (futureType != null) {
|
||||
return make(futureType);
|
||||
} else {
|
||||
throw ReflectionException(
|
||||
'No injection for Future<$t2> or $t2 was found.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Instantiates an instance of [T].
|
||||
///
|
||||
/// In contexts where a static generic type cannot be used, use
|
||||
/// the [type] argument, instead of [T].
|
||||
T make<T>([Type? type]) {
|
||||
Type t2 = T;
|
||||
if (type != null) {
|
||||
t2 = type;
|
||||
}
|
||||
|
||||
Container? search = this;
|
||||
|
||||
while (search != null) {
|
||||
if (search._singletons.containsKey(t2)) {
|
||||
// Find a singleton, if any.
|
||||
return search._singletons[t2] as T;
|
||||
} else if (search._factories.containsKey(t2)) {
|
||||
// Find a factory, if any.
|
||||
return search._factories[t2]!(this) as T;
|
||||
} else {
|
||||
search = search._parent;
|
||||
}
|
||||
}
|
||||
|
||||
var reflectedType = reflector.reflectType(t2);
|
||||
var positional = [];
|
||||
var named = <String, Object>{};
|
||||
|
||||
if (reflectedType is ReflectedClass) {
|
||||
bool isDefault(String name) {
|
||||
return name.isEmpty || name == reflectedType.name;
|
||||
}
|
||||
|
||||
var constructor = reflectedType.constructors.firstWhere(
|
||||
(c) => isDefault(c.name),
|
||||
orElse: (() => throw ReflectionException(
|
||||
'${reflectedType.name} has no default constructor, and therefore cannot be instantiated.')));
|
||||
|
||||
for (var param in constructor.parameters) {
|
||||
var value = make(param.type.reflectedType);
|
||||
|
||||
if (param.isNamed) {
|
||||
named[param.name] = value;
|
||||
} else {
|
||||
positional.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return reflectedType.newInstance(
|
||||
isDefault(constructor.name) ? '' : constructor.name,
|
||||
positional,
|
||||
named, []).reflectee as T;
|
||||
} else {
|
||||
throw ReflectionException(
|
||||
'$t2 is not a class, and therefore cannot be instantiated.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for registering a factory that injects a singleton when it runs.
|
||||
///
|
||||
/// In many cases, you might prefer this to [registerFactory].
|
||||
///
|
||||
/// Returns [f].
|
||||
T Function(Container) registerLazySingleton<T>(T Function(Container) f,
|
||||
{Type? as}) {
|
||||
return registerFactory<T>(
|
||||
(container) {
|
||||
var r = f(container);
|
||||
container.registerSingleton<T>(r, as: as);
|
||||
return r;
|
||||
},
|
||||
as: as,
|
||||
);
|
||||
}
|
||||
|
||||
/// Registers a factory. Any attempt to resolve the
|
||||
/// type within *this* container will return the result of [f].
|
||||
///
|
||||
/// Returns [f].
|
||||
T Function(Container) registerFactory<T>(T Function(Container) f,
|
||||
{Type? as}) {
|
||||
Type t2 = T;
|
||||
if (as != null) {
|
||||
t2 = as;
|
||||
}
|
||||
|
||||
if (_factories.containsKey(t2)) {
|
||||
throw StateError('This container already has a factory for $t2.');
|
||||
}
|
||||
|
||||
_factories[t2] = f;
|
||||
return f;
|
||||
}
|
||||
|
||||
/// Registers a singleton. Any attempt to resolve the
|
||||
/// type within *this* container will return [object].
|
||||
///
|
||||
/// Returns [object].
|
||||
T registerSingleton<T>(T object, {Type? as}) {
|
||||
Type t2 = T;
|
||||
if (as != null) {
|
||||
t2 = as;
|
||||
} else if (T == dynamic) {
|
||||
t2 = as ?? object.runtimeType;
|
||||
}
|
||||
//as ??= T == dynamic ? as : T;
|
||||
|
||||
if (_singletons.containsKey(t2)) {
|
||||
throw StateError('This container already has a singleton for $t2.');
|
||||
}
|
||||
|
||||
_singletons[t2] = object;
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Finds a named singleton.
|
||||
///
|
||||
/// In general, prefer using [registerSingleton] and [registerFactory].
|
||||
///
|
||||
/// [findByName] is best reserved for internal logic that end users of code should
|
||||
/// not see.
|
||||
T findByName<T>(String name) {
|
||||
if (_namedSingletons.containsKey(name)) {
|
||||
return _namedSingletons[name] as T;
|
||||
} else if (_parent != null) {
|
||||
return _parent.findByName<T>(name);
|
||||
} else {
|
||||
throw StateError(
|
||||
'This container does not have a singleton named "$name".');
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers a *named* singleton.
|
||||
///
|
||||
/// Note that this is not related to type-based injections, and exists as a mechanism
|
||||
/// to enable injecting multiple instances of a type within the same container hierarchy.
|
||||
T registerNamedSingleton<T>(String name, T object) {
|
||||
if (_namedSingletons.containsKey(name)) {
|
||||
throw StateError('This container already has a singleton named "$name".');
|
||||
}
|
||||
|
||||
_namedSingletons[name] = object;
|
||||
return object;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
class ContainerConst {
|
||||
static const String defaultErrorMessage =
|
||||
'You attempted to perform a reflective action, but you are using `ThrowingReflector`, '
|
||||
'a class which disables reflection. Consider using the `MirrorsReflector` '
|
||||
'class if you need reflection.';
|
||||
|
||||
ContainerConst._();
|
||||
}
|
112
packages/container/container/lib/src/empty/empty.dart
Normal file
112
packages/container/container/lib/src/empty/empty.dart
Normal file
|
@ -0,0 +1,112 @@
|
|||
import '../../platform_container.dart';
|
||||
|
||||
final Map<Symbol, String?> _symbolNames = <Symbol, String?>{};
|
||||
|
||||
/// A [Reflector] implementation that performs no actual reflection,
|
||||
/// instead returning empty objects on every invocation.
|
||||
///
|
||||
/// Use this in contexts where you know you won't need any reflective capabilities.
|
||||
class EmptyReflector extends Reflector {
|
||||
/// A [RegExp] that can be used to extract the name of a symbol without reflection.
|
||||
static final RegExp symbolRegex = RegExp(r'Symbol\("([^"]+)"\)');
|
||||
|
||||
const EmptyReflector();
|
||||
|
||||
@override
|
||||
String? getName(Symbol symbol) {
|
||||
return _symbolNames.putIfAbsent(
|
||||
symbol, () => symbolRegex.firstMatch(symbol.toString())?.group(1));
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedClass reflectClass(Type clazz) {
|
||||
return const _EmptyReflectedClass();
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedInstance reflectInstance(Object object) {
|
||||
return const _EmptyReflectedInstance();
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedType reflectType(Type type) {
|
||||
return const _EmptyReflectedType();
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedFunction reflectFunction(Function function) {
|
||||
return const _EmptyReflectedFunction();
|
||||
}
|
||||
}
|
||||
|
||||
class _EmptyReflectedClass extends ReflectedClass {
|
||||
const _EmptyReflectedClass()
|
||||
: super(
|
||||
'(empty)',
|
||||
const <ReflectedTypeParameter>[],
|
||||
const <ReflectedInstance>[],
|
||||
const <ReflectedFunction>[],
|
||||
const <ReflectedDeclaration>[],
|
||||
Object);
|
||||
|
||||
@override
|
||||
ReflectedInstance newInstance(
|
||||
String constructorName, List positionalArguments,
|
||||
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) {
|
||||
throw UnsupportedError(
|
||||
'Classes reflected via an EmptyReflector cannot be instantiated.');
|
||||
}
|
||||
|
||||
@override
|
||||
bool isAssignableTo(ReflectedType? other) {
|
||||
return other == this;
|
||||
}
|
||||
}
|
||||
|
||||
class _EmptyReflectedType extends ReflectedType {
|
||||
const _EmptyReflectedType()
|
||||
: super('(empty)', const <ReflectedTypeParameter>[], Object);
|
||||
|
||||
@override
|
||||
ReflectedInstance newInstance(
|
||||
String constructorName, List positionalArguments,
|
||||
[Map<String, dynamic> namedArguments = const {},
|
||||
List<Type> typeArguments = const []]) {
|
||||
throw UnsupportedError(
|
||||
'Types reflected via an EmptyReflector cannot be instantiated.');
|
||||
}
|
||||
|
||||
@override
|
||||
bool isAssignableTo(ReflectedType? other) {
|
||||
return other == this;
|
||||
}
|
||||
}
|
||||
|
||||
class _EmptyReflectedInstance extends ReflectedInstance {
|
||||
const _EmptyReflectedInstance()
|
||||
: super(const _EmptyReflectedType(), const _EmptyReflectedClass(), null);
|
||||
|
||||
@override
|
||||
ReflectedInstance getField(String name) {
|
||||
throw UnsupportedError(
|
||||
'Instances reflected via an EmptyReflector cannot call getField().');
|
||||
}
|
||||
}
|
||||
|
||||
class _EmptyReflectedFunction extends ReflectedFunction {
|
||||
const _EmptyReflectedFunction()
|
||||
: super(
|
||||
'(empty)',
|
||||
const <ReflectedTypeParameter>[],
|
||||
const <ReflectedInstance>[],
|
||||
const <ReflectedParameter>[],
|
||||
false,
|
||||
false,
|
||||
returnType: const _EmptyReflectedType());
|
||||
|
||||
@override
|
||||
ReflectedInstance invoke(Invocation invocation) {
|
||||
throw UnsupportedError(
|
||||
'Instances reflected via an EmptyReflector cannot call invoke().');
|
||||
}
|
||||
}
|
8
packages/container/container/lib/src/exception.dart
Normal file
8
packages/container/container/lib/src/exception.dart
Normal file
|
@ -0,0 +1,8 @@
|
|||
class ReflectionException implements Exception {
|
||||
final String message;
|
||||
|
||||
ReflectionException(this.message);
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export 'reflector.dart';
|
252
packages/container/container/lib/src/mirrors/reflector.dart
Normal file
252
packages/container/container/lib/src/mirrors/reflector.dart
Normal file
|
@ -0,0 +1,252 @@
|
|||
import 'dart:async';
|
||||
import 'dart:mirrors' as dart;
|
||||
|
||||
import '../exception.dart';
|
||||
import '../reflector.dart';
|
||||
|
||||
import 'package:quiver/core.dart';
|
||||
|
||||
/// A [Reflector] implementation that forwards to `dart:mirrors`.
|
||||
///
|
||||
/// Useful on the server, where reflection is supported.
|
||||
class MirrorsReflector extends Reflector {
|
||||
const MirrorsReflector();
|
||||
|
||||
@override
|
||||
String getName(Symbol symbol) => dart.MirrorSystem.getName(symbol);
|
||||
|
||||
@override
|
||||
ReflectedClass reflectClass(Type clazz) {
|
||||
var mirror = dart.reflectType(clazz);
|
||||
|
||||
if (mirror is dart.ClassMirror) {
|
||||
return _ReflectedClassMirror(mirror);
|
||||
} else {
|
||||
throw ArgumentError('$clazz is not a class.');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedFunction reflectFunction(Function function) {
|
||||
var closure = dart.reflect(function) as dart.ClosureMirror;
|
||||
return _ReflectedMethodMirror(closure.function, closure);
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedType reflectType(Type type) {
|
||||
var mirror = dart.reflectType(type);
|
||||
|
||||
if (!mirror.hasReflectedType) {
|
||||
return reflectType(dynamic);
|
||||
} else {
|
||||
if (mirror is dart.ClassMirror) {
|
||||
return _ReflectedClassMirror(mirror);
|
||||
} else {
|
||||
return _ReflectedTypeMirror(mirror);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedType reflectFutureOf(Type type) {
|
||||
var inner = reflectType(type);
|
||||
dart.TypeMirror localMirror;
|
||||
if (inner is _ReflectedClassMirror) {
|
||||
localMirror = inner.mirror;
|
||||
} else if (inner is _ReflectedTypeMirror) {
|
||||
localMirror = inner.mirror;
|
||||
} else {
|
||||
throw ArgumentError('$type is not a class or type.');
|
||||
}
|
||||
|
||||
var future = dart.reflectType(Future, [localMirror.reflectedType]);
|
||||
return _ReflectedClassMirror(future as dart.ClassMirror);
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedInstance reflectInstance(Object object) {
|
||||
return _ReflectedInstanceMirror(dart.reflect(object));
|
||||
}
|
||||
}
|
||||
|
||||
class _ReflectedTypeParameter extends ReflectedTypeParameter {
|
||||
final dart.TypeVariableMirror mirror;
|
||||
|
||||
_ReflectedTypeParameter(this.mirror)
|
||||
: super(dart.MirrorSystem.getName(mirror.simpleName));
|
||||
}
|
||||
|
||||
class _ReflectedTypeMirror extends ReflectedType {
|
||||
final dart.TypeMirror mirror;
|
||||
|
||||
_ReflectedTypeMirror(this.mirror)
|
||||
: super(
|
||||
dart.MirrorSystem.getName(mirror.simpleName),
|
||||
mirror.typeVariables.map((m) => _ReflectedTypeParameter(m)).toList(),
|
||||
mirror.reflectedType,
|
||||
);
|
||||
|
||||
@override
|
||||
bool isAssignableTo(ReflectedType? other) {
|
||||
if (other is _ReflectedClassMirror) {
|
||||
return mirror.isAssignableTo(other.mirror);
|
||||
} else if (other is _ReflectedTypeMirror) {
|
||||
return mirror.isAssignableTo(other.mirror);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedInstance newInstance(
|
||||
String constructorName, List positionalArguments,
|
||||
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) {
|
||||
throw ReflectionException(
|
||||
'$name is not a class, and therefore cannot be instantiated.');
|
||||
}
|
||||
}
|
||||
|
||||
class _ReflectedClassMirror extends ReflectedClass {
|
||||
final dart.ClassMirror mirror;
|
||||
|
||||
_ReflectedClassMirror(this.mirror)
|
||||
: super(
|
||||
dart.MirrorSystem.getName(mirror.simpleName),
|
||||
mirror.typeVariables.map((m) => _ReflectedTypeParameter(m)).toList(),
|
||||
[],
|
||||
[],
|
||||
_declarationsOf(mirror),
|
||||
mirror.reflectedType,
|
||||
);
|
||||
|
||||
static List<ReflectedFunction> _constructorsOf(dart.ClassMirror mirror) {
|
||||
var out = <ReflectedFunction>[];
|
||||
|
||||
for (var key in mirror.declarations.keys) {
|
||||
var value = mirror.declarations[key];
|
||||
|
||||
if (value is dart.MethodMirror && value.isConstructor) {
|
||||
out.add(_ReflectedMethodMirror(value));
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static List<ReflectedDeclaration> _declarationsOf(dart.ClassMirror mirror) {
|
||||
var out = <ReflectedDeclaration>[];
|
||||
|
||||
for (var key in mirror.declarations.keys) {
|
||||
var value = mirror.declarations[key];
|
||||
|
||||
if (value is dart.MethodMirror && !value.isConstructor) {
|
||||
out.add(
|
||||
_ReflectedDeclarationMirror(dart.MirrorSystem.getName(key), value));
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@override
|
||||
List<ReflectedInstance> get annotations =>
|
||||
mirror.metadata.map((m) => _ReflectedInstanceMirror(m)).toList();
|
||||
|
||||
@override
|
||||
List<ReflectedFunction> get constructors => _constructorsOf(mirror);
|
||||
|
||||
@override
|
||||
bool isAssignableTo(ReflectedType? other) {
|
||||
if (other is _ReflectedClassMirror) {
|
||||
return mirror.isAssignableTo(other.mirror);
|
||||
} else if (other is _ReflectedTypeMirror) {
|
||||
return mirror.isAssignableTo(other.mirror);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedInstance newInstance(
|
||||
String constructorName, List positionalArguments,
|
||||
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) {
|
||||
return _ReflectedInstanceMirror(
|
||||
mirror.newInstance(Symbol(constructorName), positionalArguments));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _ReflectedClassMirror && other.mirror == mirror;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hash2(mirror, " ");
|
||||
}
|
||||
|
||||
class _ReflectedDeclarationMirror extends ReflectedDeclaration {
|
||||
final dart.MethodMirror mirror;
|
||||
|
||||
_ReflectedDeclarationMirror(String name, this.mirror)
|
||||
: super(name, mirror.isStatic, null);
|
||||
|
||||
@override
|
||||
bool get isStatic => mirror.isStatic;
|
||||
|
||||
@override
|
||||
ReflectedFunction get function => _ReflectedMethodMirror(mirror);
|
||||
}
|
||||
|
||||
class _ReflectedInstanceMirror extends ReflectedInstance {
|
||||
final dart.InstanceMirror mirror;
|
||||
|
||||
_ReflectedInstanceMirror(this.mirror)
|
||||
: super(_ReflectedClassMirror(mirror.type),
|
||||
_ReflectedClassMirror(mirror.type), mirror.reflectee);
|
||||
|
||||
@override
|
||||
ReflectedInstance getField(String name) {
|
||||
return _ReflectedInstanceMirror(mirror.getField(Symbol(name)));
|
||||
}
|
||||
}
|
||||
|
||||
class _ReflectedMethodMirror extends ReflectedFunction {
|
||||
final dart.MethodMirror mirror;
|
||||
final dart.ClosureMirror? closureMirror;
|
||||
|
||||
_ReflectedMethodMirror(this.mirror, [this.closureMirror])
|
||||
: super(
|
||||
dart.MirrorSystem.getName(mirror.simpleName),
|
||||
<ReflectedTypeParameter>[],
|
||||
mirror.metadata
|
||||
.map((mirror) => _ReflectedInstanceMirror(mirror))
|
||||
.toList(),
|
||||
mirror.parameters.map(_reflectParameter).toList(),
|
||||
mirror.isGetter,
|
||||
mirror.isSetter,
|
||||
returnType: !mirror.returnType.hasReflectedType
|
||||
? const MirrorsReflector().reflectType(dynamic)
|
||||
: const MirrorsReflector()
|
||||
.reflectType(mirror.returnType.reflectedType));
|
||||
|
||||
static ReflectedParameter _reflectParameter(dart.ParameterMirror mirror) {
|
||||
return ReflectedParameter(
|
||||
dart.MirrorSystem.getName(mirror.simpleName),
|
||||
mirror.metadata
|
||||
.map((mirror) => _ReflectedInstanceMirror(mirror))
|
||||
.toList(),
|
||||
const MirrorsReflector().reflectType(mirror.type.reflectedType),
|
||||
!mirror.isOptional,
|
||||
mirror.isNamed);
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedInstance invoke(Invocation invocation) {
|
||||
if (closureMirror == null) {
|
||||
throw StateError(
|
||||
'This object was reflected without a ClosureMirror, and therefore cannot be directly invoked.');
|
||||
}
|
||||
|
||||
return _ReflectedInstanceMirror(closureMirror!.invoke(invocation.memberName,
|
||||
invocation.positionalArguments, invocation.namedArguments));
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
|
189
packages/container/container/lib/src/reflector.dart
Normal file
189
packages/container/container/lib/src/reflector.dart
Normal file
|
@ -0,0 +1,189 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:quiver/core.dart';
|
||||
|
||||
abstract class Reflector {
|
||||
const Reflector();
|
||||
|
||||
String? getName(Symbol symbol);
|
||||
|
||||
ReflectedClass? reflectClass(Type clazz);
|
||||
|
||||
ReflectedFunction? reflectFunction(Function function);
|
||||
|
||||
ReflectedType? reflectType(Type type);
|
||||
|
||||
ReflectedInstance? reflectInstance(Object object);
|
||||
|
||||
ReflectedType reflectFutureOf(Type type) {
|
||||
throw UnsupportedError('`reflectFutureOf` requires `dart:mirrors`.');
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ReflectedInstance {
|
||||
final ReflectedType type;
|
||||
final ReflectedClass clazz;
|
||||
final Object? reflectee;
|
||||
|
||||
const ReflectedInstance(this.type, this.clazz, this.reflectee);
|
||||
|
||||
@override
|
||||
int get hashCode => hash2(type, clazz);
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is ReflectedInstance && other.type == type && other.clazz == clazz;
|
||||
|
||||
ReflectedInstance getField(String name);
|
||||
}
|
||||
|
||||
abstract class ReflectedType {
|
||||
final String name;
|
||||
final List<ReflectedTypeParameter> typeParameters;
|
||||
final Type reflectedType;
|
||||
|
||||
const ReflectedType(this.name, this.typeParameters, this.reflectedType);
|
||||
|
||||
@override
|
||||
int get hashCode => hash3(name, typeParameters, reflectedType);
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is ReflectedType &&
|
||||
other.name == name &&
|
||||
const ListEquality<ReflectedTypeParameter>()
|
||||
.equals(other.typeParameters, typeParameters) &&
|
||||
other.reflectedType == reflectedType;
|
||||
|
||||
ReflectedInstance newInstance(
|
||||
String constructorName, List positionalArguments,
|
||||
[Map<String, dynamic> namedArguments = const {},
|
||||
List<Type> typeArguments = const []]);
|
||||
|
||||
bool isAssignableTo(ReflectedType? other);
|
||||
}
|
||||
|
||||
abstract class ReflectedClass extends ReflectedType {
|
||||
final List<ReflectedInstance> annotations;
|
||||
final List<ReflectedFunction> constructors;
|
||||
final List<ReflectedDeclaration> declarations;
|
||||
|
||||
const ReflectedClass(
|
||||
String name,
|
||||
List<ReflectedTypeParameter> typeParameters,
|
||||
this.annotations,
|
||||
this.constructors,
|
||||
this.declarations,
|
||||
Type reflectedType)
|
||||
: super(name, typeParameters, reflectedType);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
hash4(super.hashCode, annotations, constructors, declarations);
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is ReflectedClass &&
|
||||
super == other &&
|
||||
const ListEquality<ReflectedInstance>()
|
||||
.equals(other.annotations, annotations) &&
|
||||
const ListEquality<ReflectedFunction>()
|
||||
.equals(other.constructors, constructors) &&
|
||||
const ListEquality<ReflectedDeclaration>()
|
||||
.equals(other.declarations, declarations);
|
||||
}
|
||||
|
||||
class ReflectedDeclaration {
|
||||
final String name;
|
||||
final bool isStatic;
|
||||
final ReflectedFunction? function;
|
||||
|
||||
const ReflectedDeclaration(this.name, this.isStatic, this.function);
|
||||
|
||||
@override
|
||||
int get hashCode => hash3(name, isStatic, function);
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is ReflectedDeclaration &&
|
||||
other.name == name &&
|
||||
other.isStatic == isStatic &&
|
||||
other.function == function;
|
||||
}
|
||||
|
||||
abstract class ReflectedFunction {
|
||||
final String name;
|
||||
final List<ReflectedTypeParameter> typeParameters;
|
||||
final List<ReflectedInstance> annotations;
|
||||
final ReflectedType? returnType;
|
||||
final List<ReflectedParameter> parameters;
|
||||
final bool isGetter, isSetter;
|
||||
|
||||
const ReflectedFunction(this.name, this.typeParameters, this.annotations,
|
||||
this.parameters, this.isGetter, this.isSetter,
|
||||
{this.returnType});
|
||||
|
||||
@override
|
||||
int get hashCode => hashObjects([
|
||||
name,
|
||||
typeParameters,
|
||||
annotations,
|
||||
returnType,
|
||||
parameters,
|
||||
isGetter,
|
||||
isSetter
|
||||
]);
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is ReflectedFunction &&
|
||||
other.name == name &&
|
||||
const ListEquality<ReflectedTypeParameter>()
|
||||
.equals(other.typeParameters, typeParameters) &&
|
||||
const ListEquality<ReflectedInstance>()
|
||||
.equals(other.annotations, annotations) &&
|
||||
other.returnType == returnType &&
|
||||
const ListEquality<ReflectedParameter>()
|
||||
.equals(other.parameters, other.parameters) &&
|
||||
other.isGetter == isGetter &&
|
||||
other.isSetter == isSetter;
|
||||
|
||||
ReflectedInstance invoke(Invocation invocation);
|
||||
}
|
||||
|
||||
class ReflectedParameter {
|
||||
final String name;
|
||||
final List<ReflectedInstance> annotations;
|
||||
final ReflectedType type;
|
||||
final bool isRequired;
|
||||
final bool isNamed;
|
||||
|
||||
const ReflectedParameter(
|
||||
this.name, this.annotations, this.type, this.isRequired, this.isNamed);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
hashObjects([name, annotations, type, isRequired, isNamed]);
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is ReflectedParameter &&
|
||||
other.name == name &&
|
||||
const ListEquality<ReflectedInstance>()
|
||||
.equals(other.annotations, annotations) &&
|
||||
other.type == type &&
|
||||
other.isRequired == isRequired &&
|
||||
other.isNamed == isNamed;
|
||||
}
|
||||
|
||||
class ReflectedTypeParameter {
|
||||
final String name;
|
||||
|
||||
const ReflectedTypeParameter(this.name);
|
||||
|
||||
@override
|
||||
int get hashCode => hashObjects([name]);
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is ReflectedTypeParameter && other.name == name;
|
||||
}
|
61
packages/container/container/lib/src/static/static.dart
Normal file
61
packages/container/container/lib/src/static/static.dart
Normal file
|
@ -0,0 +1,61 @@
|
|||
import '../reflector.dart';
|
||||
|
||||
/// A [Reflector] implementation that performs simple [Map] lookups.
|
||||
///
|
||||
/// `package:angel_container_generator` uses this to create reflectors from analysis metadata.
|
||||
class StaticReflector extends Reflector {
|
||||
final Map<Symbol, String> names;
|
||||
final Map<Type, ReflectedType> types;
|
||||
final Map<Function, ReflectedFunction> functions;
|
||||
final Map<Object, ReflectedInstance> instances;
|
||||
|
||||
const StaticReflector(
|
||||
{this.names = const {},
|
||||
this.types = const {},
|
||||
this.functions = const {},
|
||||
this.instances = const {}});
|
||||
|
||||
@override
|
||||
String? getName(Symbol symbol) {
|
||||
if (!names.containsKey(symbol)) {
|
||||
throw ArgumentError(
|
||||
'The value of $symbol is unknown - it was not generated.');
|
||||
}
|
||||
|
||||
return names[symbol];
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedClass? reflectClass(Type clazz) =>
|
||||
reflectType(clazz) as ReflectedClass?;
|
||||
|
||||
@override
|
||||
ReflectedFunction? reflectFunction(Function function) {
|
||||
if (!functions.containsKey(function)) {
|
||||
throw ArgumentError(
|
||||
'There is no reflection information available about $function.');
|
||||
}
|
||||
|
||||
return functions[function];
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedInstance? reflectInstance(Object object) {
|
||||
if (!instances.containsKey(object)) {
|
||||
throw ArgumentError(
|
||||
'There is no reflection information available about $object.');
|
||||
}
|
||||
|
||||
return instances[object];
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedType? reflectType(Type type) {
|
||||
if (!types.containsKey(type)) {
|
||||
throw ArgumentError(
|
||||
'There is no reflection information available about $type.');
|
||||
}
|
||||
|
||||
return types[type];
|
||||
}
|
||||
}
|
40
packages/container/container/lib/src/throwing.dart
Normal file
40
packages/container/container/lib/src/throwing.dart
Normal file
|
@ -0,0 +1,40 @@
|
|||
import 'package:platform_container/src/container_const.dart';
|
||||
|
||||
import 'empty/empty.dart';
|
||||
import 'reflector.dart';
|
||||
|
||||
/// A [Reflector] implementation that throws exceptions on all attempts
|
||||
/// to perform reflection.
|
||||
///
|
||||
/// Use this in contexts where you know you won't need any reflective capabilities.
|
||||
class ThrowingReflector extends Reflector {
|
||||
/// The error message to give the end user when an [UnsupportedError] is thrown.
|
||||
final String errorMessage;
|
||||
|
||||
/*
|
||||
static const String defaultErrorMessage =
|
||||
'You attempted to perform a reflective action, but you are using `ThrowingReflector`, '
|
||||
'a class which disables reflection. Consider using the `MirrorsReflector` '
|
||||
'class if you need reflection.';
|
||||
*/
|
||||
|
||||
const ThrowingReflector(
|
||||
{this.errorMessage = ContainerConst.defaultErrorMessage});
|
||||
|
||||
@override
|
||||
String? getName(Symbol symbol) => const EmptyReflector().getName(symbol);
|
||||
|
||||
UnsupportedError _error() => UnsupportedError(errorMessage);
|
||||
|
||||
@override
|
||||
ReflectedClass reflectClass(Type clazz) => throw _error();
|
||||
|
||||
@override
|
||||
ReflectedInstance reflectInstance(Object object) => throw _error();
|
||||
|
||||
@override
|
||||
ReflectedType reflectType(Type type) => throw _error();
|
||||
|
||||
@override
|
||||
ReflectedFunction reflectFunction(Function function) => throw _error();
|
||||
}
|
13
packages/container/container/pubspec.yaml
Normal file
13
packages/container/container/pubspec.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
name: platform_container
|
||||
version: 9.0.0
|
||||
description: Protevus Platform hierarchical DI container, and pluggable backends for reflection.
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dart-backend/angel/tree/master/packages/container/angel_container
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
collection: ^1.17.0
|
||||
quiver: ^3.2.0
|
||||
dev_dependencies:
|
||||
test: ^1.24.0
|
||||
lints: ^4.0.0
|
122
packages/container/container/test/common.dart
Normal file
122
packages/container/container/test/common.dart
Normal file
|
@ -0,0 +1,122 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void returnVoidFromAFunction(int x) {}
|
||||
|
||||
void testReflector(Reflector reflector) {
|
||||
var blaziken = Pokemon('Blaziken', PokemonType.fire);
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container(reflector);
|
||||
container.registerSingleton(blaziken);
|
||||
container.registerFactory<Future<int>>((_) async => 46);
|
||||
});
|
||||
|
||||
test('get field', () {
|
||||
var blazikenMirror = reflector.reflectInstance(blaziken)!;
|
||||
expect(blazikenMirror.getField('type').reflectee, blaziken.type);
|
||||
});
|
||||
|
||||
group('reflectFunction', () {
|
||||
var mirror = reflector.reflectFunction(returnVoidFromAFunction);
|
||||
|
||||
test('void return type returns dynamic', () {
|
||||
expect(mirror!.returnType, reflector.reflectType(dynamic));
|
||||
});
|
||||
|
||||
test('counts parameters', () {
|
||||
expect(mirror!.parameters, hasLength(1));
|
||||
});
|
||||
|
||||
test('counts types parameters', () {
|
||||
expect(mirror!.typeParameters, isEmpty);
|
||||
});
|
||||
|
||||
test('correctly reflects parameter types', () {
|
||||
var p = mirror!.parameters[0];
|
||||
expect(p.name, 'x');
|
||||
expect(p.isRequired, true);
|
||||
expect(p.isNamed, false);
|
||||
expect(p.annotations, isEmpty);
|
||||
expect(p.type, reflector.reflectType(int));
|
||||
});
|
||||
});
|
||||
|
||||
test('make on singleton type returns singleton', () {
|
||||
expect(container.make(Pokemon), blaziken);
|
||||
});
|
||||
|
||||
test('make with generic returns same as make with explicit type', () {
|
||||
expect(container.make<Pokemon>(), blaziken);
|
||||
});
|
||||
|
||||
test('make async returns async object', () async {
|
||||
expect(container.makeAsync<int>(), completion(46));
|
||||
});
|
||||
|
||||
test('make async returns sync object', () async {
|
||||
expect(container.makeAsync<Pokemon>(), completion(blaziken));
|
||||
});
|
||||
|
||||
test('make on aliased singleton returns singleton', () {
|
||||
container.registerSingleton(blaziken, as: StateError);
|
||||
expect(container.make(StateError), blaziken);
|
||||
});
|
||||
|
||||
test('constructor injects singleton', () {
|
||||
var lower = container.make<LowerPokemon>();
|
||||
expect(lower.lowercaseName, blaziken.name.toLowerCase());
|
||||
});
|
||||
|
||||
test('newInstance works', () {
|
||||
var type = container.reflector.reflectType(Pokemon)!;
|
||||
var instance =
|
||||
type.newInstance('changeName', [blaziken, 'Charizard']).reflectee
|
||||
as Pokemon;
|
||||
print(instance);
|
||||
expect(instance.name, 'Charizard');
|
||||
expect(instance.type, PokemonType.fire);
|
||||
});
|
||||
|
||||
test('isAssignableTo', () {
|
||||
var pokemonType = container.reflector.reflectType(Pokemon);
|
||||
var kantoPokemonType = container.reflector.reflectType(KantoPokemon)!;
|
||||
|
||||
expect(kantoPokemonType.isAssignableTo(pokemonType), true);
|
||||
expect(
|
||||
kantoPokemonType
|
||||
.isAssignableTo(container.reflector.reflectType(String)),
|
||||
false);
|
||||
});
|
||||
}
|
||||
|
||||
class LowerPokemon {
|
||||
final Pokemon pokemon;
|
||||
|
||||
LowerPokemon(this.pokemon);
|
||||
|
||||
String get lowercaseName => pokemon.name.toLowerCase();
|
||||
}
|
||||
|
||||
class Pokemon {
|
||||
final String name;
|
||||
final PokemonType type;
|
||||
|
||||
Pokemon(this.name, this.type);
|
||||
|
||||
factory Pokemon.changeName(Pokemon other, String name) {
|
||||
return Pokemon(name, other.type);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'NAME: $name, TYPE: $type';
|
||||
}
|
||||
|
||||
class KantoPokemon extends Pokemon {
|
||||
KantoPokemon(super.name, super.type);
|
||||
}
|
||||
|
||||
enum PokemonType { water, fire, grass, ice, poison, flying }
|
138
packages/container/container/test/empty_reflector_test.dart
Normal file
138
packages/container/container/test/empty_reflector_test.dart
Normal file
|
@ -0,0 +1,138 @@
|
|||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
var reflector = const EmptyReflector();
|
||||
|
||||
test('getName', () {
|
||||
expect(reflector.getName(#foo), 'foo');
|
||||
expect(reflector.getName(#==), '==');
|
||||
});
|
||||
|
||||
group('reflectClass', () {
|
||||
var mirror = reflector.reflectClass(Truck);
|
||||
|
||||
test('name returns empty', () {
|
||||
expect(mirror.name, '(empty)');
|
||||
});
|
||||
|
||||
test('annotations returns empty', () {
|
||||
expect(mirror.annotations, isEmpty);
|
||||
});
|
||||
|
||||
test('typeParameters returns empty', () {
|
||||
expect(mirror.typeParameters, isEmpty);
|
||||
});
|
||||
|
||||
test('declarations returns empty', () {
|
||||
expect(mirror.declarations, isEmpty);
|
||||
});
|
||||
|
||||
test('constructors returns empty', () {
|
||||
expect(mirror.constructors, isEmpty);
|
||||
});
|
||||
|
||||
test('reflectedType returns Object', () {
|
||||
expect(mirror.reflectedType, Object);
|
||||
});
|
||||
|
||||
test('cannot call newInstance', () {
|
||||
expect(() => mirror.newInstance('', []), throwsUnsupportedError);
|
||||
});
|
||||
|
||||
test('isAssignableTo self', () {
|
||||
expect(mirror.isAssignableTo(mirror), true);
|
||||
});
|
||||
});
|
||||
|
||||
group('reflectType', () {
|
||||
var mirror = reflector.reflectType(Truck);
|
||||
|
||||
test('name returns empty', () {
|
||||
expect(mirror.name, '(empty)');
|
||||
});
|
||||
|
||||
test('typeParameters returns empty', () {
|
||||
expect(mirror.typeParameters, isEmpty);
|
||||
});
|
||||
|
||||
test('reflectedType returns Object', () {
|
||||
expect(mirror.reflectedType, Object);
|
||||
});
|
||||
|
||||
test('cannot call newInstance', () {
|
||||
expect(() => mirror.newInstance('', []), throwsUnsupportedError);
|
||||
});
|
||||
|
||||
test('isAssignableTo self', () {
|
||||
expect(mirror.isAssignableTo(mirror), true);
|
||||
});
|
||||
});
|
||||
|
||||
group('reflectFunction', () {
|
||||
void doIt(int x) {}
|
||||
|
||||
var mirror = reflector.reflectFunction(doIt);
|
||||
|
||||
test('name returns empty', () {
|
||||
expect(mirror.name, '(empty)');
|
||||
});
|
||||
|
||||
test('annotations returns empty', () {
|
||||
expect(mirror.annotations, isEmpty);
|
||||
});
|
||||
|
||||
test('typeParameters returns empty', () {
|
||||
expect(mirror.typeParameters, isEmpty);
|
||||
});
|
||||
|
||||
test('parameters returns empty', () {
|
||||
expect(mirror.parameters, isEmpty);
|
||||
});
|
||||
|
||||
test('return type is dynamic', () {
|
||||
expect(mirror.returnType, reflector.reflectType(dynamic));
|
||||
});
|
||||
|
||||
test('isGetter returns false', () {
|
||||
expect(mirror.isGetter, false);
|
||||
});
|
||||
|
||||
test('isSetter returns false', () {
|
||||
expect(mirror.isSetter, false);
|
||||
});
|
||||
|
||||
test('cannot invoke', () {
|
||||
var invocation = Invocation.method(#drive, []);
|
||||
expect(() => mirror.invoke(invocation), throwsUnsupportedError);
|
||||
});
|
||||
});
|
||||
|
||||
group('reflectInstance', () {
|
||||
var mirror = reflector.reflectInstance(Truck());
|
||||
|
||||
test('reflectee returns null', () {
|
||||
expect(mirror.reflectee, null);
|
||||
});
|
||||
|
||||
test('type returns empty', () {
|
||||
expect(mirror.type.name, '(empty)');
|
||||
});
|
||||
|
||||
test('clazz returns empty', () {
|
||||
expect(mirror.clazz.name, '(empty)');
|
||||
});
|
||||
|
||||
test('cannot getField', () {
|
||||
expect(() => mirror.getField('wheelCount'), throwsUnsupportedError);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class Truck {
|
||||
int get wheelCount => 4;
|
||||
|
||||
void drive() {
|
||||
print('Vroom!!!');
|
||||
}
|
||||
}
|
51
packages/container/container/test/has_test.dart
Normal file
51
packages/container/container/test/has_test.dart
Normal file
|
@ -0,0 +1,51 @@
|
|||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container(const EmptyReflector())
|
||||
..registerSingleton<Song>(Song(title: 'I Wish'))
|
||||
..registerNamedSingleton('foo', 1)
|
||||
..registerFactory<Artist>((container) {
|
||||
return Artist(
|
||||
name: 'Stevie Wonder',
|
||||
song: container.make<Song>(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('hasNamed', () {
|
||||
var child = container.createChild()..registerNamedSingleton('bar', 2);
|
||||
expect(child.hasNamed('foo'), true);
|
||||
expect(child.hasNamed('bar'), true);
|
||||
expect(child.hasNamed('baz'), false);
|
||||
});
|
||||
|
||||
test('has on singleton', () {
|
||||
var result = container.has<Song>();
|
||||
expect(result, true);
|
||||
});
|
||||
|
||||
test('has on factory', () {
|
||||
expect(container.has<Artist>(), true);
|
||||
});
|
||||
|
||||
test('false if neither', () {
|
||||
expect(container.has<bool>(), false);
|
||||
});
|
||||
}
|
||||
|
||||
class Artist {
|
||||
final String? name;
|
||||
final Song? song;
|
||||
|
||||
Artist({this.name, this.song});
|
||||
}
|
||||
|
||||
class Song {
|
||||
final String? title;
|
||||
|
||||
Song({this.title});
|
||||
}
|
18
packages/container/container/test/lazy_test.dart
Normal file
18
packages/container/container/test/lazy_test.dart
Normal file
|
@ -0,0 +1,18 @@
|
|||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('returns the same instance', () {
|
||||
var container = Container(const EmptyReflector())
|
||||
..registerLazySingleton<Dummy>((_) => Dummy('a'));
|
||||
|
||||
var first = container.make<Dummy>();
|
||||
expect(container.make<Dummy>(), first);
|
||||
});
|
||||
}
|
||||
|
||||
class Dummy {
|
||||
final String s;
|
||||
|
||||
Dummy(this.s);
|
||||
}
|
26
packages/container/container/test/mirrors_test.dart
Normal file
26
packages/container/container/test/mirrors_test.dart
Normal file
|
@ -0,0 +1,26 @@
|
|||
import 'dart:async';
|
||||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'common.dart';
|
||||
|
||||
void main() {
|
||||
testReflector(const MirrorsReflector());
|
||||
|
||||
test('futureOf', () {
|
||||
var r = MirrorsReflector();
|
||||
var fStr = r.reflectFutureOf(String);
|
||||
expect(fStr.reflectedType.toString(), 'Future<String>');
|
||||
// expect(fStr.reflectedType, Future<String>.value(null).runtimeType);
|
||||
});
|
||||
|
||||
test('concrete future make', () async {
|
||||
var c = Container(MirrorsReflector());
|
||||
c.registerFactory<Future<String>>((_) async => 'hey');
|
||||
var fStr = c.reflector.reflectFutureOf(String);
|
||||
var s1 = await c.make(fStr.reflectedType);
|
||||
var s2 = await c.makeAsync(String);
|
||||
print([s1, s2]);
|
||||
expect(s1, s2);
|
||||
});
|
||||
}
|
34
packages/container/container/test/named_test.dart
Normal file
34
packages/container/container/test/named_test.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container(const EmptyReflector());
|
||||
container.registerNamedSingleton('foo', Foo(bar: 'baz'));
|
||||
});
|
||||
|
||||
test('fetch by name', () {
|
||||
expect(container.findByName<Foo>('foo').bar, 'baz');
|
||||
});
|
||||
|
||||
test('cannot redefine', () {
|
||||
expect(() => container.registerNamedSingleton('foo', Foo(bar: 'quux')),
|
||||
throwsStateError);
|
||||
});
|
||||
|
||||
test('throws on unknown name', () {
|
||||
expect(() => container.findByName('bar'), throwsStateError);
|
||||
});
|
||||
|
||||
test('throws on incorrect type', () {
|
||||
expect(() => container.findByName<List<String>>('foo'), throwsA(anything));
|
||||
});
|
||||
}
|
||||
|
||||
class Foo {
|
||||
final String? bar;
|
||||
|
||||
Foo({this.bar});
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
var reflector = const ThrowingReflector();
|
||||
|
||||
test('getName', () {
|
||||
expect(reflector.getName(#foo), 'foo');
|
||||
expect(reflector.getName(#==), '==');
|
||||
});
|
||||
|
||||
test('reflectClass fails', () {
|
||||
expect(() => reflector.reflectClass(Truck), throwsUnsupportedError);
|
||||
});
|
||||
|
||||
test('reflectType fails', () {
|
||||
expect(() => reflector.reflectType(Truck), throwsUnsupportedError);
|
||||
});
|
||||
|
||||
test('reflectFunction throws', () {
|
||||
void doIt(int x) {}
|
||||
expect(() => reflector.reflectFunction(doIt), throwsUnsupportedError);
|
||||
});
|
||||
|
||||
test('reflectInstance throws', () {
|
||||
expect(() => reflector.reflectInstance(Truck()), throwsUnsupportedError);
|
||||
});
|
||||
}
|
||||
|
||||
class Truck {
|
||||
int get wheelCount => 4;
|
||||
|
||||
void drive() {
|
||||
print('Vroom!!!');
|
||||
}
|
||||
}
|
13
packages/container/container_generator/.gitignore
vendored
Normal file
13
packages/container/container_generator/.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
# See https://www.dartlang.org/guides/libraries/private-files
|
||||
|
||||
# Files and directories created by pub
|
||||
.dart_tool/
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
58
packages/container/container_generator/CHANGELOG.md
Normal file
58
packages/container/container_generator/CHANGELOG.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# Change Log
|
||||
|
||||
## 8.1.1
|
||||
|
||||
* Updated repository link
|
||||
|
||||
## 8.1.0
|
||||
|
||||
* Updated `lints` to 3.0.0
|
||||
* Fixed analyser warnings
|
||||
|
||||
## 8.0.0
|
||||
|
||||
* Require Dart >= 3.0
|
||||
|
||||
## 7.1.0-beta.1
|
||||
|
||||
* Require Dart >= 2.19
|
||||
* Upgraded `relectable` to 4.x.x
|
||||
|
||||
## 7.0.0
|
||||
|
||||
* Require Dart >= 2.17
|
||||
|
||||
## 6.0.0
|
||||
|
||||
* Require Dart >= 2.16
|
||||
|
||||
## 5.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 3.0.1
|
||||
|
||||
* Updated `package:angel3_container`
|
||||
|
||||
## 3.0.0
|
||||
|
||||
* Fixed NNBD issues
|
||||
* All 9 test cases passed
|
||||
|
||||
## 3.0.0-beta.1
|
||||
|
||||
* Migrated to support Dart >= 2.12 NNBD
|
||||
* Updated linter to `package:lints`
|
||||
* Updated to use `angel3_` packages
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Migrated to work with Dart >= 2.12 Non NNBD
|
||||
|
||||
## 1.0.1
|
||||
|
||||
* Update for `pkg:angel_container@1.0.3`.
|
29
packages/container/container_generator/LICENSE
Normal file
29
packages/container/container_generator/LICENSE
Normal file
|
@ -0,0 +1,29 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
32
packages/container/container_generator/README.md
Normal file
32
packages/container/container_generator/README.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Angel3 Container Generator
|
||||
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_container_generator?include_prereleases)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/container/angel3_container_generator/LICENSE)
|
||||
|
||||
An alternative container for Angel3 that uses `reflectable` package instead of `dart:mirrors` for reflection. However, `reflectable` has more limited relfection capabilities when compared to `dart:mirrors`.
|
||||
|
||||
## Usage
|
||||
|
||||
* Annotable the class with `@contained`.
|
||||
* Run `dart run build_runner build <Your class directory>`
|
||||
* Alternatively create a `build.xml` file with the following content
|
||||
|
||||
```yaml
|
||||
targets:
|
||||
$default:
|
||||
builders:
|
||||
reflectable:
|
||||
generate_for:
|
||||
- bin/**_controller.dart
|
||||
options:
|
||||
formatted: true
|
||||
```
|
||||
|
||||
## Known limitation
|
||||
|
||||
* `analyser` 6.x is not supported due to `reflectable`
|
||||
* Reflection on functions/closures is not supported
|
||||
* Reflection on private declarations is not supported
|
||||
* Reflection on generic type is not supported
|
|
@ -0,0 +1 @@
|
|||
include: package:lints/recommended.yaml
|
75
packages/container/container_generator/example/main.dart
Normal file
75
packages/container/container_generator/example/main.dart
Normal file
|
@ -0,0 +1,75 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:platform_container_generator/angel3_container_generator.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
// Create a container instance.
|
||||
Container container = Container(GeneratedReflector());
|
||||
|
||||
// Register a singleton.
|
||||
container.registerSingleton<Engine>(Engine(40));
|
||||
|
||||
// You can also omit the type annotation, in which the object's runtime type will be used.
|
||||
// If you're injecting an abstract class, prefer the type annotation.
|
||||
//
|
||||
// container.registerSingleton(Engine(40));
|
||||
|
||||
// Register a factory that creates a truck.
|
||||
container.registerFactory<Truck>((container) {
|
||||
return _TruckImpl(container.make<Engine>());
|
||||
});
|
||||
|
||||
// Use `make` to create an instance.
|
||||
var truck = container.make<Truck>();
|
||||
|
||||
// You can also resolve injections asynchronously.
|
||||
container.registerFactory<Future<int>>((_) async => 24);
|
||||
print(await container.makeAsync<int>());
|
||||
|
||||
// Asynchronous resolution also works for plain objects.
|
||||
await container.makeAsync<Truck>().then((t) => t.drive());
|
||||
|
||||
// Register a named singleton.
|
||||
container.registerNamedSingleton('the_truck', truck);
|
||||
|
||||
// Should print: 'Vroom! I have 40 horsepower in my engine.'
|
||||
truck.drive();
|
||||
|
||||
// Should print the same.
|
||||
container.findByName<Truck>('the_truck').drive();
|
||||
|
||||
// We can make a child container with its own factory.
|
||||
var childContainer = container.createChild();
|
||||
|
||||
childContainer.registerFactory<Truck>((container) {
|
||||
return _TruckImpl(Engine(5666));
|
||||
});
|
||||
|
||||
// Make a truck with 5666 HP.
|
||||
childContainer.make<Truck>().drive();
|
||||
|
||||
// However, calling `make<Engine>` will return the Engine singleton we created above.
|
||||
print(childContainer.make<Engine>().horsePower);
|
||||
}
|
||||
|
||||
abstract class Truck {
|
||||
void drive();
|
||||
}
|
||||
|
||||
class Engine {
|
||||
final int horsePower;
|
||||
|
||||
Engine(this.horsePower);
|
||||
}
|
||||
|
||||
class _TruckImpl implements Truck {
|
||||
final Engine engine;
|
||||
|
||||
_TruckImpl(this.engine);
|
||||
|
||||
@override
|
||||
void drive() {
|
||||
print('Vroom! I have ${engine.horsePower} horsepower in my engine.');
|
||||
}
|
||||
}
|
6520
packages/container/container_generator/example/main.reflectable.dart
Normal file
6520
packages/container/container_generator/example/main.reflectable.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,255 @@
|
|||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:reflectable/reflectable.dart';
|
||||
|
||||
/// A [Reflectable] instance that can be used as an annotation on types to generate metadata for them.
|
||||
const Reflectable contained = ContainedReflectable();
|
||||
|
||||
@contained
|
||||
class ContainedReflectable extends Reflectable {
|
||||
const ContainedReflectable()
|
||||
: super(
|
||||
topLevelInvokeCapability,
|
||||
typeAnnotationQuantifyCapability,
|
||||
superclassQuantifyCapability,
|
||||
libraryCapability,
|
||||
invokingCapability,
|
||||
metadataCapability,
|
||||
reflectedTypeCapability,
|
||||
typeCapability,
|
||||
typingCapability);
|
||||
}
|
||||
|
||||
/// A [Reflector] instance that uses a [Reflectable] to reflect upon data.
|
||||
class GeneratedReflector extends Reflector {
|
||||
final Reflectable reflectable;
|
||||
|
||||
const GeneratedReflector([this.reflectable = contained]);
|
||||
|
||||
@override
|
||||
String getName(Symbol symbol) {
|
||||
return symbol.toString().substring(7);
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedClass reflectClass(Type clazz) {
|
||||
return reflectType(clazz) as ReflectedClass;
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedFunction reflectFunction(Function function) {
|
||||
if (!reflectable.canReflect(function)) {
|
||||
throw UnsupportedError('Cannot reflect $function.');
|
||||
}
|
||||
|
||||
var mirror = reflectable.reflect(function);
|
||||
|
||||
if (mirror is ClosureMirror) {
|
||||
return _GeneratedReflectedFunction(mirror.function, this, mirror);
|
||||
} else {
|
||||
throw ArgumentError('$function is not a Function.');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedInstance reflectInstance(Object object) {
|
||||
if (!reflectable.canReflect(object)) {
|
||||
throw UnsupportedError('Cannot reflect $object.');
|
||||
} else {
|
||||
var mirror = reflectable.reflect(object);
|
||||
return _GeneratedReflectedInstance(mirror, this);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedType reflectType(Type type) {
|
||||
if (!reflectable.canReflectType(type)) {
|
||||
throw UnsupportedError('Cannot reflect $type.');
|
||||
} else {
|
||||
var mirror = reflectable.reflectType(type);
|
||||
return mirror is ClassMirror
|
||||
? _GeneratedReflectedClass(mirror, this)
|
||||
: _GeneratedReflectedType(mirror);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _GeneratedReflectedInstance extends ReflectedInstance {
|
||||
final InstanceMirror mirror;
|
||||
final GeneratedReflector reflector;
|
||||
|
||||
_GeneratedReflectedInstance(this.mirror, this.reflector)
|
||||
: super(_GeneratedReflectedType(mirror.type),
|
||||
_GeneratedReflectedClass(mirror.type, reflector), mirror.reflectee);
|
||||
|
||||
@override
|
||||
ReflectedType get type => clazz;
|
||||
|
||||
@override
|
||||
ReflectedInstance getField(String name) {
|
||||
var result = mirror.invokeGetter(name)!;
|
||||
var instance = reflector.reflectable.reflect(result);
|
||||
return _GeneratedReflectedInstance(instance, reflector);
|
||||
}
|
||||
}
|
||||
|
||||
class _GeneratedReflectedClass extends ReflectedClass {
|
||||
final ClassMirror mirror;
|
||||
final Reflector reflector;
|
||||
|
||||
_GeneratedReflectedClass(this.mirror, this.reflector)
|
||||
: super(mirror.simpleName, [], [], [], [], mirror.reflectedType);
|
||||
|
||||
@override
|
||||
List<ReflectedTypeParameter> get typeParameters =>
|
||||
mirror.typeVariables.map(_convertTypeVariable).toList();
|
||||
|
||||
@override
|
||||
List<ReflectedFunction> get constructors =>
|
||||
_constructorsOf(mirror.declarations, reflector);
|
||||
|
||||
@override
|
||||
List<ReflectedDeclaration> get declarations =>
|
||||
_declarationsOf(mirror.declarations, reflector);
|
||||
|
||||
@override
|
||||
List<ReflectedInstance> get annotations => mirror.metadata
|
||||
.map(reflector.reflectInstance)
|
||||
.whereType<ReflectedInstance>()
|
||||
.toList();
|
||||
|
||||
@override
|
||||
bool isAssignableTo(ReflectedType? other) {
|
||||
if (other is _GeneratedReflectedClass) {
|
||||
return mirror.isAssignableTo(other.mirror);
|
||||
} else if (other is _GeneratedReflectedType) {
|
||||
return mirror.isAssignableTo(other.mirror);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedInstance newInstance(
|
||||
String constructorName, List positionalArguments,
|
||||
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) {
|
||||
namedArguments ??= {};
|
||||
var result = mirror.newInstance(constructorName, positionalArguments,
|
||||
namedArguments.map((k, v) => MapEntry(Symbol(k), v)));
|
||||
return reflector.reflectInstance(result)!;
|
||||
}
|
||||
}
|
||||
|
||||
class _GeneratedReflectedType extends ReflectedType {
|
||||
final TypeMirror mirror;
|
||||
|
||||
_GeneratedReflectedType(this.mirror)
|
||||
: super(mirror.simpleName, [], mirror.reflectedType);
|
||||
|
||||
@override
|
||||
List<ReflectedTypeParameter> get typeParameters =>
|
||||
mirror.typeVariables.map(_convertTypeVariable).toList();
|
||||
|
||||
@override
|
||||
bool isAssignableTo(ReflectedType? other) {
|
||||
if (other is _GeneratedReflectedClass) {
|
||||
return mirror.isAssignableTo(other.mirror);
|
||||
} else if (other is _GeneratedReflectedType) {
|
||||
return mirror.isAssignableTo(other.mirror);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ReflectedInstance newInstance(
|
||||
String constructorName, List positionalArguments,
|
||||
[Map<String, dynamic> namedArguments = const {},
|
||||
List<Type> typeArguments = const []]) {
|
||||
throw UnsupportedError('Cannot create a new instance of $reflectedType.');
|
||||
}
|
||||
}
|
||||
|
||||
class _GeneratedReflectedFunction extends ReflectedFunction {
|
||||
final MethodMirror mirror;
|
||||
final Reflector reflector;
|
||||
final ClosureMirror? closure;
|
||||
|
||||
_GeneratedReflectedFunction(this.mirror, this.reflector, [this.closure])
|
||||
: super(
|
||||
mirror.simpleName,
|
||||
[],
|
||||
[],
|
||||
mirror.parameters
|
||||
.map((p) => _convertParameter(p, reflector))
|
||||
.toList(),
|
||||
mirror.isGetter,
|
||||
mirror.isSetter,
|
||||
returnType: !mirror.isRegularMethod
|
||||
? null
|
||||
: _GeneratedReflectedType(mirror.returnType));
|
||||
|
||||
@override
|
||||
List<ReflectedInstance> get annotations => mirror.metadata
|
||||
.map(reflector.reflectInstance)
|
||||
.whereType<ReflectedInstance>()
|
||||
.toList();
|
||||
|
||||
@override
|
||||
ReflectedInstance invoke(Invocation invocation) {
|
||||
if (closure != null) {
|
||||
throw UnsupportedError('Only closures can be invoked directly.');
|
||||
} else {
|
||||
var result = closure!.delegate(invocation)!;
|
||||
return reflector.reflectInstance(result)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<ReflectedFunction> _constructorsOf(
|
||||
Map<String, DeclarationMirror> map, Reflector reflector) {
|
||||
return map.entries.fold<List<ReflectedFunction>>([], (out, entry) {
|
||||
var v = entry.value;
|
||||
|
||||
if (v is MethodMirror && v.isConstructor) {
|
||||
return out..add(_GeneratedReflectedFunction(v, reflector));
|
||||
} else {
|
||||
return out;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<ReflectedDeclaration> _declarationsOf(
|
||||
Map<String, DeclarationMirror> map, Reflector reflector) {
|
||||
return map.entries.fold<List<ReflectedDeclaration>>([], (out, entry) {
|
||||
var v = entry.value;
|
||||
|
||||
if (v is VariableMirror) {
|
||||
var decl = ReflectedDeclaration(v.simpleName, v.isStatic, null);
|
||||
return out..add(decl);
|
||||
}
|
||||
if (v is MethodMirror) {
|
||||
var decl = ReflectedDeclaration(
|
||||
v.simpleName, v.isStatic, _GeneratedReflectedFunction(v, reflector));
|
||||
return out..add(decl);
|
||||
} else {
|
||||
return out;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ReflectedTypeParameter _convertTypeVariable(TypeVariableMirror mirror) {
|
||||
return ReflectedTypeParameter(mirror.simpleName);
|
||||
}
|
||||
|
||||
ReflectedParameter _convertParameter(
|
||||
ParameterMirror mirror, Reflector reflector) {
|
||||
return ReflectedParameter(
|
||||
mirror.simpleName,
|
||||
mirror.metadata
|
||||
.map(reflector.reflectInstance)
|
||||
.whereType<ReflectedInstance>()
|
||||
.toList(),
|
||||
reflector.reflectType(mirror.type.reflectedType)!,
|
||||
!mirror.isOptional,
|
||||
mirror.isNamed);
|
||||
}
|
18
packages/container/container_generator/pubspec.yaml
Normal file
18
packages/container/container_generator/pubspec.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
name: platform_container_generator
|
||||
version: 9.0.0
|
||||
description: Codegen support for using pkg:reflectable with pkg:angel3_container.
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dart-backend/angel/tree/master/packages/container/angel_container_generator
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
platform_container: ^9.0.0
|
||||
reflectable: ^4.0.0
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.0
|
||||
build_test: ^2.1.0
|
||||
test: ^1.24.0
|
||||
lints: ^4.0.0
|
||||
# dependency_overrides:
|
||||
# angel3_container:
|
||||
# path: ../angel_container
|
179
packages/container/container_generator/test/reflector_test.dart
Normal file
179
packages/container/container_generator/test/reflector_test.dart
Normal file
|
@ -0,0 +1,179 @@
|
|||
import 'package:platform_container/platform_container.dart';
|
||||
import 'package:platform_container_generator/angel3_container_generator.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'reflector_test.reflectable.dart';
|
||||
|
||||
void main() {
|
||||
initializeReflectable();
|
||||
|
||||
var reflector = const GeneratedReflector();
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container(reflector);
|
||||
container.registerSingleton(Artist(name: 'Stevie Wonder'));
|
||||
});
|
||||
|
||||
group('reflectClass', () {
|
||||
var mirror = reflector.reflectClass(Artist);
|
||||
|
||||
test('name', () {
|
||||
expect(mirror.name, 'Artist');
|
||||
});
|
||||
});
|
||||
|
||||
test('inject constructor parameters', () {
|
||||
var album = container.make<Album>();
|
||||
print(album.title);
|
||||
expect(album.title, 'flowers by stevie wonder');
|
||||
});
|
||||
|
||||
// Skip as pkg:reflectable cannot reflect on closures at all (yet)
|
||||
//testReflector(reflector);
|
||||
}
|
||||
|
||||
@contained
|
||||
void returnVoidFromAFunction(int x) {}
|
||||
|
||||
void testReflector(Reflector reflector) {
|
||||
var blaziken = Pokemon('Blaziken', PokemonType.fire);
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container(reflector);
|
||||
container.registerSingleton(blaziken);
|
||||
});
|
||||
|
||||
test('get field', () {
|
||||
var blazikenMirror = reflector.reflectInstance(blaziken)!;
|
||||
expect(blazikenMirror.getField('type').reflectee, blaziken.type);
|
||||
});
|
||||
|
||||
group('reflectFunction', () {
|
||||
var mirror = reflector.reflectFunction(returnVoidFromAFunction);
|
||||
|
||||
test('void return type returns dynamic', () {
|
||||
expect(mirror?.returnType, reflector.reflectType(dynamic));
|
||||
});
|
||||
|
||||
test('counts parameters', () {
|
||||
expect(mirror?.parameters, hasLength(1));
|
||||
});
|
||||
|
||||
test('counts types parameters', () {
|
||||
expect(mirror?.typeParameters, isEmpty);
|
||||
});
|
||||
|
||||
test('correctly reflects parameter types', () {
|
||||
var p = mirror?.parameters[0];
|
||||
expect(p?.name, 'x');
|
||||
expect(p?.isRequired, true);
|
||||
expect(p?.isNamed, false);
|
||||
expect(p?.annotations, isEmpty);
|
||||
expect(p?.type, reflector.reflectType(int));
|
||||
});
|
||||
}, skip: 'pkg:reflectable cannot reflect on closures at all (yet)');
|
||||
|
||||
test('make on singleton type returns singleton', () {
|
||||
expect(container.make(Pokemon), blaziken);
|
||||
});
|
||||
|
||||
test('make with generic returns same as make with explicit type', () {
|
||||
expect(container.make<Pokemon>(), blaziken);
|
||||
});
|
||||
|
||||
test('make on aliased singleton returns singleton', () {
|
||||
container.registerSingleton(blaziken, as: StateError);
|
||||
expect(container.make(StateError), blaziken);
|
||||
});
|
||||
|
||||
test('constructor injects singleton', () {
|
||||
var lower = container.make<LowerPokemon>();
|
||||
expect(lower.lowercaseName, blaziken.name.toLowerCase());
|
||||
});
|
||||
|
||||
test('newInstance works', () {
|
||||
var type = container.reflector.reflectType(Pokemon)!;
|
||||
var instance =
|
||||
type.newInstance('changeName', [blaziken, 'Charizard']).reflectee
|
||||
as Pokemon;
|
||||
print(instance);
|
||||
expect(instance.name, 'Charizard');
|
||||
expect(instance.type, PokemonType.fire);
|
||||
});
|
||||
|
||||
test('isAssignableTo', () {
|
||||
var pokemonType = container.reflector.reflectType(Pokemon);
|
||||
var kantoPokemonType = container.reflector.reflectType(KantoPokemon)!;
|
||||
|
||||
expect(kantoPokemonType.isAssignableTo(pokemonType), true);
|
||||
|
||||
expect(
|
||||
kantoPokemonType
|
||||
.isAssignableTo(container.reflector.reflectType(String)),
|
||||
false);
|
||||
});
|
||||
}
|
||||
|
||||
@contained
|
||||
class LowerPokemon {
|
||||
final Pokemon pokemon;
|
||||
|
||||
LowerPokemon(this.pokemon);
|
||||
|
||||
String get lowercaseName => pokemon.name.toLowerCase();
|
||||
}
|
||||
|
||||
@contained
|
||||
class Pokemon {
|
||||
final String name;
|
||||
final PokemonType type;
|
||||
|
||||
Pokemon(this.name, this.type);
|
||||
|
||||
factory Pokemon.changeName(Pokemon other, String name) {
|
||||
return Pokemon(name, other.type);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'NAME: $name, TYPE: $type';
|
||||
}
|
||||
|
||||
@contained
|
||||
class KantoPokemon extends Pokemon {
|
||||
KantoPokemon(super.name, super.type);
|
||||
}
|
||||
|
||||
@contained
|
||||
enum PokemonType { water, fire, grass, ice, poison, flying }
|
||||
|
||||
@contained
|
||||
class Artist {
|
||||
final String name;
|
||||
|
||||
Artist({required this.name});
|
||||
|
||||
String get lowerName {
|
||||
return name.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
@contained
|
||||
class Album {
|
||||
final Artist artist;
|
||||
|
||||
Album(this.artist);
|
||||
|
||||
String get title => 'flowers by ${artist.lowerName}';
|
||||
}
|
||||
|
||||
@contained
|
||||
class AlbumLength {
|
||||
final Artist artist;
|
||||
final Album album;
|
||||
|
||||
AlbumLength(this.artist, this.album);
|
||||
|
||||
int get totalLength => artist.name.length + album.title.length;
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue