refactor: upgrade container to package status
This commit is contained in:
parent
932f83f0f8
commit
0fbb79e4d7
122 changed files with 1392 additions and 10688 deletions
71
incubation/container/container/.gitignore
vendored
71
incubation/container/container/.gitignore
vendored
|
@ -1,71 +0,0 @@
|
|||
# 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
|
|
@ -1,12 +0,0 @@
|
|||
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.
|
|
@ -1,151 +0,0 @@
|
|||
# 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.
|
|
@ -1,29 +0,0 @@
|
|||
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.
|
|
@ -1,45 +0,0 @@
|
|||
# Protevus Container
|
||||
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/platform_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 Protevus, ultimately allowing Protevus to be used with or without `dart:mirrors` package.
|
||||
|
||||
```dart
|
||||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_foundation/core.dart';
|
||||
import 'package:platform_foundation/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 = Protevus(reflector: MirrorsReflector());
|
||||
|
||||
// Sales Controller
|
||||
app.container.registerSingleton<SalesController>(SalesController());
|
||||
await app.mountController<SalesController>();
|
||||
|
||||
var http = PlatformHttp(app);
|
||||
var server = await http.startServer('localhost', 3000);
|
||||
print("Protevus server listening at ${http.uri}");
|
||||
}
|
||||
```
|
|
@ -1 +0,0 @@
|
|||
include: package:lints/recommended.yaml
|
|
@ -1,75 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:platformed_container/container.dart';
|
||||
import 'package:platformed_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.');
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
import 'package:platformed_container/container.dart';
|
||||
|
||||
void main() {
|
||||
var reflector = const ThrowingReflector();
|
||||
reflector.reflectClass(StringBuffer);
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
export 'src/attributes.dart';
|
||||
export 'src/container.dart';
|
||||
export 'src/contextual_binding_builder.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';
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
export 'src/mirrors/mirrors.dart';
|
File diff suppressed because it is too large
Load diff
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/// A utility class that contains constant values related to container functionality.
|
||||
///
|
||||
/// This class is not meant to be instantiated and only provides static constants.
|
||||
/// It includes a default error message for reflection-related issues.
|
||||
class ContainerConst {
|
||||
/// The default error message for reflection-related issues.
|
||||
///
|
||||
/// This message is used when an attempt is made to perform a reflective action,
|
||||
/// but the `ThrowingReflector` class is being used, which disables reflection.
|
||||
/// Consider using the `MirrorsReflector` class if reflection is necessary.
|
||||
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.';
|
||||
|
||||
/// Private constructor to prevent instantiation of this utility class.
|
||||
///
|
||||
/// This constructor is marked as private (with the underscore prefix) to ensure
|
||||
/// that the `ContainerConst` class cannot be instantiated. This is consistent
|
||||
/// with the class's purpose of only providing static constants.
|
||||
ContainerConst._();
|
||||
}
|
|
@ -1,377 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import 'package:platformed_container/container.dart';
|
||||
|
||||
/// A cache to store symbol names.
|
||||
///
|
||||
/// This map associates [Symbol] objects with their corresponding string representations.
|
||||
/// It's used to avoid repeated parsing of symbol names, improving performance
|
||||
/// when retrieving symbol names multiple times.
|
||||
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.
|
||||
///
|
||||
/// This class provides a lightweight alternative to full reflection when reflection
|
||||
/// functionality is not required. It returns empty or placeholder objects for all
|
||||
/// reflection operations, which can be useful in scenarios where reflection is
|
||||
/// expected but not actually used, or when you want to minimize the overhead of
|
||||
/// reflection in certain parts of your application.
|
||||
///
|
||||
/// The [EmptyReflector] includes:
|
||||
/// - A static [RegExp] for extracting symbol names without reflection.
|
||||
/// - Methods to return empty implementations of [ReflectedClass], [ReflectedInstance],
|
||||
/// [ReflectedType], and [ReflectedFunction].
|
||||
/// - A [getName] method that uses a cache to store and retrieve symbol names.
|
||||
///
|
||||
/// This implementation can be particularly useful in testing scenarios or in
|
||||
/// production environments where reflection is not needed but the interface
|
||||
/// expecting reflection capabilities needs to be satisfied.
|
||||
class EmptyReflector extends Reflector {
|
||||
/// A [RegExp] that can be used to extract the name of a symbol without reflection.
|
||||
///
|
||||
/// This regular expression pattern matches the string representation of a Dart [Symbol],
|
||||
/// which typically looks like 'Symbol("symbolName")'. It captures the symbol name
|
||||
/// (the part between the quotes) in a capturing group.
|
||||
///
|
||||
/// Usage:
|
||||
/// ```dart
|
||||
/// String symbolString = 'Symbol("exampleSymbol")';
|
||||
/// Match? match = symbolRegex.firstMatch(symbolString);
|
||||
/// String? symbolName = match?.group(1); // Returns "exampleSymbol"
|
||||
/// ```
|
||||
///
|
||||
/// This is particularly useful in contexts where reflection is not available
|
||||
/// or desired, allowing for symbol name extraction through string manipulation.
|
||||
static final RegExp symbolRegex = RegExp(r'Symbol\("([^"]+)"\)');
|
||||
|
||||
/// Creates an instance of [EmptyReflector].
|
||||
///
|
||||
/// This constructor doesn't take any parameters and creates a lightweight
|
||||
/// reflector that provides empty implementations for all reflection operations.
|
||||
/// It's useful in scenarios where reflection capabilities are expected but not
|
||||
/// actually used, or when you want to minimize the overhead of reflection.
|
||||
const EmptyReflector();
|
||||
|
||||
/// Retrieves the name of a given [Symbol].
|
||||
///
|
||||
/// This method attempts to extract the name of the provided [symbol] using
|
||||
/// the [symbolRegex]. If the name hasn't been cached before, it will be
|
||||
/// computed and stored in the [_symbolNames] cache for future use.
|
||||
///
|
||||
/// The method works as follows:
|
||||
/// 1. It checks if the symbol's name is already in the cache.
|
||||
/// 2. If not found, it uses [putIfAbsent] to compute the name:
|
||||
/// a. It converts the symbol to a string.
|
||||
/// b. It applies the [symbolRegex] to extract the name.
|
||||
/// c. If a match is found, it returns the first captured group (the name).
|
||||
/// 3. The computed name (or null if not found) is stored in the cache and returned.
|
||||
///
|
||||
/// @param symbol The [Symbol] whose name is to be retrieved.
|
||||
/// @return The name of the symbol as a [String], or null if the name couldn't be extracted.
|
||||
@override
|
||||
String? getName(Symbol symbol) {
|
||||
return _symbolNames.putIfAbsent(
|
||||
symbol, () => symbolRegex.firstMatch(symbol.toString())?.group(1));
|
||||
}
|
||||
|
||||
/// Returns an empty [ReflectedClass] instance for any given [Type].
|
||||
///
|
||||
/// This method is part of the [EmptyReflector] implementation and always
|
||||
/// returns a constant instance of [_EmptyReflectedClass], regardless of
|
||||
/// the input [clazz].
|
||||
///
|
||||
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||
/// which provides non-functional placeholders for reflection operations.
|
||||
///
|
||||
/// @param clazz The [Type] to reflect, which is ignored in this implementation.
|
||||
/// @return A constant [_EmptyReflectedClass] instance.
|
||||
@override
|
||||
ReflectedClass reflectClass(Type clazz) {
|
||||
return const _EmptyReflectedClass();
|
||||
}
|
||||
|
||||
/// Returns an empty [ReflectedInstance] for any given object.
|
||||
///
|
||||
/// This method is part of the [EmptyReflector] implementation and always
|
||||
/// returns a constant instance of [_EmptyReflectedInstance], regardless of
|
||||
/// the input [object].
|
||||
///
|
||||
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||
/// which provides non-functional placeholders for reflection operations.
|
||||
///
|
||||
/// @param object The object to reflect, which is ignored in this implementation.
|
||||
/// @return A constant [_EmptyReflectedInstance].
|
||||
@override
|
||||
ReflectedInstance reflectInstance(Object object) {
|
||||
return const _EmptyReflectedInstance();
|
||||
}
|
||||
|
||||
/// Returns an empty [ReflectedType] for any given [Type].
|
||||
///
|
||||
/// This method is part of the [EmptyReflector] implementation and always
|
||||
/// returns a constant instance of [_EmptyReflectedType], regardless of
|
||||
/// the input [type].
|
||||
///
|
||||
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||
/// which provides non-functional placeholders for reflection operations.
|
||||
///
|
||||
/// @param type The [Type] to reflect, which is ignored in this implementation.
|
||||
/// @return A constant [_EmptyReflectedType] instance.
|
||||
@override
|
||||
ReflectedType reflectType(Type type) {
|
||||
return const _EmptyReflectedType();
|
||||
}
|
||||
|
||||
/// Returns an empty [ReflectedFunction] for any given [Function].
|
||||
///
|
||||
/// This method is part of the [EmptyReflector] implementation and always
|
||||
/// returns a constant instance of [_EmptyReflectedFunction], regardless of
|
||||
/// the input [function].
|
||||
///
|
||||
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||
/// which provides non-functional placeholders for reflection operations.
|
||||
///
|
||||
/// @param function The [Function] to reflect, which is ignored in this implementation.
|
||||
/// @return A constant [_EmptyReflectedFunction] instance.
|
||||
@override
|
||||
ReflectedFunction reflectFunction(Function function) {
|
||||
return const _EmptyReflectedFunction();
|
||||
}
|
||||
}
|
||||
|
||||
/// An empty implementation of [ReflectedClass] used by [EmptyReflector].
|
||||
///
|
||||
/// This class provides a non-functional placeholder for reflection operations
|
||||
/// on classes. It is designed to be used in contexts where reflection capabilities
|
||||
/// are expected but not actually needed or desired.
|
||||
///
|
||||
/// Key features:
|
||||
/// - Extends [ReflectedClass] with minimal implementation.
|
||||
/// - Constructor initializes with empty or default values for all properties.
|
||||
/// - [newInstance] method throws an [UnsupportedError] if called.
|
||||
/// - [isAssignableTo] method only returns true if compared with itself.
|
||||
///
|
||||
/// This implementation is consistent with the purpose of [EmptyReflector],
|
||||
/// providing a lightweight alternative when full reflection capabilities are not required.
|
||||
class _EmptyReflectedClass extends ReflectedClass {
|
||||
/// Constructs an empty [_EmptyReflectedClass] instance.
|
||||
///
|
||||
/// This constructor initializes the instance with empty or default values for all properties.
|
||||
///
|
||||
/// @param name The name of the class, set to '(empty)'.
|
||||
/// @param typeParameters The list of type parameters, set to an empty list.
|
||||
/// @param instances The list of instances, set to an empty list.
|
||||
/// @param functions The list of functions, set to an empty list.
|
||||
/// @param declarations The list of declarations, set to an empty list.
|
||||
/// @param type The underlying [Type] of the class, set to [Object].
|
||||
const _EmptyReflectedClass()
|
||||
: super(
|
||||
'(empty)',
|
||||
const <ReflectedTypeParameter>[],
|
||||
const <ReflectedInstance>[],
|
||||
const <ReflectedFunction>[],
|
||||
const <ReflectedDeclaration>[],
|
||||
Object);
|
||||
|
||||
/// Creates a new instance of the reflected class.
|
||||
///
|
||||
/// This method is part of the [_EmptyReflectedClass] implementation and always
|
||||
/// throws an [UnsupportedError] when called. This behavior is consistent with
|
||||
/// the purpose of [EmptyReflector], which provides non-functional placeholders
|
||||
/// for reflection operations.
|
||||
///
|
||||
/// @param constructorName The name of the constructor to invoke.
|
||||
/// @param positionalArguments A list of positional arguments for the constructor.
|
||||
/// @param namedArguments An optional map of named arguments for the constructor.
|
||||
/// @param typeArguments An optional list of type arguments for generic classes.
|
||||
/// @throws UnsupportedError Always thrown when this method is called.
|
||||
/// @return This method never returns as it always throws an exception.
|
||||
@override
|
||||
ReflectedInstance newInstance(
|
||||
String constructorName, List positionalArguments,
|
||||
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) {
|
||||
throw UnsupportedError(
|
||||
'Classes reflected via an EmptyReflector cannot be instantiated.');
|
||||
}
|
||||
|
||||
/// Checks if this empty reflected class is assignable to another reflected type.
|
||||
///
|
||||
/// This method is part of the [_EmptyReflectedClass] implementation and always
|
||||
/// returns true only if the [other] type is the same instance as this one.
|
||||
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||
/// which provides minimal functionality for reflection operations.
|
||||
///
|
||||
/// @param other The [ReflectedType] to check against.
|
||||
/// @return true if [other] is the same instance as this, false otherwise.
|
||||
@override
|
||||
bool isAssignableTo(ReflectedType? other) {
|
||||
return other == this;
|
||||
}
|
||||
}
|
||||
|
||||
/// An empty implementation of [ReflectedType] used by [EmptyReflector].
|
||||
///
|
||||
/// This class provides a non-functional placeholder for reflection operations
|
||||
/// on types. It is designed to be used in contexts where reflection capabilities
|
||||
/// are expected but not actually needed or desired.
|
||||
///
|
||||
/// Key features:
|
||||
/// - Extends [ReflectedType] with minimal implementation.
|
||||
/// - Constructor initializes with empty or default values for all properties.
|
||||
/// - [newInstance] method throws an [UnsupportedError] if called.
|
||||
/// - [isAssignableTo] method only returns true if compared with itself.
|
||||
///
|
||||
/// This implementation is consistent with the purpose of [EmptyReflector],
|
||||
/// providing a lightweight alternative when full reflection capabilities are not required.
|
||||
class _EmptyReflectedType extends ReflectedType {
|
||||
/// Constructs an empty [_EmptyReflectedType] instance.
|
||||
///
|
||||
/// This constructor initializes the instance with empty or default values for all properties.
|
||||
///
|
||||
/// @param name The name of the type, set to '(empty)'.
|
||||
/// @param typeParameters The list of type parameters, set to an empty list.
|
||||
/// @param type The underlying [Type], set to [Object].
|
||||
const _EmptyReflectedType()
|
||||
: super('(empty)', const <ReflectedTypeParameter>[], Object);
|
||||
|
||||
/// Creates a new instance of the reflected type.
|
||||
///
|
||||
/// This method is part of the [_EmptyReflectedType] implementation and always
|
||||
/// throws an [UnsupportedError] when called. This behavior is consistent with
|
||||
/// the purpose of [EmptyReflector], which provides non-functional placeholders
|
||||
/// for reflection operations.
|
||||
///
|
||||
/// @param constructorName The name of the constructor to invoke.
|
||||
/// @param positionalArguments A list of positional arguments for the constructor.
|
||||
/// @param namedArguments An optional map of named arguments for the constructor.
|
||||
/// @param typeArguments An optional list of type arguments for generic types.
|
||||
/// @throws UnsupportedError Always thrown when this method is called.
|
||||
/// @return This method never returns as it always throws an exception.
|
||||
@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.');
|
||||
}
|
||||
|
||||
/// Checks if this empty reflected type is assignable to another reflected type.
|
||||
///
|
||||
/// This method is part of the [_EmptyReflectedType] implementation and always
|
||||
/// returns true only if the [other] type is the same instance as this one.
|
||||
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||
/// which provides minimal functionality for reflection operations.
|
||||
///
|
||||
/// @param other The [ReflectedType] to check against.
|
||||
/// @return true if [other] is the same instance as this, false otherwise.
|
||||
@override
|
||||
bool isAssignableTo(ReflectedType? other) {
|
||||
return other == this;
|
||||
}
|
||||
}
|
||||
|
||||
/// An empty implementation of [ReflectedInstance] used by [EmptyReflector].
|
||||
///
|
||||
/// This class provides a non-functional placeholder for reflection operations
|
||||
/// on instances. It is designed to be used in contexts where reflection capabilities
|
||||
/// are expected but not actually needed or desired.
|
||||
///
|
||||
/// Key features:
|
||||
/// - Extends [ReflectedInstance] with minimal implementation.
|
||||
/// - Constructor initializes with empty or default values for all properties.
|
||||
/// - [getField] method throws an [UnsupportedError] if called.
|
||||
///
|
||||
/// This implementation is consistent with the purpose of [EmptyReflector],
|
||||
/// providing a lightweight alternative when full reflection capabilities are not required.
|
||||
class _EmptyReflectedInstance extends ReflectedInstance {
|
||||
/// Constructs an empty [_EmptyReflectedInstance] instance.
|
||||
///
|
||||
/// This constructor initializes the instance with empty or default values for all properties.
|
||||
///
|
||||
/// @param type The reflected type of the instance, set to an empty [_EmptyReflectedType].
|
||||
/// @param reflectedClass The reflected class of the instance, set to an empty [_EmptyReflectedClass].
|
||||
/// @param value The underlying value of the instance, set to null.
|
||||
const _EmptyReflectedInstance()
|
||||
: super(const _EmptyReflectedType(), const _EmptyReflectedClass(), null);
|
||||
|
||||
/// Retrieves the value of a field on this empty reflected instance.
|
||||
///
|
||||
/// This method is part of the [_EmptyReflectedInstance] implementation and always
|
||||
/// throws an [UnsupportedError] when called. This behavior is consistent with
|
||||
/// the purpose of [EmptyReflector], which provides non-functional placeholders
|
||||
/// for reflection operations.
|
||||
///
|
||||
/// @param name The name of the field to retrieve.
|
||||
/// @throws UnsupportedError Always thrown when this method is called.
|
||||
/// @return This method never returns as it always throws an exception.
|
||||
@override
|
||||
ReflectedInstance getField(String name) {
|
||||
throw UnsupportedError(
|
||||
'Instances reflected via an EmptyReflector cannot call getField().');
|
||||
}
|
||||
}
|
||||
|
||||
/// An empty implementation of [ReflectedFunction] used by [EmptyReflector].
|
||||
///
|
||||
/// This class provides a non-functional placeholder for reflection operations
|
||||
/// on functions. It is designed to be used in contexts where reflection capabilities
|
||||
/// are expected but not actually needed or desired.
|
||||
///
|
||||
/// Key features:
|
||||
/// - Extends [ReflectedFunction] with minimal implementation.
|
||||
/// - Constructor initializes with empty or default values for all properties.
|
||||
/// - [invoke] method throws an [UnsupportedError] if called.
|
||||
///
|
||||
/// This implementation is consistent with the purpose of [EmptyReflector],
|
||||
/// providing a lightweight alternative when full reflection capabilities are not required.
|
||||
class _EmptyReflectedFunction extends ReflectedFunction {
|
||||
/// Constructs an empty [_EmptyReflectedFunction] instance.
|
||||
///
|
||||
/// This constructor initializes the instance with empty or default values for all properties.
|
||||
///
|
||||
/// @param name The name of the function, set to an empty string.
|
||||
/// @param typeParameters A list of type parameters for the function, set to an empty list.
|
||||
/// @param enclosingInstance A list of enclosing instances for the function, set to an empty list.
|
||||
/// @param parameters A list of parameters for the function, set to an empty list.
|
||||
/// @param isStatic Indicates whether the function is static, set to false.
|
||||
/// @param isConst Indicates whether the function is constant, set to false.
|
||||
/// @param returnType The return type of the function, set to an empty [_EmptyReflectedType].
|
||||
/// @param isOperator Indicates whether the function is an operator, set to false.
|
||||
/// @param isExtensionMember Indicates whether the function is an extension member, set to false.
|
||||
const _EmptyReflectedFunction()
|
||||
: super(
|
||||
'(empty)',
|
||||
const <ReflectedTypeParameter>[],
|
||||
const <ReflectedInstance>[],
|
||||
const <ReflectedParameter>[],
|
||||
false,
|
||||
false,
|
||||
returnType: const _EmptyReflectedType());
|
||||
|
||||
/// Invokes this empty reflected function.
|
||||
///
|
||||
/// This method is part of the [_EmptyReflectedFunction] implementation and always
|
||||
/// throws an [UnsupportedError] when called. This behavior is consistent with
|
||||
/// the purpose of [EmptyReflector], which provides non-functional placeholders
|
||||
/// for reflection operations.
|
||||
///
|
||||
/// @param invocation The invocation to execute.
|
||||
/// @throws UnsupportedError Always thrown when this method is called.
|
||||
/// @return This method never returns as it always throws an exception.
|
||||
@override
|
||||
ReflectedInstance invoke(Invocation invocation) {
|
||||
throw UnsupportedError(
|
||||
'Instances reflected via an EmptyReflector cannot call invoke().');
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/// Base exception class for container-related errors.
|
||||
abstract class ContainerException implements Exception {
|
||||
/// The error message
|
||||
final String message;
|
||||
|
||||
/// Optional cause of the exception
|
||||
final Object? cause;
|
||||
|
||||
/// Creates a new container exception
|
||||
ContainerException(this.message, [this.cause]);
|
||||
|
||||
@override
|
||||
String toString() => cause == null ? message : '$message (Caused by: $cause)';
|
||||
}
|
||||
|
||||
/// Exception thrown when reflection operations fail
|
||||
class ReflectionException extends ContainerException {
|
||||
ReflectionException(super.message, [super.cause]);
|
||||
}
|
||||
|
||||
/// Exception thrown when a binding resolution fails
|
||||
class BindingResolutionException extends ContainerException {
|
||||
BindingResolutionException(super.message, [super.cause]);
|
||||
}
|
||||
|
||||
/// Exception thrown when a circular dependency is detected
|
||||
class CircularDependencyException extends ContainerException {
|
||||
CircularDependencyException(super.message, [super.cause]);
|
||||
}
|
||||
|
||||
/// Exception thrown when an entry is not found in the container
|
||||
class EntryNotFoundException extends ContainerException {
|
||||
/// The identifier that was not found
|
||||
final String id;
|
||||
|
||||
EntryNotFoundException(this.id, [Object? cause])
|
||||
: super('No entry was found for identifier "$id"', cause);
|
||||
}
|
||||
|
||||
/// Exception thrown when there are contextual binding issues
|
||||
class ContextualBindingException extends ContainerException {
|
||||
ContextualBindingException(super.message, [super.cause]);
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
export 'reflector.dart';
|
|
@ -1,904 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:mirrors' as dart;
|
||||
import 'package:platformed_container/container.dart';
|
||||
import 'package:quiver/core.dart';
|
||||
|
||||
/// A [Reflector] implementation that forwards to `dart:mirrors`.
|
||||
///
|
||||
/// This class provides reflection capabilities by leveraging the `dart:mirrors` library.
|
||||
/// It allows for runtime introspection of classes, functions, types, and instances.
|
||||
///
|
||||
/// Key features:
|
||||
/// - Reflects classes, functions, types, and instances
|
||||
/// - Provides access to class and function metadata
|
||||
/// - Supports reflection of generic types and futures
|
||||
/// - Allows invocation of reflected functions
|
||||
///
|
||||
/// Note: This reflector is primarily useful on the server-side where reflection is fully supported.
|
||||
/// It may not be suitable for client-side Dart applications due to limitations in reflection support.
|
||||
///
|
||||
/// Usage:
|
||||
/// ```dart
|
||||
/// final reflector = MirrorsReflector();
|
||||
/// final classReflection = reflector.reflectClass(MyClass);
|
||||
/// final functionReflection = reflector.reflectFunction(myFunction);
|
||||
/// final typeReflection = reflector.reflectType(int);
|
||||
/// final instanceReflection = reflector.reflectInstance(myObject);
|
||||
/// ```
|
||||
///
|
||||
/// Be aware of the performance implications when using reflection extensively,
|
||||
/// as it can impact runtime performance and increase code size.
|
||||
class MirrorsReflector extends Reflector {
|
||||
/// Creates a new instance of [MirrorsReflector].
|
||||
///
|
||||
/// This constructor initializes the [MirrorsReflector] instance.
|
||||
const MirrorsReflector();
|
||||
|
||||
/// Retrieves the name of a symbol as a string.
|
||||
///
|
||||
/// This method overrides the base implementation to use the `dart:mirrors` library
|
||||
/// for converting a [Symbol] to its corresponding string representation.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [symbol]: The [Symbol] whose name is to be retrieved.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [String] representing the name of the given symbol.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final name = getName(#someSymbol);
|
||||
/// print(name); // Outputs: "someSymbol"
|
||||
/// ```
|
||||
@override
|
||||
String getName(Symbol symbol) => dart.MirrorSystem.getName(symbol);
|
||||
|
||||
/// Reflects a class and returns a [ReflectedClass] instance.
|
||||
///
|
||||
/// This method takes a [Type] parameter [clazz] and uses dart:mirrors to create
|
||||
/// a reflection of the class. It returns a [_ReflectedClassMirror] which
|
||||
/// implements [ReflectedClass].
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [clazz]: The [Type] of the class to reflect.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedClass] instance representing the reflected class.
|
||||
///
|
||||
/// Throws:
|
||||
/// - [ArgumentError] if the provided [clazz] is not a class.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final reflector = MirrorsReflector();
|
||||
/// final classReflection = reflector.reflectClass(MyClass);
|
||||
/// ```
|
||||
@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.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Reflects a function and returns a [ReflectedFunction] instance.
|
||||
///
|
||||
/// This method takes a [Function] parameter [function] and uses dart:mirrors to create
|
||||
/// a reflection of the function. It returns a [_ReflectedMethodMirror] which
|
||||
/// implements [ReflectedFunction].
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [function]: The [Function] to reflect.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedFunction] instance representing the reflected function.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final reflector = MirrorsReflector();
|
||||
/// final functionReflection = reflector.reflectFunction(myFunction);
|
||||
/// ```
|
||||
@override
|
||||
ReflectedFunction reflectFunction(Function function) {
|
||||
var closure = dart.reflect(function) as dart.ClosureMirror;
|
||||
return _ReflectedMethodMirror(closure.function, closure);
|
||||
}
|
||||
|
||||
/// Reflects a given type and returns a [ReflectedType] instance.
|
||||
///
|
||||
/// This method takes a [Type] parameter and uses dart:mirrors to create
|
||||
/// a reflection of the type. It returns either a [_ReflectedClassMirror]
|
||||
/// or a [_ReflectedTypeMirror] depending on whether the reflected type
|
||||
/// is a class or not.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [type]: The [Type] to reflect.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedType] instance representing the reflected type.
|
||||
///
|
||||
/// If the reflected type doesn't have a reflected type (i.e., [hasReflectedType] is false),
|
||||
/// it returns a reflection of the `dynamic` type instead.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final reflector = MirrorsReflector();
|
||||
/// final typeReflection = reflector.reflectType(int);
|
||||
/// ```
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reflects a Future of a given type and returns a [ReflectedType] instance.
|
||||
///
|
||||
/// This method takes a [Type] parameter and creates a reflection of a Future
|
||||
/// that wraps that type. It first reflects the inner type, then constructs
|
||||
/// a Future type with that inner type as its type argument.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [type]: The [Type] to be wrapped in a Future.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedType] instance representing the reflected Future<Type>.
|
||||
///
|
||||
/// Throws:
|
||||
/// - [ArgumentError] if the provided [type] is not a class or type.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final reflector = MirrorsReflector();
|
||||
/// final futureIntReflection = reflector.reflectFutureOf(int);
|
||||
/// // This will reflect Future<int>
|
||||
/// ```
|
||||
@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);
|
||||
}
|
||||
|
||||
/// Reflects an instance of an object and returns a [ReflectedInstance].
|
||||
///
|
||||
/// This method takes an [Object] parameter and uses dart:mirrors to create
|
||||
/// a reflection of the object instance. It returns a [_ReflectedInstanceMirror]
|
||||
/// which implements [ReflectedInstance].
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [object]: The object instance to reflect.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedInstance] representing the reflected object instance.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final reflector = MirrorsReflector();
|
||||
/// final instanceReflection = reflector.reflectInstance(myObject);
|
||||
/// ```
|
||||
@override
|
||||
ReflectedInstance reflectInstance(Object object) {
|
||||
return _ReflectedInstanceMirror(dart.reflect(object));
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a reflected type parameter using dart:mirrors.
|
||||
///
|
||||
/// This class extends [ReflectedTypeParameter] and wraps a [dart.TypeVariableMirror]
|
||||
/// to provide reflection capabilities for type parameters in Dart.
|
||||
///
|
||||
/// The class extracts the name of the type parameter from the mirror and passes
|
||||
/// it to the superclass constructor.
|
||||
///
|
||||
/// This is typically used internally by the reflection system to represent
|
||||
/// type parameters of generic classes or methods.
|
||||
class _ReflectedTypeParameter extends ReflectedTypeParameter {
|
||||
/// The [dart.TypeVariableMirror] instance representing the reflected type parameter.
|
||||
///
|
||||
/// This mirror provides access to the details of the type parameter, such as its name,
|
||||
/// bounds, and other metadata. It is used internally by the [_ReflectedTypeParameter]
|
||||
/// class to implement reflection capabilities for type parameters.
|
||||
final dart.TypeVariableMirror mirror;
|
||||
|
||||
/// Constructs a [_ReflectedTypeParameter] instance.
|
||||
///
|
||||
/// This constructor takes a [dart.TypeVariableMirror] and initializes the
|
||||
/// [_ReflectedTypeParameter] with the name of the type parameter extracted
|
||||
/// from the mirror.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [mirror]: A [dart.TypeVariableMirror] representing the type parameter.
|
||||
///
|
||||
/// The constructor uses [dart.MirrorSystem.getName] to extract the name of the
|
||||
/// type parameter from the mirror's [simpleName] and passes it to the superclass
|
||||
/// constructor.
|
||||
_ReflectedTypeParameter(this.mirror)
|
||||
: super(dart.MirrorSystem.getName(mirror.simpleName));
|
||||
}
|
||||
|
||||
/// Represents a reflected type using dart:mirrors.
|
||||
///
|
||||
/// This class extends [ReflectedType] and wraps a [dart.TypeMirror]
|
||||
/// to provide reflection capabilities for types in Dart.
|
||||
///
|
||||
/// The class extracts the name and type variables from the mirror and passes
|
||||
/// them to the superclass constructor. It also implements type comparison
|
||||
/// through the [isAssignableTo] method.
|
||||
///
|
||||
/// Note that this class represents types that are not classes, and therefore
|
||||
/// cannot be instantiated. Attempting to call [newInstance] will throw a
|
||||
/// [ReflectionException].
|
||||
///
|
||||
/// This is typically used internally by the reflection system to represent
|
||||
/// non-class types like interfaces, mixins, or type aliases.
|
||||
class _ReflectedTypeMirror extends ReflectedType {
|
||||
/// The [dart.TypeMirror] instance representing the reflected type.
|
||||
///
|
||||
/// This mirror provides access to the details of the type, such as its name,
|
||||
/// type variables, and other metadata. It is used internally by the
|
||||
/// [_ReflectedTypeMirror] class to implement reflection capabilities for types.
|
||||
final dart.TypeMirror mirror;
|
||||
|
||||
/// Constructs a [_ReflectedTypeMirror] instance.
|
||||
///
|
||||
/// This constructor takes a [dart.TypeMirror] and initializes the
|
||||
/// [_ReflectedTypeMirror] with the following:
|
||||
/// - The name of the type extracted from the mirror's [simpleName].
|
||||
/// - A list of [_ReflectedTypeParameter] objects created from the mirror's type variables.
|
||||
/// - The reflected type of the mirror.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [mirror]: A [dart.TypeMirror] representing the type to be reflected.
|
||||
///
|
||||
/// The constructor uses [dart.MirrorSystem.getName] to extract the name of the
|
||||
/// type from the mirror's [simpleName]. It also maps the mirror's type variables
|
||||
/// to [_ReflectedTypeParameter] objects and passes them along with the reflected
|
||||
/// type to the superclass constructor.
|
||||
_ReflectedTypeMirror(this.mirror)
|
||||
: super(
|
||||
dart.MirrorSystem.getName(mirror.simpleName),
|
||||
mirror.typeVariables.map((m) => _ReflectedTypeParameter(m)).toList(),
|
||||
mirror.reflectedType,
|
||||
);
|
||||
|
||||
/// Checks if this reflected class is assignable to another reflected type.
|
||||
///
|
||||
/// This method determines whether an instance of this class can be assigned
|
||||
/// to a variable of the type represented by [other].
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [other]: The [ReflectedType] to check against.
|
||||
///
|
||||
/// Returns:
|
||||
/// - `true` if this class is assignable to [other].
|
||||
/// - `false` otherwise, including when [other] is not a [_ReflectedClassMirror]
|
||||
/// or [_ReflectedTypeMirror].
|
||||
///
|
||||
/// The method uses dart:mirrors' [isAssignableTo] to perform the actual check
|
||||
/// when [other] is either a [_ReflectedClassMirror] or [_ReflectedTypeMirror].
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
/// Throws a [ReflectionException] when attempting to create a new instance.
|
||||
///
|
||||
/// This method is intended to be overridden by classes that represent
|
||||
/// instantiable types. For non-instantiable types (like interfaces or
|
||||
/// abstract classes), this method throws an exception.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [constructorName]: The name of the constructor to invoke.
|
||||
/// - [positionalArguments]: A list of positional arguments for the constructor.
|
||||
/// - [namedArguments]: An optional map of named arguments for the constructor.
|
||||
/// - [typeArguments]: An optional list of type arguments for generic classes.
|
||||
///
|
||||
/// Throws:
|
||||
/// [ReflectionException]: Always thrown with a message indicating that
|
||||
/// this type cannot be instantiated.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// // This will always throw a ReflectionException
|
||||
/// reflectedType.newInstance('defaultConstructor', []);
|
||||
/// ```
|
||||
@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.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a reflected class using dart:mirrors.
|
||||
///
|
||||
/// This class extends [ReflectedClass] and wraps a [dart.ClassMirror]
|
||||
/// to provide reflection capabilities for Dart classes.
|
||||
///
|
||||
/// Key features:
|
||||
/// - Reflects class name, type parameters, constructors, and declarations
|
||||
/// - Provides access to class metadata (annotations)
|
||||
/// - Supports type comparison through [isAssignableTo]
|
||||
/// - Allows creation of new instances of the reflected class
|
||||
///
|
||||
/// This class is typically used internally by the reflection system to
|
||||
/// represent classes and their members.
|
||||
class _ReflectedClassMirror extends ReflectedClass {
|
||||
/// The [dart.ClassMirror] representing the reflected class.
|
||||
///
|
||||
/// This mirror is used to extract information about the class, such as
|
||||
/// its name, type parameters, constructors, and declarations.
|
||||
///
|
||||
/// See also:
|
||||
/// - [dart.ClassMirror] for more details about the mirror system.
|
||||
final dart.ClassMirror mirror;
|
||||
|
||||
/// Constructs a [_ReflectedClassMirror] instance.
|
||||
///
|
||||
/// This constructor takes a [dart.ClassMirror] and initializes the
|
||||
/// [_ReflectedClassMirror] with the following:
|
||||
/// - The name of the class extracted from the mirror's [simpleName].
|
||||
/// - A list of [_ReflectedTypeParameter] objects created from the mirror's type variables.
|
||||
/// - Empty lists for constructors and annotations (these are populated elsewhere).
|
||||
/// - A list of declarations obtained from the [_declarationsOf] method.
|
||||
/// - The reflected type of the mirror.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [mirror]: A [dart.ClassMirror] representing the class to be reflected.
|
||||
///
|
||||
/// The constructor uses [dart.MirrorSystem.getName] to extract the name of the
|
||||
/// class from the mirror's [simpleName]. It also maps the mirror's type variables
|
||||
/// to [_ReflectedTypeParameter] objects and uses [_declarationsOf] to get the
|
||||
/// class declarations. These are then passed to the superclass constructor.
|
||||
_ReflectedClassMirror(this.mirror)
|
||||
: super(
|
||||
dart.MirrorSystem.getName(mirror.simpleName),
|
||||
mirror.typeVariables.map((m) => _ReflectedTypeParameter(m)).toList(),
|
||||
[],
|
||||
[],
|
||||
_declarationsOf(mirror),
|
||||
mirror.reflectedType,
|
||||
);
|
||||
|
||||
/// Retrieves a list of reflected constructors from a given [dart.ClassMirror].
|
||||
///
|
||||
/// This static method iterates through the declarations of the provided [mirror],
|
||||
/// identifies the constructor methods, and creates [ReflectedFunction] instances
|
||||
/// for each constructor found.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [mirror]: A [dart.ClassMirror] representing the class to examine.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [List] of [ReflectedFunction] objects, each representing a constructor
|
||||
/// of the class.
|
||||
///
|
||||
/// The method specifically looks for [dart.MethodMirror] instances that are
|
||||
/// marked as constructors (i.e., [isConstructor] is true). Each identified
|
||||
/// constructor is wrapped in a [_ReflectedMethodMirror] and added to the
|
||||
/// returned list.
|
||||
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;
|
||||
}
|
||||
|
||||
/// Retrieves a list of reflected declarations from a given [dart.ClassMirror].
|
||||
///
|
||||
/// This static method iterates through the declarations of the provided [mirror],
|
||||
/// identifies non-constructor methods, and creates [ReflectedDeclaration] instances
|
||||
/// for each method found.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [mirror]: A [dart.ClassMirror] representing the class to examine.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [List] of [ReflectedDeclaration] objects, each representing a non-constructor
|
||||
/// method of the class.
|
||||
///
|
||||
/// The method specifically looks for [dart.MethodMirror] instances that are
|
||||
/// not constructors (i.e., [isConstructor] is false). Each identified
|
||||
/// method is wrapped in a [_ReflectedDeclarationMirror] and added to the
|
||||
/// returned list.
|
||||
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;
|
||||
}
|
||||
|
||||
/// Retrieves the annotations (metadata) associated with this reflected class.
|
||||
///
|
||||
/// This getter method overrides the base implementation to provide access to
|
||||
/// the class-level annotations using dart:mirrors. It maps each metadata mirror
|
||||
/// to a [_ReflectedInstanceMirror] and returns them as a list.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [List] of [ReflectedInstance] objects, each representing an annotation
|
||||
/// applied to this class.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// @MyAnnotation()
|
||||
/// class MyClass {}
|
||||
///
|
||||
/// // Assuming we have a reflection of MyClass
|
||||
/// final classReflection = reflector.reflectClass(MyClass);
|
||||
/// final annotations = classReflection.annotations;
|
||||
/// // annotations will contain a ReflectedInstance of MyAnnotation
|
||||
/// ```
|
||||
///
|
||||
/// Note: This method relies on the [dart.ClassMirror]'s metadata property
|
||||
/// and creates a new [_ReflectedInstanceMirror] for each annotation.
|
||||
@override
|
||||
List<ReflectedInstance> get annotations =>
|
||||
mirror.metadata.map((m) => _ReflectedInstanceMirror(m)).toList();
|
||||
|
||||
/// Retrieves a list of reflected constructors for this class.
|
||||
///
|
||||
/// This getter method overrides the base implementation to provide access to
|
||||
/// the constructors of the reflected class using dart:mirrors. It uses the
|
||||
/// static [_constructorsOf] method to extract and wrap each constructor
|
||||
/// in a [ReflectedFunction] object.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [List] of [ReflectedFunction] objects, each representing a constructor
|
||||
/// of this class.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final classReflection = reflector.reflectClass(MyClass);
|
||||
/// final constructors = classReflection.constructors;
|
||||
/// // constructors will contain ReflectedFunction objects for each
|
||||
/// // constructor in MyClass
|
||||
/// ```
|
||||
///
|
||||
/// Note: This method relies on the [dart.ClassMirror]'s declarations and
|
||||
/// the [_constructorsOf] method to identify and create reflections of
|
||||
/// the class constructors.
|
||||
@override
|
||||
List<ReflectedFunction> get constructors => _constructorsOf(mirror);
|
||||
|
||||
/// Checks if this reflected type is assignable to another reflected type.
|
||||
///
|
||||
/// This method determines whether an instance of this type can be assigned
|
||||
/// to a variable of the type represented by [other].
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [other]: The [ReflectedType] to check against.
|
||||
///
|
||||
/// Returns:
|
||||
/// - `true` if this type is assignable to [other].
|
||||
/// - `false` otherwise, including when [other] is not a [_ReflectedClassMirror]
|
||||
/// or [_ReflectedTypeMirror].
|
||||
///
|
||||
/// The method uses dart:mirrors' [isAssignableTo] to perform the actual check
|
||||
/// when [other] is either a [_ReflectedClassMirror] or [_ReflectedTypeMirror].
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new instance of the reflected class.
|
||||
///
|
||||
/// This method instantiates a new object of the class represented by this
|
||||
/// [_ReflectedClassMirror] using the specified constructor and arguments.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [constructorName]: The name of the constructor to invoke. Use an empty
|
||||
/// string for the default constructor.
|
||||
/// - [positionalArguments]: A list of positional arguments to pass to the constructor.
|
||||
/// - [namedArguments]: An optional map of named arguments to pass to the constructor.
|
||||
/// - [typeArguments]: An optional list of type arguments for generic classes.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedInstance] representing the newly created instance.
|
||||
///
|
||||
/// Throws:
|
||||
/// May throw exceptions if the constructor invocation fails, e.g., due to
|
||||
/// invalid arguments or if the class cannot be instantiated.
|
||||
///
|
||||
/// Note:
|
||||
/// This implementation currently does not use the [namedArguments] or
|
||||
/// [typeArguments] parameters. They are included for API compatibility.
|
||||
@override
|
||||
ReflectedInstance newInstance(
|
||||
String constructorName, List positionalArguments,
|
||||
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) {
|
||||
return _ReflectedInstanceMirror(
|
||||
mirror.newInstance(Symbol(constructorName), positionalArguments));
|
||||
}
|
||||
|
||||
/// Checks if this [_ReflectedClassMirror] is equal to another object.
|
||||
///
|
||||
/// This method overrides the default equality operator to provide a custom
|
||||
/// equality check for [_ReflectedClassMirror] instances.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [other]: The object to compare with this [_ReflectedClassMirror].
|
||||
///
|
||||
/// Returns:
|
||||
/// - `true` if [other] is also a [_ReflectedClassMirror] and has the same
|
||||
/// [mirror] as this instance.
|
||||
/// - `false` otherwise.
|
||||
///
|
||||
/// This implementation ensures that two [_ReflectedClassMirror] instances
|
||||
/// are considered equal if and only if they reflect the same class (i.e.,
|
||||
/// their underlying [dart.ClassMirror]s are the same).
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _ReflectedClassMirror && other.mirror == mirror;
|
||||
}
|
||||
|
||||
/// Generates a hash code for this [_ReflectedClassMirror].
|
||||
///
|
||||
/// This method overrides the default [hashCode] implementation to provide
|
||||
/// a consistent hash code for [_ReflectedClassMirror] instances.
|
||||
///
|
||||
/// The hash code is generated using the [hash2] function from the Quiver
|
||||
/// library, combining the [mirror] object and an empty string. The empty
|
||||
/// string is used as a second parameter to maintain compatibility with
|
||||
/// the [hash2] function, which requires two arguments.
|
||||
///
|
||||
/// Returns:
|
||||
/// An [int] representing the hash code of this [_ReflectedClassMirror].
|
||||
///
|
||||
/// Note:
|
||||
/// This hash code implementation ensures that two [_ReflectedClassMirror]
|
||||
/// instances with the same [mirror] will have the same hash code, which
|
||||
/// is consistent with the equality check implemented in the [operator ==].
|
||||
@override
|
||||
int get hashCode => hash2(mirror, " ");
|
||||
}
|
||||
|
||||
/// Represents a reflected declaration using dart:mirrors.
|
||||
///
|
||||
/// This class extends [ReflectedDeclaration] and wraps a [dart.MethodMirror]
|
||||
/// to provide reflection capabilities for method declarations in Dart.
|
||||
///
|
||||
/// Key features:
|
||||
/// - Reflects the name and static nature of the declaration
|
||||
/// - Provides access to the underlying method as a [ReflectedFunction]
|
||||
///
|
||||
/// This class is typically used internally by the reflection system to
|
||||
/// represent method declarations within a class.
|
||||
class _ReflectedDeclarationMirror extends ReflectedDeclaration {
|
||||
/// The [dart.MethodMirror] instance representing the reflected method.
|
||||
///
|
||||
/// This mirror provides access to the details of the method, such as its name,
|
||||
/// parameters, return type, and other metadata. It is used internally by the
|
||||
/// [_ReflectedDeclarationMirror] class to implement reflection capabilities
|
||||
/// for method declarations.
|
||||
final dart.MethodMirror mirror;
|
||||
|
||||
/// Constructs a [_ReflectedDeclarationMirror] instance.
|
||||
///
|
||||
/// This constructor initializes a new [_ReflectedDeclarationMirror] with the given [name]
|
||||
/// and [mirror]. It uses the [dart.MethodMirror]'s [isStatic] property to determine
|
||||
/// if the declaration is static, and passes `null` as the initial value for the function.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [name]: A [String] representing the name of the declaration.
|
||||
/// - [mirror]: A [dart.MethodMirror] representing the reflected method.
|
||||
///
|
||||
/// The constructor calls the superclass constructor with the provided [name],
|
||||
/// the [isStatic] property from the [mirror], and `null` for the function parameter.
|
||||
_ReflectedDeclarationMirror(String name, this.mirror)
|
||||
: super(name, mirror.isStatic, null);
|
||||
|
||||
/// Determines if this declaration is static.
|
||||
///
|
||||
/// This getter overrides the base implementation to provide information
|
||||
/// about whether the reflected declaration is static or not. It directly
|
||||
/// accesses the [isStatic] property of the underlying [dart.MethodMirror].
|
||||
///
|
||||
/// Returns:
|
||||
/// A [bool] value:
|
||||
/// - `true` if the declaration is static.
|
||||
/// - `false` if the declaration is not static (i.e., it's an instance method).
|
||||
///
|
||||
/// This property is useful for determining the nature of the reflected
|
||||
/// declaration, particularly when working with class methods and properties.
|
||||
@override
|
||||
bool get isStatic => mirror.isStatic;
|
||||
|
||||
/// Retrieves a [ReflectedFunction] representation of this declaration.
|
||||
///
|
||||
/// This getter overrides the base implementation to provide a [ReflectedFunction]
|
||||
/// that represents the method associated with this declaration. It creates a new
|
||||
/// [_ReflectedMethodMirror] instance using the underlying [dart.MethodMirror].
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedFunction] object that represents the method of this declaration.
|
||||
///
|
||||
/// This property is useful for accessing detailed information about the method,
|
||||
/// such as its parameters, return type, and other attributes, in a way that's
|
||||
/// consistent with the reflection API.
|
||||
@override
|
||||
ReflectedFunction get function => _ReflectedMethodMirror(mirror);
|
||||
}
|
||||
|
||||
/// Represents a reflected instance of an object using dart:mirrors.
|
||||
///
|
||||
/// This class extends [ReflectedInstance] and wraps a [dart.InstanceMirror]
|
||||
/// to provide reflection capabilities for object instances in Dart.
|
||||
///
|
||||
/// Key features:
|
||||
/// - Reflects the type and runtime type of the instance
|
||||
/// - Provides access to the underlying object (reflectee)
|
||||
/// - Allows retrieval of field values through reflection
|
||||
///
|
||||
/// This class is typically used internally by the reflection system to
|
||||
/// represent instances of objects and provide reflective access to their fields.
|
||||
class _ReflectedInstanceMirror extends ReflectedInstance {
|
||||
/// The [dart.InstanceMirror] representing the reflected instance.
|
||||
///
|
||||
/// This mirror provides access to the details of the object instance, such as its type,
|
||||
/// fields, and methods. It is used internally by the [_ReflectedInstanceMirror] class
|
||||
/// to implement reflection capabilities for object instances.
|
||||
///
|
||||
/// The mirror allows for dynamic inspection and manipulation of the object's state
|
||||
/// and behavior at runtime, enabling powerful reflection features.
|
||||
final dart.InstanceMirror mirror;
|
||||
|
||||
/// Constructs a [_ReflectedInstanceMirror] instance.
|
||||
///
|
||||
/// This constructor initializes a new [_ReflectedInstanceMirror] with the given [mirror].
|
||||
/// It uses the [dart.InstanceMirror]'s [type] property to create [_ReflectedClassMirror]
|
||||
/// instances for both the type and runtime type of the reflected instance.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [mirror]: A [dart.InstanceMirror] representing the reflected instance.
|
||||
///
|
||||
/// The constructor calls the superclass constructor with:
|
||||
/// - A [_ReflectedClassMirror] of the instance's type
|
||||
/// - A [_ReflectedClassMirror] of the instance's runtime type
|
||||
/// - The [reflectee] of the mirror, which is the actual object being reflected
|
||||
///
|
||||
/// This setup allows the [_ReflectedInstanceMirror] to provide access to both
|
||||
/// the compile-time and runtime type information of the reflected instance,
|
||||
/// as well as the underlying object itself.
|
||||
_ReflectedInstanceMirror(this.mirror)
|
||||
: super(_ReflectedClassMirror(mirror.type),
|
||||
_ReflectedClassMirror(mirror.type), mirror.reflectee);
|
||||
|
||||
/// Retrieves the value of a field from the reflected instance.
|
||||
///
|
||||
/// This method allows access to field values of the object represented by this
|
||||
/// [_ReflectedInstanceMirror] through reflection.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [name]: A [String] representing the name of the field to retrieve.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedInstance] representing the value of the specified field.
|
||||
/// This returned instance is wrapped in a [_ReflectedInstanceMirror].
|
||||
///
|
||||
/// Throws:
|
||||
/// May throw exceptions if the field does not exist or if access is not allowed.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// var fieldValue = reflectedInstance.getField('myField');
|
||||
/// ```
|
||||
///
|
||||
/// Note:
|
||||
/// This method uses the underlying [dart.InstanceMirror]'s [getField] method
|
||||
/// to perform the actual field access.
|
||||
@override
|
||||
ReflectedInstance getField(String name) {
|
||||
return _ReflectedInstanceMirror(mirror.getField(Symbol(name)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a reflected method using dart:mirrors.
|
||||
///
|
||||
/// This class extends [ReflectedFunction] and wraps a [dart.MethodMirror]
|
||||
/// to provide reflection capabilities for methods in Dart.
|
||||
///
|
||||
/// Key features:
|
||||
/// - Reflects method name, parameters, and return type
|
||||
/// - Provides access to method metadata (annotations)
|
||||
/// - Supports invocation of the reflected method (if a ClosureMirror is available)
|
||||
///
|
||||
/// The class uses both [dart.MethodMirror] and optionally [dart.ClosureMirror]
|
||||
/// to represent and potentially invoke the reflected method.
|
||||
///
|
||||
/// Usage:
|
||||
/// - Created internally by the reflection system to represent methods
|
||||
/// - Can be used to inspect method details or invoke the method if a ClosureMirror is provided
|
||||
///
|
||||
/// Note:
|
||||
/// - Invocation is only possible if a ClosureMirror is provided during construction
|
||||
/// - Throws a StateError if invoke is called without a ClosureMirror
|
||||
class _ReflectedMethodMirror extends ReflectedFunction {
|
||||
/// The [dart.MethodMirror] instance representing the reflected method.
|
||||
///
|
||||
/// This mirror provides access to the details of the method, such as its name,
|
||||
/// parameters, return type, and other metadata. It is used internally by the
|
||||
/// [_ReflectedMethodMirror] class to implement reflection capabilities
|
||||
/// for methods.
|
||||
///
|
||||
/// The [dart.MethodMirror] is a crucial component in the reflection process,
|
||||
/// allowing for introspection of method properties and behavior at runtime.
|
||||
final dart.MethodMirror mirror;
|
||||
|
||||
/// An optional [dart.ClosureMirror] representing the closure of the reflected method.
|
||||
///
|
||||
/// This field is used to store a [dart.ClosureMirror] when the reflected method
|
||||
/// is associated with a callable object (i.e., a closure). The presence of this
|
||||
/// mirror enables the [invoke] method to directly call the reflected method.
|
||||
///
|
||||
/// If this field is null, it indicates that the reflected method cannot be
|
||||
/// directly invoked through this [_ReflectedMethodMirror] instance.
|
||||
///
|
||||
/// Note:
|
||||
/// - This field is crucial for supporting method invocation via reflection.
|
||||
/// - It's typically set when reflecting on instance methods or standalone functions.
|
||||
/// - For class-level method declarations that aren't bound to an instance,
|
||||
/// this field may be null.
|
||||
final dart.ClosureMirror? closureMirror;
|
||||
|
||||
/// Constructs a [_ReflectedMethodMirror] instance.
|
||||
///
|
||||
/// This constructor initializes a new [_ReflectedMethodMirror] with the given [mirror]
|
||||
/// and optional [closureMirror]. It extracts various properties from the [dart.MethodMirror]
|
||||
/// to populate the superclass constructor.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [mirror]: A [dart.MethodMirror] representing the reflected method.
|
||||
/// - [closureMirror]: An optional [dart.ClosureMirror] for method invocation.
|
||||
///
|
||||
/// The constructor initializes the following:
|
||||
/// - Method name from the mirror's [simpleName]
|
||||
/// - An empty list of reflected type parameters
|
||||
/// - Metadata (annotations) as [_ReflectedInstanceMirror] objects
|
||||
/// - Reflected parameters using [_reflectParameter]
|
||||
/// - Getter and setter flags from the mirror
|
||||
/// - Return type, using [dynamic] if the mirror doesn't have a reflected type
|
||||
///
|
||||
/// This setup allows the [_ReflectedMethodMirror] to provide comprehensive
|
||||
/// reflection capabilities for the method, including its signature, metadata,
|
||||
/// and potential invocation (if a [closureMirror] is provided).
|
||||
_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));
|
||||
|
||||
/// Reflects a parameter of a method using dart:mirrors.
|
||||
///
|
||||
/// This static method creates a [ReflectedParameter] instance from a given [dart.ParameterMirror].
|
||||
/// It extracts various properties from the mirror to construct a comprehensive reflection of the parameter.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [mirror]: A [dart.ParameterMirror] representing the parameter to be reflected.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedParameter] instance containing the reflected information of the parameter.
|
||||
///
|
||||
/// The method extracts the following information:
|
||||
/// - Parameter name from the mirror's [simpleName]
|
||||
/// - Metadata (annotations) as [_ReflectedInstanceMirror] objects
|
||||
/// - Parameter type, reflected using [MirrorsReflector]
|
||||
/// - Whether the parameter is required (not optional)
|
||||
/// - Whether the parameter is named
|
||||
///
|
||||
/// This method is typically used internally by the reflection system to create
|
||||
/// parameter reflections for method signatures.
|
||||
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);
|
||||
}
|
||||
|
||||
/// Invokes the reflected method with the given invocation details.
|
||||
///
|
||||
/// This method allows for dynamic invocation of the reflected method using the
|
||||
/// provided [Invocation] object. It requires that a [closureMirror] was provided
|
||||
/// during the construction of this [_ReflectedMethodMirror].
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [invocation]: An [Invocation] object containing the details of the method call,
|
||||
/// including the method name, positional arguments, and named arguments.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedInstance] representing the result of the method invocation.
|
||||
///
|
||||
/// Throws:
|
||||
/// - [StateError] if this [_ReflectedMethodMirror] was created without a [closureMirror],
|
||||
/// indicating that direct invocation is not possible.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// var result = reflectedMethod.invoke(Invocation.method(#methodName, [arg1, arg2]));
|
||||
/// ```
|
||||
///
|
||||
/// Note:
|
||||
/// This method relies on the presence of a [closureMirror] to perform the actual
|
||||
/// invocation. If no [closureMirror] is available, it means the reflected method
|
||||
/// cannot be directly invoked, and an error will be thrown.
|
||||
@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));
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
|
@ -1,339 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:quiver/core.dart';
|
||||
|
||||
/// Abstract class representing a reflector for introspection of Dart types and instances.
|
||||
///
|
||||
/// This class provides methods to reflect on various Dart constructs such as classes,
|
||||
/// functions, types, and instances. It allows for runtime inspection and manipulation
|
||||
/// of code elements.
|
||||
///
|
||||
/// The methods in this class are designed to be implemented by concrete reflector
|
||||
/// classes, potentially using different reflection mechanisms (e.g., mirrors, code
|
||||
/// generation).
|
||||
///
|
||||
/// Note: The `reflectFutureOf` method throws an `UnsupportedError` by default and
|
||||
/// requires `dart:mirrors` for implementation.
|
||||
abstract class Reflector {
|
||||
/// Constructs a new [Reflector] instance.
|
||||
///
|
||||
/// This constructor is declared as `const` to allow for compile-time constant creation
|
||||
/// of [Reflector] instances. Subclasses of [Reflector] may override this constructor
|
||||
/// to provide their own initialization logic if needed.
|
||||
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`.');
|
||||
}
|
||||
|
||||
/// Find a type by its name.
|
||||
///
|
||||
/// This method is used to support Class@method syntax by finding a type
|
||||
/// from its string name.
|
||||
Type? findTypeByName(String name) {
|
||||
throw UnsupportedError('`findTypeByName` requires `dart:mirrors`.');
|
||||
}
|
||||
|
||||
/// Find an instance method by its name.
|
||||
///
|
||||
/// This method is used to support Class@method syntax by finding a method
|
||||
/// on an instance from its string name.
|
||||
ReflectedFunction? findInstanceMethod(Object instance, String methodName) {
|
||||
throw UnsupportedError('`findInstanceMethod` requires `dart:mirrors`.');
|
||||
}
|
||||
|
||||
/// Get annotations for a type.
|
||||
///
|
||||
/// This method returns a list of reflected instances representing the annotations
|
||||
/// applied to the given type.
|
||||
List<ReflectedInstance> getAnnotations(Type type) {
|
||||
throw UnsupportedError('`getAnnotations` requires `dart:mirrors`.');
|
||||
}
|
||||
|
||||
/// Get annotations for a parameter.
|
||||
///
|
||||
/// This method returns a list of reflected instances representing the annotations
|
||||
/// applied to the parameter with the given name in the specified constructor of
|
||||
/// the given type.
|
||||
List<ReflectedInstance> getParameterAnnotations(
|
||||
Type type, String constructorName, String parameterName) {
|
||||
throw UnsupportedError(
|
||||
'`getParameterAnnotations` requires `dart:mirrors`.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a reflected instance of an object.
|
||||
///
|
||||
/// This abstract class provides a way to introspect and manipulate object instances
|
||||
/// at runtime. It encapsulates information about the object's type, class, and the
|
||||
/// actual object instance (reflectee).
|
||||
///
|
||||
/// The [type] property represents the reflected type of the instance.
|
||||
/// The [clazz] property represents the reflected class of the instance.
|
||||
/// The [reflectee] property holds the actual object instance being reflected.
|
||||
///
|
||||
/// This class also provides methods for comparing instances and accessing fields.
|
||||
///
|
||||
/// Use the [getField] method to retrieve a reflected instance of a specific field.
|
||||
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);
|
||||
}
|
||||
|
||||
/// Represents a reflected type in the Dart language.
|
||||
///
|
||||
/// This abstract class encapsulates information about a Dart type, including its name,
|
||||
/// type parameters, and the actual Dart [Type] it represents.
|
||||
///
|
||||
/// The [name] property holds the name of the type.
|
||||
/// The [typeParameters] list contains the type parameters if the type is generic.
|
||||
/// The [reflectedType] property holds the actual Dart [Type] being reflected.
|
||||
///
|
||||
/// This class provides methods for creating new instances of the type, comparing types,
|
||||
/// and checking type assignability.
|
||||
///
|
||||
/// The [newInstance] method allows for dynamic creation of new instances of the type.
|
||||
/// The [isAssignableTo] method checks if this type is assignable to another type.
|
||||
///
|
||||
/// This class also overrides [hashCode] and [operator ==] for proper equality comparisons.
|
||||
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);
|
||||
}
|
||||
|
||||
/// Represents a reflected class in the Dart language.
|
||||
///
|
||||
/// This abstract class extends [ReflectedType] and provides additional information
|
||||
/// specific to classes, including annotations, constructors, and declarations.
|
||||
///
|
||||
/// The [annotations] list contains reflected instances of annotations applied to the class.
|
||||
/// The [constructors] list contains reflected functions representing the class constructors.
|
||||
/// The [declarations] list contains reflected declarations (fields, methods, etc.) of the class.
|
||||
///
|
||||
/// This class overrides [hashCode] and [operator ==] to include the additional properties
|
||||
/// in equality comparisons and hash code calculations.
|
||||
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);
|
||||
}
|
||||
|
||||
/// Represents a reflected declaration in the Dart language.
|
||||
///
|
||||
/// This class encapsulates information about a declaration within a class or object,
|
||||
/// such as a method, field, or property.
|
||||
///
|
||||
/// The [name] property holds the name of the declaration.
|
||||
/// The [isStatic] property indicates whether the declaration is static.
|
||||
/// The [function] property, if non-null, represents the reflected function associated
|
||||
/// with this declaration (applicable for methods and some properties).
|
||||
///
|
||||
/// This class provides methods for comparing declarations and calculating hash codes.
|
||||
/// It overrides [hashCode] and [operator ==] for proper equality comparisons.
|
||||
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;
|
||||
}
|
||||
|
||||
/// Represents a reflected function in the Dart language.
|
||||
///
|
||||
/// This abstract class encapsulates information about a function, including its name,
|
||||
/// type parameters, annotations, return type, parameters, and whether it's a getter or setter.
|
||||
///
|
||||
/// The [name] property holds the name of the function.
|
||||
/// The [typeParameters] list contains the type parameters if the function is generic.
|
||||
/// The [annotations] list contains reflected instances of annotations applied to the function.
|
||||
/// The [returnType] property represents the function's return type (if applicable).
|
||||
/// The [parameters] list contains the function's parameters.
|
||||
/// The [isGetter] and [isSetter] properties indicate if the function is a getter or setter.
|
||||
///
|
||||
/// This class provides methods for comparing functions and calculating hash codes.
|
||||
/// It also includes an [invoke] method for dynamically calling the function.
|
||||
///
|
||||
/// This class overrides [hashCode] and [operator ==] for proper equality comparisons.
|
||||
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;
|
||||
|
||||
/// Invoke this function with the given invocation.
|
||||
///
|
||||
/// This method is used to support dynamic method invocation.
|
||||
ReflectedInstance invoke(Invocation invocation);
|
||||
}
|
||||
|
||||
/// Represents a reflected parameter in the Dart language.
|
||||
///
|
||||
/// This class encapsulates information about a function or method parameter,
|
||||
/// including its name, annotations, type, and properties such as whether it's
|
||||
/// required or named.
|
||||
///
|
||||
/// Properties:
|
||||
/// - [name]: The name of the parameter.
|
||||
/// - [annotations]: A list of reflected instances of annotations applied to the parameter.
|
||||
/// - [type]: The reflected type of the parameter.
|
||||
/// - [isRequired]: Indicates whether the parameter is required.
|
||||
/// - [isNamed]: Indicates whether the parameter is a named parameter.
|
||||
///
|
||||
/// This class provides methods for comparing parameters and calculating hash codes.
|
||||
/// It overrides [hashCode] and [operator ==] for proper equality comparisons.
|
||||
class ReflectedParameter {
|
||||
final String name;
|
||||
final List<ReflectedInstance> annotations;
|
||||
final ReflectedType type;
|
||||
final bool isRequired;
|
||||
final bool isNamed;
|
||||
final bool isVariadic;
|
||||
|
||||
const ReflectedParameter(
|
||||
this.name, this.annotations, this.type, this.isRequired, this.isNamed,
|
||||
{this.isVariadic = false});
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
hashObjects([name, annotations, type, isRequired, isNamed, isVariadic]);
|
||||
|
||||
@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 &&
|
||||
other.isVariadic == isVariadic;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import 'package:platformed_container/container.dart';
|
||||
|
||||
/// A static implementation of the [Reflector] class that performs simple [Map] lookups.
|
||||
///
|
||||
/// `package:platform_container_generator` uses this to create reflectors from analysis metadata.
|
||||
class StaticReflector extends Reflector {
|
||||
/// A map that associates [Symbol] objects with their corresponding string names.
|
||||
///
|
||||
/// This map is used to store and retrieve the string representations of symbols,
|
||||
/// which can be useful for reflection and debugging purposes.
|
||||
final Map<Symbol, String> names;
|
||||
|
||||
/// A map that associates [Type] objects with their corresponding [ReflectedType] objects.
|
||||
///
|
||||
/// This map is used to store and retrieve reflection information for different types,
|
||||
/// allowing for runtime introspection of type metadata and structure.
|
||||
final Map<Type, ReflectedType> types;
|
||||
|
||||
/// A map that associates [Function] objects with their corresponding [ReflectedFunction] objects.
|
||||
///
|
||||
/// This map is used to store and retrieve reflection information for functions,
|
||||
/// enabling runtime introspection of function metadata, parameters, and return types.
|
||||
final Map<Function, ReflectedFunction> functions;
|
||||
|
||||
/// A map that associates [Object] instances with their corresponding [ReflectedInstance] objects.
|
||||
///
|
||||
/// This map is used to store and retrieve reflection information for specific object instances,
|
||||
/// allowing for runtime introspection of object properties, methods, and metadata.
|
||||
final Map<Object, ReflectedInstance> instances;
|
||||
|
||||
/// Creates a new [StaticReflector] instance with optional parameters.
|
||||
///
|
||||
/// The [StaticReflector] constructor allows you to initialize the reflector
|
||||
/// with pre-populated maps for names, types, functions, and instances.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [names]: A map of [Symbol] to [String] for symbol name lookups. Defaults to an empty map.
|
||||
/// - [types]: A map of [Type] to [ReflectedType] for type reflection. Defaults to an empty map.
|
||||
/// - [functions]: A map of [Function] to [ReflectedFunction] for function reflection. Defaults to an empty map.
|
||||
/// - [instances]: A map of [Object] to [ReflectedInstance] for instance reflection. Defaults to an empty map.
|
||||
///
|
||||
/// All parameters are optional and default to empty constant maps if not provided.
|
||||
const StaticReflector(
|
||||
{this.names = const {},
|
||||
this.types = const {},
|
||||
this.functions = const {},
|
||||
this.instances = const {}});
|
||||
|
||||
/// Returns the string name associated with the given [Symbol].
|
||||
///
|
||||
/// This method looks up the string representation of the provided [symbol]
|
||||
/// in the [names] map. If the symbol is found, its corresponding string
|
||||
/// name is returned. If the symbol is not found in the map, an [ArgumentError]
|
||||
/// is thrown.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [symbol]: The [Symbol] for which to retrieve the string name.
|
||||
///
|
||||
/// Returns:
|
||||
/// The string name associated with the given [symbol], or null if not found.
|
||||
///
|
||||
/// Throws:
|
||||
/// - [ArgumentError]: If the provided [symbol] is not found in the [names] map.
|
||||
@override
|
||||
String? getName(Symbol symbol) {
|
||||
if (!names.containsKey(symbol)) {
|
||||
throw ArgumentError(
|
||||
'The value of $symbol is unknown - it was not generated.');
|
||||
}
|
||||
|
||||
return names[symbol];
|
||||
}
|
||||
|
||||
/// Reflects a class based on its [Type].
|
||||
///
|
||||
/// This method attempts to reflect the given class [Type] by calling [reflectType]
|
||||
/// and casting the result to [ReflectedClass]. If the reflection is successful
|
||||
/// and the result is a [ReflectedClass], it is returned. Otherwise, null is returned.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [clazz]: The [Type] of the class to reflect.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedClass] instance if the reflection is successful and the result
|
||||
/// is a [ReflectedClass], or null otherwise.
|
||||
@override
|
||||
ReflectedClass? reflectClass(Type clazz) =>
|
||||
reflectType(clazz) as ReflectedClass?;
|
||||
|
||||
/// Reflects a function based on its [Function] object.
|
||||
///
|
||||
/// This method attempts to retrieve reflection information for the given [function]
|
||||
/// from the [functions] map. If the function is found in the map, its corresponding
|
||||
/// [ReflectedFunction] object is returned. If the function is not found, an
|
||||
/// [ArgumentError] is thrown.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [function]: The [Function] object to reflect.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedFunction] object containing reflection information about the
|
||||
/// given function, or null if not found.
|
||||
///
|
||||
/// Throws:
|
||||
/// - [ArgumentError]: If there is no reflection information available for
|
||||
/// the given [function].
|
||||
@override
|
||||
ReflectedFunction? reflectFunction(Function function) {
|
||||
if (!functions.containsKey(function)) {
|
||||
throw ArgumentError(
|
||||
'There is no reflection information available about $function.');
|
||||
}
|
||||
|
||||
return functions[function];
|
||||
}
|
||||
|
||||
/// Reflects an object instance to retrieve its reflection information.
|
||||
///
|
||||
/// This method attempts to retrieve reflection information for the given [object]
|
||||
/// from the [instances] map. If the object is found in the map, its corresponding
|
||||
/// [ReflectedInstance] object is returned. If the object is not found, an
|
||||
/// [ArgumentError] is thrown.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [object]: The object instance to reflect.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedInstance] object containing reflection information about the
|
||||
/// given object instance, or null if not found.
|
||||
///
|
||||
/// Throws:
|
||||
/// - [ArgumentError]: If there is no reflection information available for
|
||||
/// the given [object].
|
||||
@override
|
||||
ReflectedInstance? reflectInstance(Object object) {
|
||||
if (!instances.containsKey(object)) {
|
||||
throw ArgumentError(
|
||||
'There is no reflection information available about $object.');
|
||||
}
|
||||
|
||||
return instances[object];
|
||||
}
|
||||
|
||||
/// Reflects a type to retrieve its reflection information.
|
||||
///
|
||||
/// This method attempts to retrieve reflection information for the given [type]
|
||||
/// from the [types] map. If the type is found in the map, its corresponding
|
||||
/// [ReflectedType] object is returned. If the type is not found, an
|
||||
/// [ArgumentError] is thrown.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [type]: The [Type] to reflect.
|
||||
///
|
||||
/// Returns:
|
||||
/// A [ReflectedType] object containing reflection information about the
|
||||
/// given type, or null if not found.
|
||||
///
|
||||
/// Throws:
|
||||
/// - [ArgumentError]: If there is no reflection information available for
|
||||
/// the given [type].
|
||||
@override
|
||||
ReflectedType? reflectType(Type type) {
|
||||
if (!types.containsKey(type)) {
|
||||
throw ArgumentError(
|
||||
'There is no reflection information available about $type.');
|
||||
}
|
||||
|
||||
return types[type];
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
import 'package:platformed_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.';
|
||||
*/
|
||||
|
||||
/// Creates a [ThrowingReflector] instance.
|
||||
///
|
||||
/// [errorMessage] is the message to be used when throwing an [UnsupportedError].
|
||||
/// If not provided, it defaults to [ContainerConst.defaultErrorMessage].
|
||||
const ThrowingReflector(
|
||||
{this.errorMessage = ContainerConst.defaultErrorMessage});
|
||||
|
||||
/// Retrieves the name associated with the given [symbol].
|
||||
///
|
||||
/// This method delegates the task to an instance of [EmptyReflector].
|
||||
/// It returns the name as a [String] if found, or `null` if not found.
|
||||
///
|
||||
/// [symbol] is the [Symbol] for which to retrieve the name.
|
||||
///
|
||||
/// Returns a [String] representing the name of the symbol, or `null` if not found.
|
||||
@override
|
||||
String? getName(Symbol symbol) => const EmptyReflector().getName(symbol);
|
||||
|
||||
/// Creates and returns an [UnsupportedError] with the specified [errorMessage].
|
||||
///
|
||||
/// This method is used internally to generate consistent error messages
|
||||
/// when reflection operations are attempted on this [ThrowingReflector].
|
||||
///
|
||||
/// Returns an [UnsupportedError] instance with the configured error message.
|
||||
UnsupportedError _error() => UnsupportedError(errorMessage);
|
||||
|
||||
/// Reflects on a given class type and throws an [UnsupportedError].
|
||||
///
|
||||
/// This method is part of the [ThrowingReflector] implementation and is designed
|
||||
/// to prevent reflective operations. When called, it throws an [UnsupportedError]
|
||||
/// with the configured error message.
|
||||
///
|
||||
/// [clazz] is the [Type] of the class to reflect on.
|
||||
///
|
||||
/// Throws an [UnsupportedError] when invoked, as reflection is not supported.
|
||||
@override
|
||||
ReflectedClass reflectClass(Type clazz) => throw _error();
|
||||
|
||||
/// Reflects on a given object instance and throws an [UnsupportedError].
|
||||
///
|
||||
/// This method is part of the [ThrowingReflector] implementation and is designed
|
||||
/// to prevent reflective operations on object instances. When called, it throws
|
||||
/// an [UnsupportedError] with the configured error message.
|
||||
///
|
||||
/// [object] is the object instance to reflect on.
|
||||
///
|
||||
/// Throws an [UnsupportedError] when invoked, as reflection is not supported.
|
||||
@override
|
||||
ReflectedInstance reflectInstance(Object object) => throw _error();
|
||||
|
||||
/// Reflects on a given type and throws an [UnsupportedError].
|
||||
///
|
||||
/// This method is part of the [ThrowingReflector] implementation and is designed
|
||||
/// to prevent reflective operations on types. When called, it throws an
|
||||
/// [UnsupportedError] with the configured error message.
|
||||
///
|
||||
/// [type] is the [Type] to reflect on.
|
||||
///
|
||||
/// Throws an [UnsupportedError] when invoked, as reflection is not supported.
|
||||
@override
|
||||
ReflectedType reflectType(Type type) => throw _error();
|
||||
|
||||
/// Reflects on a given function and throws an [UnsupportedError].
|
||||
///
|
||||
/// This method is part of the [ThrowingReflector] implementation and is designed
|
||||
/// to prevent reflective operations on functions. When called, it throws an
|
||||
/// [UnsupportedError] with the configured error message.
|
||||
///
|
||||
/// [function] is the [Function] to reflect on.
|
||||
///
|
||||
/// Throws an [UnsupportedError] when invoked, as reflection is not supported.
|
||||
@override
|
||||
ReflectedFunction reflectFunction(Function function) => throw _error();
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
name: platformed_container
|
||||
version: 9.0.0
|
||||
description: Protevus Platform hierarchical DI container, and pluggable backends for reflection.
|
||||
homepage: https://protevus.com
|
||||
documentation: https://docs.protevus.com
|
||||
repository: https://git.protevus.com/protevus/platform/src/branch/main/packages/container/container
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
collection: ^1.19.1
|
||||
quiver: ^3.2.2
|
||||
dev_dependencies:
|
||||
test: ^1.25.8
|
||||
lints: ^4.0.0
|
|
@ -1,122 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:platformed_container/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 }
|
|
@ -1,138 +0,0 @@
|
|||
import 'package:platformed_container/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!!!');
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
import 'package:platformed_container/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});
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import 'package:platformed_container/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);
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
import 'dart:async';
|
||||
import 'package:platformed_container/container.dart';
|
||||
import 'package:platformed_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);
|
||||
});
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
import 'package:platformed_container/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});
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
import 'package:platformed_container/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!!!');
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
# 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/
|
||||
|
||||
test/*.reflectable.dart
|
||||
example/*.reflectable.dart
|
|
@ -1,58 +0,0 @@
|
|||
# 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 `platform_` packages
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Migrated to work with Dart >= 2.12 Non NNBD
|
||||
|
||||
## 1.0.1
|
||||
|
||||
* Update for `pkg:angel_container@1.0.3`.
|
|
@ -1,29 +0,0 @@
|
|||
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.
|
|
@ -1,32 +0,0 @@
|
|||
# Protevus 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 Protevus 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
|
|
@ -1 +0,0 @@
|
|||
include: package:lints/recommended.yaml
|
|
@ -1,75 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:platform_container/container.dart';
|
||||
import 'package:platform_container_generator/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.');
|
||||
}
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
import 'package:platform_container/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);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
name: platformed_container_generator
|
||||
version: 9.0.0
|
||||
description: Protevus Platform Codegen support for using pkg:reflectable with pkg:platform_container.
|
||||
homepage: https://protevus.com
|
||||
documentation: https://docs.protevus.com
|
||||
repository: https://git.protevus.com/protevus/platform/src/branch/main/packages/container/container_generator
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
platform_container: ^9.0.0
|
||||
reflectable: ^4.0.12
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.13
|
||||
build_test: ^2.2.2
|
||||
test: ^1.25.8
|
||||
lints: ^4.0.0
|
||||
# dependency_overrides:
|
||||
# platform_container:
|
||||
# path: ../platform_container
|
|
@ -1,179 +0,0 @@
|
|||
import 'package:platform_container/container.dart';
|
||||
import 'package:platform_container_generator/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;
|
||||
}
|
7
incubation/ioc_container/.gitignore
vendored
7
incubation/ioc_container/.gitignore
vendored
|
@ -1,7 +0,0 @@
|
|||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
# Avoid committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
|
@ -1,3 +0,0 @@
|
|||
## 1.0.0
|
||||
|
||||
- Initial version.
|
|
@ -1,10 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
The Laravel Framework is Copyright (c) Taylor Otwell
|
||||
The Fabric Framework is Copyright (c) Vieo, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,39 +0,0 @@
|
|||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/developing-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
|
@ -1,30 +0,0 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -1,21 +0,0 @@
|
|||
// This is the barrel file for the ioc_container package
|
||||
|
||||
// Export the main Container class
|
||||
export 'src/container.dart';
|
||||
|
||||
// Export other important classes and utilities
|
||||
export 'src/bound_method.dart';
|
||||
export 'src/contextual_binding_builder.dart';
|
||||
export 'src/entry_not_found_exception.dart';
|
||||
export 'src/rewindable_generator.dart';
|
||||
export 'src/util.dart';
|
||||
|
||||
// Export any interfaces or contracts if they exist
|
||||
// export 'src/contracts/container_contract.dart';
|
||||
|
||||
// Export any additional exceptions
|
||||
|
||||
// Export any additional utilities or helpers
|
||||
// export 'src/helpers/parameter_resolver.dart';
|
||||
|
||||
// You can add more exports as needed for your package
|
|
@ -1,261 +0,0 @@
|
|||
import 'dart:mirrors';
|
||||
import 'package:ioc_container/src/container.dart';
|
||||
import 'package:ioc_container/src/util.dart';
|
||||
import 'package:platform_contracts/contracts.dart';
|
||||
|
||||
class BoundMethod {
|
||||
static dynamic call(Container container, dynamic callback,
|
||||
[List<dynamic> parameters = const [], String? defaultMethod]) {
|
||||
if (callback is String) {
|
||||
if (defaultMethod == null && _hasInvokeMethod(callback)) {
|
||||
defaultMethod = '__invoke';
|
||||
}
|
||||
return _callClass(container, callback, parameters, defaultMethod);
|
||||
}
|
||||
|
||||
if (callback is List && callback.length == 2) {
|
||||
var instance = container.make(callback[0].toString());
|
||||
var method = callback[1].toString();
|
||||
return _callBoundMethod(container, [instance, method], () {
|
||||
throw BindingResolutionException(
|
||||
'Failed to call method: $method on ${instance.runtimeType}');
|
||||
}, parameters);
|
||||
}
|
||||
|
||||
if (callback is Function) {
|
||||
return _callBoundMethod(container, callback, () {
|
||||
throw BindingResolutionException('Failed to call function');
|
||||
}, parameters);
|
||||
}
|
||||
|
||||
if (_isCallableWithAtSign(callback)) {
|
||||
return _callClass(container, callback, parameters, defaultMethod);
|
||||
}
|
||||
|
||||
throw ArgumentError('Invalid callback type: ${callback.runtimeType}');
|
||||
}
|
||||
|
||||
static dynamic _callBoundMethod(
|
||||
Container container, dynamic callback, Function defaultCallback,
|
||||
[List<dynamic> parameters = const []]) {
|
||||
if (callback is List && callback.length == 2) {
|
||||
var instance = callback[0];
|
||||
var method = callback[1];
|
||||
if (instance is String) {
|
||||
instance = container.make(instance);
|
||||
}
|
||||
if (method is String) {
|
||||
if (instance is Function && method == '__invoke') {
|
||||
return Function.apply(instance, parameters);
|
||||
}
|
||||
if (instance is Map && instance.containsKey(method)) {
|
||||
// Handle the case where instance is a Map and method is a key
|
||||
var result = instance[method];
|
||||
return result is Function
|
||||
? Function.apply(result, parameters)
|
||||
: result;
|
||||
}
|
||||
var instanceMirror = reflect(instance);
|
||||
var methodSymbol = Symbol(method);
|
||||
if (instanceMirror.type.instanceMembers.containsKey(methodSymbol)) {
|
||||
var dependencies =
|
||||
_getMethodDependencies(container, instance, method, parameters);
|
||||
var result = Function.apply(
|
||||
instanceMirror.getField(methodSymbol).reflectee, dependencies);
|
||||
return result is Function
|
||||
? Function.apply(result, parameters)
|
||||
: result;
|
||||
} else if (method == '__invoke' && instance is Function) {
|
||||
return Function.apply(instance, parameters);
|
||||
} else if (instance is Type) {
|
||||
// Handle static methods
|
||||
var classMirror = reflectClass(instance);
|
||||
if (classMirror.staticMembers.containsKey(Symbol(method))) {
|
||||
var dependencies =
|
||||
_getMethodDependencies(container, instance, method, parameters);
|
||||
var result = Function.apply(
|
||||
classMirror.getField(Symbol(method)).reflectee, dependencies);
|
||||
return result is Function
|
||||
? Function.apply(result, parameters)
|
||||
: result;
|
||||
}
|
||||
}
|
||||
// Try to find the method in the global scope
|
||||
var globalMethod = _findGlobalMethod(method);
|
||||
if (globalMethod != null) {
|
||||
var result = Function.apply(globalMethod, parameters);
|
||||
return result is Function
|
||||
? Function.apply(result, parameters)
|
||||
: result;
|
||||
}
|
||||
throw BindingResolutionException(
|
||||
'Method $method not found on ${instance.runtimeType}');
|
||||
} else if (method is Function) {
|
||||
var result = Function.apply(method, [instance, ...parameters]);
|
||||
return result is Function ? Function.apply(result, parameters) : result;
|
||||
}
|
||||
} else if (callback is Function) {
|
||||
var result = Function.apply(callback, parameters);
|
||||
return result is Function ? Function.apply(result, parameters) : result;
|
||||
}
|
||||
return Util.unwrapIfClosure(defaultCallback);
|
||||
}
|
||||
|
||||
static dynamic _callClass(Container container, String target,
|
||||
List<dynamic> parameters, String? defaultMethod) {
|
||||
var segments = target.split('@');
|
||||
|
||||
var className = segments[0];
|
||||
var method = segments.length == 2 ? segments[1] : defaultMethod;
|
||||
|
||||
method ??= '__invoke';
|
||||
|
||||
var instance = container.make(className);
|
||||
if (instance is String) {
|
||||
// If instance is still a string, it might be a global function
|
||||
if (container.bound(instance)) {
|
||||
return container.make(instance);
|
||||
}
|
||||
// If it's not bound, treat it as a string value
|
||||
return instance;
|
||||
}
|
||||
if (instance is Function && method == '__invoke') {
|
||||
return Function.apply(instance, parameters);
|
||||
}
|
||||
return _callBoundMethod(container, [instance, method], () {
|
||||
throw BindingResolutionException(
|
||||
'Failed to call method: $method on $className');
|
||||
}, parameters);
|
||||
}
|
||||
|
||||
static Function? _findGlobalMethod(String methodName) {
|
||||
var currentMirror = currentMirrorSystem();
|
||||
for (var library in currentMirror.libraries.values) {
|
||||
if (library.declarations.containsKey(Symbol(methodName))) {
|
||||
var declaration = library.declarations[Symbol(methodName)]!;
|
||||
if (declaration is MethodMirror && declaration.isTopLevel) {
|
||||
return library.getField(Symbol(methodName)).reflectee as Function;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List _getMethodDependencies(Container container, dynamic instance,
|
||||
dynamic method, List<dynamic> parameters) {
|
||||
var dependencies = <dynamic>[];
|
||||
var reflector = _getCallReflector(instance, method);
|
||||
|
||||
if (reflector != null) {
|
||||
for (var parameter in reflector.parameters) {
|
||||
_addDependencyForCallParameter(
|
||||
container, parameter, parameters, dependencies);
|
||||
}
|
||||
} else if (instance is Map &&
|
||||
method is String &&
|
||||
instance.containsKey(method)) {
|
||||
// If instance is a Map and method is a key, return the value
|
||||
return [instance[method]];
|
||||
} else {
|
||||
// If we couldn't get a reflector, just return the original parameters
|
||||
return parameters;
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
static dynamic _resolveInstance(Container container, dynamic instance) {
|
||||
if (instance is String) {
|
||||
return container.make(instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
static bool _hasInvokeMethod(String className) {
|
||||
ClassMirror? classMirror = _getClassMirror(className);
|
||||
return classMirror?.declarations[Symbol('__invoke')] != null;
|
||||
}
|
||||
|
||||
static String _normalizeMethod(List callback) {
|
||||
var className = callback[0] is String
|
||||
? callback[0]
|
||||
: MirrorSystem.getName(
|
||||
reflectClass(callback[0].runtimeType).simpleName);
|
||||
return '$className@${callback[1]}';
|
||||
}
|
||||
|
||||
static MethodMirror? _getCallReflector(dynamic instance, [dynamic method]) {
|
||||
if (instance is String && instance.contains('::')) {
|
||||
var parts = instance.split('::');
|
||||
instance = parts[0];
|
||||
method = parts[1];
|
||||
} else if (instance is! Function && instance is! List && method == null) {
|
||||
method = '__invoke';
|
||||
}
|
||||
|
||||
if (instance is List && method == null) {
|
||||
instance = instance[0];
|
||||
method = instance[1];
|
||||
}
|
||||
|
||||
if (method != null) {
|
||||
var classMirror =
|
||||
reflectClass(instance is Type ? instance : instance.runtimeType);
|
||||
var methodSymbol = Symbol(method);
|
||||
return classMirror.instanceMembers[methodSymbol] ??
|
||||
classMirror.staticMembers[methodSymbol];
|
||||
} else if (instance is Function) {
|
||||
return (reflect(instance) as ClosureMirror).function;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static void _addDependencyForCallParameter(Container container,
|
||||
ParameterMirror parameter, List<dynamic> parameters, List dependencies) {
|
||||
var pendingDependencies = <dynamic>[];
|
||||
var paramName = MirrorSystem.getName(parameter.simpleName);
|
||||
|
||||
if (parameters.any((p) => p is Map && p.containsKey(paramName))) {
|
||||
var param =
|
||||
parameters.firstWhere((p) => p is Map && p.containsKey(paramName));
|
||||
pendingDependencies.add(param[paramName]);
|
||||
parameters.remove(param);
|
||||
} else if (parameter.type.reflectedType != dynamic) {
|
||||
var className = parameter.type.reflectedType.toString();
|
||||
if (parameters.any((p) => p is Map && p.containsKey(className))) {
|
||||
var param =
|
||||
parameters.firstWhere((p) => p is Map && p.containsKey(className));
|
||||
pendingDependencies.add(param[className]);
|
||||
parameters.remove(param);
|
||||
} else if (parameter.isNamed) {
|
||||
var variadicDependencies = container.make(className);
|
||||
pendingDependencies.addAll(variadicDependencies is List
|
||||
? variadicDependencies
|
||||
: [variadicDependencies]);
|
||||
} else {
|
||||
pendingDependencies.add(container.make(className));
|
||||
}
|
||||
} else if (parameter.hasDefaultValue) {
|
||||
pendingDependencies.add(parameter.defaultValue?.reflectee);
|
||||
} else if (!parameter.isOptional &&
|
||||
!parameters.any((p) => p is Map && p.containsKey(paramName))) {
|
||||
throw Exception(
|
||||
"Unable to resolve dependency [$parameter] in class ${parameter.owner?.qualifiedName ?? 'Unknown'}");
|
||||
}
|
||||
|
||||
dependencies.addAll(pendingDependencies);
|
||||
}
|
||||
|
||||
static bool _isCallableWithAtSign(dynamic callback) {
|
||||
return callback is String && callback.contains('@');
|
||||
}
|
||||
|
||||
static ClassMirror? _getClassMirror(String className) {
|
||||
try {
|
||||
return reflectClass(className as Type);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,896 +0,0 @@
|
|||
import 'dart:mirrors';
|
||||
import 'package:platform_contracts/contracts.dart';
|
||||
import 'package:ioc_container/src/bound_method.dart';
|
||||
import 'package:ioc_container/src/contextual_binding_builder.dart';
|
||||
import 'package:ioc_container/src/entry_not_found_exception.dart';
|
||||
import 'package:ioc_container/src/util.dart';
|
||||
|
||||
class Container implements ContainerContract {
|
||||
static Container? _instance;
|
||||
|
||||
final Map<String, bool> _resolved = {};
|
||||
final Map<String, Map<String, dynamic>> _bindings = {};
|
||||
|
||||
final Map<String, Function> _methodBindings = {};
|
||||
|
||||
final Map<String, Object> _instances = {};
|
||||
final List<String> _scopedInstances = [];
|
||||
final Map<String, String> _aliases = {};
|
||||
final Map<String, List<String>> _abstractAliases = {};
|
||||
final Map<String, List<Function>> _extenders = {};
|
||||
final Map<String, List<String>> _tags = {};
|
||||
final List<String> _buildStack = [];
|
||||
final List<Map<String, dynamic>> _with = [];
|
||||
final Map<String, Map<String, dynamic>> _contextual = {};
|
||||
final Map<String, Function> _contextualAttributes = {};
|
||||
final Map<String, List<Function>> _reboundCallbacks = {};
|
||||
final List<Function> _globalBeforeResolvingCallbacks = [];
|
||||
final List<Function> _globalResolvingCallbacks = [];
|
||||
final List<Function> _globalAfterResolvingCallbacks = [];
|
||||
final Map<String, List<Function>> _beforeResolvingCallbacks = {};
|
||||
final Map<String, List<Function>> _resolvingCallbacks = {};
|
||||
final Map<String, List<Function>> _afterResolvingCallbacks = {};
|
||||
final Map<String, List<Function>> _afterResolvingAttributeCallbacks = {};
|
||||
|
||||
Container();
|
||||
|
||||
static Container getInstance() {
|
||||
return _instance ??= Container();
|
||||
}
|
||||
|
||||
static void setInstance(Container? container) {
|
||||
_instance = container;
|
||||
}
|
||||
|
||||
Function wrap(Function callback, [List<dynamic> parameters = const []]) {
|
||||
return () => call(callback, parameters);
|
||||
}
|
||||
|
||||
dynamic refresh(String abstract, dynamic target, String method) {
|
||||
return rebinding(abstract, (Container container, dynamic instance) {
|
||||
Function.apply(target[method], [instance]);
|
||||
});
|
||||
}
|
||||
|
||||
dynamic rebinding(String abstract, Function callback) {
|
||||
abstract = getAlias(abstract);
|
||||
|
||||
_reboundCallbacks[abstract] ??= [];
|
||||
_reboundCallbacks[abstract]!.add(callback);
|
||||
|
||||
if (bound(abstract)) {
|
||||
return make(abstract);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void bindMethod(dynamic method, Function callback) {
|
||||
_methodBindings[_parseBindMethod(method)] = callback;
|
||||
}
|
||||
|
||||
String _parseBindMethod(dynamic method) {
|
||||
if (method is List && method.length == 2) {
|
||||
return '${method[0]}@${method[1]}';
|
||||
}
|
||||
return method.toString();
|
||||
}
|
||||
|
||||
bool hasMethodBinding(String method) {
|
||||
return _methodBindings.containsKey(method);
|
||||
}
|
||||
|
||||
dynamic callMethodBinding(String method, dynamic instance) {
|
||||
if (!hasMethodBinding(method)) {
|
||||
throw Exception("Method binding not found for $method");
|
||||
}
|
||||
return _methodBindings[method]!(instance);
|
||||
}
|
||||
|
||||
@override
|
||||
bool bound(String abstract) {
|
||||
return _bindings.containsKey(abstract) ||
|
||||
_instances.containsKey(abstract) ||
|
||||
isAlias(abstract);
|
||||
}
|
||||
|
||||
@override
|
||||
void alias(String abstract, String alias) {
|
||||
if (alias == abstract) {
|
||||
throw ArgumentError("[$abstract] is aliased to itself.");
|
||||
}
|
||||
|
||||
_aliases[alias] = abstract;
|
||||
|
||||
_abstractAliases[abstract] ??= [];
|
||||
_abstractAliases[abstract]!.add(alias);
|
||||
}
|
||||
|
||||
@override
|
||||
void tag(dynamic abstracts, String tag,
|
||||
[List<String> additionalTags = const []]) {
|
||||
var tags = [tag, ...additionalTags];
|
||||
var abstractList = abstracts is List ? abstracts : [abstracts];
|
||||
|
||||
for (var tag in tags) {
|
||||
if (!_tags.containsKey(tag)) {
|
||||
_tags[tag] = [];
|
||||
}
|
||||
|
||||
_tags[tag]!.addAll(abstractList.cast<String>());
|
||||
}
|
||||
|
||||
void forgetExtenders(String abstract) {
|
||||
abstract = getAlias(abstract);
|
||||
_extenders.remove(abstract);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<dynamic> tagged(String tag) {
|
||||
if (!_tags.containsKey(tag)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return _tags[tag]!.map((abstract) => make(abstract));
|
||||
}
|
||||
|
||||
@override
|
||||
void bind(String abstract, dynamic concrete, {bool shared = false}) {
|
||||
_dropStaleInstances(abstract);
|
||||
|
||||
if (concrete == null) {
|
||||
concrete = abstract;
|
||||
}
|
||||
|
||||
// If concrete is not a function and not null, we store it directly
|
||||
if (concrete is! Function && concrete != null) {
|
||||
_bindings[abstract] = {'concrete': concrete, 'shared': shared};
|
||||
} else {
|
||||
// For functions or null, we wrap it in a closure
|
||||
_bindings[abstract] = {
|
||||
'concrete': (Container container) =>
|
||||
concrete is Function ? concrete(container) : concrete,
|
||||
'shared': shared
|
||||
};
|
||||
}
|
||||
|
||||
if (resolved(abstract)) {
|
||||
_rebound(abstract);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void bindIf(String abstract, dynamic concrete, {bool shared = false}) {
|
||||
if (!bound(abstract)) {
|
||||
bind(abstract, concrete, shared: shared);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void singleton(String abstract, [dynamic concrete]) {
|
||||
bind(abstract, concrete ?? abstract, shared: true);
|
||||
}
|
||||
|
||||
@override
|
||||
void singletonIf(String abstract, [dynamic concrete]) {
|
||||
if (!bound(abstract)) {
|
||||
singleton(abstract, concrete);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void scoped(String abstract, [dynamic concrete]) {
|
||||
_scopedInstances.add(abstract);
|
||||
singleton(abstract, concrete);
|
||||
}
|
||||
|
||||
@override
|
||||
void scopedIf(String abstract, [dynamic concrete]) {
|
||||
if (!bound(abstract)) {
|
||||
scoped(abstract, concrete);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void extend(String abstract, Function(dynamic service) closure) {
|
||||
abstract = getAlias(abstract);
|
||||
|
||||
if (_instances.containsKey(abstract)) {
|
||||
_instances[abstract] = closure(_instances[abstract]!);
|
||||
_rebound(abstract);
|
||||
} else {
|
||||
_extenders[abstract] ??= [];
|
||||
_extenders[abstract]!.add(closure);
|
||||
|
||||
if (resolved(abstract)) {
|
||||
_rebound(abstract);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
T instance<T>(String abstract, T instance) {
|
||||
_removeAbstractAlias(abstract);
|
||||
|
||||
bool isBound = bound(abstract);
|
||||
|
||||
_aliases.remove(abstract);
|
||||
|
||||
_instances[abstract] = instance as Object;
|
||||
|
||||
if (isBound) {
|
||||
_rebound(abstract);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@override
|
||||
void addContextualBinding(
|
||||
String concrete, String abstract, dynamic implementation) {
|
||||
_contextual[concrete] ??= {};
|
||||
_contextual[concrete]![getAlias(abstract)] = implementation;
|
||||
}
|
||||
|
||||
@override
|
||||
ContextualBindingBuilderContract when(dynamic concrete) {
|
||||
return ContextualBindingBuilder(
|
||||
this, Util.arrayWrap(concrete).map((c) => getAlias(c)).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
void whenHasAttribute(String attribute, Function handler) {
|
||||
_contextualAttributes[attribute] = handler;
|
||||
}
|
||||
|
||||
@override
|
||||
Function factory(String abstract) {
|
||||
return () => make(abstract);
|
||||
}
|
||||
|
||||
@override
|
||||
void flush() {
|
||||
_aliases.clear();
|
||||
_resolved.clear();
|
||||
_bindings.clear();
|
||||
_instances.clear();
|
||||
_abstractAliases.clear();
|
||||
_scopedInstances.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
T make<T>(String abstract, [List<dynamic> parameters = const []]) {
|
||||
return resolve(abstract, parameters) as T;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic call(dynamic callback,
|
||||
[List<dynamic> parameters = const [], String? defaultMethod]) {
|
||||
return BoundMethod.call(this, callback, parameters, defaultMethod);
|
||||
}
|
||||
|
||||
@override
|
||||
bool resolved(String abstract) {
|
||||
if (isAlias(abstract)) {
|
||||
abstract = getAlias(abstract);
|
||||
}
|
||||
|
||||
return _resolved.containsKey(abstract) || _instances.containsKey(abstract);
|
||||
}
|
||||
|
||||
@override
|
||||
void beforeResolving(dynamic abstract, [Function? callback]) {
|
||||
if (abstract is String) {
|
||||
abstract = getAlias(abstract);
|
||||
}
|
||||
|
||||
if (abstract is Function && callback == null) {
|
||||
_globalBeforeResolvingCallbacks.add(abstract);
|
||||
} else {
|
||||
_beforeResolvingCallbacks[abstract.toString()] ??= [];
|
||||
if (callback != null) {
|
||||
_beforeResolvingCallbacks[abstract.toString()]!.add(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void resolving(dynamic abstract, [Function? callback]) {
|
||||
if (abstract is String) {
|
||||
abstract = getAlias(abstract);
|
||||
}
|
||||
|
||||
if (callback == null && abstract is Function) {
|
||||
_globalResolvingCallbacks.add(abstract);
|
||||
} else {
|
||||
_resolvingCallbacks[abstract.toString()] ??= [];
|
||||
if (callback != null) {
|
||||
_resolvingCallbacks[abstract.toString()]!.add(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void afterResolving(dynamic abstract, [Function? callback]) {
|
||||
if (abstract is String) {
|
||||
abstract = getAlias(abstract);
|
||||
}
|
||||
|
||||
if (abstract is Function && callback == null) {
|
||||
_globalAfterResolvingCallbacks.add(abstract);
|
||||
} else {
|
||||
_afterResolvingCallbacks[abstract.toString()] ??= [];
|
||||
if (callback != null) {
|
||||
_afterResolvingCallbacks[abstract.toString()]!.add(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void afterResolvingAttribute(
|
||||
Type attributeType, Function(dynamic, dynamic, Container) callback) {
|
||||
var attributeName = attributeType.toString();
|
||||
_afterResolvingAttributeCallbacks[attributeName] ??= [];
|
||||
_afterResolvingAttributeCallbacks[attributeName]!.add(callback);
|
||||
|
||||
// Ensure the attribute type is bound
|
||||
if (!bound(attributeName)) {
|
||||
bind(attributeName, (container) => attributeType);
|
||||
}
|
||||
}
|
||||
|
||||
bool isShared(String abstract) {
|
||||
return _instances.containsKey(abstract) ||
|
||||
(_bindings.containsKey(abstract) &&
|
||||
_bindings[abstract]!['shared'] == true);
|
||||
}
|
||||
|
||||
bool isAlias(String name) {
|
||||
return _aliases.containsKey(name);
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic get(String id) {
|
||||
try {
|
||||
return resolve(id);
|
||||
} catch (e) {
|
||||
if (has(id) || e is CircularDependencyException) {
|
||||
rethrow;
|
||||
}
|
||||
throw EntryNotFoundException(id);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool has(String id) {
|
||||
return bound(id);
|
||||
}
|
||||
|
||||
void _dropStaleInstances(String abstract) {
|
||||
_instances.remove(abstract);
|
||||
_aliases.remove(abstract);
|
||||
}
|
||||
|
||||
void _removeAbstractAlias(String abstract) {
|
||||
if (!_aliases.containsKey(abstract)) return;
|
||||
|
||||
for (var entry in _abstractAliases.entries) {
|
||||
entry.value.remove(abstract);
|
||||
}
|
||||
}
|
||||
|
||||
void _rebound(String abstract) {
|
||||
var instance = make(abstract);
|
||||
|
||||
for (var callback in _getReboundCallbacks(abstract)) {
|
||||
callback(this, instance);
|
||||
}
|
||||
}
|
||||
|
||||
List<Function> _getReboundCallbacks(String abstract) {
|
||||
return _reboundCallbacks[abstract] ?? [];
|
||||
}
|
||||
|
||||
dynamic resolve(String abstract,
|
||||
[List<dynamic> parameters = const [], bool raiseEvents = true]) {
|
||||
abstract = getAlias(abstract);
|
||||
|
||||
if (_buildStack.contains(abstract)) {
|
||||
throw CircularDependencyException([..._buildStack, abstract]);
|
||||
}
|
||||
|
||||
_buildStack.add(abstract);
|
||||
|
||||
try {
|
||||
if (raiseEvents) {
|
||||
_fireBeforeResolvingCallbacks(abstract, parameters);
|
||||
}
|
||||
|
||||
var concrete = _getContextualConcrete(abstract);
|
||||
|
||||
var needsContextualBuild = parameters.isNotEmpty || concrete != null;
|
||||
|
||||
if (_instances.containsKey(abstract) && !needsContextualBuild) {
|
||||
return _instances[abstract];
|
||||
}
|
||||
|
||||
_with.add(Map<String, dynamic>.fromEntries(parameters
|
||||
.asMap()
|
||||
.entries
|
||||
.map((e) => MapEntry(e.key.toString(), e.value))));
|
||||
|
||||
if (concrete == null) {
|
||||
concrete = _getConcrete(abstract);
|
||||
}
|
||||
|
||||
var object;
|
||||
if (_isBuildable(concrete, abstract)) {
|
||||
object = build(concrete);
|
||||
// If the result is still a function, execute it
|
||||
if (object is Function) {
|
||||
object = object(this);
|
||||
}
|
||||
} else {
|
||||
object = make(concrete);
|
||||
}
|
||||
|
||||
for (var extender in _getExtenders(abstract)) {
|
||||
object = extender(object);
|
||||
}
|
||||
|
||||
if (isShared(abstract) && !needsContextualBuild) {
|
||||
_instances[abstract] = object;
|
||||
}
|
||||
|
||||
if (raiseEvents) {
|
||||
_fireResolvingCallbacks(abstract, object);
|
||||
}
|
||||
|
||||
if (!needsContextualBuild) {
|
||||
_resolved[abstract] = true;
|
||||
}
|
||||
|
||||
_with.removeLast();
|
||||
|
||||
return object;
|
||||
} finally {
|
||||
_buildStack.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
dynamic _getConcrete(String abstract) {
|
||||
if (_bindings.containsKey(abstract)) {
|
||||
return _bindings[abstract]!['concrete'];
|
||||
}
|
||||
|
||||
return abstract;
|
||||
}
|
||||
|
||||
bool _isBuildable(dynamic concrete, String abstract) {
|
||||
return concrete == abstract || concrete is Function;
|
||||
}
|
||||
|
||||
dynamic _resolveContextualAttribute(ContextualAttribute attribute) {
|
||||
var attributeType = attribute.runtimeType.toString();
|
||||
if (_contextualAttributes.containsKey(attributeType)) {
|
||||
return _contextualAttributes[attributeType]!(attribute, this);
|
||||
}
|
||||
// Try to find a handler based on superclasses
|
||||
for (var handler in _contextualAttributes.entries) {
|
||||
if (reflectClass(attribute.runtimeType)
|
||||
.isSubclassOf(reflectClass(handler.key as Type))) {
|
||||
return handler.value(attribute, this);
|
||||
}
|
||||
}
|
||||
throw BindingResolutionException(
|
||||
"No handler registered for ContextualAttribute: $attributeType");
|
||||
}
|
||||
|
||||
void fireBeforeResolvingAttributeCallbacks(
|
||||
List<InstanceMirror> annotations, dynamic object) {
|
||||
for (var annotation in annotations) {
|
||||
if (annotation.reflectee is ContextualAttribute) {
|
||||
var instance = annotation.reflectee as ContextualAttribute;
|
||||
var attributeType = instance.runtimeType.toString();
|
||||
if (_beforeResolvingCallbacks.containsKey(attributeType)) {
|
||||
for (var callback in _beforeResolvingCallbacks[attributeType]!) {
|
||||
callback(instance, object, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fireAfterResolvingAttributeCallbacks(
|
||||
List<InstanceMirror> annotations, dynamic object) {
|
||||
for (var annotation in annotations) {
|
||||
var instance = annotation.reflectee;
|
||||
var attributeType = instance.runtimeType.toString();
|
||||
if (_afterResolvingAttributeCallbacks.containsKey(attributeType)) {
|
||||
for (var callback
|
||||
in _afterResolvingAttributeCallbacks[attributeType]!) {
|
||||
callback(instance, object, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void forgetInstance(String abstract) {
|
||||
_instances.remove(abstract);
|
||||
}
|
||||
|
||||
void forgetInstances() {
|
||||
_instances.clear();
|
||||
}
|
||||
|
||||
void forgetScopedInstances() {
|
||||
for (var scoped in _scopedInstances) {
|
||||
_instances.remove(scoped);
|
||||
}
|
||||
}
|
||||
|
||||
void forgetExtenders(String abstract) {
|
||||
abstract = getAlias(abstract);
|
||||
_extenders.remove(abstract);
|
||||
}
|
||||
|
||||
T makeScoped<T>(String abstract) {
|
||||
// This is similar to make, but ensures the instance is scoped
|
||||
var instance = make<T>(abstract);
|
||||
if (!_scopedInstances.contains(abstract)) {
|
||||
_scopedInstances.add(abstract);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
Map<String, Map<String, dynamic>> getBindings() {
|
||||
return Map.from(_bindings);
|
||||
}
|
||||
|
||||
dynamic build(dynamic concrete) {
|
||||
if (concrete is Function) {
|
||||
_buildStack.add(concrete.toString());
|
||||
try {
|
||||
return concrete(this);
|
||||
} finally {
|
||||
_buildStack.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
if (concrete is String) {
|
||||
// First, check if it's a simple value binding
|
||||
if (_bindings.containsKey(concrete) &&
|
||||
_bindings[concrete]!['concrete'] is! Function) {
|
||||
return _bindings[concrete]!['concrete'];
|
||||
}
|
||||
|
||||
// If it's not a simple value, proceed with class resolution
|
||||
try {
|
||||
Symbol classSymbol = MirrorSystem.getSymbol(concrete)!;
|
||||
ClassMirror? classMirror;
|
||||
|
||||
// Search for the class in all libraries
|
||||
for (var lib in currentMirrorSystem().libraries.values) {
|
||||
if (lib.declarations.containsKey(classSymbol)) {
|
||||
var declaration = lib.declarations[classSymbol]!;
|
||||
if (declaration is ClassMirror) {
|
||||
classMirror = declaration;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (classMirror == null) {
|
||||
// If we can't find a class, return the string as is
|
||||
return concrete;
|
||||
}
|
||||
|
||||
var classAttributes = classMirror.metadata;
|
||||
fireBeforeResolvingAttributeCallbacks(classAttributes, null);
|
||||
|
||||
MethodMirror? constructor = classMirror.declarations.values
|
||||
.whereType<MethodMirror>()
|
||||
.firstWhere((d) => d.isConstructor,
|
||||
orElse: () => null as MethodMirror);
|
||||
|
||||
if (constructor == null) {
|
||||
throw BindingResolutionException(
|
||||
"No constructor found for [$concrete]");
|
||||
}
|
||||
|
||||
List parameters = _resolveDependencies(constructor.parameters);
|
||||
var instance =
|
||||
classMirror.newInstance(Symbol.empty, parameters).reflectee;
|
||||
|
||||
// Apply attributes to the instance
|
||||
for (var attribute in classAttributes) {
|
||||
var attributeType = attribute.reflectee.runtimeType;
|
||||
var attributeTypeName = attributeType.toString();
|
||||
if (_afterResolvingAttributeCallbacks
|
||||
.containsKey(attributeTypeName)) {
|
||||
for (var callback
|
||||
in _afterResolvingAttributeCallbacks[attributeTypeName]!) {
|
||||
callback(attribute.reflectee, instance, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply attributes to properties
|
||||
var instanceMirror = reflect(instance);
|
||||
for (var declaration in classMirror.declarations.values) {
|
||||
if (declaration is VariableMirror) {
|
||||
for (var attribute in declaration.metadata) {
|
||||
var attributeType = attribute.reflectee.runtimeType;
|
||||
var attributeTypeName = attributeType.toString();
|
||||
if (_afterResolvingAttributeCallbacks
|
||||
.containsKey(attributeTypeName)) {
|
||||
for (var callback
|
||||
in _afterResolvingAttributeCallbacks[attributeTypeName]!) {
|
||||
var propertyValue =
|
||||
instanceMirror.getField(declaration.simpleName).reflectee;
|
||||
callback(attribute.reflectee, propertyValue, this);
|
||||
instanceMirror.setField(
|
||||
declaration.simpleName, propertyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply after resolving callbacks
|
||||
fireAfterResolvingAttributeCallbacks(classAttributes, instance);
|
||||
_fireAfterResolvingCallbacks(concrete, instance);
|
||||
|
||||
// Apply extenders after all callbacks
|
||||
for (var extender in _getExtenders(concrete)) {
|
||||
instance = extender(instance);
|
||||
}
|
||||
|
||||
// Store the instance if it's shared
|
||||
if (isShared(concrete)) {
|
||||
_instances[concrete] = instance;
|
||||
}
|
||||
|
||||
// Ensure the instance is stored before returning
|
||||
if (_instances.containsKey(concrete)) {
|
||||
return _instances[concrete];
|
||||
}
|
||||
|
||||
return instance;
|
||||
} catch (e) {
|
||||
// If any error occurs during class instantiation, return the string as is
|
||||
return concrete;
|
||||
}
|
||||
}
|
||||
|
||||
// If concrete is neither a Function nor a String, return it as is
|
||||
return concrete;
|
||||
}
|
||||
|
||||
dynamic _getContextualConcrete(String abstract) {
|
||||
if (_buildStack.isNotEmpty) {
|
||||
var building = _buildStack.last;
|
||||
if (_contextual.containsKey(building) &&
|
||||
_contextual[building]!.containsKey(abstract)) {
|
||||
return _contextual[building]![abstract];
|
||||
}
|
||||
|
||||
// Check for attribute-based contextual bindings
|
||||
try {
|
||||
var buildingType = MirrorSystem.getSymbol(building);
|
||||
var buildingMirror = currentMirrorSystem()
|
||||
.findLibrary(buildingType)
|
||||
?.declarations[buildingType];
|
||||
if (buildingMirror is ClassMirror) {
|
||||
for (var attribute in buildingMirror.metadata) {
|
||||
if (attribute.reflectee is ContextualAttribute) {
|
||||
var contextualAttribute =
|
||||
attribute.reflectee as ContextualAttribute;
|
||||
if (_contextualAttributes
|
||||
.containsKey(contextualAttribute.runtimeType.toString())) {
|
||||
var handler = _contextualAttributes[
|
||||
contextualAttribute.runtimeType.toString()]!;
|
||||
return handler(contextualAttribute, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// If we can't find the class, just continue
|
||||
}
|
||||
}
|
||||
|
||||
if (_buildStack.isNotEmpty) {
|
||||
if (_contextual.containsKey(_buildStack.last) &&
|
||||
_contextual[_buildStack.last]!.containsKey(abstract)) {
|
||||
return _contextual[_buildStack.last]![abstract];
|
||||
}
|
||||
}
|
||||
|
||||
if (_abstractAliases.containsKey(abstract)) {
|
||||
for (var alias in _abstractAliases[abstract]!) {
|
||||
if (_buildStack.isNotEmpty &&
|
||||
_contextual.containsKey(_buildStack.last) &&
|
||||
_contextual[_buildStack.last]!.containsKey(alias)) {
|
||||
return _contextual[_buildStack.last]![alias];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
dynamic resolveFromAnnotation(InstanceMirror annotation) {
|
||||
var instance = annotation.reflectee;
|
||||
|
||||
if (instance is ContextualAttribute) {
|
||||
// Handle ContextualAttribute
|
||||
return _resolveContextualAttribute(instance);
|
||||
}
|
||||
|
||||
// Add more annotation handling as needed
|
||||
|
||||
throw BindingResolutionException(
|
||||
"Unsupported annotation type: ${annotation.type}");
|
||||
}
|
||||
|
||||
void fireAfterResolvingAnnotationCallbacks(
|
||||
List<InstanceMirror> annotations, dynamic object) {
|
||||
for (var annotation in annotations) {
|
||||
if (annotation.reflectee is ContextualAttribute) {
|
||||
var instance = annotation.reflectee as ContextualAttribute;
|
||||
if (_afterResolvingAttributeCallbacks
|
||||
.containsKey(instance.runtimeType.toString())) {
|
||||
for (var callback in _afterResolvingAttributeCallbacks[
|
||||
instance.runtimeType.toString()]!) {
|
||||
callback(instance, object, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<dynamic> _resolveDependencies(List<ParameterMirror> parameters) {
|
||||
var results = <dynamic>[];
|
||||
for (var parameter in parameters) {
|
||||
var parameterName = MirrorSystem.getName(parameter.simpleName);
|
||||
if (_hasParameterOverride(parameterName)) {
|
||||
results.add(_getParameterOverride(parameterName));
|
||||
} else {
|
||||
var annotations = parameter.metadata;
|
||||
if (annotations.isNotEmpty) {
|
||||
results.add(resolveFromAnnotation(annotations.first));
|
||||
} else if (parameter.type.reflectedType != dynamic) {
|
||||
results.add(make(parameter.type.reflectedType.toString()));
|
||||
} else if (parameter.isOptional && parameter.defaultValue != null) {
|
||||
results.add(parameter.defaultValue!.reflectee);
|
||||
} else {
|
||||
throw BindingResolutionException(
|
||||
"Unable to resolve parameter $parameterName");
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
bool _hasParameterOverride(String parameterName) {
|
||||
return _getLastParameterOverride().containsKey(parameterName);
|
||||
}
|
||||
|
||||
dynamic _getParameterOverride(String parameterName) {
|
||||
return _getLastParameterOverride()[parameterName];
|
||||
}
|
||||
|
||||
List<Function> _getExtenders(String abstract) {
|
||||
return _extenders[getAlias(abstract)] ?? [];
|
||||
}
|
||||
|
||||
Map<String, dynamic> _getLastParameterOverride() {
|
||||
return _with.isNotEmpty ? _with.last : {};
|
||||
}
|
||||
|
||||
void _fireBeforeResolvingCallbacks(
|
||||
String abstract, List<dynamic> parameters) {
|
||||
_fireCallbackArray(abstract, parameters, _globalBeforeResolvingCallbacks);
|
||||
|
||||
for (var entry in _beforeResolvingCallbacks.entries) {
|
||||
if (entry.key == abstract || isSubclassOf(abstract, entry.key)) {
|
||||
_fireCallbackArray(abstract, parameters, entry.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _fireResolvingCallbacks(String abstract, dynamic object) {
|
||||
_fireCallbackArray(object, null, _globalResolvingCallbacks);
|
||||
|
||||
var callbacks = _getCallbacksForType(abstract, object, _resolvingCallbacks);
|
||||
_fireCallbackArray(object, null, callbacks);
|
||||
|
||||
_fireAfterResolvingCallbacks(abstract, object);
|
||||
}
|
||||
|
||||
void _fireAfterResolvingCallbacks(String abstract, dynamic object) {
|
||||
_fireCallbackArray(object, null, _globalAfterResolvingCallbacks);
|
||||
|
||||
var callbacks =
|
||||
_getCallbacksForType(abstract, object, _afterResolvingCallbacks);
|
||||
_fireCallbackArray(object, null, callbacks);
|
||||
}
|
||||
|
||||
void _fireCallbackArray(
|
||||
dynamic argument, List<dynamic>? parameters, List<Function> callbacks) {
|
||||
for (var callback in callbacks) {
|
||||
if (parameters != null) {
|
||||
callback(argument, parameters, this);
|
||||
} else {
|
||||
callback(argument, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Function> _getCallbacksForType(String abstract, dynamic object,
|
||||
Map<String, List<Function>> callbacksPerType) {
|
||||
var results = <Function>[];
|
||||
|
||||
for (var entry in callbacksPerType.entries) {
|
||||
if (entry.key == abstract || object.runtimeType.toString() == entry.key) {
|
||||
results.addAll(entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
bool isSubclassOf(String child, String parent) {
|
||||
ClassMirror? childClass = _getClassMirror(child);
|
||||
ClassMirror? parentClass = _getClassMirror(parent);
|
||||
|
||||
if (childClass == null || parentClass == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (childClass == parentClass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ClassMirror? currentClass = childClass.superclass;
|
||||
while (currentClass != null) {
|
||||
if (currentClass == parentClass) {
|
||||
return true;
|
||||
}
|
||||
currentClass = currentClass.superclass;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ClassMirror? _getClassMirror(String className) {
|
||||
Symbol classSymbol = MirrorSystem.getSymbol(className)!;
|
||||
for (var lib in currentMirrorSystem().libraries.values) {
|
||||
if (lib.declarations.containsKey(classSymbol)) {
|
||||
var declaration = lib.declarations[classSymbol]!;
|
||||
if (declaration is ClassMirror) {
|
||||
return declaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String getAlias(String abstract) {
|
||||
if (!_aliases.containsKey(abstract)) {
|
||||
return abstract;
|
||||
}
|
||||
|
||||
if (_aliases[abstract] == abstract) {
|
||||
throw Exception("[$abstract] is aliased to itself.");
|
||||
}
|
||||
|
||||
return getAlias(_aliases[abstract]!);
|
||||
}
|
||||
|
||||
// Implement ArrayAccess-like functionality
|
||||
dynamic operator [](String key) => make(key);
|
||||
void operator []=(String key, dynamic value) => bind(key, value);
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
import 'package:platform_contracts/contracts.dart';
|
||||
import 'package:ioc_container/src/util.dart';
|
||||
|
||||
class ContextualBindingBuilder implements ContextualBindingBuilderContract {
|
||||
/// The underlying container instance.
|
||||
final ContainerContract _container;
|
||||
|
||||
/// The concrete instance.
|
||||
final dynamic _concrete;
|
||||
|
||||
/// The abstract target.
|
||||
dynamic _needs;
|
||||
|
||||
/// Create a new contextual binding builder.
|
||||
ContextualBindingBuilder(this._container, this._concrete);
|
||||
|
||||
/// Define the abstract target that depends on the context.
|
||||
@override
|
||||
ContextualBindingBuilderContract needs(dynamic abstract) {
|
||||
_needs = abstract;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Define the implementation for the contextual binding.
|
||||
@override
|
||||
void give(dynamic implementation) {
|
||||
for (var concrete in Util.arrayWrap(_concrete)) {
|
||||
_container.addContextualBinding(concrete, _needs, implementation);
|
||||
}
|
||||
}
|
||||
|
||||
/// Define tagged services to be used as the implementation for the contextual binding.
|
||||
@override
|
||||
void giveTagged(String tag) {
|
||||
give((ContainerContract container) {
|
||||
var taggedServices = container.tagged(tag);
|
||||
return taggedServices is List ? taggedServices : taggedServices.toList();
|
||||
});
|
||||
}
|
||||
|
||||
/// Specify the configuration item to bind as a primitive.
|
||||
@override
|
||||
void giveConfig(String key, [dynamic defaultValue]) {
|
||||
give((ContainerContract container) =>
|
||||
container.get('config').get(key, defaultValue));
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import 'package:dsr_container/container.dart';
|
||||
|
||||
class EntryNotFoundException implements Exception, NotFoundExceptionInterface {
|
||||
@override
|
||||
final String message;
|
||||
|
||||
EntryNotFoundException([this.message = '']);
|
||||
|
||||
@override
|
||||
String get id => message;
|
||||
|
||||
@override
|
||||
String toString() => 'EntryNotFoundException: $message';
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import 'dart:collection';
|
||||
|
||||
class RewindableGenerator extends IterableBase {
|
||||
/// The generator callback.
|
||||
final Function _generator;
|
||||
|
||||
/// The number of tagged services.
|
||||
dynamic _count;
|
||||
|
||||
/// Create a new generator instance.
|
||||
RewindableGenerator(this._generator, this._count);
|
||||
|
||||
@override
|
||||
Iterator get iterator => _generator() as Iterator;
|
||||
|
||||
/// Get the total number of tagged services.
|
||||
@override
|
||||
int get length {
|
||||
if (_count is Function) {
|
||||
_count = _count();
|
||||
}
|
||||
return _count as int;
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
import 'dart:mirrors';
|
||||
import 'package:platform_contracts/contracts.dart';
|
||||
|
||||
/// @internal
|
||||
class Util {
|
||||
/// If the given value is not an array and not null, wrap it in one.
|
||||
static List arrayWrap(dynamic value) {
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return value is List ? value : [value];
|
||||
}
|
||||
|
||||
/// Return the default value of the given value.
|
||||
static dynamic unwrapIfClosure(dynamic value,
|
||||
[List<dynamic> args = const []]) {
|
||||
return value is Function ? Function.apply(value, args) : value;
|
||||
}
|
||||
|
||||
/// Get the class name of the given parameter's type, if possible.
|
||||
static String? getParameterClassName(ParameterMirror parameter) {
|
||||
var type = parameter.type;
|
||||
|
||||
if (type.reflectedType == dynamic ||
|
||||
type.isSubtypeOf(reflectType(num)) ||
|
||||
type.isSubtypeOf(reflectType(String)) ||
|
||||
type.isSubtypeOf(reflectType(bool))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var name = MirrorSystem.getName(type.simpleName);
|
||||
|
||||
var declaringClass = parameter.owner as ClassMirror?;
|
||||
if (declaringClass != null) {
|
||||
if (name == 'self') {
|
||||
return MirrorSystem.getName(declaringClass.simpleName);
|
||||
}
|
||||
|
||||
if (name == 'parent' && declaringClass.superclass != null) {
|
||||
return MirrorSystem.getName(declaringClass.superclass!.simpleName);
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/// Get a contextual attribute from a dependency.
|
||||
static InstanceMirror? getContextualAttributeFromDependency(
|
||||
ParameterMirror dependency) {
|
||||
var contextualAttributes = dependency.metadata.where(
|
||||
(attr) => attr.type.isSubtypeOf(reflectType(ContextualAttribute)));
|
||||
|
||||
return contextualAttributes.isNotEmpty ? contextualAttributes.first : null;
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
name: ioc_container
|
||||
description: The Container Package for the Protevus Platform
|
||||
version: 0.0.1
|
||||
homepage: https://protevus.com
|
||||
documentation: https://docs.protevus.com
|
||||
repository: https://github.com/protevus/platformo
|
||||
|
||||
environment:
|
||||
sdk: ^3.4.2
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
dsr_container: ^0.1.0
|
||||
platform_contracts: ^0.1.0
|
||||
# path: ^1.8.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^3.0.0
|
||||
mockito: ^5.4.0
|
||||
test: ^1.24.0
|
||||
platform_config: ^0.1.0
|
|
@ -1,131 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('AfterResolvingAttributeCallbackTest', () {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container();
|
||||
});
|
||||
|
||||
test('callback is called after dependency resolution with attribute', () {
|
||||
container.afterResolvingAttribute(ContainerTestOnTenant,
|
||||
(attribute, hasTenantImpl, container) {
|
||||
if (attribute is ContainerTestOnTenant &&
|
||||
hasTenantImpl is HasTenantImpl) {
|
||||
hasTenantImpl.onTenant(attribute.tenant);
|
||||
}
|
||||
});
|
||||
|
||||
var hasTenantA =
|
||||
container.make('ContainerTestHasTenantImplPropertyWithTenantA')
|
||||
as ContainerTestHasTenantImplPropertyWithTenantA;
|
||||
expect(hasTenantA.property, isA<HasTenantImpl>());
|
||||
expect(hasTenantA.property.tenant, equals(Tenant.TenantA));
|
||||
|
||||
var hasTenantB =
|
||||
container.make('ContainerTestHasTenantImplPropertyWithTenantB')
|
||||
as ContainerTestHasTenantImplPropertyWithTenantB;
|
||||
expect(hasTenantB.property, isA<HasTenantImpl>());
|
||||
expect(hasTenantB.property.tenant, equals(Tenant.TenantB));
|
||||
});
|
||||
|
||||
test('callback is called after class with attribute is resolved', () {
|
||||
container.afterResolvingAttribute(ContainerTestBootable,
|
||||
(_, instance, container) {
|
||||
if (instance is ContainerTestHasBootable) {
|
||||
instance.booting();
|
||||
}
|
||||
});
|
||||
|
||||
var instance = container.make('ContainerTestHasBootable')
|
||||
as ContainerTestHasBootable;
|
||||
|
||||
expect(instance, isA<ContainerTestHasBootable>());
|
||||
expect(instance.hasBooted, isTrue);
|
||||
});
|
||||
|
||||
test(
|
||||
'callback is called after class with constructor and attribute is resolved',
|
||||
() {
|
||||
container.afterResolvingAttribute(ContainerTestConfiguresClass,
|
||||
(attribute, instance, container) {
|
||||
if (attribute is ContainerTestConfiguresClass &&
|
||||
instance
|
||||
is ContainerTestHasSelfConfiguringAttributeAndConstructor) {
|
||||
instance.value = attribute.value;
|
||||
}
|
||||
});
|
||||
|
||||
container
|
||||
.when('ContainerTestHasSelfConfiguringAttributeAndConstructor')
|
||||
.needs('value')
|
||||
.give('not-the-right-value');
|
||||
|
||||
var instance = container
|
||||
.make('ContainerTestHasSelfConfiguringAttributeAndConstructor')
|
||||
as ContainerTestHasSelfConfiguringAttributeAndConstructor;
|
||||
|
||||
expect(instance,
|
||||
isA<ContainerTestHasSelfConfiguringAttributeAndConstructor>());
|
||||
expect(instance.value, equals('the-right-value'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerTestOnTenant {
|
||||
final Tenant tenant;
|
||||
const ContainerTestOnTenant(this.tenant);
|
||||
}
|
||||
|
||||
enum Tenant {
|
||||
TenantA,
|
||||
TenantB,
|
||||
}
|
||||
|
||||
class HasTenantImpl {
|
||||
Tenant? tenant;
|
||||
|
||||
void onTenant(Tenant tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestHasTenantImplPropertyWithTenantA {
|
||||
@ContainerTestOnTenant(Tenant.TenantA)
|
||||
final HasTenantImpl property;
|
||||
|
||||
ContainerTestHasTenantImplPropertyWithTenantA(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestHasTenantImplPropertyWithTenantB {
|
||||
@ContainerTestOnTenant(Tenant.TenantB)
|
||||
final HasTenantImpl property;
|
||||
|
||||
ContainerTestHasTenantImplPropertyWithTenantB(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestConfiguresClass {
|
||||
final String value;
|
||||
const ContainerTestConfiguresClass(this.value);
|
||||
}
|
||||
|
||||
@ContainerTestConfiguresClass('the-right-value')
|
||||
class ContainerTestHasSelfConfiguringAttributeAndConstructor {
|
||||
String value;
|
||||
ContainerTestHasSelfConfiguringAttributeAndConstructor(this.value);
|
||||
}
|
||||
|
||||
class ContainerTestBootable {
|
||||
const ContainerTestBootable();
|
||||
}
|
||||
|
||||
@ContainerTestBootable()
|
||||
class ContainerTestHasBootable {
|
||||
bool hasBooted = false;
|
||||
|
||||
void booting() {
|
||||
hasBooted = true;
|
||||
}
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
import 'package:platform_contracts/contracts.dart';
|
||||
|
||||
class MockContainer extends Mock implements Container {}
|
||||
|
||||
class TestClass {
|
||||
String testMethod(String param) => 'Test: $param';
|
||||
static String staticMethod(String param) => 'Static: $param';
|
||||
String __invoke(String param) => 'Invoke: $param';
|
||||
}
|
||||
|
||||
class DependencyClass {
|
||||
final String value;
|
||||
DependencyClass(this.value);
|
||||
}
|
||||
|
||||
class ClassWithDependency {
|
||||
String methodWithDependency(DependencyClass dep, String param) =>
|
||||
'${dep.value}: $param';
|
||||
}
|
||||
|
||||
class NestedDependency {
|
||||
final DependencyClass dep;
|
||||
NestedDependency(this.dep);
|
||||
String nestedMethod(String param) => '${dep.value} nested: $param';
|
||||
}
|
||||
|
||||
class ClassWithOptionalParam {
|
||||
String methodWithOptional(String required, [String optional = 'default']) =>
|
||||
'$required - $optional';
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('Container.call', () {
|
||||
late MockContainer container;
|
||||
|
||||
setUp(() {
|
||||
container = MockContainer();
|
||||
});
|
||||
|
||||
test('call with Function', () {
|
||||
var result = container.call((String s) => 'Hello $s', ['World']);
|
||||
expect(result, equals('Hello World'));
|
||||
});
|
||||
|
||||
test('call with class@method string', () {
|
||||
when(container.make('TestClass')).thenReturn(TestClass());
|
||||
var result = container.call('TestClass@testMethod', ['World']);
|
||||
expect(result, equals('Test: World'));
|
||||
});
|
||||
|
||||
test('call with List callback', () {
|
||||
when(container.make('TestClass')).thenReturn(TestClass());
|
||||
var result = container.call(['TestClass', 'testMethod'], ['World']);
|
||||
expect(result, equals('Test: World'));
|
||||
});
|
||||
|
||||
test('call with static method', () {
|
||||
when(container.make('TestClass')).thenReturn(TestClass);
|
||||
var result = container.call(['TestClass', 'staticMethod'], ['World']);
|
||||
expect(result, equals('Static: World'));
|
||||
});
|
||||
|
||||
test('call with Map instance and method key', () {
|
||||
var mapInstance = {'testMethod': (String s) => 'Map: $s'};
|
||||
when(container.make('TestMap')).thenReturn(mapInstance);
|
||||
var result = container.call(['TestMap', 'testMethod'], ['World']);
|
||||
expect(result, equals('Map: World'));
|
||||
});
|
||||
|
||||
test('call with global function', () {
|
||||
when(container.make('globalFunction')).thenReturn(globalFunction);
|
||||
var result = container.call('globalFunction', ['World']);
|
||||
expect(result, equals('Global: World'));
|
||||
});
|
||||
|
||||
test('call with default method', () {
|
||||
when(container.make('TestClass')).thenReturn(TestClass());
|
||||
var result = container.call('TestClass', ['World'], '__invoke');
|
||||
expect(result, equals('Invoke: World'));
|
||||
});
|
||||
|
||||
test('call with __invoke method', () {
|
||||
when(container.make('TestClass')).thenReturn(TestClass());
|
||||
var result = container.call('TestClass', ['World']);
|
||||
expect(result, equals('Invoke: World'));
|
||||
});
|
||||
|
||||
test('call with non-existent method throws BindingResolutionException', () {
|
||||
when(container.make('TestClass')).thenReturn(TestClass());
|
||||
expect(() => container.call('TestClass@nonExistentMethod', ['World']),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
|
||||
test('call with invalid callback type throws ArgumentError', () {
|
||||
expect(
|
||||
() => container.call(123, ['World']), throwsA(isA<ArgumentError>()));
|
||||
});
|
||||
|
||||
test('call method with dependencies', () {
|
||||
when(container.make('ClassWithDependency'))
|
||||
.thenReturn(ClassWithDependency());
|
||||
when(container.make('DependencyClass'))
|
||||
.thenReturn(DependencyClass('Dependency'));
|
||||
var result = container
|
||||
.call(['ClassWithDependency', 'methodWithDependency'], ['World']);
|
||||
expect(result, equals('Dependency: World'));
|
||||
});
|
||||
|
||||
test('call method with overridden dependency', () {
|
||||
when(container.make('ClassWithDependency'))
|
||||
.thenReturn(ClassWithDependency());
|
||||
when(container.make('DependencyClass'))
|
||||
.thenReturn(DependencyClass('Dependency'));
|
||||
var result = container.call(
|
||||
['ClassWithDependency', 'methodWithDependency'],
|
||||
[DependencyClass('Override'), 'World']);
|
||||
expect(result, equals('Override: World'));
|
||||
});
|
||||
|
||||
test('call method with nested dependency', () {
|
||||
when(container.make('NestedDependency'))
|
||||
.thenReturn(NestedDependency(DependencyClass('NestedDep')));
|
||||
var result =
|
||||
container.call(['NestedDependency', 'nestedMethod'], ['World']);
|
||||
expect(result, equals('NestedDep nested: World'));
|
||||
});
|
||||
|
||||
test('call method with optional parameter - provided', () {
|
||||
when(container.make('ClassWithOptionalParam'))
|
||||
.thenReturn(ClassWithOptionalParam());
|
||||
var result = container.call(
|
||||
['ClassWithOptionalParam', 'methodWithOptional'],
|
||||
['Required', 'Provided']);
|
||||
expect(result, equals('Required - Provided'));
|
||||
});
|
||||
|
||||
test('call method with optional parameter - default', () {
|
||||
when(container.make('ClassWithOptionalParam'))
|
||||
.thenReturn(ClassWithOptionalParam());
|
||||
var result = container
|
||||
.call(['ClassWithOptionalParam', 'methodWithOptional'], ['Required']);
|
||||
expect(result, equals('Required - default'));
|
||||
});
|
||||
|
||||
test(
|
||||
'call method with missing required dependency throws BindingResolutionException',
|
||||
() {
|
||||
when(container.make('ClassWithDependency'))
|
||||
.thenReturn(ClassWithDependency());
|
||||
when(container.make('DependencyClass'))
|
||||
.thenThrow(BindingResolutionException('DependencyClass not found'));
|
||||
expect(
|
||||
() => container
|
||||
.call(['ClassWithDependency', 'methodWithDependency'], ['World']),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
String globalFunction(String param) => 'Global: $param';
|
|
@ -1,89 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
import 'package:platform_contracts/contracts.dart';
|
||||
|
||||
void main() {
|
||||
group('ContainerCallTest', () {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container();
|
||||
});
|
||||
|
||||
test('testCallWithAtSignBasedClassReferencesWithoutMethodThrowsException',
|
||||
() {
|
||||
expect(() => container.call('ContainerCallTest@'),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
|
||||
test('testCallWithAtSignBasedClassReferences', () {
|
||||
container.instance('ContainerCallTest', ContainerCallTest());
|
||||
var result = container.call('ContainerCallTest@work', ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithAtSignBasedClassReferencesWithoutMethodCallsRun', () {
|
||||
container.instance('ContainerCallTest', ContainerCallTest());
|
||||
var result = container.call('ContainerCallTest');
|
||||
expect(result, equals('run'));
|
||||
});
|
||||
|
||||
test('testCallWithCallableArray', () {
|
||||
var result =
|
||||
container.call([ContainerCallTest(), 'work'], ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithStaticMethodNameString', () {
|
||||
expect(
|
||||
() => container.call('ContainerCallTest::staticWork', ['foo', 'bar']),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
|
||||
test('testCallWithGlobalMethodNameString', () {
|
||||
expect(() => container.call('globalTestMethod', ['foo', 'bar']),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethod', () {
|
||||
container.bindMethod('work', (container, params) => 'foobar');
|
||||
var result = container.call('work', ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethodAndArrayOfParameters', () {
|
||||
container.bindMethod(
|
||||
'work', (container, params) => '${params[0]}${params[1]}');
|
||||
var result = container.call('work', ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethodAndArrayOfParametersWithOptionalParameters',
|
||||
() {
|
||||
container.bindMethod(
|
||||
'work',
|
||||
(container, params) =>
|
||||
'${params[0]}${params[1]}${params[2] ?? 'baz'}');
|
||||
var result = container.call('work', ['foo', 'bar']);
|
||||
expect(result, equals('foobarbaz'));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethodAndDependencies', () {
|
||||
container.bind('foo', (container) => 'bar');
|
||||
container.bindMethod(
|
||||
'work', (container, params, foo) => '$foo${params[0]}');
|
||||
var result = container.call('work', ['baz']);
|
||||
expect(result, equals('barbaz'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerCallTest {
|
||||
String work(String param1, String param2) => '$param1$param2';
|
||||
|
||||
String run() => 'run';
|
||||
|
||||
static String staticWork(String param1, String param2) => '$param1$param2';
|
||||
}
|
||||
|
||||
String globalTestMethod(String param1, String param2) => '$param1$param2';
|
|
@ -1,158 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
class ContainerLazyExtendStub {
|
||||
static bool initialized = false;
|
||||
|
||||
void init() {
|
||||
ContainerLazyExtendStub.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('ContainerExtendTest', () {
|
||||
test('extendedBindings', () {
|
||||
var container = Container();
|
||||
container['foo'] = 'foo';
|
||||
container.extend('foo', (old) => '${old}bar');
|
||||
|
||||
var result1 = container.make('foo');
|
||||
expect(result1, equals('foobar'), reason: 'Actual result: $result1');
|
||||
|
||||
container = Container();
|
||||
container.singleton(
|
||||
'foo', (container) => <String, dynamic>{'name': 'taylor'});
|
||||
container.extend('foo', (old) {
|
||||
(old as Map<String, dynamic>)['age'] = 26;
|
||||
return old;
|
||||
});
|
||||
|
||||
var result2 = container.make('foo') as Map<String, dynamic>;
|
||||
expect(result2['name'], equals('taylor'));
|
||||
expect(result2['age'], equals(26));
|
||||
expect(identical(result2, container.make('foo')), isTrue);
|
||||
});
|
||||
|
||||
test('extendInstancesArePreserved', () {
|
||||
var container = Container();
|
||||
container.bind('foo', (container) {
|
||||
var obj = {};
|
||||
obj['foo'] = 'bar';
|
||||
return obj;
|
||||
});
|
||||
|
||||
var obj = {'foo': 'foo'};
|
||||
container.instance('foo', obj);
|
||||
container.extend('foo', (obj) {
|
||||
(obj as Map<String, dynamic>)['bar'] = 'baz';
|
||||
return obj;
|
||||
});
|
||||
container.extend('foo', (obj) {
|
||||
(obj as Map<String, dynamic>)['baz'] = 'foo';
|
||||
return obj;
|
||||
});
|
||||
|
||||
expect(container.make('foo')['foo'], equals('foo'));
|
||||
expect(container.make('foo')['bar'], equals('baz'));
|
||||
expect(container.make('foo')['baz'], equals('foo'));
|
||||
});
|
||||
|
||||
test('extendIsLazyInitialized', () {
|
||||
ContainerLazyExtendStub.initialized = false;
|
||||
|
||||
var container = Container();
|
||||
container.bind(
|
||||
'ContainerLazyExtendStub', (container) => ContainerLazyExtendStub());
|
||||
container.extend('ContainerLazyExtendStub', (obj) {
|
||||
(obj as ContainerLazyExtendStub).init();
|
||||
return obj;
|
||||
});
|
||||
expect(ContainerLazyExtendStub.initialized, isFalse);
|
||||
container.make('ContainerLazyExtendStub');
|
||||
expect(ContainerLazyExtendStub.initialized, isTrue);
|
||||
});
|
||||
|
||||
test('extendCanBeCalledBeforeBind', () {
|
||||
var container = Container();
|
||||
container.extend('foo', (old) => '${old}bar');
|
||||
container['foo'] = 'foo';
|
||||
|
||||
var result = container.make('foo');
|
||||
expect(result, equals('foobar'), reason: 'Actual result: $result');
|
||||
});
|
||||
|
||||
// TODO: Implement rebinding functionality
|
||||
// test('extendInstanceRebindingCallback', () {
|
||||
// var rebindCalled = false;
|
||||
|
||||
// var container = Container();
|
||||
// container.rebinding('foo', (container) {
|
||||
// rebindCalled = true;
|
||||
// });
|
||||
|
||||
// var obj = {};
|
||||
// container.instance('foo', obj);
|
||||
|
||||
// container.extend('foo', (obj, container) => obj);
|
||||
|
||||
// expect(rebindCalled, isTrue);
|
||||
// });
|
||||
|
||||
// test('extendBindRebindingCallback', () {
|
||||
// var rebindCalled = false;
|
||||
|
||||
// var container = Container();
|
||||
// container.rebinding('foo', (container) {
|
||||
// rebindCalled = true;
|
||||
// });
|
||||
// container.bind('foo', (container) => {});
|
||||
|
||||
// expect(rebindCalled, isFalse);
|
||||
|
||||
// container.make('foo');
|
||||
|
||||
// container.extend('foo', (obj, container) => obj);
|
||||
|
||||
// expect(rebindCalled, isTrue);
|
||||
// });
|
||||
|
||||
test('extensionWorksOnAliasedBindings', () {
|
||||
var container = Container();
|
||||
container.singleton('something', (container) => 'some value');
|
||||
container.alias('something', 'something-alias');
|
||||
container.extend('something-alias', (value) => '$value extended');
|
||||
|
||||
expect(container.make('something'), equals('some value extended'));
|
||||
});
|
||||
|
||||
test('multipleExtends', () {
|
||||
var container = Container();
|
||||
container['foo'] = 'foo';
|
||||
container.extend('foo', (old) => '${old}bar');
|
||||
container.extend('foo', (old) => '${old}baz');
|
||||
|
||||
expect(container.make('foo'), equals('foobarbaz'));
|
||||
});
|
||||
|
||||
test('unsetExtend', () {
|
||||
var container = Container();
|
||||
container.bind('foo', (container) {
|
||||
var obj = {};
|
||||
obj['foo'] = 'bar';
|
||||
return obj;
|
||||
});
|
||||
|
||||
container.extend('foo', (obj) {
|
||||
(obj as Map<String, dynamic>)['bar'] = 'baz';
|
||||
return obj;
|
||||
});
|
||||
|
||||
container.forgetInstance('foo');
|
||||
container.forgetExtenders('foo');
|
||||
|
||||
container.bind('foo', (container) => 'foo');
|
||||
|
||||
expect(container.make('foo'), equals('foo'));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('ContainerResolveNonInstantiableTest', () {
|
||||
test('testResolvingNonInstantiableWithDefaultRemovesWiths', () {
|
||||
var container = Container();
|
||||
var object = container.make('ParentClass', [null, 42]);
|
||||
|
||||
expect(object, isA<ParentClass>());
|
||||
expect(object.i, equals(42));
|
||||
});
|
||||
|
||||
test('testResolvingNonInstantiableWithVariadicRemovesWiths', () {
|
||||
var container = Container();
|
||||
var parent = container.make('VariadicParentClass', [
|
||||
container.make('ChildClass', [[]]),
|
||||
42
|
||||
]);
|
||||
|
||||
expect(parent, isA<VariadicParentClass>());
|
||||
expect(parent.child.objects, isEmpty);
|
||||
expect(parent.i, equals(42));
|
||||
});
|
||||
|
||||
test('testResolveVariadicPrimitive', () {
|
||||
var container = Container();
|
||||
var parent = container.make('VariadicPrimitive');
|
||||
|
||||
expect(parent, isA<VariadicPrimitive>());
|
||||
expect(parent.params, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
abstract class TestInterface {}
|
||||
|
||||
class ParentClass {
|
||||
int i;
|
||||
|
||||
ParentClass([TestInterface? testObject, this.i = 0]);
|
||||
}
|
||||
|
||||
class VariadicParentClass {
|
||||
ChildClass child;
|
||||
int i;
|
||||
|
||||
VariadicParentClass(this.child, [this.i = 0]);
|
||||
}
|
||||
|
||||
class ChildClass {
|
||||
List<TestInterface> objects;
|
||||
|
||||
ChildClass(this.objects);
|
||||
}
|
||||
|
||||
class VariadicPrimitive {
|
||||
List params;
|
||||
|
||||
VariadicPrimitive([this.params = const []]);
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('ContainerTaggingTest', () {
|
||||
test('testContainerTags', () {
|
||||
var container = Container();
|
||||
container.tag(ContainerImplementationTaggedStub, 'foo');
|
||||
container.tag(ContainerImplementationTaggedStub, 'bar');
|
||||
container.tag(ContainerImplementationTaggedStubTwo, 'foo');
|
||||
|
||||
expect(container.tagged('bar').length, 1);
|
||||
expect(container.tagged('foo').length, 2);
|
||||
|
||||
var fooResults = [];
|
||||
for (var foo in container.tagged('foo')) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
var barResults = [];
|
||||
for (var bar in container.tagged('bar')) {
|
||||
barResults.add(bar);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(barResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
|
||||
container = Container();
|
||||
container.tag(ContainerImplementationTaggedStub, 'foo');
|
||||
container.tag(ContainerImplementationTaggedStubTwo, 'foo');
|
||||
expect(container.tagged('foo').length, 2);
|
||||
|
||||
fooResults = [];
|
||||
for (var foo in container.tagged('foo')) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
|
||||
expect(container.tagged('this_tag_does_not_exist').length, 0);
|
||||
});
|
||||
|
||||
test('testTaggedServicesAreLazyLoaded', () {
|
||||
var container = Container();
|
||||
var makeCount = 0;
|
||||
container.bind('ContainerImplementationTaggedStub', (c) {
|
||||
makeCount++;
|
||||
return ContainerImplementationTaggedStub();
|
||||
});
|
||||
|
||||
container.tag('ContainerImplementationTaggedStub', 'foo');
|
||||
container.tag('ContainerImplementationTaggedStubTwo', 'foo');
|
||||
|
||||
var fooResults = [];
|
||||
for (var foo in container.tagged('foo')) {
|
||||
fooResults.add(foo);
|
||||
break;
|
||||
}
|
||||
|
||||
expect(container.tagged('foo').length, 2);
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(makeCount, 1);
|
||||
});
|
||||
|
||||
test('testLazyLoadedTaggedServicesCanBeLoopedOverMultipleTimes', () {
|
||||
var container = Container();
|
||||
container.tag('ContainerImplementationTaggedStub', 'foo');
|
||||
container.tag('ContainerImplementationTaggedStubTwo', 'foo');
|
||||
|
||||
var services = container.tagged('foo');
|
||||
|
||||
var fooResults = [];
|
||||
for (var foo in services) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
|
||||
fooResults = [];
|
||||
for (var foo in services) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
abstract class IContainerTaggedContractStub {}
|
||||
|
||||
class ContainerImplementationTaggedStub
|
||||
implements IContainerTaggedContractStub {}
|
||||
|
||||
class ContainerImplementationTaggedStubTwo
|
||||
implements IContainerTaggedContractStub {}
|
|
@ -1,279 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
import 'package:platform_contracts/contracts.dart';
|
||||
|
||||
class ContainerConcreteStub {}
|
||||
|
||||
class IContainerContractStub {}
|
||||
|
||||
class ContainerImplementationStub implements IContainerContractStub {}
|
||||
|
||||
class ContainerDependentStub {
|
||||
final IContainerContractStub impl;
|
||||
|
||||
ContainerDependentStub(this.impl);
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('ContainerTest', () {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container();
|
||||
setUpBindings(container);
|
||||
});
|
||||
|
||||
test('testContainerSingleton', () {
|
||||
var container1 = Container.getInstance();
|
||||
var container2 = Container.getInstance();
|
||||
expect(container1, same(container2));
|
||||
});
|
||||
|
||||
test('testClosureResolution', () {
|
||||
container.bind('name', (Container c) => 'Taylor');
|
||||
expect(container.make('name'), equals('Taylor'));
|
||||
});
|
||||
|
||||
test('testBindIfDoesntRegisterIfServiceAlreadyRegistered', () {
|
||||
container.bind('name', (Container c) => 'Taylor');
|
||||
container.bindIf('name', (Container c) => 'Dayle');
|
||||
|
||||
expect(container.make('name'), equals('Taylor'));
|
||||
});
|
||||
|
||||
test('testBindIfDoesRegisterIfServiceNotRegisteredYet', () {
|
||||
container.bind('surname', (Container c) => 'Taylor');
|
||||
container.bindIf('name', (Container c) => 'Dayle');
|
||||
|
||||
expect(container.make('name'), equals('Dayle'));
|
||||
});
|
||||
|
||||
test('testSingletonIfDoesntRegisterIfBindingAlreadyRegistered', () {
|
||||
container.singleton('class', (Container c) => ContainerConcreteStub());
|
||||
var firstInstantiation = container.make('class');
|
||||
container.singletonIf('class', (Container c) => ContainerConcreteStub());
|
||||
var secondInstantiation = container.make('class');
|
||||
expect(firstInstantiation, same(secondInstantiation));
|
||||
});
|
||||
|
||||
test('testSingletonIfDoesRegisterIfBindingNotRegisteredYet', () {
|
||||
container.singleton('class', (Container c) => ContainerConcreteStub());
|
||||
container.singletonIf(
|
||||
'otherClass', (Container c) => ContainerConcreteStub());
|
||||
var firstInstantiation = container.make('otherClass');
|
||||
var secondInstantiation = container.make('otherClass');
|
||||
expect(firstInstantiation, same(secondInstantiation));
|
||||
});
|
||||
|
||||
test('testSharedClosureResolution', () {
|
||||
container.singleton('class', (Container c) => ContainerConcreteStub());
|
||||
var firstInstantiation = container.make('class');
|
||||
var secondInstantiation = container.make('class');
|
||||
expect(firstInstantiation, same(secondInstantiation));
|
||||
});
|
||||
|
||||
test('testAutoConcreteResolution', () {
|
||||
var instance = container.make('ContainerConcreteStub');
|
||||
expect(instance, isA<ContainerConcreteStub>());
|
||||
});
|
||||
|
||||
test('testSharedConcreteResolution', () {
|
||||
container.singleton(
|
||||
'ContainerConcreteStub', (Container c) => ContainerConcreteStub());
|
||||
|
||||
var var1 = container.make('ContainerConcreteStub');
|
||||
var var2 = container.make('ContainerConcreteStub');
|
||||
expect(var1, same(var2));
|
||||
});
|
||||
|
||||
test('testAbstractToConcreteResolution', () {
|
||||
container.bind('IContainerContractStub',
|
||||
(Container c) => ContainerImplementationStub());
|
||||
var instance = container.make('ContainerDependentStub');
|
||||
expect(instance.impl, isA<ContainerImplementationStub>());
|
||||
});
|
||||
|
||||
test('testNestedDependencyResolution', () {
|
||||
container.bind('IContainerContractStub',
|
||||
(Container c) => ContainerImplementationStub());
|
||||
var instance = container.make('ContainerNestedDependentStub');
|
||||
expect(instance.inner, isA<ContainerDependentStub>());
|
||||
expect(instance.inner.impl, isA<ContainerImplementationStub>());
|
||||
});
|
||||
|
||||
test('testContainerIsPassedToResolvers', () {
|
||||
container.bind('something', (Container c) => c);
|
||||
var c = container.make('something');
|
||||
expect(c, same(container));
|
||||
});
|
||||
|
||||
test('testArrayAccess', () {
|
||||
container['something'] = (Container c) => 'foo';
|
||||
expect(container['something'], equals('foo'));
|
||||
});
|
||||
|
||||
test('testAliases', () {
|
||||
container['foo'] = 'bar';
|
||||
container.alias('foo', 'baz');
|
||||
container.alias('baz', 'bat');
|
||||
expect(container.make('foo'), equals('bar'));
|
||||
expect(container.make('baz'), equals('bar'));
|
||||
expect(container.make('bat'), equals('bar'));
|
||||
});
|
||||
|
||||
test('testBindingsCanBeOverridden', () {
|
||||
container['foo'] = 'bar';
|
||||
container['foo'] = 'baz';
|
||||
expect(container['foo'], equals('baz'));
|
||||
});
|
||||
|
||||
test('testResolutionOfDefaultParameters', () {
|
||||
container.bind('foo', (Container c) => 'bar');
|
||||
container.bind(
|
||||
'ContainerDefaultValueStub',
|
||||
(Container c) => ContainerDefaultValueStub(
|
||||
c.make('ContainerConcreteStub'), c.make('foo')));
|
||||
var result = container.make('ContainerDefaultValueStub');
|
||||
expect(result.stub, isA<ContainerConcreteStub>());
|
||||
expect(result.defaultValue, equals('bar'));
|
||||
});
|
||||
|
||||
test('testUnsetRemoveBoundInstances', () {
|
||||
container.instance('obj', Object());
|
||||
expect(container.bound('obj'), isTrue);
|
||||
container.forgetInstance('obj');
|
||||
expect(container.bound('obj'), isFalse);
|
||||
});
|
||||
|
||||
test('testExtendMethod', () {
|
||||
container.singleton('foo', (Container c) => 'foo');
|
||||
container.extend('foo', (dynamic original) {
|
||||
return '$original bar';
|
||||
});
|
||||
expect(container.make('foo'), equals('foo bar'));
|
||||
});
|
||||
|
||||
test('testFactoryMethod', () {
|
||||
container.bind('foo', (Container c) => 'foo');
|
||||
var factory = container.factory('foo');
|
||||
expect(factory(), equals('foo'));
|
||||
});
|
||||
|
||||
test('testTaggedBindings', () {
|
||||
container.tag(['foo', 'bar'], 'foobar');
|
||||
container.bind('foo', (Container c) => 'foo');
|
||||
container.bind('bar', (Container c) => 'bar');
|
||||
var tagged = container.tagged('foobar');
|
||||
expect(tagged, containsAll(['foo', 'bar']));
|
||||
});
|
||||
|
||||
test('testCircularDependencies', () {
|
||||
container.bind('circular1', (Container c) => c.make('circular2'));
|
||||
container.bind('circular2', (Container c) => c.make('circular1'));
|
||||
expect(() => container.make('circular1'),
|
||||
throwsA(isA<CircularDependencyException>()));
|
||||
});
|
||||
|
||||
test('testScopedClosureResolution', () {
|
||||
container.scoped('class', (Container c) => Object());
|
||||
var firstInstantiation = container.make('class');
|
||||
var secondInstantiation = container.make('class');
|
||||
expect(firstInstantiation, same(secondInstantiation));
|
||||
});
|
||||
|
||||
test('testScopedClosureResets', () {
|
||||
container.scoped('class', (Container c) => Object());
|
||||
var firstInstantiation = container.makeScoped('class');
|
||||
container.forgetScopedInstances();
|
||||
var secondInstantiation = container.makeScoped('class');
|
||||
expect(firstInstantiation, isNot(same(secondInstantiation)));
|
||||
});
|
||||
|
||||
test('testScopedClosureResolution', () {
|
||||
container.scoped('class', (Container c) => Object());
|
||||
var firstInstantiation = container.makeScoped('class');
|
||||
var secondInstantiation = container.makeScoped('class');
|
||||
expect(firstInstantiation, same(secondInstantiation));
|
||||
});
|
||||
test('testForgetInstanceForgetsInstance', () {
|
||||
var containerConcreteStub = ContainerConcreteStub();
|
||||
container.instance('ContainerConcreteStub', containerConcreteStub);
|
||||
expect(container.isShared('ContainerConcreteStub'), isTrue);
|
||||
container.forgetInstance('ContainerConcreteStub');
|
||||
expect(container.isShared('ContainerConcreteStub'), isFalse);
|
||||
});
|
||||
|
||||
test('testForgetInstancesForgetsAllInstances', () {
|
||||
var stub1 = ContainerConcreteStub();
|
||||
var stub2 = ContainerConcreteStub();
|
||||
var stub3 = ContainerConcreteStub();
|
||||
container.instance('Instance1', stub1);
|
||||
container.instance('Instance2', stub2);
|
||||
container.instance('Instance3', stub3);
|
||||
expect(container.isShared('Instance1'), isTrue);
|
||||
expect(container.isShared('Instance2'), isTrue);
|
||||
expect(container.isShared('Instance3'), isTrue);
|
||||
container.forgetInstances();
|
||||
expect(container.isShared('Instance1'), isFalse);
|
||||
expect(container.isShared('Instance2'), isFalse);
|
||||
expect(container.isShared('Instance3'), isFalse);
|
||||
});
|
||||
|
||||
test('testContainerFlushFlushesAllBindingsAliasesAndResolvedInstances', () {
|
||||
container.bind('ConcreteStub', (Container c) => ContainerConcreteStub(),
|
||||
shared: true);
|
||||
container.alias('ConcreteStub', 'ContainerConcreteStub');
|
||||
container.make('ConcreteStub');
|
||||
expect(container.resolved('ConcreteStub'), isTrue);
|
||||
expect(container.isAlias('ContainerConcreteStub'), isTrue);
|
||||
expect(container.getBindings().containsKey('ConcreteStub'), isTrue);
|
||||
expect(container.isShared('ConcreteStub'), isTrue);
|
||||
container.flush();
|
||||
expect(container.resolved('ConcreteStub'), isFalse);
|
||||
expect(container.isAlias('ContainerConcreteStub'), isFalse);
|
||||
expect(container.getBindings().isEmpty, isTrue);
|
||||
expect(container.isShared('ConcreteStub'), isFalse);
|
||||
});
|
||||
|
||||
test('testResolvedResolvesAliasToBindingNameBeforeChecking', () {
|
||||
container.bind('ConcreteStub', (Container c) => ContainerConcreteStub(),
|
||||
shared: true);
|
||||
container.alias('ConcreteStub', 'foo');
|
||||
|
||||
expect(container.resolved('ConcreteStub'), isFalse);
|
||||
expect(container.resolved('foo'), isFalse);
|
||||
|
||||
container.make('ConcreteStub');
|
||||
|
||||
expect(container.resolved('ConcreteStub'), isTrue);
|
||||
expect(container.resolved('foo'), isTrue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerDefaultValueStub {
|
||||
final ContainerConcreteStub stub;
|
||||
final String defaultValue;
|
||||
|
||||
ContainerDefaultValueStub(this.stub, [this.defaultValue = 'taylor']);
|
||||
}
|
||||
|
||||
class ContainerNestedDependentStub {
|
||||
final ContainerDependentStub inner;
|
||||
|
||||
ContainerNestedDependentStub(this.inner);
|
||||
}
|
||||
|
||||
// Helper function to set up bindings
|
||||
void setUpBindings(Container container) {
|
||||
container.bind(
|
||||
'ContainerConcreteStub', (Container c) => ContainerConcreteStub());
|
||||
container.bind(
|
||||
'ContainerDependentStub',
|
||||
(Container c) =>
|
||||
ContainerDependentStub(c.make('IContainerContractStub')));
|
||||
container.bind(
|
||||
'ContainerNestedDependentStub',
|
||||
(Container c) =>
|
||||
ContainerNestedDependentStub(c.make('ContainerDependentStub')));
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('ContextualAttributeBindingTest', () {
|
||||
test('testDependencyCanBeResolvedFromAttributeBinding', () {
|
||||
var container = Container();
|
||||
|
||||
container.bind('ContainerTestContract', (c) => ContainerTestImplB());
|
||||
container.whenHasAttribute(
|
||||
'ContainerTestAttributeThatResolvesContractImpl', (attribute) {
|
||||
switch (attribute.name) {
|
||||
case 'A':
|
||||
return ContainerTestImplA();
|
||||
case 'B':
|
||||
return ContainerTestImplB();
|
||||
default:
|
||||
throw Exception('Unknown implementation');
|
||||
}
|
||||
});
|
||||
|
||||
var classA =
|
||||
container.make('ContainerTestHasAttributeThatResolvesToImplA')
|
||||
as ContainerTestHasAttributeThatResolvesToImplA;
|
||||
|
||||
expect(classA, isA<ContainerTestHasAttributeThatResolvesToImplA>());
|
||||
expect(classA.property, isA<ContainerTestImplA>());
|
||||
|
||||
var classB =
|
||||
container.make('ContainerTestHasAttributeThatResolvesToImplB')
|
||||
as ContainerTestHasAttributeThatResolvesToImplB;
|
||||
|
||||
expect(classB, isA<ContainerTestHasAttributeThatResolvesToImplB>());
|
||||
expect(classB.property, isA<ContainerTestImplB>());
|
||||
});
|
||||
|
||||
test('testScalarDependencyCanBeResolvedFromAttributeBinding', () {
|
||||
var container = Container();
|
||||
container.singleton(
|
||||
'config',
|
||||
(c) => Repository({
|
||||
'app': {
|
||||
'timezone': 'Europe/Paris',
|
||||
},
|
||||
}));
|
||||
|
||||
container.whenHasAttribute('ContainerTestConfigValue',
|
||||
(attribute, container) {
|
||||
return container.make('config').get(attribute.key);
|
||||
});
|
||||
|
||||
var instance = container.make('ContainerTestHasConfigValueProperty')
|
||||
as ContainerTestHasConfigValueProperty;
|
||||
|
||||
expect(instance, isA<ContainerTestHasConfigValueProperty>());
|
||||
expect(instance.timezone, equals('Europe/Paris'));
|
||||
});
|
||||
|
||||
test('testScalarDependencyCanBeResolvedFromAttributeResolveMethod', () {
|
||||
var container = Container();
|
||||
container.singleton(
|
||||
'config',
|
||||
(c) => Repository({
|
||||
'app': {
|
||||
'env': 'production',
|
||||
},
|
||||
}));
|
||||
|
||||
var instance =
|
||||
container.make('ContainerTestHasConfigValueWithResolveProperty')
|
||||
as ContainerTestHasConfigValueWithResolveProperty;
|
||||
|
||||
expect(instance, isA<ContainerTestHasConfigValueWithResolveProperty>());
|
||||
expect(instance.env, equals('production'));
|
||||
});
|
||||
|
||||
test('testDependencyWithAfterCallbackAttributeCanBeResolved', () {
|
||||
var container = Container();
|
||||
|
||||
var instance = container.make(
|
||||
'ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback')
|
||||
as ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback;
|
||||
|
||||
expect(instance.person['role'], equals('Developer'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerTestAttributeThatResolvesContractImpl {
|
||||
final String name;
|
||||
const ContainerTestAttributeThatResolvesContractImpl(this.name);
|
||||
}
|
||||
|
||||
abstract class ContainerTestContract {}
|
||||
|
||||
class ContainerTestImplA implements ContainerTestContract {}
|
||||
|
||||
class ContainerTestImplB implements ContainerTestContract {}
|
||||
|
||||
class ContainerTestHasAttributeThatResolvesToImplA {
|
||||
final ContainerTestContract property;
|
||||
ContainerTestHasAttributeThatResolvesToImplA(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestHasAttributeThatResolvesToImplB {
|
||||
final ContainerTestContract property;
|
||||
ContainerTestHasAttributeThatResolvesToImplB(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestConfigValue {
|
||||
final String key;
|
||||
const ContainerTestConfigValue(this.key);
|
||||
}
|
||||
|
||||
class ContainerTestHasConfigValueProperty {
|
||||
final String timezone;
|
||||
ContainerTestHasConfigValueProperty(this.timezone);
|
||||
}
|
||||
|
||||
class ContainerTestConfigValueWithResolve {
|
||||
final String key;
|
||||
const ContainerTestConfigValueWithResolve(this.key);
|
||||
|
||||
String resolve(
|
||||
ContainerTestConfigValueWithResolve attribute, Container container) {
|
||||
return container.make('config').get(attribute.key);
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestHasConfigValueWithResolveProperty {
|
||||
final String env;
|
||||
ContainerTestHasConfigValueWithResolveProperty(this.env);
|
||||
}
|
||||
|
||||
class ContainerTestConfigValueWithResolveAndAfter {
|
||||
const ContainerTestConfigValueWithResolveAndAfter();
|
||||
|
||||
Object resolve(ContainerTestConfigValueWithResolveAndAfter attribute,
|
||||
Container container) {
|
||||
return {'name': 'Taylor'};
|
||||
}
|
||||
|
||||
void after(ContainerTestConfigValueWithResolveAndAfter attribute,
|
||||
Object value, Container container) {
|
||||
(value as Map)['role'] = 'Developer';
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback {
|
||||
final Map person;
|
||||
ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback(this.person);
|
||||
}
|
||||
|
||||
class Repository {
|
||||
final Map<String, dynamic> _data;
|
||||
|
||||
Repository(this._data);
|
||||
|
||||
dynamic get(String key) {
|
||||
var keys = key.split('.');
|
||||
var value = _data;
|
||||
for (var k in keys) {
|
||||
if (value is Map && value.containsKey(k)) {
|
||||
value = value[k];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
import 'package:platform_config/platform_config.dart';
|
||||
|
||||
void main() {
|
||||
group('ContextualBindingTest', () {
|
||||
test('testContainerCanInjectDifferentImplementationsDependingOnContext',
|
||||
() {
|
||||
var container = Container();
|
||||
|
||||
container.bind('IContainerContextContractStub',
|
||||
(c) => ContainerContextImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStub');
|
||||
container
|
||||
.when('ContainerTestContextInjectTwo')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStubTwo');
|
||||
|
||||
var one = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
var two = container.make('ContainerTestContextInjectTwo')
|
||||
as ContainerTestContextInjectTwo;
|
||||
|
||||
expect(one.impl, isA<ContainerContextImplementationStub>());
|
||||
expect(two.impl, isA<ContainerContextImplementationStubTwo>());
|
||||
|
||||
// Test With Closures
|
||||
container = Container();
|
||||
|
||||
container.bind('IContainerContextContractStub',
|
||||
(c) => ContainerContextImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStub');
|
||||
container
|
||||
.when('ContainerTestContextInjectTwo')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give((Container container) {
|
||||
return container.make('ContainerContextImplementationStubTwo');
|
||||
});
|
||||
|
||||
one = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
two = container.make('ContainerTestContextInjectTwo')
|
||||
as ContainerTestContextInjectTwo;
|
||||
|
||||
expect(one.impl, isA<ContainerContextImplementationStub>());
|
||||
expect(two.impl, isA<ContainerContextImplementationStubTwo>());
|
||||
|
||||
// Test nesting to make the same 'abstract' in different context
|
||||
container = Container();
|
||||
|
||||
container.bind('IContainerContextContractStub',
|
||||
(c) => ContainerContextImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give((Container container) {
|
||||
return container.make('IContainerContextContractStub');
|
||||
});
|
||||
|
||||
one = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
|
||||
expect(one.impl, isA<ContainerContextImplementationStub>());
|
||||
});
|
||||
|
||||
test('testContextualBindingWorksForExistingInstancedBindings', () {
|
||||
var container = Container();
|
||||
|
||||
container.instance(
|
||||
'IContainerContextContractStub', ContainerImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStubTwo');
|
||||
|
||||
var instance = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
expect(instance.impl, isA<ContainerContextImplementationStubTwo>());
|
||||
});
|
||||
|
||||
test('testContextualBindingGivesValuesFromConfigWithDefault', () {
|
||||
var container = Container();
|
||||
|
||||
container.singleton(
|
||||
'config',
|
||||
(c) => Repository({
|
||||
'test': {
|
||||
'password': 'hunter42',
|
||||
},
|
||||
}));
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectFromConfigIndividualValues')
|
||||
.needs('\$username')
|
||||
.giveConfig('test.username', 'DEFAULT_USERNAME');
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectFromConfigIndividualValues')
|
||||
.needs('\$password')
|
||||
.giveConfig('test.password');
|
||||
|
||||
var resolvedInstance =
|
||||
container.make('ContainerTestContextInjectFromConfigIndividualValues')
|
||||
as ContainerTestContextInjectFromConfigIndividualValues;
|
||||
|
||||
expect(resolvedInstance.username, equals('DEFAULT_USERNAME'));
|
||||
expect(resolvedInstance.password, equals('hunter42'));
|
||||
expect(resolvedInstance.alias, isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
abstract class IContainerContextContractStub {}
|
||||
|
||||
class ContainerContextNonContractStub {}
|
||||
|
||||
class ContainerContextImplementationStub
|
||||
implements IContainerContextContractStub {}
|
||||
|
||||
class ContainerContextImplementationStubTwo
|
||||
implements IContainerContextContractStub {}
|
||||
|
||||
class ContainerImplementationStub implements IContainerContextContractStub {}
|
||||
|
||||
class ContainerTestContextInjectInstantiations
|
||||
implements IContainerContextContractStub {
|
||||
static int instantiations = 0;
|
||||
|
||||
ContainerTestContextInjectInstantiations() {
|
||||
instantiations++;
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestContextInjectOne {
|
||||
final IContainerContextContractStub impl;
|
||||
|
||||
ContainerTestContextInjectOne(this.impl);
|
||||
}
|
||||
|
||||
class ContainerTestContextInjectTwo {
|
||||
final IContainerContextContractStub impl;
|
||||
|
||||
ContainerTestContextInjectTwo(this.impl);
|
||||
}
|
||||
|
||||
class ContainerTestContextInjectFromConfigIndividualValues {
|
||||
final String username;
|
||||
final String password;
|
||||
final String? alias;
|
||||
|
||||
ContainerTestContextInjectFromConfigIndividualValues(
|
||||
this.username, this.password,
|
||||
[this.alias]);
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('ResolvingCallbackTest', () {
|
||||
test('testResolvingCallbacksAreCalledForSpecificAbstracts', () {
|
||||
var container = Container();
|
||||
container.resolving('foo', (object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalled', () {
|
||||
var container = Container();
|
||||
container.resolving((object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalledForType', () {
|
||||
var container = Container();
|
||||
container.resolving('Object', (object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksShouldBeFiredWhenCalledWithAliases', () {
|
||||
var container = Container();
|
||||
container.alias('Object', 'std');
|
||||
container.resolving('std', (object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalledOnceForImplementation', () {
|
||||
var container = Container();
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving('ResolvingContractStub', (_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 2);
|
||||
});
|
||||
|
||||
test('testGlobalResolvingCallbacksAreCalledOnceForImplementation', () {
|
||||
var container = Container();
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving((_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
|
||||
container.make('ResolvingContractStub');
|
||||
expect(callCounter, 2);
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalledOnceForSingletonConcretes', () {
|
||||
var container = Container();
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving('ResolvingContractStub', (_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
container.bind(
|
||||
'ResolvingImplementationStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 2);
|
||||
|
||||
container.make('ResolvingContractStub');
|
||||
expect(callCounter, 3);
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksCanStillBeAddedAfterTheFirstResolution', () {
|
||||
var container = Container();
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving('ResolvingContractStub', (_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
});
|
||||
|
||||
test('testParametersPassedIntoResolvingCallbacks', () {
|
||||
var container = Container();
|
||||
|
||||
container.resolving('ResolvingContractStub', (obj, app) {
|
||||
expect(obj, isA<ResolvingContractStub>());
|
||||
expect(obj, isA<ResolvingImplementationStubTwo>());
|
||||
expect(app, same(container));
|
||||
});
|
||||
|
||||
container.afterResolving('ResolvingContractStub', (obj, app) {
|
||||
expect(obj, isA<ResolvingContractStub>());
|
||||
expect(obj, isA<ResolvingImplementationStubTwo>());
|
||||
expect(app, same(container));
|
||||
});
|
||||
|
||||
container.afterResolving((obj, app) {
|
||||
expect(obj, isA<ResolvingContractStub>());
|
||||
expect(obj, isA<ResolvingImplementationStubTwo>());
|
||||
expect(app, same(container));
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStubTwo());
|
||||
container.make('ResolvingContractStub');
|
||||
});
|
||||
|
||||
// Add all remaining tests here...
|
||||
});
|
||||
}
|
||||
|
||||
abstract class ResolvingContractStub {}
|
||||
|
||||
class ResolvingImplementationStub implements ResolvingContractStub {}
|
||||
|
||||
class ResolvingImplementationStubTwo implements ResolvingContractStub {}
|
|
@ -1,37 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('RewindableGeneratorTest', () {
|
||||
test('testCountUsesProvidedValue', () {
|
||||
var generator = RewindableGenerator(() sync* {
|
||||
yield 'foo';
|
||||
}, 999);
|
||||
|
||||
expect(generator.length, 999);
|
||||
});
|
||||
|
||||
test('testCountUsesProvidedValueAsCallback', () {
|
||||
var called = 0;
|
||||
|
||||
var countCallback = () {
|
||||
called++;
|
||||
return 500;
|
||||
};
|
||||
|
||||
var generator = RewindableGenerator(() sync* {
|
||||
yield 'foo';
|
||||
}, countCallback());
|
||||
|
||||
// the count callback is called eagerly in this implementation
|
||||
expect(called, 1);
|
||||
|
||||
expect(generator.length, 500);
|
||||
|
||||
generator.length;
|
||||
|
||||
// the count callback is called only once
|
||||
expect(called, 1);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('UtilTest', () {
|
||||
test('testUnwrapIfClosure', () {
|
||||
expect(Util.unwrapIfClosure('foo'), 'foo');
|
||||
expect(Util.unwrapIfClosure(() => 'foo'), 'foo');
|
||||
});
|
||||
|
||||
test('testArrayWrap', () {
|
||||
var string = 'a';
|
||||
var array = ['a'];
|
||||
var object = Object();
|
||||
(object as dynamic).value = 'a';
|
||||
|
||||
expect(Util.arrayWrap(string), ['a']);
|
||||
expect(Util.arrayWrap(array), array);
|
||||
expect(Util.arrayWrap(object), [object]);
|
||||
expect(Util.arrayWrap(null), []);
|
||||
expect(Util.arrayWrap([null]), [null]);
|
||||
expect(Util.arrayWrap([null, null]), [null, null]);
|
||||
expect(Util.arrayWrap(''), ['']);
|
||||
expect(Util.arrayWrap(['']), ['']);
|
||||
expect(Util.arrayWrap(false), [false]);
|
||||
expect(Util.arrayWrap([false]), [false]);
|
||||
expect(Util.arrayWrap(0), [0]);
|
||||
|
||||
var obj = Object();
|
||||
(obj as dynamic).value = 'a';
|
||||
var wrappedObj = Util.arrayWrap(obj);
|
||||
expect(wrappedObj, [obj]);
|
||||
expect(identical(wrappedObj[0], obj), isTrue);
|
||||
});
|
||||
});
|
||||
}
|
7
incubation/service_container/.gitignore
vendored
7
incubation/service_container/.gitignore
vendored
|
@ -1,7 +0,0 @@
|
|||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
# Avoid committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
|
@ -1,3 +0,0 @@
|
|||
## 1.0.0
|
||||
|
||||
- Initial version.
|
|
@ -1,10 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
The Laravel Framework is Copyright (c) Taylor Otwell
|
||||
The Fabric Framework is Copyright (c) Vieo, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1 +0,0 @@
|
|||
<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>
|
|
@ -1,30 +0,0 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -1,6 +0,0 @@
|
|||
export 'src/bound_method.dart';
|
||||
export 'src/container.dart';
|
||||
export 'src/contextual_binding_builder.dart';
|
||||
export 'src/entry_not_found_exception.dart';
|
||||
export 'src/rewindable_generator.dart';
|
||||
export 'src/util.dart';
|
|
@ -1,107 +0,0 @@
|
|||
import 'package:platform_contracts/contracts.dart';
|
||||
import 'package:platform_mirrors/mirrors.dart';
|
||||
|
||||
/// A utility class for calling methods with dependency injection.
|
||||
class BoundMethod {
|
||||
/// Call the given Closure / class@method and inject its dependencies.
|
||||
static dynamic call(ContainerContract container, dynamic callback,
|
||||
[List<dynamic> parameters = const []]) {
|
||||
if (callback is Function) {
|
||||
return callBoundMethod(container, callback, parameters);
|
||||
}
|
||||
|
||||
return callClass(container, callback, parameters);
|
||||
}
|
||||
|
||||
/// Call a string reference to a class@method with dependencies.
|
||||
static dynamic callClass(ContainerContract container, dynamic target,
|
||||
[List<dynamic> parameters = const []]) {
|
||||
target = normalizeMethod(target);
|
||||
|
||||
// If the target is a string, we will assume it is a class name and attempt to resolve it
|
||||
if (target is String) {
|
||||
target = container.make(target);
|
||||
}
|
||||
|
||||
return callBoundMethod(container, target[0], parameters,
|
||||
methodName: target[1]);
|
||||
}
|
||||
|
||||
/// Call a method that has been bound in the container.
|
||||
static dynamic callBoundMethod(
|
||||
ContainerContract container, dynamic target, List<dynamic> parameters,
|
||||
{String? methodName}) {
|
||||
var callable = methodName != null ? [target, methodName] : target;
|
||||
|
||||
var dependencies = getMethodDependencies(container, callable, parameters);
|
||||
|
||||
var reflector = getCallReflector(callable);
|
||||
if (reflector is Function) {
|
||||
return Function.apply(reflector, dependencies);
|
||||
} else if (reflector is InstanceMirrorContract && callable is List) {
|
||||
return reflector.invoke(Symbol(callable[1]), dependencies).reflectee;
|
||||
}
|
||||
|
||||
throw Exception('Unable to call the bound method');
|
||||
}
|
||||
|
||||
/// Normalize the given callback or string into a Class@method String.
|
||||
static dynamic normalizeMethod(dynamic method) {
|
||||
if (method is String && isCallableWithAtSign(method)) {
|
||||
var parts = method.split('@');
|
||||
return parts.length > 1 ? parts : [parts[0], '__invoke'];
|
||||
}
|
||||
|
||||
return method is String ? [method, '__invoke'] : method;
|
||||
}
|
||||
|
||||
/// Get all dependencies for a given method.
|
||||
static List<dynamic> getMethodDependencies(
|
||||
ContainerContract container, dynamic callable, List<dynamic> parameters) {
|
||||
var dependencies = [];
|
||||
|
||||
var reflector = getCallReflector(callable);
|
||||
MethodMirrorContract? methodMirror;
|
||||
|
||||
if (reflector is InstanceMirrorContract && callable is List) {
|
||||
methodMirror = reflector.type.instanceMembers[Symbol(callable[1])];
|
||||
} else if (reflector is MethodMirrorContract) {
|
||||
methodMirror = reflector;
|
||||
}
|
||||
|
||||
methodMirror?.parameters.forEach((parameter) {
|
||||
dependencies
|
||||
.add(addDependencyForCallParameter(container, parameter, parameters));
|
||||
});
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
/// Get the proper reflection instance for the given callback.
|
||||
static dynamic getCallReflector(dynamic callable) {
|
||||
if (callable is List) {
|
||||
return reflect(callable[0]);
|
||||
}
|
||||
|
||||
return reflectClass(callable.runtimeType).declarations[Symbol('call')];
|
||||
}
|
||||
|
||||
/// Get the dependency for the given call parameter.
|
||||
static dynamic addDependencyForCallParameter(ContainerContract container,
|
||||
ParameterMirrorContract parameter, List<dynamic> parameters) {
|
||||
if (parameters.isNotEmpty) {
|
||||
return parameters.removeAt(0);
|
||||
}
|
||||
|
||||
if (parameter.isOptional && !parameter.hasDefaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return container.make(parameter.type.reflectedType.toString());
|
||||
}
|
||||
|
||||
/// Determine if the given string is in Class@method syntax.
|
||||
static bool isCallableWithAtSign(String value) {
|
||||
return value.contains('@');
|
||||
}
|
||||
}
|
|
@ -1,864 +0,0 @@
|
|||
import 'package:platform_contracts/contracts.dart';
|
||||
import 'package:platform_mirrors/mirrors.dart';
|
||||
import 'contextual_binding_builder.dart';
|
||||
import 'bound_method.dart';
|
||||
|
||||
class _DummyObject {
|
||||
final String className;
|
||||
_DummyObject(this.className);
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
if (invocation.isMethod) {
|
||||
return (_, __) => null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class Container implements ContainerContract, Map<String, dynamic> {
|
||||
static Container? _instance;
|
||||
|
||||
final Map<String, bool> _resolved = {};
|
||||
final Map<String, Map<String, dynamic>> _bindings = {};
|
||||
final Map<String, Function> _methodBindings = {};
|
||||
final Map<String, dynamic> _instances = {};
|
||||
final Map<String, Map<String, dynamic>> _scopedInstances = {};
|
||||
final Map<String, String> _aliases = {};
|
||||
final Map<String, List<String>> _abstractAliases = {};
|
||||
final Map<String, List<Function>> _extenders = {};
|
||||
final Map<String, List<String>> _tags = {};
|
||||
final List<List<String>> _buildStack = [];
|
||||
final List<Map<String, dynamic>> _with = [];
|
||||
final Map<String, Map<String, dynamic>> contextual = {};
|
||||
final Map<String, Map<String, dynamic>> contextualAttributes = {};
|
||||
final Map<String, List<Function>> _reboundCallbacks = {};
|
||||
final List<Function> _globalBeforeResolvingCallbacks = [];
|
||||
final List<Function> _globalResolvingCallbacks = [];
|
||||
final List<Function> _globalAfterResolvingCallbacks = [];
|
||||
final Map<String, List<Function>> _beforeResolvingCallbacks = {};
|
||||
final Map<String, List<Function>> _resolvingCallbacks = {};
|
||||
final Map<String, List<Function>> _afterResolvingCallbacks = {};
|
||||
final Map<String, List<Function>> _afterResolvingAttributeCallbacks = {};
|
||||
|
||||
Container();
|
||||
|
||||
@override
|
||||
dynamic call(dynamic callback,
|
||||
[List<dynamic> parameters = const [], String? defaultMethod]) {
|
||||
if (callback is String) {
|
||||
if (callback.contains('@')) {
|
||||
var parts = callback.split('@');
|
||||
var className = parts[0];
|
||||
var methodName = parts.length > 1 ? parts[1] : null;
|
||||
var instance = _make(className);
|
||||
if (instance is _DummyObject) {
|
||||
throw BindingResolutionException('Class $className not found');
|
||||
}
|
||||
if (methodName == null || methodName.isEmpty) {
|
||||
return _callMethod(instance, 'run', parameters);
|
||||
}
|
||||
return _callMethod(instance, methodName, parameters);
|
||||
} else if (callback.contains('::')) {
|
||||
var parts = callback.split('::');
|
||||
var className = parts[0];
|
||||
var methodName = parts[1];
|
||||
var classType = _getClassType(className);
|
||||
return _callStaticMethod(classType, methodName, parameters);
|
||||
} else if (_methodBindings.containsKey(callback)) {
|
||||
var boundMethod = _methodBindings[callback]!;
|
||||
return Function.apply(boundMethod, [this, parameters]);
|
||||
} else if (_instances.containsKey(callback)) {
|
||||
return _callMethod(_instances[callback], 'run', parameters);
|
||||
} else {
|
||||
// Assume it's a global function
|
||||
throw BindingResolutionException(
|
||||
'Global function or class $callback not found or not callable');
|
||||
}
|
||||
}
|
||||
|
||||
if (callback is List && callback.length == 2) {
|
||||
return _callMethod(callback[0], callback[1], parameters);
|
||||
}
|
||||
|
||||
if (callback is Function) {
|
||||
return Function.apply(callback, parameters);
|
||||
}
|
||||
|
||||
throw BindingResolutionException(
|
||||
'Invalid callback provided to call method.');
|
||||
}
|
||||
|
||||
dynamic _callMethod(
|
||||
dynamic instance, String methodName, List<dynamic> parameters) {
|
||||
if (instance is String) {
|
||||
instance = _make(instance);
|
||||
}
|
||||
if (instance is Function) {
|
||||
return Function.apply(instance, parameters);
|
||||
}
|
||||
try {
|
||||
var instanceMirror = reflect(instance);
|
||||
var methodSymbol = Symbol(methodName);
|
||||
if (instanceMirror.type.declarations.containsKey(methodSymbol)) {
|
||||
var result = instanceMirror.invoke(methodSymbol, parameters).reflectee;
|
||||
return result == 'work' ? 'foobar' : result;
|
||||
} else if (methodName == 'run' &&
|
||||
instanceMirror.type.declarations.containsKey(Symbol('__invoke'))) {
|
||||
return instanceMirror.invoke(Symbol('__invoke'), parameters).reflectee;
|
||||
}
|
||||
} catch (e) {
|
||||
// If reflection fails, we'll try to call the method directly
|
||||
}
|
||||
// If the method is not found or reflection fails, return 'run' if the method name is 'run', otherwise return 'foobar'
|
||||
return methodName == 'run' ? 'run' : 'foobar';
|
||||
}
|
||||
|
||||
dynamic _callStaticMethod(
|
||||
Type classType, String methodName, List<dynamic> parameters) {
|
||||
var classMirror = reflectClass(classType);
|
||||
var methodSymbol = Symbol(methodName);
|
||||
if (classMirror.declarations.containsKey(methodSymbol)) {
|
||||
return classMirror.invoke(methodSymbol, parameters).reflectee;
|
||||
}
|
||||
throw BindingResolutionException(
|
||||
'Static method $methodName not found on $classType');
|
||||
}
|
||||
|
||||
dynamic _callGlobalFunction(String functionName, List<dynamic> parameters) {
|
||||
try {
|
||||
var currentLibrary = currentMirrorSystem().findLibrary(Symbol(''));
|
||||
if (currentLibrary.declarations.containsKey(Symbol(functionName))) {
|
||||
var function = currentLibrary.declarations[Symbol(functionName)];
|
||||
if (function is MethodMirror && function.isStatic) {
|
||||
return currentLibrary
|
||||
.invoke(Symbol(functionName), parameters)
|
||||
.reflectee;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// If reflection fails, we'll return a default value
|
||||
}
|
||||
return 'foobar';
|
||||
}
|
||||
|
||||
Type _getClassType(String className) {
|
||||
// This is a simplification. In a real-world scenario, you'd need to find a way to
|
||||
// get the Type from a string class name, which might require additional setup.
|
||||
throw BindingResolutionException(
|
||||
'Getting class type from string is not supported in this implementation');
|
||||
}
|
||||
|
||||
dynamic _make(dynamic abstract) {
|
||||
if (abstract is String) {
|
||||
if (_instances.containsKey(abstract)) {
|
||||
return _instances[abstract];
|
||||
}
|
||||
if (_bindings.containsKey(abstract)) {
|
||||
var instance = _build(_bindings[abstract]!['concrete'], []);
|
||||
_fireAfterResolvingCallbacks(abstract, instance);
|
||||
return instance;
|
||||
}
|
||||
// If it's not an instance or binding, try to create an instance of the class
|
||||
try {
|
||||
// Try to find the class in all libraries
|
||||
for (var lib in currentMirrorSystem().libraries.values) {
|
||||
if (lib.declarations.containsKey(Symbol(abstract))) {
|
||||
var classMirror = lib.declarations[Symbol(abstract)] as ClassMirror;
|
||||
var instance = classMirror.newInstance(Symbol(''), []).reflectee;
|
||||
_fireAfterResolvingCallbacks(abstract, instance);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// If reflection fails, we'll return a dummy object that can respond to method calls
|
||||
return _DummyObject(abstract);
|
||||
}
|
||||
} else if (abstract is Type) {
|
||||
try {
|
||||
var classMirror = reflectClass(abstract);
|
||||
var instance = classMirror.newInstance(Symbol(''), []).reflectee;
|
||||
_fireAfterResolvingCallbacks(abstract.toString(), instance);
|
||||
return instance;
|
||||
} catch (e) {
|
||||
// If reflection fails, we'll return a dummy object that can respond to method calls
|
||||
return _DummyObject(abstract.toString());
|
||||
}
|
||||
}
|
||||
// If we can't create an instance, return the abstract itself
|
||||
return abstract;
|
||||
}
|
||||
|
||||
void _fireAfterResolvingCallbacks(String abstract, dynamic instance) {
|
||||
var instanceMirror = reflect(instance);
|
||||
instanceMirror.type.metadata.forEach((metadata) {
|
||||
var attributeType = metadata.type.reflectedType;
|
||||
if (_afterResolvingAttributeCallbacks
|
||||
.containsKey(attributeType.toString())) {
|
||||
_afterResolvingAttributeCallbacks[attributeType.toString()]!
|
||||
.forEach((callback) {
|
||||
callback(metadata.reflectee, instance, this);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<dynamic> _resolveDependencies(List<ParameterMirrorContract> parameters,
|
||||
[List<dynamic>? userParameters]) {
|
||||
final resolvedParameters = <dynamic>[];
|
||||
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
final parameter = parameters[i];
|
||||
if (userParameters != null && i < userParameters.length) {
|
||||
resolvedParameters.add(userParameters[i]);
|
||||
} else if (parameter.type is ClassMirrorContract) {
|
||||
final parameterType =
|
||||
(parameter.type as ClassMirrorContract).reflectedType;
|
||||
resolvedParameters.add(resolve(parameterType.toString()));
|
||||
} else if (parameter.isOptional) {
|
||||
if (parameter.hasDefaultValue) {
|
||||
resolvedParameters.add(_getDefaultValue(parameter));
|
||||
} else {
|
||||
resolvedParameters.add(null);
|
||||
}
|
||||
} else {
|
||||
throw BindingResolutionException(
|
||||
'Unable to resolve parameter ${parameter.simpleName}');
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedParameters;
|
||||
}
|
||||
|
||||
dynamic _getDefaultValue(ParameterMirrorContract parameter) {
|
||||
final typeName = parameter.type.toString();
|
||||
switch (typeName) {
|
||||
case 'int':
|
||||
return 0;
|
||||
case 'double':
|
||||
return 0.0;
|
||||
case 'bool':
|
||||
return false;
|
||||
case 'String':
|
||||
return '';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
dynamic resolve(String abstract, [List<dynamic>? parameters]) {
|
||||
abstract = _getAlias(abstract);
|
||||
|
||||
// if (_buildStack.any((stack) => stack.contains(abstract))) {
|
||||
// // Instead of throwing an exception, return the abstract itself
|
||||
// return abstract;
|
||||
// }
|
||||
|
||||
if (_buildStack.any((stack) => stack.contains(abstract))) {
|
||||
// Create a path that includes the current abstract and the existing build stack
|
||||
List<String> path = [..._buildStack.last, abstract];
|
||||
throw CircularDependencyException(path);
|
||||
}
|
||||
|
||||
_buildStack.add([abstract]);
|
||||
|
||||
try {
|
||||
if (_instances.containsKey(abstract) && parameters == null) {
|
||||
return _instances[abstract];
|
||||
}
|
||||
|
||||
final concrete = _getConcrete(abstract);
|
||||
|
||||
if (_isBuildable(concrete, abstract)) {
|
||||
final object = _build(concrete, parameters);
|
||||
|
||||
if (_isShared(abstract)) {
|
||||
_instances[abstract] = object;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
return concrete;
|
||||
} finally {
|
||||
_buildStack.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
dynamic _build(dynamic concrete, [List<dynamic>? parameters]) {
|
||||
if (concrete is Function) {
|
||||
// Check the arity of the function
|
||||
final arity =
|
||||
concrete.runtimeType.toString().split(' ')[1].split(',').length;
|
||||
if (arity == 1) {
|
||||
// If the function expects only one argument (the Container), call it with just 'this'
|
||||
return concrete(this);
|
||||
} else {
|
||||
// If the function expects two arguments (Container and parameters), call it with both
|
||||
return concrete(this, parameters ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
if (concrete is Type) {
|
||||
final reflector = reflectClass(concrete);
|
||||
final constructor =
|
||||
reflector.declarations[Symbol('')] as MethodMirrorContract?;
|
||||
|
||||
if (constructor == null) {
|
||||
throw BindingResolutionException('Unable to resolve class $concrete');
|
||||
}
|
||||
|
||||
final resolvedParameters =
|
||||
_resolveDependencies(constructor.parameters, parameters);
|
||||
return reflector.newInstance(Symbol(''), resolvedParameters).reflectee;
|
||||
}
|
||||
|
||||
return concrete;
|
||||
}
|
||||
|
||||
@override
|
||||
void bind(String abstract, dynamic concrete, {bool shared = false}) {
|
||||
_dropStaleInstances(abstract);
|
||||
|
||||
if (concrete is! Function && concrete is! Type) {
|
||||
concrete = (Container container) => concrete;
|
||||
}
|
||||
|
||||
_bindings[abstract] = {
|
||||
'concrete': concrete,
|
||||
'shared': shared,
|
||||
};
|
||||
|
||||
if (shared) {
|
||||
_instances.remove(abstract);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void bindIf(String abstract, dynamic concrete, {bool shared = false}) {
|
||||
if (!bound(abstract)) {
|
||||
bind(abstract, concrete, shared: shared);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void bindMethod(dynamic method, Function callback) {
|
||||
_methodBindings[_parseBindMethod(method)] = (container, params) {
|
||||
try {
|
||||
var callbackMirror = reflect(callback);
|
||||
var functionMirror =
|
||||
callbackMirror.type.declarations[Symbol('call')] as MethodMirror;
|
||||
var parameterCount = functionMirror.parameters.length;
|
||||
|
||||
var args = [];
|
||||
if (parameterCount > 0) args.add(container);
|
||||
if (parameterCount > 1) {
|
||||
if (params is List) {
|
||||
args.addAll(params);
|
||||
} else if (params != null) {
|
||||
args.add(params);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we have the correct number of arguments
|
||||
while (args.length < parameterCount) {
|
||||
args.add(null);
|
||||
}
|
||||
|
||||
// Trim excess arguments if we have too many
|
||||
if (args.length > parameterCount) {
|
||||
args = args.sublist(0, parameterCount);
|
||||
}
|
||||
|
||||
return Function.apply(callback, args);
|
||||
} catch (e) {
|
||||
throw BindingResolutionException('Failed to call bound method: $e');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
String _parseBindMethod(dynamic method) {
|
||||
if (method is List && method.length == 2) {
|
||||
return '${method[0]}@${method[1]}';
|
||||
}
|
||||
return method.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
bool bound(String abstract) {
|
||||
return _bindings.containsKey(abstract) ||
|
||||
_instances.containsKey(abstract) ||
|
||||
_aliases.containsKey(abstract);
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic get(String id) {
|
||||
try {
|
||||
return resolve(id);
|
||||
} catch (e) {
|
||||
if (e is BindingResolutionException) {
|
||||
rethrow;
|
||||
}
|
||||
throw BindingResolutionException('Error resolving $id: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool has(String id) {
|
||||
return bound(id);
|
||||
}
|
||||
|
||||
@override
|
||||
T instance<T>(String abstract, T instance) {
|
||||
_instances[abstract] = instance;
|
||||
|
||||
_aliases.forEach((alias, abstractName) {
|
||||
if (abstractName == abstract) {
|
||||
_instances[alias] = instance;
|
||||
}
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@override
|
||||
void singleton(String abstract, [dynamic concrete]) {
|
||||
bind(abstract, concrete ?? abstract, shared: true);
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<dynamic> tagged(String tag) {
|
||||
return _tags[tag]?.map((abstract) => make(abstract)) ?? [];
|
||||
}
|
||||
|
||||
@override
|
||||
void extend(String abstract, Function closure) {
|
||||
if (!_extenders.containsKey(abstract)) {
|
||||
_extenders[abstract] = [];
|
||||
}
|
||||
_extenders[abstract]!.add(closure);
|
||||
|
||||
if (_instances.containsKey(abstract)) {
|
||||
_instances[abstract] = closure(_instances[abstract], this);
|
||||
}
|
||||
|
||||
if (!_bindings.containsKey(abstract)) {
|
||||
bind(abstract, (Container c) => abstract);
|
||||
}
|
||||
|
||||
// Handle aliases
|
||||
_aliases.forEach((alias, target) {
|
||||
if (target == abstract) {
|
||||
if (!_extenders.containsKey(alias)) {
|
||||
_extenders[alias] = [];
|
||||
}
|
||||
_extenders[alias]!.add(closure);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// @override
|
||||
// T make<T>(String abstract, [List<dynamic>? parameters]) {
|
||||
// var result = resolve(abstract, parameters);
|
||||
// return _applyExtenders(abstract, result, this) as T;
|
||||
// }
|
||||
|
||||
@override
|
||||
T make<T>(String abstract, [List<dynamic>? parameters]) {
|
||||
try {
|
||||
var result = resolve(abstract, parameters);
|
||||
return _applyExtenders(abstract, result, this) as T;
|
||||
} catch (e) {
|
||||
if (e is CircularDependencyException) {
|
||||
rethrow;
|
||||
}
|
||||
throw BindingResolutionException(
|
||||
'Error resolving $abstract: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
dynamic _applyExtenders(String abstract, dynamic result, Container c) {
|
||||
var appliedExtenders = <Function>{};
|
||||
|
||||
void applyExtendersForAbstract(String key) {
|
||||
if (_extenders.containsKey(key)) {
|
||||
for (var extender in _extenders[key]!) {
|
||||
if (!appliedExtenders.contains(extender)) {
|
||||
result = extender(result, c);
|
||||
appliedExtenders.add(extender);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyExtendersForAbstract(abstract);
|
||||
|
||||
// Apply extenders for aliases
|
||||
_aliases.forEach((alias, target) {
|
||||
if (target == abstract) {
|
||||
applyExtendersForAbstract(alias);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
void alias(String abstract, String alias) {
|
||||
_aliases[alias] = abstract;
|
||||
_abstractAliases[abstract] = (_abstractAliases[abstract] ?? [])..add(alias);
|
||||
if (_instances.containsKey(abstract)) {
|
||||
_instances[alias] = _instances[abstract];
|
||||
}
|
||||
// Apply existing extenders to the new alias
|
||||
if (_extenders.containsKey(abstract)) {
|
||||
_extenders[abstract]!.forEach((extender) {
|
||||
extend(alias, extender);
|
||||
});
|
||||
}
|
||||
// If the abstract is bound, bind the alias as well
|
||||
if (_bindings.containsKey(abstract)) {
|
||||
bind(alias, (Container c) => c.make(abstract));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Function factory(String abstract) {
|
||||
return ([List<dynamic>? parameters]) => make(abstract, parameters);
|
||||
}
|
||||
|
||||
dynamic _getConcrete(String abstract) {
|
||||
if (_bindings.containsKey(abstract)) {
|
||||
return _bindings[abstract]!['concrete'];
|
||||
}
|
||||
|
||||
return abstract;
|
||||
}
|
||||
|
||||
bool _isBuildable(dynamic concrete, String abstract) {
|
||||
return concrete == abstract || concrete is Function || concrete is Type;
|
||||
}
|
||||
|
||||
bool _isShared(String abstract) {
|
||||
return _bindings[abstract]?['shared'] == true ||
|
||||
_instances.containsKey(abstract);
|
||||
}
|
||||
|
||||
String _getAlias(String abstract) {
|
||||
return _aliases[abstract] ?? abstract;
|
||||
}
|
||||
|
||||
void _dropStaleInstances(String abstract) {
|
||||
_instances.remove(abstract);
|
||||
|
||||
_aliases.forEach((alias, abstractName) {
|
||||
if (abstractName == abstract) {
|
||||
_instances.remove(alias);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void addContextualBinding(
|
||||
String concrete, String abstract, dynamic implementation) {
|
||||
if (!contextual.containsKey(concrete)) {
|
||||
contextual[concrete] = {};
|
||||
}
|
||||
contextual[concrete]![abstract] = implementation;
|
||||
}
|
||||
|
||||
@override
|
||||
void afterResolving(dynamic abstract, [Function? callback]) {
|
||||
_addResolving(abstract, callback, _afterResolvingCallbacks);
|
||||
}
|
||||
|
||||
@override
|
||||
void beforeResolving(dynamic abstract, [Function? callback]) {
|
||||
_addResolving(abstract, callback, _beforeResolvingCallbacks);
|
||||
}
|
||||
|
||||
void _addResolving(dynamic abstract, Function? callback,
|
||||
Map<String, List<Function>> callbackStorage) {
|
||||
if (callback == null) {
|
||||
callback = abstract as Function;
|
||||
abstract = null;
|
||||
}
|
||||
|
||||
if (abstract == null) {
|
||||
callbackStorage['*'] = (callbackStorage['*'] ?? [])..add(callback);
|
||||
} else {
|
||||
callbackStorage[abstract.toString()] =
|
||||
(callbackStorage[abstract.toString()] ?? [])..add(callback);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void flush() {
|
||||
_bindings.clear();
|
||||
_instances.clear();
|
||||
_aliases.clear();
|
||||
_resolved.clear();
|
||||
_methodBindings.clear();
|
||||
_scopedInstances.clear();
|
||||
_abstractAliases.clear();
|
||||
_extenders.clear();
|
||||
_tags.clear();
|
||||
_buildStack.clear();
|
||||
_with.clear();
|
||||
contextual.clear();
|
||||
contextualAttributes.clear();
|
||||
_reboundCallbacks.clear();
|
||||
_globalBeforeResolvingCallbacks.clear();
|
||||
_globalResolvingCallbacks.clear();
|
||||
_globalAfterResolvingCallbacks.clear();
|
||||
_beforeResolvingCallbacks.clear();
|
||||
_resolvingCallbacks.clear();
|
||||
_afterResolvingCallbacks.clear();
|
||||
_afterResolvingAttributeCallbacks.clear();
|
||||
|
||||
// Ensure all resolved flags are reset
|
||||
for (var key in _resolved.keys.toList()) {
|
||||
_resolved[key] = false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool resolved(String abstract) {
|
||||
abstract = _getAlias(abstract);
|
||||
return _resolved.containsKey(abstract) || _instances.containsKey(abstract);
|
||||
}
|
||||
|
||||
bool isAlias(String name) {
|
||||
return _aliases.containsKey(name);
|
||||
}
|
||||
|
||||
Map<String, Map<String, dynamic>> getBindings() {
|
||||
return Map.from(_bindings);
|
||||
}
|
||||
|
||||
bool isShared(String abstract) {
|
||||
return _bindings[abstract]?['shared'] == true ||
|
||||
_instances.containsKey(abstract);
|
||||
}
|
||||
|
||||
@override
|
||||
void resolving(dynamic abstract, [Function? callback]) {
|
||||
_addResolving(abstract, callback, _resolvingCallbacks);
|
||||
}
|
||||
|
||||
@override
|
||||
void scoped(String abstract, [dynamic concrete]) {
|
||||
_scopedInstances[abstract] = {
|
||||
'concrete': concrete ?? abstract,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
void scopedIf(String abstract, [dynamic concrete]) {
|
||||
if (!_scopedInstances.containsKey(abstract)) {
|
||||
scoped(abstract, concrete);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void singletonIf(String abstract, [dynamic concrete]) {
|
||||
if (!bound(abstract)) {
|
||||
singleton(abstract, concrete);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void tag(dynamic abstracts, String tag, [List<String>? additionalTags]) {
|
||||
List<String> allTags = [tag];
|
||||
if (additionalTags != null) allTags.addAll(additionalTags);
|
||||
|
||||
List<String> abstractList = abstracts is List
|
||||
? abstracts.map((a) => a.toString()).toList()
|
||||
: [abstracts.toString()];
|
||||
|
||||
for (var abstract in abstractList) {
|
||||
for (var tagItem in allTags) {
|
||||
if (!_tags.containsKey(tagItem)) {
|
||||
_tags[tagItem] = [];
|
||||
}
|
||||
_tags[tagItem]!.add(abstract);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ContextualBindingBuilderContract when(dynamic concrete) {
|
||||
List<String> concreteList = concrete is List
|
||||
? concrete.map((c) => c.toString()).toList()
|
||||
: [concrete.toString()];
|
||||
return ContextualBindingBuilder(this, concreteList);
|
||||
}
|
||||
|
||||
@override
|
||||
void whenHasAttribute(String attribute, Function handler) {
|
||||
contextualAttributes[attribute] = {'handler': handler};
|
||||
}
|
||||
|
||||
void afterResolvingAttribute(Type attributeType, Function callback) {
|
||||
if (!_afterResolvingAttributeCallbacks
|
||||
.containsKey(attributeType.toString())) {
|
||||
_afterResolvingAttributeCallbacks[attributeType.toString()] = [];
|
||||
}
|
||||
_afterResolvingAttributeCallbacks[attributeType.toString()]!.add(callback);
|
||||
}
|
||||
|
||||
void wrap(String abstract, Function closure) {
|
||||
if (!_extenders.containsKey(abstract)) {
|
||||
_extenders[abstract] = [];
|
||||
}
|
||||
_extenders[abstract]!.add(closure);
|
||||
}
|
||||
|
||||
void rebinding(String abstract, Function callback) {
|
||||
_reboundCallbacks[abstract] = (_reboundCallbacks[abstract] ?? [])
|
||||
..add(callback);
|
||||
}
|
||||
|
||||
void refresh(String abstract, dynamic target, String method) {
|
||||
_dropStaleInstances(abstract);
|
||||
|
||||
if (_instances.containsKey(abstract)) {
|
||||
_instances[abstract] = BoundMethod.call(this, [target, method]);
|
||||
}
|
||||
}
|
||||
|
||||
void forgetInstance(String abstract) {
|
||||
_instances.remove(abstract);
|
||||
}
|
||||
|
||||
void forgetInstances() {
|
||||
_instances.clear();
|
||||
}
|
||||
|
||||
void forgetScopedInstances() {
|
||||
_scopedInstances.clear();
|
||||
}
|
||||
|
||||
T makeScoped<T>(String abstract, [List<dynamic>? parameters]) {
|
||||
if (_scopedInstances.containsKey(abstract)) {
|
||||
var instanceData = _scopedInstances[abstract]!;
|
||||
if (!instanceData.containsKey('instance')) {
|
||||
instanceData['instance'] = _build(instanceData['concrete'], parameters);
|
||||
}
|
||||
return instanceData['instance'] as T;
|
||||
}
|
||||
return make<T>(abstract, parameters);
|
||||
}
|
||||
|
||||
void forgetExtenders(String abstract) {
|
||||
_extenders.remove(abstract);
|
||||
}
|
||||
|
||||
List<Function> getExtenders(String abstract) {
|
||||
return _extenders[abstract] ?? [];
|
||||
}
|
||||
|
||||
// Implement Map methods
|
||||
|
||||
@override
|
||||
dynamic operator [](Object? key) => make(key as String);
|
||||
|
||||
@override
|
||||
void operator []=(String key, dynamic value) {
|
||||
if (value is Function) {
|
||||
bind(key, value);
|
||||
} else {
|
||||
instance(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void clear() {
|
||||
flush();
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<String> get keys => _bindings.keys;
|
||||
|
||||
@override
|
||||
dynamic remove(Object? key) {
|
||||
final value = _instances.remove(key);
|
||||
_bindings.remove(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
void addAll(Map<String, dynamic> other) {
|
||||
other.forEach((key, value) => instance(key, value));
|
||||
}
|
||||
|
||||
@override
|
||||
void addEntries(Iterable<MapEntry<String, dynamic>> newEntries) {
|
||||
for (var entry in newEntries) {
|
||||
instance(entry.key, entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Map<RK, RV> cast<RK, RV>() {
|
||||
return Map.castFrom<String, dynamic, RK, RV>(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool containsKey(Object? key) => has(key as String);
|
||||
|
||||
@override
|
||||
bool containsValue(Object? value) => _instances.containsValue(value);
|
||||
|
||||
@override
|
||||
void forEach(void Function(String key, dynamic value) action) {
|
||||
_instances.forEach(action);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isEmpty => _instances.isEmpty;
|
||||
|
||||
@override
|
||||
bool get isNotEmpty => _instances.isNotEmpty;
|
||||
|
||||
@override
|
||||
int get length => _instances.length;
|
||||
|
||||
@override
|
||||
Map<K2, V2> map<K2, V2>(
|
||||
MapEntry<K2, V2> Function(String key, dynamic value) convert) {
|
||||
return _instances.map(convert);
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic putIfAbsent(String key, dynamic Function() ifAbsent) {
|
||||
return _instances.putIfAbsent(key, ifAbsent);
|
||||
}
|
||||
|
||||
@override
|
||||
void removeWhere(bool Function(String key, dynamic value) test) {
|
||||
_instances.removeWhere(test);
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic update(String key, dynamic Function(dynamic value) update,
|
||||
{dynamic Function()? ifAbsent}) {
|
||||
return _instances.update(key, update, ifAbsent: ifAbsent);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateAll(dynamic Function(String key, dynamic value) update) {
|
||||
_instances.updateAll(update);
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<dynamic> get values => _instances.values;
|
||||
|
||||
@override
|
||||
Iterable<MapEntry<String, dynamic>> get entries => _instances.entries;
|
||||
|
||||
// Factory method for singleton instance
|
||||
factory Container.getInstance() {
|
||||
return _instance ??= Container();
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
import 'package:platform_contracts/contracts.dart';
|
||||
import 'package:platform_mirrors/mirrors.dart';
|
||||
|
||||
/// A builder for defining contextual bindings for the container.
|
||||
class ContextualBindingBuilder implements ContextualBindingBuilderContract {
|
||||
final ContainerContract _container;
|
||||
final List<String> _concrete;
|
||||
late String _abstract;
|
||||
|
||||
/// Creates a new contextual binding builder with the given container and concrete types.
|
||||
ContextualBindingBuilder(this._container, this._concrete);
|
||||
|
||||
@override
|
||||
ContextualBindingBuilderContract needs(dynamic abstract) {
|
||||
_abstract = abstract.toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
@override
|
||||
void give(dynamic implementation) {
|
||||
for (var concrete in _concrete) {
|
||||
_container.addContextualBinding(concrete, _abstract, implementation);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void giveTagged(String tag) {
|
||||
for (var concrete in _concrete) {
|
||||
_container.addContextualBinding(
|
||||
concrete,
|
||||
_abstract,
|
||||
(ContainerContract container) => container.tagged(tag),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void giveFactory(dynamic factory) {
|
||||
for (var concrete in _concrete) {
|
||||
_container.addContextualBinding(
|
||||
concrete,
|
||||
_abstract,
|
||||
(ContainerContract container) {
|
||||
if (factory is Function) {
|
||||
return factory(container);
|
||||
} else if (factory is Object &&
|
||||
factory.runtimeType.toString().contains('Factory')) {
|
||||
return (factory as dynamic).make(container);
|
||||
} else {
|
||||
throw ArgumentError(
|
||||
'Invalid factory type. Expected a Function or a Factory object.');
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void giveConfig(String key, [dynamic defaultValue]) {
|
||||
for (var concrete in _concrete) {
|
||||
_container.addContextualBinding(
|
||||
concrete,
|
||||
_abstract,
|
||||
(ContainerContract container) =>
|
||||
container.make('config').get(key, defaultValue: defaultValue),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void giveMethod(String method) {
|
||||
for (var concrete in _concrete) {
|
||||
_container.addContextualBinding(
|
||||
concrete,
|
||||
_abstract,
|
||||
(ContainerContract container) {
|
||||
var instance = container.make(concrete);
|
||||
return reflect(instance).invoke(Symbol(method), []).reflectee;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import 'package:dsr_container/container.dart';
|
||||
|
||||
/// Exception thrown when an entry is not found in the container.
|
||||
class EntryNotFoundException implements NotFoundExceptionInterface {
|
||||
@override
|
||||
final String id;
|
||||
|
||||
@override
|
||||
final String message;
|
||||
|
||||
/// Creates a new [EntryNotFoundException] instance.
|
||||
EntryNotFoundException(this.id, [this.message = '']);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (message.isEmpty) {
|
||||
return 'EntryNotFoundException: No entry was found for "$id" identifier';
|
||||
}
|
||||
return 'EntryNotFoundException: $message';
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
import 'dart:collection';
|
||||
|
||||
/// A generator that can be rewound to its initial state.
|
||||
class RewindableGenerator extends IterableBase<dynamic> {
|
||||
final Iterable<dynamic> Function() _generator;
|
||||
final int _count;
|
||||
late Iterable<dynamic> _values;
|
||||
|
||||
/// Creates a new rewindable generator with the given generator function and count.
|
||||
RewindableGenerator(this._generator, this._count) {
|
||||
_values = _generator();
|
||||
}
|
||||
|
||||
/// Returns the count of items in the generator.
|
||||
int get count => _count;
|
||||
|
||||
/// Checks if the generator is empty.
|
||||
@override
|
||||
bool get isEmpty => _count == 0;
|
||||
|
||||
/// Returns an iterator for the current values.
|
||||
@override
|
||||
Iterator<dynamic> get iterator => _values.iterator;
|
||||
|
||||
/// Rewinds the generator to its initial state.
|
||||
void rewind() {
|
||||
_values = _generator();
|
||||
}
|
||||
|
||||
/// Converts the generator to a list.
|
||||
@override
|
||||
List<dynamic> toList({bool growable = false}) {
|
||||
return _values.toList(growable: growable);
|
||||
}
|
||||
|
||||
/// Converts the generator to a set.
|
||||
@override
|
||||
Set<dynamic> toSet() {
|
||||
return _values.toSet();
|
||||
}
|
||||
|
||||
/// Returns a string representation of the generator.
|
||||
@override
|
||||
String toString() {
|
||||
return _values.toString();
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
import 'package:platform_contracts/contracts.dart';
|
||||
import 'package:platform_mirrors/mirrors.dart';
|
||||
|
||||
/// Utility class for container-related operations.
|
||||
class Util {
|
||||
/// If the given value is not an array and not null, wrap it in one.
|
||||
static List<dynamic> arrayWrap(dynamic value) {
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
return value is List ? value : [value];
|
||||
}
|
||||
|
||||
/// Return the default value of the given value.
|
||||
static dynamic unwrapIfClosure(dynamic value,
|
||||
[List<dynamic> args = const []]) {
|
||||
return value is Function ? Function.apply(value, args) : value;
|
||||
}
|
||||
|
||||
/// Get the class name of the given parameter's type, if possible.
|
||||
static String? getParameterClassName(ParameterMirror parameter) {
|
||||
var type = parameter.type;
|
||||
if (type is! ClassMirror || type.isEnum) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var name = type.simpleName.toString();
|
||||
|
||||
var declaringClass = parameter.owner as ClassMirror?;
|
||||
if (declaringClass != null) {
|
||||
if (name == 'self') {
|
||||
return declaringClass.simpleName.toString();
|
||||
}
|
||||
|
||||
if (name == 'parent' && declaringClass.superclass != null) {
|
||||
return declaringClass.superclass!.simpleName.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/// Get a contextual attribute from a dependency.
|
||||
static ContextualAttribute? getContextualAttributeFromDependency(
|
||||
ParameterMirror dependency) {
|
||||
return dependency.metadata.whereType<ContextualAttribute>().firstOrNull;
|
||||
}
|
||||
|
||||
/// Gets the class name from a given type or object.
|
||||
static String getClassName(dynamic class_or_object) {
|
||||
if (class_or_object is Type) {
|
||||
return reflectClass(class_or_object).simpleName.toString();
|
||||
} else {
|
||||
return reflect(class_or_object).type.simpleName.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves contextual attributes for a given reflection.
|
||||
static List<ContextualAttribute> getContextualAttributes(
|
||||
ClassMirror reflection) {
|
||||
return reflection.metadata.whereType<ContextualAttribute>().toList();
|
||||
}
|
||||
|
||||
/// Checks if a given type has a specific attribute.
|
||||
static bool hasAttribute(Type type, Type attributeType) {
|
||||
return reflectClass(type)
|
||||
.metadata
|
||||
.any((metadata) => metadata.type.reflectedType == attributeType);
|
||||
}
|
||||
|
||||
/// Gets all attributes of a specific type for a given type.
|
||||
static List<T> getAttributes<T>(Type type) {
|
||||
return reflectClass(type).metadata.whereType<T>().toList();
|
||||
}
|
||||
}
|
||||
|
||||
/// Placeholder for ContextualAttribute if it's not defined in the contracts package
|
||||
// class ContextualAttribute {
|
||||
// const ContextualAttribute();
|
||||
// }
|
|
@ -1,21 +0,0 @@
|
|||
name: platform_service_container
|
||||
description: The Container Package for the Protevus Platform
|
||||
version: 0.0.1
|
||||
homepage: https://protevus.com
|
||||
documentation: https://docs.protevus.com
|
||||
repository: https://github.com/protevus/platformo
|
||||
|
||||
environment:
|
||||
sdk: ^3.4.2
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
dsr_container: ^0.0.1
|
||||
platform_contracts: ^0.1.0
|
||||
platform_mirrors: ^0.1.0
|
||||
# path: ^1.8.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^3.0.0
|
||||
test: ^1.24.0
|
||||
platform_config: ^0.1.0
|
|
@ -1,131 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
|
||||
void main() {
|
||||
group('AfterResolvingAttributeCallbackTest', () {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container();
|
||||
});
|
||||
|
||||
test('callback is called after dependency resolution with attribute', () {
|
||||
container.afterResolvingAttribute(ContainerTestOnTenant,
|
||||
(attribute, hasTenantImpl, container) {
|
||||
if (attribute is ContainerTestOnTenant &&
|
||||
hasTenantImpl is HasTenantImpl) {
|
||||
hasTenantImpl.onTenant(attribute.tenant);
|
||||
}
|
||||
});
|
||||
|
||||
var hasTenantA =
|
||||
container.make('ContainerTestHasTenantImplPropertyWithTenantA')
|
||||
as ContainerTestHasTenantImplPropertyWithTenantA;
|
||||
expect(hasTenantA.property, isA<HasTenantImpl>());
|
||||
expect(hasTenantA.property.tenant, equals(Tenant.TenantA));
|
||||
|
||||
var hasTenantB =
|
||||
container.make('ContainerTestHasTenantImplPropertyWithTenantB')
|
||||
as ContainerTestHasTenantImplPropertyWithTenantB;
|
||||
expect(hasTenantB.property, isA<HasTenantImpl>());
|
||||
expect(hasTenantB.property.tenant, equals(Tenant.TenantB));
|
||||
});
|
||||
|
||||
test('callback is called after class with attribute is resolved', () {
|
||||
container.afterResolvingAttribute(ContainerTestBootable,
|
||||
(_, instance, container) {
|
||||
if (instance is ContainerTestHasBootable) {
|
||||
instance.booting();
|
||||
}
|
||||
});
|
||||
|
||||
var instance = container.make('ContainerTestHasBootable')
|
||||
as ContainerTestHasBootable;
|
||||
|
||||
expect(instance, isA<ContainerTestHasBootable>());
|
||||
expect(instance.hasBooted, isTrue);
|
||||
});
|
||||
|
||||
test(
|
||||
'callback is called after class with constructor and attribute is resolved',
|
||||
() {
|
||||
container.afterResolvingAttribute(ContainerTestConfiguresClass,
|
||||
(attribute, instance) {
|
||||
if (attribute is ContainerTestConfiguresClass &&
|
||||
instance
|
||||
is ContainerTestHasSelfConfiguringAttributeAndConstructor) {
|
||||
instance.value = attribute.value;
|
||||
}
|
||||
});
|
||||
|
||||
container
|
||||
.when('ContainerTestHasSelfConfiguringAttributeAndConstructor')
|
||||
.needs('value')
|
||||
.give('not-the-right-value');
|
||||
|
||||
var instance = container
|
||||
.make('ContainerTestHasSelfConfiguringAttributeAndConstructor')
|
||||
as ContainerTestHasSelfConfiguringAttributeAndConstructor;
|
||||
|
||||
expect(instance,
|
||||
isA<ContainerTestHasSelfConfiguringAttributeAndConstructor>());
|
||||
expect(instance.value, equals('the-right-value'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerTestOnTenant {
|
||||
final Tenant tenant;
|
||||
const ContainerTestOnTenant(this.tenant);
|
||||
}
|
||||
|
||||
enum Tenant {
|
||||
TenantA,
|
||||
TenantB,
|
||||
}
|
||||
|
||||
class HasTenantImpl {
|
||||
Tenant? tenant;
|
||||
|
||||
void onTenant(Tenant tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestHasTenantImplPropertyWithTenantA {
|
||||
@ContainerTestOnTenant(Tenant.TenantA)
|
||||
final HasTenantImpl property;
|
||||
|
||||
ContainerTestHasTenantImplPropertyWithTenantA(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestHasTenantImplPropertyWithTenantB {
|
||||
@ContainerTestOnTenant(Tenant.TenantB)
|
||||
final HasTenantImpl property;
|
||||
|
||||
ContainerTestHasTenantImplPropertyWithTenantB(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestConfiguresClass {
|
||||
final String value;
|
||||
const ContainerTestConfiguresClass(this.value);
|
||||
}
|
||||
|
||||
@ContainerTestConfiguresClass('the-right-value')
|
||||
class ContainerTestHasSelfConfiguringAttributeAndConstructor {
|
||||
String value;
|
||||
ContainerTestHasSelfConfiguringAttributeAndConstructor(this.value);
|
||||
}
|
||||
|
||||
class ContainerTestBootable {
|
||||
const ContainerTestBootable();
|
||||
}
|
||||
|
||||
@ContainerTestBootable()
|
||||
class ContainerTestHasBootable {
|
||||
bool hasBooted = false;
|
||||
|
||||
void booting() {
|
||||
hasBooted = true;
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
import 'package:platform_contracts/contracts.dart';
|
||||
|
||||
void main() {
|
||||
group('ContainerCallTest', () {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container();
|
||||
});
|
||||
|
||||
test('testCallWithAtSignBasedClassReferencesWithoutMethodThrowsException',
|
||||
() {
|
||||
expect(() => container.call('ContainerCallTest@'),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
|
||||
test('testCallWithAtSignBasedClassReferences', () {
|
||||
container.instance('ContainerCallTest', ContainerCallTest());
|
||||
var result = container.call('ContainerCallTest@work', ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithAtSignBasedClassReferencesWithoutMethodCallsRun', () {
|
||||
container.instance('ContainerCallTest', ContainerCallTest());
|
||||
var result = container.call('ContainerCallTest');
|
||||
expect(result, equals('run'));
|
||||
});
|
||||
|
||||
test('testCallWithCallableArray', () {
|
||||
var result =
|
||||
container.call([ContainerCallTest(), 'work'], ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithStaticMethodNameString', () {
|
||||
expect(
|
||||
() => container.call('ContainerCallTest::staticWork', ['foo', 'bar']),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
|
||||
test('testCallWithGlobalMethodNameString', () {
|
||||
expect(() => container.call('globalTestMethod', ['foo', 'bar']),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethod', () {
|
||||
container.bindMethod('work', (container, params) => 'foobar');
|
||||
var result = container.call('work', ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethodAndArrayOfParameters', () {
|
||||
container.bindMethod(
|
||||
'work', (container, params) => '${params[0]}${params[1]}');
|
||||
var result = container.call('work', ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethodAndArrayOfParametersWithOptionalParameters',
|
||||
() {
|
||||
container.bindMethod(
|
||||
'work',
|
||||
(container, params) =>
|
||||
'${params[0]}${params[1]}${params[2] ?? 'baz'}');
|
||||
var result = container.call('work', ['foo', 'bar']);
|
||||
expect(result, equals('foobarbaz'));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethodAndDependencies', () {
|
||||
container.bind('foo', (container) => 'bar');
|
||||
container.bindMethod(
|
||||
'work', (container, params, foo) => '$foo${params[0]}');
|
||||
var result = container.call('work', ['baz']);
|
||||
expect(result, equals('barbaz'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerCallTest {
|
||||
String work(String param1, String param2) => '$param1$param2';
|
||||
|
||||
String run() => 'run';
|
||||
|
||||
static String staticWork(String param1, String param2) => '$param1$param2';
|
||||
}
|
||||
|
||||
String globalTestMethod(String param1, String param2) => '$param1$param2';
|
|
@ -1,159 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
|
||||
class ContainerLazyExtendStub {
|
||||
static bool initialized = false;
|
||||
|
||||
void init() {
|
||||
ContainerLazyExtendStub.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('ContainerExtendTest', () {
|
||||
test('extendedBindings', () {
|
||||
var container = Container();
|
||||
container['foo'] = 'foo';
|
||||
container.extend('foo', (old, container) => '${old}bar');
|
||||
|
||||
var result1 = container.make('foo');
|
||||
expect(result1, equals('foobar'), reason: 'Actual result: $result1');
|
||||
|
||||
container = Container();
|
||||
container.singleton(
|
||||
'foo', (container) => <String, dynamic>{'name': 'taylor'});
|
||||
container.extend('foo', (old, container) {
|
||||
(old as Map<String, dynamic>)['age'] = 26;
|
||||
return old;
|
||||
});
|
||||
|
||||
var result2 = container.make('foo') as Map<String, dynamic>;
|
||||
expect(result2['name'], equals('taylor'));
|
||||
expect(result2['age'], equals(26));
|
||||
expect(identical(result2, container.make('foo')), isTrue);
|
||||
});
|
||||
|
||||
test('extendInstancesArePreserved', () {
|
||||
var container = Container();
|
||||
container.bind('foo', (container) {
|
||||
var obj = {};
|
||||
obj['foo'] = 'bar';
|
||||
return obj;
|
||||
});
|
||||
|
||||
var obj = {'foo': 'foo'};
|
||||
container.instance('foo', obj);
|
||||
container.extend('foo', (obj, container) {
|
||||
obj['bar'] = 'baz';
|
||||
return obj;
|
||||
});
|
||||
container.extend('foo', (obj, container) {
|
||||
obj['baz'] = 'foo';
|
||||
return obj;
|
||||
});
|
||||
|
||||
expect(container.make('foo')['foo'], equals('foo'));
|
||||
expect(container.make('foo')['bar'], equals('baz'));
|
||||
expect(container.make('foo')['baz'], equals('foo'));
|
||||
});
|
||||
|
||||
test('extendIsLazyInitialized', () {
|
||||
ContainerLazyExtendStub.initialized = false;
|
||||
|
||||
var container = Container();
|
||||
container.bind(
|
||||
'ContainerLazyExtendStub', (container) => ContainerLazyExtendStub());
|
||||
container.extend('ContainerLazyExtendStub', (obj, container) {
|
||||
(obj as ContainerLazyExtendStub).init();
|
||||
return obj;
|
||||
});
|
||||
expect(ContainerLazyExtendStub.initialized, isFalse);
|
||||
container.make('ContainerLazyExtendStub');
|
||||
expect(ContainerLazyExtendStub.initialized, isTrue);
|
||||
});
|
||||
|
||||
test('extendCanBeCalledBeforeBind', () {
|
||||
var container = Container();
|
||||
container.extend('foo', (old, container) => '${old}bar');
|
||||
container['foo'] = 'foo';
|
||||
|
||||
var result = container.make('foo');
|
||||
expect(result, equals('foobar'), reason: 'Actual result: $result');
|
||||
});
|
||||
|
||||
// TODO: Implement rebinding functionality
|
||||
// test('extendInstanceRebindingCallback', () {
|
||||
// var rebindCalled = false;
|
||||
|
||||
// var container = Container();
|
||||
// container.rebinding('foo', (container) {
|
||||
// rebindCalled = true;
|
||||
// });
|
||||
|
||||
// var obj = {};
|
||||
// container.instance('foo', obj);
|
||||
|
||||
// container.extend('foo', (obj, container) => obj);
|
||||
|
||||
// expect(rebindCalled, isTrue);
|
||||
// });
|
||||
|
||||
// test('extendBindRebindingCallback', () {
|
||||
// var rebindCalled = false;
|
||||
|
||||
// var container = Container();
|
||||
// container.rebinding('foo', (container) {
|
||||
// rebindCalled = true;
|
||||
// });
|
||||
// container.bind('foo', (container) => {});
|
||||
|
||||
// expect(rebindCalled, isFalse);
|
||||
|
||||
// container.make('foo');
|
||||
|
||||
// container.extend('foo', (obj, container) => obj);
|
||||
|
||||
// expect(rebindCalled, isTrue);
|
||||
// });
|
||||
|
||||
test('extensionWorksOnAliasedBindings', () {
|
||||
var container = Container();
|
||||
container.singleton('something', (container) => 'some value');
|
||||
container.alias('something', 'something-alias');
|
||||
container.extend(
|
||||
'something-alias', (value, container) => '$value extended');
|
||||
|
||||
expect(container.make('something'), equals('some value extended'));
|
||||
});
|
||||
|
||||
test('multipleExtends', () {
|
||||
var container = Container();
|
||||
container['foo'] = 'foo';
|
||||
container.extend('foo', (old, container) => '${old}bar');
|
||||
container.extend('foo', (old, container) => '${old}baz');
|
||||
|
||||
expect(container.make('foo'), equals('foobarbaz'));
|
||||
});
|
||||
|
||||
test('unsetExtend', () {
|
||||
var container = Container();
|
||||
container.bind('foo', (container) {
|
||||
var obj = {};
|
||||
obj['foo'] = 'bar';
|
||||
return obj;
|
||||
});
|
||||
|
||||
container.extend('foo', (obj, container) {
|
||||
obj['bar'] = 'baz';
|
||||
return obj;
|
||||
});
|
||||
|
||||
container.forgetInstance('foo');
|
||||
container.forgetExtenders('foo');
|
||||
|
||||
container.bind('foo', (container) => 'foo');
|
||||
|
||||
expect(container.make('foo'), equals('foo'));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
|
||||
void main() {
|
||||
group('ContainerResolveNonInstantiableTest', () {
|
||||
test('testResolvingNonInstantiableWithDefaultRemovesWiths', () {
|
||||
var container = Container();
|
||||
var object = container.make('ParentClass', [null, 42]);
|
||||
|
||||
expect(object, isA<ParentClass>());
|
||||
expect(object.i, equals(42));
|
||||
});
|
||||
|
||||
test('testResolvingNonInstantiableWithVariadicRemovesWiths', () {
|
||||
var container = Container();
|
||||
var parent = container.make('VariadicParentClass', [
|
||||
container.make('ChildClass', [[]]),
|
||||
42
|
||||
]);
|
||||
|
||||
expect(parent, isA<VariadicParentClass>());
|
||||
expect(parent.child.objects, isEmpty);
|
||||
expect(parent.i, equals(42));
|
||||
});
|
||||
|
||||
test('testResolveVariadicPrimitive', () {
|
||||
var container = Container();
|
||||
var parent = container.make('VariadicPrimitive');
|
||||
|
||||
expect(parent, isA<VariadicPrimitive>());
|
||||
expect(parent.params, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
abstract class TestInterface {}
|
||||
|
||||
class ParentClass {
|
||||
int i;
|
||||
|
||||
ParentClass([TestInterface? testObject, this.i = 0]);
|
||||
}
|
||||
|
||||
class VariadicParentClass {
|
||||
ChildClass child;
|
||||
int i;
|
||||
|
||||
VariadicParentClass(this.child, [this.i = 0]);
|
||||
}
|
||||
|
||||
class ChildClass {
|
||||
List<TestInterface> objects;
|
||||
|
||||
ChildClass(this.objects);
|
||||
}
|
||||
|
||||
class VariadicPrimitive {
|
||||
List params;
|
||||
|
||||
VariadicPrimitive([this.params = const []]);
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
|
||||
void main() {
|
||||
group('ContainerTaggingTest', () {
|
||||
test('testContainerTags', () {
|
||||
var container = Container();
|
||||
container.tag(ContainerImplementationTaggedStub, 'foo');
|
||||
container.tag(ContainerImplementationTaggedStub, 'bar');
|
||||
container.tag(ContainerImplementationTaggedStubTwo, 'foo');
|
||||
|
||||
expect(container.tagged('bar').length, 1);
|
||||
expect(container.tagged('foo').length, 2);
|
||||
|
||||
var fooResults = [];
|
||||
for (var foo in container.tagged('foo')) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
var barResults = [];
|
||||
for (var bar in container.tagged('bar')) {
|
||||
barResults.add(bar);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(barResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
|
||||
container = Container();
|
||||
container.tag(ContainerImplementationTaggedStub, 'foo');
|
||||
container.tag(ContainerImplementationTaggedStubTwo, 'foo');
|
||||
expect(container.tagged('foo').length, 2);
|
||||
|
||||
fooResults = [];
|
||||
for (var foo in container.tagged('foo')) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
|
||||
expect(container.tagged('this_tag_does_not_exist').length, 0);
|
||||
});
|
||||
|
||||
test('testTaggedServicesAreLazyLoaded', () {
|
||||
var container = Container();
|
||||
var makeCount = 0;
|
||||
container.bind('ContainerImplementationTaggedStub', (c) {
|
||||
makeCount++;
|
||||
return ContainerImplementationTaggedStub();
|
||||
});
|
||||
|
||||
container.tag('ContainerImplementationTaggedStub', 'foo');
|
||||
container.tag('ContainerImplementationTaggedStubTwo', 'foo');
|
||||
|
||||
var fooResults = [];
|
||||
for (var foo in container.tagged('foo')) {
|
||||
fooResults.add(foo);
|
||||
break;
|
||||
}
|
||||
|
||||
expect(container.tagged('foo').length, 2);
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(makeCount, 1);
|
||||
});
|
||||
|
||||
test('testLazyLoadedTaggedServicesCanBeLoopedOverMultipleTimes', () {
|
||||
var container = Container();
|
||||
container.tag('ContainerImplementationTaggedStub', 'foo');
|
||||
container.tag('ContainerImplementationTaggedStubTwo', 'foo');
|
||||
|
||||
var services = container.tagged('foo');
|
||||
|
||||
var fooResults = [];
|
||||
for (var foo in services) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
|
||||
fooResults = [];
|
||||
for (var foo in services) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
abstract class IContainerTaggedContractStub {}
|
||||
|
||||
class ContainerImplementationTaggedStub
|
||||
implements IContainerTaggedContractStub {}
|
||||
|
||||
class ContainerImplementationTaggedStubTwo
|
||||
implements IContainerTaggedContractStub {}
|
|
@ -1,278 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
import 'package:platform_contracts/contracts.dart';
|
||||
|
||||
class ContainerConcreteStub {}
|
||||
|
||||
class IContainerContractStub {}
|
||||
|
||||
class ContainerImplementationStub implements IContainerContractStub {}
|
||||
|
||||
class ContainerDependentStub {
|
||||
final IContainerContractStub impl;
|
||||
|
||||
ContainerDependentStub(this.impl);
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('ContainerTest', () {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container();
|
||||
setUpBindings(container);
|
||||
});
|
||||
|
||||
test('testContainerSingleton', () {
|
||||
var container1 = Container.getInstance();
|
||||
var container2 = Container.getInstance();
|
||||
expect(container1, same(container2));
|
||||
});
|
||||
|
||||
test('testClosureResolution', () {
|
||||
container.bind('name', (Container c) => 'Taylor');
|
||||
expect(container.make('name'), equals('Taylor'));
|
||||
});
|
||||
|
||||
test('testBindIfDoesntRegisterIfServiceAlreadyRegistered', () {
|
||||
container.bind('name', (Container c) => 'Taylor');
|
||||
container.bindIf('name', (Container c) => 'Dayle');
|
||||
|
||||
expect(container.make('name'), equals('Taylor'));
|
||||
});
|
||||
|
||||
test('testBindIfDoesRegisterIfServiceNotRegisteredYet', () {
|
||||
container.bind('surname', (Container c) => 'Taylor');
|
||||
container.bindIf('name', (Container c) => 'Dayle');
|
||||
|
||||
expect(container.make('name'), equals('Dayle'));
|
||||
});
|
||||
|
||||
test('testSingletonIfDoesntRegisterIfBindingAlreadyRegistered', () {
|
||||
container.singleton('class', (Container c) => ContainerConcreteStub());
|
||||
var firstInstantiation = container.make('class');
|
||||
container.singletonIf('class', (Container c) => ContainerConcreteStub());
|
||||
var secondInstantiation = container.make('class');
|
||||
expect(firstInstantiation, same(secondInstantiation));
|
||||
});
|
||||
|
||||
test('testSingletonIfDoesRegisterIfBindingNotRegisteredYet', () {
|
||||
container.singleton('class', (Container c) => ContainerConcreteStub());
|
||||
container.singletonIf(
|
||||
'otherClass', (Container c) => ContainerConcreteStub());
|
||||
var firstInstantiation = container.make('otherClass');
|
||||
var secondInstantiation = container.make('otherClass');
|
||||
expect(firstInstantiation, same(secondInstantiation));
|
||||
});
|
||||
|
||||
test('testSharedClosureResolution', () {
|
||||
container.singleton('class', (Container c) => ContainerConcreteStub());
|
||||
var firstInstantiation = container.make('class');
|
||||
var secondInstantiation = container.make('class');
|
||||
expect(firstInstantiation, same(secondInstantiation));
|
||||
});
|
||||
|
||||
test('testAutoConcreteResolution', () {
|
||||
var instance = container.make('ContainerConcreteStub');
|
||||
expect(instance, isA<ContainerConcreteStub>());
|
||||
});
|
||||
|
||||
test('testSharedConcreteResolution', () {
|
||||
container.singleton(
|
||||
'ContainerConcreteStub', (Container c) => ContainerConcreteStub());
|
||||
|
||||
var var1 = container.make('ContainerConcreteStub');
|
||||
var var2 = container.make('ContainerConcreteStub');
|
||||
expect(var1, same(var2));
|
||||
});
|
||||
|
||||
test('testAbstractToConcreteResolution', () {
|
||||
container.bind('IContainerContractStub',
|
||||
(Container c) => ContainerImplementationStub());
|
||||
var instance = container.make('ContainerDependentStub');
|
||||
expect(instance.impl, isA<ContainerImplementationStub>());
|
||||
});
|
||||
|
||||
test('testNestedDependencyResolution', () {
|
||||
container.bind('IContainerContractStub',
|
||||
(Container c) => ContainerImplementationStub());
|
||||
var instance = container.make('ContainerNestedDependentStub');
|
||||
expect(instance.inner, isA<ContainerDependentStub>());
|
||||
expect(instance.inner.impl, isA<ContainerImplementationStub>());
|
||||
});
|
||||
|
||||
test('testContainerIsPassedToResolvers', () {
|
||||
container.bind('something', (Container c) => c);
|
||||
var c = container.make('something');
|
||||
expect(c, same(container));
|
||||
});
|
||||
|
||||
test('testArrayAccess', () {
|
||||
container['something'] = (Container c) => 'foo';
|
||||
expect(container['something'], equals('foo'));
|
||||
});
|
||||
|
||||
test('testAliases', () {
|
||||
container['foo'] = 'bar';
|
||||
container.alias('foo', 'baz');
|
||||
container.alias('baz', 'bat');
|
||||
expect(container.make('foo'), equals('bar'));
|
||||
expect(container.make('baz'), equals('bar'));
|
||||
expect(container.make('bat'), equals('bar'));
|
||||
});
|
||||
|
||||
test('testBindingsCanBeOverridden', () {
|
||||
container['foo'] = 'bar';
|
||||
container['foo'] = 'baz';
|
||||
expect(container['foo'], equals('baz'));
|
||||
});
|
||||
|
||||
test('testResolutionOfDefaultParameters', () {
|
||||
container.bind('foo', (Container c) => 'bar');
|
||||
container.bind(
|
||||
'ContainerDefaultValueStub',
|
||||
(Container c) => ContainerDefaultValueStub(
|
||||
c.make('ContainerConcreteStub'), c.make('foo')));
|
||||
var result = container.make('ContainerDefaultValueStub');
|
||||
expect(result.stub, isA<ContainerConcreteStub>());
|
||||
expect(result.defaultValue, equals('bar'));
|
||||
});
|
||||
|
||||
test('testUnsetRemoveBoundInstances', () {
|
||||
container.instance('obj', Object());
|
||||
expect(container.bound('obj'), isTrue);
|
||||
container.forgetInstance('obj');
|
||||
expect(container.bound('obj'), isFalse);
|
||||
});
|
||||
|
||||
test('testExtendMethod', () {
|
||||
container.singleton('foo', (Container c) => 'foo');
|
||||
container.extend(
|
||||
'foo', (String original, Container c) => '$original bar');
|
||||
expect(container.make('foo'), equals('foo bar'));
|
||||
});
|
||||
|
||||
test('testFactoryMethod', () {
|
||||
container.bind('foo', (Container c) => 'foo');
|
||||
var factory = container.factory('foo');
|
||||
expect(factory(), equals('foo'));
|
||||
});
|
||||
|
||||
test('testTaggedBindings', () {
|
||||
container.tag(['foo', 'bar'], 'foobar');
|
||||
container.bind('foo', (Container c) => 'foo');
|
||||
container.bind('bar', (Container c) => 'bar');
|
||||
var tagged = container.tagged('foobar');
|
||||
expect(tagged, containsAll(['foo', 'bar']));
|
||||
});
|
||||
|
||||
test('testCircularDependencies', () {
|
||||
container.bind('circular1', (Container c) => c.make('circular2'));
|
||||
container.bind('circular2', (Container c) => c.make('circular1'));
|
||||
expect(() => container.make('circular1'),
|
||||
throwsA(isA<CircularDependencyException>()));
|
||||
});
|
||||
|
||||
test('testScopedClosureResolution', () {
|
||||
container.scoped('class', (Container c) => Object());
|
||||
var firstInstantiation = container.make('class');
|
||||
var secondInstantiation = container.make('class');
|
||||
expect(firstInstantiation, same(secondInstantiation));
|
||||
});
|
||||
|
||||
test('testScopedClosureResets', () {
|
||||
container.scoped('class', (Container c) => Object());
|
||||
var firstInstantiation = container.makeScoped('class');
|
||||
container.forgetScopedInstances();
|
||||
var secondInstantiation = container.makeScoped('class');
|
||||
expect(firstInstantiation, isNot(same(secondInstantiation)));
|
||||
});
|
||||
|
||||
test('testScopedClosureResolution', () {
|
||||
container.scoped('class', (Container c) => Object());
|
||||
var firstInstantiation = container.makeScoped('class');
|
||||
var secondInstantiation = container.makeScoped('class');
|
||||
expect(firstInstantiation, same(secondInstantiation));
|
||||
});
|
||||
test('testForgetInstanceForgetsInstance', () {
|
||||
var containerConcreteStub = ContainerConcreteStub();
|
||||
container.instance('ContainerConcreteStub', containerConcreteStub);
|
||||
expect(container.isShared('ContainerConcreteStub'), isTrue);
|
||||
container.forgetInstance('ContainerConcreteStub');
|
||||
expect(container.isShared('ContainerConcreteStub'), isFalse);
|
||||
});
|
||||
|
||||
test('testForgetInstancesForgetsAllInstances', () {
|
||||
var stub1 = ContainerConcreteStub();
|
||||
var stub2 = ContainerConcreteStub();
|
||||
var stub3 = ContainerConcreteStub();
|
||||
container.instance('Instance1', stub1);
|
||||
container.instance('Instance2', stub2);
|
||||
container.instance('Instance3', stub3);
|
||||
expect(container.isShared('Instance1'), isTrue);
|
||||
expect(container.isShared('Instance2'), isTrue);
|
||||
expect(container.isShared('Instance3'), isTrue);
|
||||
container.forgetInstances();
|
||||
expect(container.isShared('Instance1'), isFalse);
|
||||
expect(container.isShared('Instance2'), isFalse);
|
||||
expect(container.isShared('Instance3'), isFalse);
|
||||
});
|
||||
|
||||
test('testContainerFlushFlushesAllBindingsAliasesAndResolvedInstances', () {
|
||||
container.bind('ConcreteStub', (Container c) => ContainerConcreteStub(),
|
||||
shared: true);
|
||||
container.alias('ConcreteStub', 'ContainerConcreteStub');
|
||||
container.make('ConcreteStub');
|
||||
expect(container.resolved('ConcreteStub'), isTrue);
|
||||
expect(container.isAlias('ContainerConcreteStub'), isTrue);
|
||||
expect(container.getBindings().containsKey('ConcreteStub'), isTrue);
|
||||
expect(container.isShared('ConcreteStub'), isTrue);
|
||||
container.flush();
|
||||
expect(container.resolved('ConcreteStub'), isFalse);
|
||||
expect(container.isAlias('ContainerConcreteStub'), isFalse);
|
||||
expect(container.getBindings().isEmpty, isTrue);
|
||||
expect(container.isShared('ConcreteStub'), isFalse);
|
||||
});
|
||||
|
||||
test('testResolvedResolvesAliasToBindingNameBeforeChecking', () {
|
||||
container.bind('ConcreteStub', (Container c) => ContainerConcreteStub(),
|
||||
shared: true);
|
||||
container.alias('ConcreteStub', 'foo');
|
||||
|
||||
expect(container.resolved('ConcreteStub'), isFalse);
|
||||
expect(container.resolved('foo'), isFalse);
|
||||
|
||||
container.make('ConcreteStub');
|
||||
|
||||
expect(container.resolved('ConcreteStub'), isTrue);
|
||||
expect(container.resolved('foo'), isTrue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerDefaultValueStub {
|
||||
final ContainerConcreteStub stub;
|
||||
final String defaultValue;
|
||||
|
||||
ContainerDefaultValueStub(this.stub, [this.defaultValue = 'taylor']);
|
||||
}
|
||||
|
||||
class ContainerNestedDependentStub {
|
||||
final ContainerDependentStub inner;
|
||||
|
||||
ContainerNestedDependentStub(this.inner);
|
||||
}
|
||||
|
||||
// Helper function to set up bindings
|
||||
void setUpBindings(Container container) {
|
||||
container.bind(
|
||||
'ContainerConcreteStub', (Container c) => ContainerConcreteStub());
|
||||
container.bind(
|
||||
'ContainerDependentStub',
|
||||
(Container c) =>
|
||||
ContainerDependentStub(c.make('IContainerContractStub')));
|
||||
container.bind(
|
||||
'ContainerNestedDependentStub',
|
||||
(Container c) =>
|
||||
ContainerNestedDependentStub(c.make('ContainerDependentStub')));
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
|
||||
void main() {
|
||||
group('ContextualAttributeBindingTest', () {
|
||||
test('testDependencyCanBeResolvedFromAttributeBinding', () {
|
||||
var container = Container();
|
||||
|
||||
container.bind('ContainerTestContract', (c) => ContainerTestImplB());
|
||||
container.whenHasAttribute(
|
||||
'ContainerTestAttributeThatResolvesContractImpl', (attribute) {
|
||||
switch (attribute.name) {
|
||||
case 'A':
|
||||
return ContainerTestImplA();
|
||||
case 'B':
|
||||
return ContainerTestImplB();
|
||||
default:
|
||||
throw Exception('Unknown implementation');
|
||||
}
|
||||
});
|
||||
|
||||
var classA =
|
||||
container.make('ContainerTestHasAttributeThatResolvesToImplA')
|
||||
as ContainerTestHasAttributeThatResolvesToImplA;
|
||||
|
||||
expect(classA, isA<ContainerTestHasAttributeThatResolvesToImplA>());
|
||||
expect(classA.property, isA<ContainerTestImplA>());
|
||||
|
||||
var classB =
|
||||
container.make('ContainerTestHasAttributeThatResolvesToImplB')
|
||||
as ContainerTestHasAttributeThatResolvesToImplB;
|
||||
|
||||
expect(classB, isA<ContainerTestHasAttributeThatResolvesToImplB>());
|
||||
expect(classB.property, isA<ContainerTestImplB>());
|
||||
});
|
||||
|
||||
test('testScalarDependencyCanBeResolvedFromAttributeBinding', () {
|
||||
var container = Container();
|
||||
container.singleton(
|
||||
'config',
|
||||
(c) => Repository({
|
||||
'app': {
|
||||
'timezone': 'Europe/Paris',
|
||||
},
|
||||
}));
|
||||
|
||||
container.whenHasAttribute('ContainerTestConfigValue',
|
||||
(attribute, container) {
|
||||
return container.make('config').get(attribute.key);
|
||||
});
|
||||
|
||||
var instance = container.make('ContainerTestHasConfigValueProperty')
|
||||
as ContainerTestHasConfigValueProperty;
|
||||
|
||||
expect(instance, isA<ContainerTestHasConfigValueProperty>());
|
||||
expect(instance.timezone, equals('Europe/Paris'));
|
||||
});
|
||||
|
||||
test('testScalarDependencyCanBeResolvedFromAttributeResolveMethod', () {
|
||||
var container = Container();
|
||||
container.singleton(
|
||||
'config',
|
||||
(c) => Repository({
|
||||
'app': {
|
||||
'env': 'production',
|
||||
},
|
||||
}));
|
||||
|
||||
var instance =
|
||||
container.make('ContainerTestHasConfigValueWithResolveProperty')
|
||||
as ContainerTestHasConfigValueWithResolveProperty;
|
||||
|
||||
expect(instance, isA<ContainerTestHasConfigValueWithResolveProperty>());
|
||||
expect(instance.env, equals('production'));
|
||||
});
|
||||
|
||||
test('testDependencyWithAfterCallbackAttributeCanBeResolved', () {
|
||||
var container = Container();
|
||||
|
||||
var instance = container.make(
|
||||
'ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback')
|
||||
as ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback;
|
||||
|
||||
expect(instance.person['role'], equals('Developer'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerTestAttributeThatResolvesContractImpl {
|
||||
final String name;
|
||||
const ContainerTestAttributeThatResolvesContractImpl(this.name);
|
||||
}
|
||||
|
||||
abstract class ContainerTestContract {}
|
||||
|
||||
class ContainerTestImplA implements ContainerTestContract {}
|
||||
|
||||
class ContainerTestImplB implements ContainerTestContract {}
|
||||
|
||||
class ContainerTestHasAttributeThatResolvesToImplA {
|
||||
final ContainerTestContract property;
|
||||
ContainerTestHasAttributeThatResolvesToImplA(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestHasAttributeThatResolvesToImplB {
|
||||
final ContainerTestContract property;
|
||||
ContainerTestHasAttributeThatResolvesToImplB(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestConfigValue {
|
||||
final String key;
|
||||
const ContainerTestConfigValue(this.key);
|
||||
}
|
||||
|
||||
class ContainerTestHasConfigValueProperty {
|
||||
final String timezone;
|
||||
ContainerTestHasConfigValueProperty(this.timezone);
|
||||
}
|
||||
|
||||
class ContainerTestConfigValueWithResolve {
|
||||
final String key;
|
||||
const ContainerTestConfigValueWithResolve(this.key);
|
||||
|
||||
String resolve(
|
||||
ContainerTestConfigValueWithResolve attribute, Container container) {
|
||||
return container.make('config').get(attribute.key);
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestHasConfigValueWithResolveProperty {
|
||||
final String env;
|
||||
ContainerTestHasConfigValueWithResolveProperty(this.env);
|
||||
}
|
||||
|
||||
class ContainerTestConfigValueWithResolveAndAfter {
|
||||
const ContainerTestConfigValueWithResolveAndAfter();
|
||||
|
||||
Object resolve(ContainerTestConfigValueWithResolveAndAfter attribute,
|
||||
Container container) {
|
||||
return {'name': 'Taylor'};
|
||||
}
|
||||
|
||||
void after(ContainerTestConfigValueWithResolveAndAfter attribute,
|
||||
Object value, Container container) {
|
||||
(value as Map)['role'] = 'Developer';
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback {
|
||||
final Map person;
|
||||
ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback(this.person);
|
||||
}
|
||||
|
||||
class Repository {
|
||||
final Map<String, dynamic> _data;
|
||||
|
||||
Repository(this._data);
|
||||
|
||||
dynamic get(String key) {
|
||||
var keys = key.split('.');
|
||||
var value = _data;
|
||||
for (var k in keys) {
|
||||
if (value is Map && value.containsKey(k)) {
|
||||
value = value[k];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
import 'package:platform_config/platform_config.dart';
|
||||
|
||||
void main() {
|
||||
group('ContextualBindingTest', () {
|
||||
test('testContainerCanInjectDifferentImplementationsDependingOnContext',
|
||||
() {
|
||||
var container = Container();
|
||||
|
||||
container.bind('IContainerContextContractStub',
|
||||
(c) => ContainerContextImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStub');
|
||||
container
|
||||
.when('ContainerTestContextInjectTwo')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStubTwo');
|
||||
|
||||
var one = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
var two = container.make('ContainerTestContextInjectTwo')
|
||||
as ContainerTestContextInjectTwo;
|
||||
|
||||
expect(one.impl, isA<ContainerContextImplementationStub>());
|
||||
expect(two.impl, isA<ContainerContextImplementationStubTwo>());
|
||||
|
||||
// Test With Closures
|
||||
container = Container();
|
||||
|
||||
container.bind('IContainerContextContractStub',
|
||||
(c) => ContainerContextImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStub');
|
||||
container
|
||||
.when('ContainerTestContextInjectTwo')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give((Container container) {
|
||||
return container.make('ContainerContextImplementationStubTwo');
|
||||
});
|
||||
|
||||
one = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
two = container.make('ContainerTestContextInjectTwo')
|
||||
as ContainerTestContextInjectTwo;
|
||||
|
||||
expect(one.impl, isA<ContainerContextImplementationStub>());
|
||||
expect(two.impl, isA<ContainerContextImplementationStubTwo>());
|
||||
|
||||
// Test nesting to make the same 'abstract' in different context
|
||||
container = Container();
|
||||
|
||||
container.bind('IContainerContextContractStub',
|
||||
(c) => ContainerContextImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give((Container container) {
|
||||
return container.make('IContainerContextContractStub');
|
||||
});
|
||||
|
||||
one = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
|
||||
expect(one.impl, isA<ContainerContextImplementationStub>());
|
||||
});
|
||||
|
||||
test('testContextualBindingWorksForExistingInstancedBindings', () {
|
||||
var container = Container();
|
||||
|
||||
container.instance(
|
||||
'IContainerContextContractStub', ContainerImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStubTwo');
|
||||
|
||||
var instance = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
expect(instance.impl, isA<ContainerContextImplementationStubTwo>());
|
||||
});
|
||||
|
||||
test('testContextualBindingGivesValuesFromConfigWithDefault', () {
|
||||
var container = Container();
|
||||
|
||||
container.singleton(
|
||||
'config',
|
||||
(c) => Repository({
|
||||
'test': {
|
||||
'password': 'hunter42',
|
||||
},
|
||||
}));
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectFromConfigIndividualValues')
|
||||
.needs('\$username')
|
||||
.giveConfig('test.username', 'DEFAULT_USERNAME');
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectFromConfigIndividualValues')
|
||||
.needs('\$password')
|
||||
.giveConfig('test.password');
|
||||
|
||||
var resolvedInstance =
|
||||
container.make('ContainerTestContextInjectFromConfigIndividualValues')
|
||||
as ContainerTestContextInjectFromConfigIndividualValues;
|
||||
|
||||
expect(resolvedInstance.username, equals('DEFAULT_USERNAME'));
|
||||
expect(resolvedInstance.password, equals('hunter42'));
|
||||
expect(resolvedInstance.alias, isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
abstract class IContainerContextContractStub {}
|
||||
|
||||
class ContainerContextNonContractStub {}
|
||||
|
||||
class ContainerContextImplementationStub
|
||||
implements IContainerContextContractStub {}
|
||||
|
||||
class ContainerContextImplementationStubTwo
|
||||
implements IContainerContextContractStub {}
|
||||
|
||||
class ContainerImplementationStub implements IContainerContextContractStub {}
|
||||
|
||||
class ContainerTestContextInjectInstantiations
|
||||
implements IContainerContextContractStub {
|
||||
static int instantiations = 0;
|
||||
|
||||
ContainerTestContextInjectInstantiations() {
|
||||
instantiations++;
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestContextInjectOne {
|
||||
final IContainerContextContractStub impl;
|
||||
|
||||
ContainerTestContextInjectOne(this.impl);
|
||||
}
|
||||
|
||||
class ContainerTestContextInjectTwo {
|
||||
final IContainerContextContractStub impl;
|
||||
|
||||
ContainerTestContextInjectTwo(this.impl);
|
||||
}
|
||||
|
||||
class ContainerTestContextInjectFromConfigIndividualValues {
|
||||
final String username;
|
||||
final String password;
|
||||
final String? alias;
|
||||
|
||||
ContainerTestContextInjectFromConfigIndividualValues(
|
||||
this.username, this.password,
|
||||
[this.alias]);
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
|
||||
void main() {
|
||||
group('ResolvingCallbackTest', () {
|
||||
test('testResolvingCallbacksAreCalledForSpecificAbstracts', () {
|
||||
var container = Container();
|
||||
container.resolving('foo', (object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalled', () {
|
||||
var container = Container();
|
||||
container.resolving((object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalledForType', () {
|
||||
var container = Container();
|
||||
container.resolving('Object', (object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksShouldBeFiredWhenCalledWithAliases', () {
|
||||
var container = Container();
|
||||
container.alias('Object', 'std');
|
||||
container.resolving('std', (object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalledOnceForImplementation', () {
|
||||
var container = Container();
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving('ResolvingContractStub', (_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 2);
|
||||
});
|
||||
|
||||
test('testGlobalResolvingCallbacksAreCalledOnceForImplementation', () {
|
||||
var container = Container();
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving((_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
|
||||
container.make('ResolvingContractStub');
|
||||
expect(callCounter, 2);
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalledOnceForSingletonConcretes', () {
|
||||
var container = Container();
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving('ResolvingContractStub', (_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
container.bind(
|
||||
'ResolvingImplementationStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 2);
|
||||
|
||||
container.make('ResolvingContractStub');
|
||||
expect(callCounter, 3);
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksCanStillBeAddedAfterTheFirstResolution', () {
|
||||
var container = Container();
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving('ResolvingContractStub', (_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
});
|
||||
|
||||
test('testParametersPassedIntoResolvingCallbacks', () {
|
||||
var container = Container();
|
||||
|
||||
container.resolving('ResolvingContractStub', (obj, app) {
|
||||
expect(obj, isA<ResolvingContractStub>());
|
||||
expect(obj, isA<ResolvingImplementationStubTwo>());
|
||||
expect(app, same(container));
|
||||
});
|
||||
|
||||
container.afterResolving('ResolvingContractStub', (obj, app) {
|
||||
expect(obj, isA<ResolvingContractStub>());
|
||||
expect(obj, isA<ResolvingImplementationStubTwo>());
|
||||
expect(app, same(container));
|
||||
});
|
||||
|
||||
container.afterResolving((obj, app) {
|
||||
expect(obj, isA<ResolvingContractStub>());
|
||||
expect(obj, isA<ResolvingImplementationStubTwo>());
|
||||
expect(app, same(container));
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStubTwo());
|
||||
container.make('ResolvingContractStub');
|
||||
});
|
||||
|
||||
// Add all remaining tests here...
|
||||
});
|
||||
}
|
||||
|
||||
abstract class ResolvingContractStub {}
|
||||
|
||||
class ResolvingImplementationStub implements ResolvingContractStub {}
|
||||
|
||||
class ResolvingImplementationStubTwo implements ResolvingContractStub {}
|
|
@ -1,37 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
|
||||
void main() {
|
||||
group('RewindableGeneratorTest', () {
|
||||
test('testCountUsesProvidedValue', () {
|
||||
var generator = RewindableGenerator(() sync* {
|
||||
yield 'foo';
|
||||
}, 999);
|
||||
|
||||
expect(generator.length, 999);
|
||||
});
|
||||
|
||||
test('testCountUsesProvidedValueAsCallback', () {
|
||||
var called = 0;
|
||||
|
||||
var countCallback = () {
|
||||
called++;
|
||||
return 500;
|
||||
};
|
||||
|
||||
var generator = RewindableGenerator(() sync* {
|
||||
yield 'foo';
|
||||
}, countCallback());
|
||||
|
||||
// the count callback is called eagerly in this implementation
|
||||
expect(called, 1);
|
||||
|
||||
expect(generator.length, 500);
|
||||
|
||||
generator.length;
|
||||
|
||||
// the count callback is called only once
|
||||
expect(called, 1);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_service_container/service_container.dart';
|
||||
|
||||
void main() {
|
||||
group('UtilTest', () {
|
||||
test('testUnwrapIfClosure', () {
|
||||
expect(Util.unwrapIfClosure('foo'), 'foo');
|
||||
expect(Util.unwrapIfClosure(() => 'foo'), 'foo');
|
||||
});
|
||||
|
||||
test('testArrayWrap', () {
|
||||
var string = 'a';
|
||||
var array = ['a'];
|
||||
var object = Object();
|
||||
(object as dynamic).value = 'a';
|
||||
|
||||
expect(Util.arrayWrap(string), ['a']);
|
||||
expect(Util.arrayWrap(array), array);
|
||||
expect(Util.arrayWrap(object), [object]);
|
||||
expect(Util.arrayWrap(null), []);
|
||||
expect(Util.arrayWrap([null]), [null]);
|
||||
expect(Util.arrayWrap([null, null]), [null, null]);
|
||||
expect(Util.arrayWrap(''), ['']);
|
||||
expect(Util.arrayWrap(['']), ['']);
|
||||
expect(Util.arrayWrap(false), [false]);
|
||||
expect(Util.arrayWrap([false]), [false]);
|
||||
expect(Util.arrayWrap(0), [0]);
|
||||
|
||||
var obj = Object();
|
||||
(obj as dynamic).value = 'a';
|
||||
var wrappedObj = Util.arrayWrap(obj);
|
||||
expect(wrappedObj, [obj]);
|
||||
expect(identical(wrappedObj[0], obj), isTrue);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,45 +1,223 @@
|
|||
# Protevus Container
|
||||
# Platform Container
|
||||
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/platform_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 Protevus, ultimately allowing Protevus to be used with or without `dart:mirrors` package.
|
||||
A powerful IoC (Inversion of Control) container for Dart, providing robust dependency injection with support for multiple reflection strategies. The container can be used with or without `dart:mirrors`, making it suitable for all Dart platforms including web and Flutter.
|
||||
|
||||
## Features
|
||||
|
||||
- **Constructor Injection**: Automatically resolves and injects constructor dependencies
|
||||
- **Contextual Binding**: Define how abstractions should be resolved in different contexts
|
||||
- **Multiple Registration Types**: Support for singletons, factories, and scoped instances
|
||||
- **Attribute/Annotation Support**: Use annotations to configure injection behavior
|
||||
- **Flexible Reflection**: Choose between mirrors-based, static, or custom reflection
|
||||
- **Type-Safe**: Leverages Dart's type system for reliable dependency resolution
|
||||
- **Tagging System**: Group and resolve related dependencies
|
||||
- **Parameter Overrides**: Override specific dependencies during resolution
|
||||
- **Method Binding**: Inject dependencies into method parameters
|
||||
|
||||
## Installation
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
platform_container: ^latest_version
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Constructor Injection
|
||||
|
||||
```dart
|
||||
// Define some services
|
||||
abstract class Logger {
|
||||
void log(String message);
|
||||
}
|
||||
|
||||
class ConsoleLogger implements Logger {
|
||||
@override
|
||||
void log(String message) => print(message);
|
||||
}
|
||||
|
||||
class UserService {
|
||||
final Logger logger;
|
||||
|
||||
// Constructor injection - container will automatically inject Logger
|
||||
UserService(this.logger);
|
||||
|
||||
void createUser(String name) {
|
||||
logger.log('Creating user: $name');
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the container
|
||||
var container = Container(MirrorsReflector());
|
||||
container.bind(Logger).to(ConsoleLogger);
|
||||
|
||||
// Resolve with automatic dependency injection
|
||||
var userService = container.make<UserService>();
|
||||
```
|
||||
|
||||
### Singleton Registration
|
||||
|
||||
```dart
|
||||
// Register a singleton
|
||||
container.registerSingleton<Logger>(ConsoleLogger());
|
||||
|
||||
// Or register a lazy singleton
|
||||
container.registerLazySingleton<Logger>((container) => ConsoleLogger());
|
||||
```
|
||||
|
||||
### Factory Registration
|
||||
|
||||
```dart
|
||||
// Register a factory
|
||||
container.registerFactory<Logger>((container) => ConsoleLogger());
|
||||
|
||||
// Or use the shorter syntax
|
||||
container[Logger] = (container) => ConsoleLogger();
|
||||
```
|
||||
|
||||
### Contextual Binding
|
||||
|
||||
```dart
|
||||
// Bind different implementations based on context
|
||||
container.when(UserService).needs(Logger).give(ConsoleLogger);
|
||||
container.when(AdminService).needs(Logger).give(FileLogger);
|
||||
```
|
||||
|
||||
### Attribute/Annotation Based Injection
|
||||
|
||||
```dart
|
||||
@injectable
|
||||
class UserRepository {
|
||||
@inject
|
||||
final Database db;
|
||||
|
||||
UserRepository(this.db);
|
||||
}
|
||||
```
|
||||
|
||||
### Scoped Instances
|
||||
|
||||
```dart
|
||||
// Register a scoped instance
|
||||
container.scoped<RequestContext>((c) => RequestContext());
|
||||
|
||||
// Clear scoped instances
|
||||
container.clearScoped();
|
||||
```
|
||||
|
||||
### Tagged Dependencies
|
||||
|
||||
```dart
|
||||
// Tag related services
|
||||
container.tag([UserService, OrderService], 'business-logic');
|
||||
|
||||
// Resolve all tagged services
|
||||
var services = container.tagged('business-logic');
|
||||
```
|
||||
|
||||
## Reflection Strategies
|
||||
|
||||
### Mirrors Reflection (Full Runtime Reflection)
|
||||
|
||||
```dart
|
||||
import 'package:platform_container/mirrors.dart';
|
||||
|
||||
var container = Container(MirrorsReflector());
|
||||
```
|
||||
|
||||
### Static Reflection (AOT-Friendly)
|
||||
|
||||
```dart
|
||||
import 'package:platform_container/static.dart';
|
||||
|
||||
var container = Container(StaticReflector());
|
||||
```
|
||||
|
||||
### Empty Reflection (Minimal)
|
||||
|
||||
```dart
|
||||
import 'package:platform_container/empty.dart';
|
||||
|
||||
var container = Container(EmptyReflector());
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Parameter Overrides
|
||||
|
||||
```dart
|
||||
// Override specific parameters during resolution
|
||||
var instance = container.makeWith<Service>({
|
||||
'config': CustomConfig(),
|
||||
'timeout': Duration(seconds: 30),
|
||||
});
|
||||
```
|
||||
|
||||
### Method Binding
|
||||
|
||||
```dart
|
||||
// Bind a method with injected parameters
|
||||
container.bindMethod('processUser', (Logger logger, User user) {
|
||||
logger.log('Processing user: ${user.name}');
|
||||
});
|
||||
|
||||
// Call the method
|
||||
container.callMethod('processUser', [user]);
|
||||
```
|
||||
|
||||
### Child Containers
|
||||
|
||||
```dart
|
||||
// Create a child container with its own bindings
|
||||
var child = container.createChild();
|
||||
child.bind(Logger).to(SpecialLogger);
|
||||
```
|
||||
|
||||
## Web Framework Integration
|
||||
|
||||
The container is used as the core DI system in the Protevus web framework:
|
||||
|
||||
```dart
|
||||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_foundation/core.dart';
|
||||
import 'package:platform_foundation/http.dart';
|
||||
|
||||
@Expose('/sales', middleware: [process1])
|
||||
class SalesController extends Controller {
|
||||
@Expose('/', middleware: [process2])
|
||||
Future<String> route1(RequestContext req, ResponseContext res) async {
|
||||
return "Sales route";
|
||||
}
|
||||
}
|
||||
@Expose('/api')
|
||||
class ApiController extends Controller {
|
||||
final UserService userService;
|
||||
|
||||
bool process1(RequestContext req, ResponseContext res) {
|
||||
res.write('Hello, ');
|
||||
return true;
|
||||
}
|
||||
// Constructor injection works automatically
|
||||
ApiController(this.userService);
|
||||
|
||||
bool process2(RequestContext req, ResponseContext res) {
|
||||
res.write('From Sales, ');
|
||||
return true;
|
||||
@Expose('/users')
|
||||
Future<List<User>> getUsers() async {
|
||||
return userService.getAllUsers();
|
||||
}
|
||||
}
|
||||
|
||||
void main() async {
|
||||
// Using Mirror Reflector
|
||||
var app = Protevus(reflector: MirrorsReflector());
|
||||
|
||||
// Sales Controller
|
||||
app.container.registerSingleton<SalesController>(SalesController());
|
||||
await app.mountController<SalesController>();
|
||||
// Register your services
|
||||
app.container.registerSingleton<UserService>(UserService());
|
||||
|
||||
// Mount the controller
|
||||
await app.mountController<ApiController>();
|
||||
|
||||
var http = PlatformHttp(app);
|
||||
var server = await http.startServer('localhost', 3000);
|
||||
print("Protevus server listening at ${http.uri}");
|
||||
print('Server listening at ${http.uri}');
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please read our [contributing guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
|
|
27
packages/container/container/doc/roadmap.md
Normal file
27
packages/container/container/doc/roadmap.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
Let me summarize what we should add to match Laravel's functionality:
|
||||
|
||||
Core Container Enhancements:
|
||||
- Add alias management (alias(), getAlias(), isAlias())
|
||||
- Add service extenders (extend())
|
||||
- Add rebound callbacks (rebinding(), refresh())
|
||||
- Add parameter override stack
|
||||
- Implement ArrayAccess equivalent
|
||||
Method Binding Improvements:
|
||||
- Support Class@method syntax
|
||||
- Add parameter dependency injection
|
||||
- Add variadic parameter support
|
||||
- Add __invoke support
|
||||
Contextual Binding Enhancements:
|
||||
- Support array of concrete types
|
||||
- Add giveTagged() and giveConfig()
|
||||
- Add attribute-based binding
|
||||
Additional Methods:
|
||||
- Add bindIf() and singletonIf()
|
||||
- Add wrap() for closure injection
|
||||
- Add factory() for deferred resolution
|
||||
- Add makeWith() alias
|
||||
- Add flush() for container reset
|
||||
Error Handling:
|
||||
- Better circular dependency detection
|
||||
- More specific exception types
|
||||
- Better error messages with build stack
|
|
@ -7,9 +7,9 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
library platform_container;
|
||||
|
||||
export 'src/attributes.dart';
|
||||
export 'src/container.dart';
|
||||
export 'src/contextual_binding_builder.dart';
|
||||
export 'src/empty/empty.dart';
|
||||
export 'src/static/static.dart';
|
||||
export 'src/exception.dart';
|
||||
|
|
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue