refactor: adding __invoke support
This commit is contained in:
parent
dee0cbd03a
commit
c904c2bec3
2 changed files with 191 additions and 0 deletions
|
@ -1061,6 +1061,12 @@ class Container {
|
||||||
|
|
||||||
// Find and call the method
|
// Find and call the method
|
||||||
var method = reflector.findInstanceMethod(instance, methodName);
|
var method = reflector.findInstanceMethod(instance, methodName);
|
||||||
|
|
||||||
|
// If method not found and it's __invoke, try 'call' instead
|
||||||
|
if (method == null && methodName == '__invoke') {
|
||||||
|
method = reflector.findInstanceMethod(instance, 'call');
|
||||||
|
}
|
||||||
|
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw ArgumentError('Method not found: $methodName on $className');
|
throw ArgumentError('Method not found: $methodName on $className');
|
||||||
}
|
}
|
||||||
|
|
185
incubation/container/container/test/invoke_test.dart
Normal file
185
incubation/container/container/test/invoke_test.dart
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
class InvokableClass {
|
||||||
|
String call(String message) => 'Invoked with: $message';
|
||||||
|
String invoke(String message) => 'Called invoke with: $message';
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflector extends Reflector {
|
||||||
|
@override
|
||||||
|
String? getName(Symbol symbol) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedClass? reflectClass(Type clazz) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType? reflectType(Type type) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance? reflectInstance(Object? instance) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedFunction? reflectFunction(Function function) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType reflectFutureOf(Type type) => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Type? findTypeByName(String name) {
|
||||||
|
if (name == 'InvokableClass') return InvokableClass;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedFunction? findInstanceMethod(Object instance, String methodName) {
|
||||||
|
if (instance is InvokableClass) {
|
||||||
|
switch (methodName) {
|
||||||
|
case '__invoke':
|
||||||
|
case 'call':
|
||||||
|
return MockMethod('call', (invocation) {
|
||||||
|
var args = invocation.positionalArguments;
|
||||||
|
if (args.isEmpty) {
|
||||||
|
throw ArgumentError('Method call requires a message parameter');
|
||||||
|
}
|
||||||
|
return MockReflectedInstance(instance.call(args[0] as String));
|
||||||
|
}, [MockParameter('message', String, true, false)]);
|
||||||
|
case 'invoke':
|
||||||
|
return MockMethod('invoke', (invocation) {
|
||||||
|
var args = invocation.positionalArguments;
|
||||||
|
if (args.isEmpty) {
|
||||||
|
throw ArgumentError('Method invoke requires a message parameter');
|
||||||
|
}
|
||||||
|
return MockReflectedInstance(instance.invoke(args[0] as String));
|
||||||
|
}, [MockParameter('message', String, true, false)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockMethod implements ReflectedFunction {
|
||||||
|
final String methodName;
|
||||||
|
final ReflectedInstance Function(Invocation) handler;
|
||||||
|
final List<ReflectedParameter> _parameters;
|
||||||
|
|
||||||
|
MockMethod(this.methodName, this.handler,
|
||||||
|
[List<ReflectedParameter>? parameters])
|
||||||
|
: _parameters = parameters ?? [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedInstance> get annotations => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isGetter => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isSetter => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => methodName;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedParameter> get parameters => _parameters;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType? get returnType => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedTypeParameter> get typeParameters => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance invoke(Invocation invocation) => handler(invocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockParameter implements ReflectedParameter {
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
@override
|
||||||
|
final bool isRequired;
|
||||||
|
@override
|
||||||
|
final bool isNamed;
|
||||||
|
final Type paramType;
|
||||||
|
final bool isVariadic;
|
||||||
|
|
||||||
|
MockParameter(this.name, this.paramType, this.isRequired, this.isNamed,
|
||||||
|
{this.isVariadic = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedInstance> get annotations => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType get type => MockReflectedType(paramType);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedType implements ReflectedType {
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
@override
|
||||||
|
final Type reflectedType;
|
||||||
|
|
||||||
|
MockReflectedType(this.reflectedType) : name = reflectedType.toString();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedTypeParameter> get typeParameters => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]) =>
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedInstance implements ReflectedInstance {
|
||||||
|
final dynamic value;
|
||||||
|
|
||||||
|
MockReflectedInstance(this.value);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedClass get clazz => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance getField(String name) => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
dynamic get reflectee => value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType get type => throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(MockReflector());
|
||||||
|
container.registerSingleton(InvokableClass());
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Invoke Tests', () {
|
||||||
|
test('can call __invoke method', () {
|
||||||
|
var result = container.call('InvokableClass@__invoke', ['Hello world']);
|
||||||
|
expect(result, equals('Invoked with: Hello world'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('__invoke is alias for call method', () {
|
||||||
|
var invokeResult = container.call('InvokableClass@__invoke', ['Test']);
|
||||||
|
var callResult = container.call('InvokableClass@call', ['Test']);
|
||||||
|
expect(invokeResult, equals(callResult));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can still call other methods', () {
|
||||||
|
var result = container.call('InvokableClass@invoke', ['Hello world']);
|
||||||
|
expect(result, equals('Called invoke with: Hello world'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when required parameter is missing', () {
|
||||||
|
expect(() => container.call('InvokableClass@__invoke'),
|
||||||
|
throwsA(isA<BindingResolutionException>()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue