refactor: upgrade container to package status

This commit is contained in:
Patrick Stewart 2024-12-27 11:47:28 -07:00
parent 932f83f0f8
commit 0fbb79e4d7
122 changed files with 1392 additions and 10688 deletions

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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}");
}
```

View file

@ -1 +0,0 @@
include: package:lints/recommended.yaml

View file

@ -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.');
}
}

View file

@ -1,6 +0,0 @@
import 'package:platformed_container/container.dart';
void main() {
var reflector = const ThrowingReflector();
reflector.reflectClass(StringBuffer);
}

View file

@ -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';

View file

@ -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

View file

@ -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._();
}

View file

@ -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().');
}
}

View file

@ -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]);
}

View file

@ -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';

View file

@ -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));
}
}

View file

@ -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.
*/

View file

@ -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;
}

View file

@ -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];
}
}

View file

@ -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();
}

View file

@ -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

View file

@ -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 }

View file

@ -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!!!');
}
}

View file

@ -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});
}

View file

@ -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);
}

View file

@ -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);
});
}

View file

@ -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});
}

View file

@ -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!!!');
}
}

View file

@ -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

View file

@ -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`.

View file

@ -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.

View file

@ -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

View file

@ -1 +0,0 @@
include: package:lints/recommended.yaml

View file

@ -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.');
}
}

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -1,3 +0,0 @@
## 1.0.0
- Initial version.

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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);
}

View file

@ -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));
}
}

View file

@ -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';
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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

View file

@ -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;
}
}

View file

@ -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';

View file

@ -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';

View file

@ -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'));
});
});
}

View file

@ -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 []]);
}

View file

@ -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 {}

View file

@ -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')));
}

View file

@ -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;
}
}

View file

@ -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]);
}

View file

@ -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 {}

View file

@ -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);
});
});
}

View file

@ -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);
});
});
}

View file

@ -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

View file

@ -1,3 +0,0 @@
## 1.0.0
- Initial version.

View file

@ -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.

View file

@ -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>

View file

@ -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

View file

@ -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';

View file

@ -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('@');
}
}

View file

@ -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();
}
}

View file

@ -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;
},
);
}
}
}

View file

@ -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';
}
}

View file

@ -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();
}
}

View file

@ -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();
// }

View file

@ -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

View file

@ -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;
}
}

View file

@ -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';

View file

@ -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'));
});
});
}

View file

@ -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 []]);
}

View file

@ -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 {}

View file

@ -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')));
}

View file

@ -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;
}
}

View file

@ -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]);
}

View file

@ -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 {}

View file

@ -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);
});
});
}

View file

@ -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);
});
});
}

View file

@ -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
import 'package:platform_container/mirrors.dart';
import 'package:platform_foundation/core.dart';
import 'package:platform_foundation/http.dart';
// Define some services
abstract class Logger {
void log(String message);
}
@Expose('/sales', middleware: [process1])
class SalesController extends Controller {
@Expose('/', middleware: [process2])
Future<String> route1(RequestContext req, ResponseContext res) async {
return "Sales route";
}
}
class ConsoleLogger implements Logger {
@override
void log(String message) => print(message);
}
bool process1(RequestContext req, ResponseContext res) {
res.write('Hello, ');
return true;
}
class UserService {
final Logger logger;
bool process2(RequestContext req, ResponseContext res) {
res.write('From Sales, ');
return true;
}
// Constructor injection - container will automatically inject Logger
UserService(this.logger);
void main() async {
// Using Mirror Reflector
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('/api')
class ApiController extends Controller {
final UserService userService;
// Constructor injection works automatically
ApiController(this.userService);
@Expose('/users')
Future<List<User>> getUsers() async {
return userService.getAllUsers();
}
}
void main() async {
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.

View 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

View file

@ -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