From 09fe888fae9f9a94b2fb3b08bbea84a04ab9cf78 Mon Sep 17 00:00:00 2001 From: Jacob Moura Date: Thu, 12 May 2022 13:32:42 -0300 Subject: [PATCH 1/3] Fix: Inject.get should return instance. --- flutter_modular/CHANGELOG.md | 3 ++ flutter_modular/lib/flutter_modular.dart | 30 ++++++++----------- .../src/infra/services/bind_service_impl.dart | 5 ++-- .../src/presenter/widgets/widget_module.dart | 22 ++++++-------- flutter_modular/pubspec.yaml | 4 +-- .../test/src/flutter_modular_module_test.dart | 4 +-- .../services/bind_service_impl_test.dart | 19 +++++------- modular_core/CHANGELOG.md | 3 ++ modular_core/lib/src/di/bind_context.dart | 24 ++++++++++----- modular_core/lib/src/di/injector.dart | 20 +++++++++---- modular_core/pubspec.yaml | 5 ++-- .../test/src/di/bind_context_test.dart | 9 ++++-- modular_core/test/src/di/injector_test.dart | 14 +++++---- modular_interfaces/CHANGELOG.md | 4 +++ modular_interfaces/lib/src/di/injector.dart | 7 +++-- modular_interfaces/pubspec.yaml | 2 +- 16 files changed, 99 insertions(+), 76 deletions(-) diff --git a/flutter_modular/CHANGELOG.md b/flutter_modular/CHANGELOG.md index 93017a63..69be5973 100644 --- a/flutter_modular/CHANGELOG.md +++ b/flutter_modular/CHANGELOG.md @@ -1,3 +1,6 @@ +## [5.0.1] - 2022-04-22 +- Fix: Inject.get should return instance. + ## [5.0.0] - 2022-04-21 - Support Flutter 3.0.0 - [BREAK CHANGE]: Removed `MaterialApp.modular()` and `Cupertino().modular()`. diff --git a/flutter_modular/lib/flutter_modular.dart b/flutter_modular/lib/flutter_modular.dart index 4e5ca73f..525a44ae 100644 --- a/flutter_modular/lib/flutter_modular.dart +++ b/flutter_modular/lib/flutter_modular.dart @@ -12,24 +12,24 @@ import 'src/presenter/navigation/modular_router_delegate.dart'; import 'src/presenter/navigation/router_outlet_delegate.dart'; export 'package:flutter_modular_annotations/flutter_modular_annotations.dart'; +export 'package:modular_core/modular_core.dart' show ModularRoute, Disposable, ReassembleMixin; + export 'src/presenter/guards/route_guard.dart'; export 'src/presenter/models/bind.dart'; export 'src/presenter/models/child_route.dart'; -export 'src/presenter/models/module_route.dart'; -export 'src/presenter/models/wildcard_route.dart'; -export 'src/presenter/models/redirect_to_route.dart'; export 'src/presenter/models/modular_args.dart'; +export 'src/presenter/models/modular_navigator.dart'; export 'src/presenter/models/module.dart'; +export 'src/presenter/models/module_route.dart'; +export 'src/presenter/models/redirect_to_route.dart'; export 'src/presenter/models/route.dart'; -export 'src/presenter/models/modular_navigator.dart'; -export 'src/presenter/widgets/modular_app.dart'; -export 'src/presenter/widgets/navigation_listener.dart'; -export 'src/presenter/widgets/widget_module.dart'; +export 'src/presenter/models/wildcard_route.dart'; export 'src/presenter/navigation/transitions/page_transition.dart'; export 'src/presenter/navigation/transitions/transitions.dart'; +export 'src/presenter/widgets/modular_app.dart'; export 'src/presenter/widgets/modular_state.dart'; -export 'package:modular_core/modular_core.dart' - show ModularRoute, Disposable, ReassembleMixin; +export 'src/presenter/widgets/navigation_listener.dart'; +export 'src/presenter/widgets/widget_module.dart'; IModularBase? _modular; @@ -53,11 +53,7 @@ void cleanGlobals() { extension InjectorExtends on Injector { /// get arguments - ModularArguments get args => injector - .get() - .value - .call() - .getOrElse((l) => ModularArguments.empty()); + ModularArguments get args => injector.get().call().getOrElse((l) => ModularArguments.empty()); } /// It acts as a Nested Browser that will be populated by the children of this route. @@ -90,11 +86,9 @@ class RouterOutletState extends State { void didChangeDependencies() { super.didChangeDependencies(); final modal = (ModalRoute.of(context)?.settings as ModularPage); - delegate ??= RouterOutletDelegate(modal.route.uri.toString(), - injector.get().value, navigatorKey); + delegate ??= RouterOutletDelegate(modal.route.uri.toString(), injector.get(), navigatorKey); final router = Router.of(context); - _backButtonDispatcher = - router.backButtonDispatcher!.createChildBackButtonDispatcher(); + _backButtonDispatcher = router.backButtonDispatcher!.createChildBackButtonDispatcher(); } @override diff --git a/flutter_modular/lib/src/infra/services/bind_service_impl.dart b/flutter_modular/lib/src/infra/services/bind_service_impl.dart index a90ee251..40084d15 100644 --- a/flutter_modular/lib/src/infra/services/bind_service_impl.dart +++ b/flutter_modular/lib/src/infra/services/bind_service_impl.dart @@ -1,7 +1,8 @@ import 'package:modular_core/modular_core.dart'; + import '../../domain/errors/errors.dart'; -import '../../shared/either.dart'; import '../../domain/services/bind_service.dart'; +import '../../shared/either.dart'; class BindServiceImpl extends BindService { final Injector injector; @@ -17,7 +18,7 @@ class BindServiceImpl extends BindService { @override Either> getBind() { try { - final result = injector.get(); + final result = injector.getBind(); return right(result); } on BindNotFound catch (e, s) { return left(BindNotFoundException('$T not found.', s)); diff --git a/flutter_modular/lib/src/presenter/widgets/widget_module.dart b/flutter_modular/lib/src/presenter/widgets/widget_module.dart index 54ef4e02..c5c82f30 100644 --- a/flutter_modular/lib/src/presenter/widgets/widget_module.dart +++ b/flutter_modular/lib/src/presenter/widgets/widget_module.dart @@ -1,10 +1,11 @@ import 'package:flutter/widgets.dart'; +import 'package:modular_core/modular_core.dart'; + import '../../domain/usecases/bind_module.dart'; import '../../domain/usecases/unbind_module.dart'; import '../../flutter_modular_module.dart'; import '../models/bind.dart'; import '../models/module.dart'; -import 'package:modular_core/modular_core.dart'; abstract class WidgetModule extends StatelessWidget implements BindContextImpl { Widget get view; @@ -35,12 +36,10 @@ abstract class WidgetModule extends StatelessWidget implements BindContextImpl { final List imports = const []; @override - List get instanciatedSingletons => - _fakeModule.instanciatedSingletons; + List get instanciatedSingletons => _fakeModule.instanciatedSingletons; @override - void instantiateSingletonBinds( - List> singletons, Injector injector) { + void instantiateSingletonBinds(List> singletons, Injector injector) { _fakeModule.instantiateSingletonBinds(singletons, injector); } @@ -67,8 +66,7 @@ abstract class WidgetModule extends StatelessWidget implements BindContextImpl { } @override - void changeBinds(List> newBinds) => - _fakeModule.changeBinds(newBinds); + void changeBinds(List> newBinds) => _fakeModule.changeBinds(newBinds); @override // ignore: invalid_use_of_visible_for_testing_member @@ -86,19 +84,17 @@ class ModularProvider extends StatefulWidget { final BindContext module; final Widget child; - const ModularProvider({Key? key, required this.module, required this.child}) - : super(key: key); + const ModularProvider({Key? key, required this.module, required this.child}) : super(key: key); @override _ModularProviderState createState() => _ModularProviderState(); } -class _ModularProviderState - extends State { +class _ModularProviderState extends State { @override void initState() { super.initState(); - injector.get().value.call(widget.module); + injector.get().call(widget.module); } @override @@ -109,6 +105,6 @@ class _ModularProviderState @override void dispose() { super.dispose(); - injector.get().value.call(); + injector.get().call(); } } diff --git a/flutter_modular/pubspec.yaml b/flutter_modular/pubspec.yaml index 9c960b76..69ee4d8b 100644 --- a/flutter_modular/pubspec.yaml +++ b/flutter_modular/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_modular description: Smart project structure with dependency injection and route management -version: 5.0.0 +version: 5.0.1 homepage: https://github.com/Flutterando/modular environment: @@ -8,7 +8,7 @@ environment: dependencies: flutter_modular_annotations: ^0.0.2 - modular_core: ">=2.0.0 <3.0.0" + modular_core: ">=2.0.1 <3.0.0" meta: ">=1.3.0 <2.0.0" flutter: sdk: flutter diff --git a/flutter_modular/test/src/flutter_modular_module_test.dart b/flutter_modular/test/src/flutter_modular_module_test.dart index 4da1e2df..95f58f42 100644 --- a/flutter_modular/test/src/flutter_modular_module_test.dart +++ b/flutter_modular/test/src/flutter_modular_module_test.dart @@ -1,9 +1,9 @@ -import 'package:flutter_modular/src/presenter/modular_base.dart'; import 'package:flutter_modular/src/flutter_modular_module.dart'; +import 'package:flutter_modular/src/presenter/modular_base.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { test('resolver injection (ModularBase)', () { - expect(injector.get().value, isA()); + expect(injector.get(), isA()); }); } diff --git a/flutter_modular/test/src/infra/services/bind_service_impl_test.dart b/flutter_modular/test/src/infra/services/bind_service_impl_test.dart index cba86443..d28bfd77 100644 --- a/flutter_modular/test/src/infra/services/bind_service_impl_test.dart +++ b/flutter_modular/test/src/infra/services/bind_service_impl_test.dart @@ -1,10 +1,10 @@ import 'package:flutter_modular/flutter_modular.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:modular_core/modular_core.dart'; -import 'package:flutter_modular/src/shared/either.dart'; import 'package:flutter_modular/src/domain/errors/errors.dart'; import 'package:flutter_modular/src/infra/services/bind_service_impl.dart'; +import 'package:flutter_modular/src/shared/either.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:modular_core/modular_core.dart'; import '../../mocks/mocks.dart'; @@ -14,16 +14,12 @@ void main() { group('getBind', () { test('should get bind', () { - when(() => injector.get()) - .thenReturn(BindEntry(bind: Bind((i) => ''), value: 'test')); - expect( - service.getBind().map((r) => r.value).getOrElse((left) => ''), - 'test'); + when(() => injector.getBind()).thenReturn(BindEntry(bind: Bind((i) => ''), value: 'test')); + expect(service.getBind().map((r) => r.value).getOrElse((left) => ''), 'test'); }); test('should throw error not found bind', () { when(() => injector.get()).thenThrow(BindNotFound('String')); - expect( - service.getBind().fold(id, id), isA()); + expect(service.getBind().fold(id, id), isA()); }); }); @@ -37,8 +33,7 @@ void main() { group('releaseScopedBinds', () { test('should return true', () { when(() => injector.removeScopedBinds()); - expect( - service.releaseScopedBinds().getOrElse((left) => throw left), unit); + expect(service.releaseScopedBinds().getOrElse((left) => throw left), unit); }); }); } diff --git a/modular_core/CHANGELOG.md b/modular_core/CHANGELOG.md index 58bc90ae..b7cdfe32 100644 --- a/modular_core/CHANGELOG.md +++ b/modular_core/CHANGELOG.md @@ -1,3 +1,6 @@ +## 2.0.1 - 2022/05/12 +* Fix: Inject.get should return instance + ## 2.0.0 - 2022/05/11 * Remove `setDisposeResolver`. * Apply `BindContract.onDispose` of `modular_interface`. diff --git a/modular_core/lib/src/di/bind_context.dart b/modular_core/lib/src/di/bind_context.dart index d509b43a..1909aa96 100644 --- a/modular_core/lib/src/di/bind_context.dart +++ b/modular_core/lib/src/di/bind_context.dart @@ -25,7 +25,8 @@ abstract class BindContextImpl implements BindContext { @override @visibleForTesting - List getProcessBinds() => _binds.where((element) => !element.export).toList(); + List getProcessBinds() => + _binds.where((element) => !element.export).toList(); @override void changeBinds(List newBinds) { _binds.removeWhere((element) => !element.alwaysSerialized); @@ -52,7 +53,9 @@ abstract class BindContextImpl implements BindContext { return _singletonBinds[type]!.cast(); } - var bind = getProcessBinds().firstWhere((b) => b.factoryFunction is T Function(Injector), orElse: () => BindEmpty()); + var bind = getProcessBinds().firstWhere( + (b) => b.factoryFunction is T Function(Injector), + orElse: () => BindEmpty()); if (bind is BindEmpty) { return null; } @@ -108,7 +111,8 @@ abstract class BindContextImpl implements BindContext { Future isReady() async { if (_mutableValue.isReadyFlag) return; _mutableValue.isReadyFlag = true; - final asyncBindList = getProcessBinds().whereType().toList(); + final asyncBindList = + getProcessBinds().whereType().toList(); for (var bind in asyncBindList) { final resolvedBind = await bind.convertToBind(); _binds.insert(0, resolvedBind); @@ -116,8 +120,10 @@ abstract class BindContextImpl implements BindContext { } @mustCallSuper - void instantiateSingletonBinds(List singletons, Injector injector) { - final filteredList = getProcessBinds().where((bind) => !bind.isLazy && !_containBind(singletons, bind)); + void instantiateSingletonBinds( + List singletons, Injector injector) { + final filteredList = getProcessBinds() + .where((bind) => !bind.isLazy && !_containBind(singletons, bind)); for (final bindElement in filteredList) { var b = bindElement.factoryFunction(injector); if (!_singletonBinds.containsKey(b.runtimeType)) { @@ -131,7 +137,9 @@ abstract class BindContextImpl implements BindContext { } bool _containBind(List singletons, BindContract bind) { - return singletons.indexWhere((element) => element.bind.factoryFunction == bind.factoryFunction) != -1; + return singletons.indexWhere((element) => + element.bind.factoryFunction == bind.factoryFunction) != + -1; } Type _getInjectType() { @@ -139,7 +147,9 @@ abstract class BindContextImpl implements BindContext { for (var singleton in _singletonBinds.values) { if (singleton.value is B) { - foundType = _singletonBinds.entries.firstWhere((map) => map.value.value == singleton.value).key; + foundType = _singletonBinds.entries + .firstWhere((map) => map.value.value == singleton.value) + .key; break; } } diff --git a/modular_core/lib/src/di/injector.dart b/modular_core/lib/src/di/injector.dart index 4b03f41c..309db5c4 100644 --- a/modular_core/lib/src/di/injector.dart +++ b/modular_core/lib/src/di/injector.dart @@ -1,15 +1,16 @@ +import 'package:characters/characters.dart'; import 'package:meta/meta.dart'; -import 'reassemble_mixin.dart'; import 'package:modular_interfaces/modular_interfaces.dart'; + import 'bind_context.dart'; +import 'reassemble_mixin.dart'; import 'resolvers.dart'; -import 'package:characters/characters.dart'; class InjectorImpl extends Injector { final _allBindContexts = {}; @override - BindEntry get() { + BindEntry getBind() { BindEntry? entry; for (var module in _allBindContexts.values.toList().reversed) { @@ -26,9 +27,13 @@ class InjectorImpl extends Injector { return entry; } + @override + B get() => getBind().value; + @override @mustCallSuper - bool isModuleAlive() => _allBindContexts.containsKey(_getType()); + bool isModuleAlive() => + _allBindContexts.containsKey(_getType()); @override @mustCallSuper @@ -46,7 +51,8 @@ class InjectorImpl extends Injector { final typeModule = module.runtimeType; if (!_allBindContexts.containsKey(typeModule)) { _allBindContexts[typeModule] = module; - (_allBindContexts[typeModule] as BindContextImpl).instantiateSingletonBinds(_getAllSingletons(), this); + (_allBindContexts[typeModule] as BindContextImpl) + .instantiateSingletonBinds(_getAllSingletons(), this); (_allBindContexts[typeModule] as BindContextImpl).tags.add(tag); debugPrint("-- $typeModule INITIALIZED"); } else { @@ -107,7 +113,9 @@ class InjectorImpl extends Injector { @override void updateBinds(BindContext context) { - final key = _allBindContexts.keys.firstWhere((key) => key.toString() == context.runtimeType.toString(), orElse: () => _KeyNotFound); + final key = _allBindContexts.keys.firstWhere( + (key) => key.toString() == context.runtimeType.toString(), + orElse: () => _KeyNotFound); if (key == _KeyNotFound) { return; } diff --git a/modular_core/pubspec.yaml b/modular_core/pubspec.yaml index c84d6108..b70be033 100644 --- a/modular_core/pubspec.yaml +++ b/modular_core/pubspec.yaml @@ -1,6 +1,6 @@ name: modular_core description: Smart project structure with dependency injection and route management -version: 2.0.0 +version: 2.0.1 homepage: https://github.com/Flutterando/modular environment: @@ -9,7 +9,8 @@ environment: dependencies: characters: ">=1.1.0 <2.0.0" meta: ">=1.3.0 <2.0.0" - modular_interfaces: ">=2.0.0 <3.0.0" + modular_interfaces: ">=2.0.1 <3.0.0" + dev_dependencies: lints: ^1.0.1 diff --git a/modular_core/test/src/di/bind_context_test.dart b/modular_core/test/src/di/bind_context_test.dart index 65b80c72..a7e9299e 100644 --- a/modular_core/test/src/di/bind_context_test.dart +++ b/modular_core/test/src/di/bind_context_test.dart @@ -101,7 +101,8 @@ void main() { }); test('instantiateSingletonBinds', () { - instance.instantiateSingletonBinds([BindEntry(bind: _Bind((i) => 0.0), value: 0.0)], injector); + instance.instantiateSingletonBinds( + [BindEntry(bind: _Bind((i) => 0.0), value: 0.0)], injector); expect(instance.instanciatedSingletons.length, 1); }); } @@ -190,11 +191,13 @@ class Repository extends IRepository with Disposable { } } -class AsyncBind extends _Bind> implements AsyncBindContract { +class AsyncBind extends _Bind> + implements AsyncBindContract { @override final Future Function(Injector i) asyncInject; - AsyncBind(this.asyncInject, {bool export = false}) : super(asyncInject, export: export); + AsyncBind(this.asyncInject, {bool export = false}) + : super(asyncInject, export: export); @override Future resolveAsyncBind() async { diff --git a/modular_core/test/src/di/injector_test.dart b/modular_core/test/src/di/injector_test.dart index 4aa87e89..afd08c06 100644 --- a/modular_core/test/src/di/injector_test.dart +++ b/modular_core/test/src/di/injector_test.dart @@ -28,7 +28,7 @@ void main() { expect(await injector.isModuleReady(), true); }); test('get bind', () { - final bindString = injector.get().value; + final bindString = injector.get(); expect(bindString, 'Jacob'); expect(() => injector(), throwsA(isA())); @@ -43,7 +43,7 @@ void main() { test('Injector removeScopedBinds', () { expect(module.instanciatedSingletons.length, 3); - final bindString = injector.get().value; + final bindString = injector.get(); expect(bindString, 'Jacob'); expect(module.instanciatedSingletons.length, 4); @@ -57,8 +57,8 @@ void main() { }); test('reassemble', () { - final withReassemble = injector.get().value; - final withlessReassemble = injector.get().value; + final withReassemble = injector.get(); + final withlessReassemble = injector.get(); expect(withReassemble.count, 0); expect(withlessReassemble.count, 0); @@ -79,8 +79,10 @@ class MyInjectModule extends BindContextImpl { _Bind((i) => 'Jacob', scoped: true), _Bind((i) => true), _Bind((i) => 0.0, lazy: false), - _Bind((i) => MyObjectWithReassemble(), lazy: false), - _Bind((i) => MyObjectWithlessReassemble(), lazy: false), + _Bind((i) => MyObjectWithReassemble(), + lazy: false), + _Bind((i) => MyObjectWithlessReassemble(), + lazy: false), ]; } diff --git a/modular_interfaces/CHANGELOG.md b/modular_interfaces/CHANGELOG.md index 3c897ff3..bf019609 100644 --- a/modular_interfaces/CHANGELOG.md +++ b/modular_interfaces/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.1 - 2022/05/12 + +- Fix: Inject.get returns instance + ## 2.0.0 - 2022/05/11 - Added `BindContract.onDispose`. diff --git a/modular_interfaces/lib/src/di/injector.dart b/modular_interfaces/lib/src/di/injector.dart index c8122551..d489470b 100644 --- a/modular_interfaces/lib/src/di/injector.dart +++ b/modular_interfaces/lib/src/di/injector.dart @@ -2,10 +2,13 @@ import 'package:modular_interfaces/modular_interfaces.dart'; /// Service injector that is responsible for searching for instances in all bind contexts. abstract class Injector { - B call() => get().value; + B call() => get(); /// Request an bind by [Type] - BindEntry get(); + B get(); + + /// Request an bindEntry by [Type] + BindEntry getBind(); /// Checks if the context (Module) is in the context of binds. bool isModuleAlive(); diff --git a/modular_interfaces/pubspec.yaml b/modular_interfaces/pubspec.yaml index 79ec34c0..26960024 100644 --- a/modular_interfaces/pubspec.yaml +++ b/modular_interfaces/pubspec.yaml @@ -1,6 +1,6 @@ name: modular_interfaces description: Smart project structure with dependency injection and route management -version: 2.0.0 +version: 2.0.1 homepage: https://github.com/Flutterando/modular # homepage: https://www.example.com From 8bbc2d53c9fa6bf408ecfa14cbe5eeccd6e6929d Mon Sep 17 00:00:00 2001 From: Jacob Moura Date: Thu, 12 May 2022 13:55:18 -0300 Subject: [PATCH 2/3] fix: test modular_test --- modular_test/pubspec.yaml | 4 ++-- modular_test/test/modular_test_test.dart | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modular_test/pubspec.yaml b/modular_test/pubspec.yaml index 3b7f4b15..d797f71e 100644 --- a/modular_test/pubspec.yaml +++ b/modular_test/pubspec.yaml @@ -1,13 +1,13 @@ name: modular_test description: Smart project structure with dependency injection and route management -version: 2.0.0 +version: 2.0.1 homepage: https://github.com/Flutterando/modular environment: sdk: ">=2.12.0 <3.0.0" dependencies: - modular_core: ">=2.0.0 <3.0.0" + modular_core: ">=2.0.1 <3.0.0" dev_dependencies: lints: ^1.0.1 diff --git a/modular_test/test/modular_test_test.dart b/modular_test/test/modular_test_test.dart index 319b58d4..a05f28c4 100644 --- a/modular_test/test/modular_test_test.dart +++ b/modular_test/test/modular_test_test.dart @@ -37,17 +37,17 @@ void main() { ); test('init Module', () { - final text = modularTracker.injector.get().value; + final text = modularTracker.injector.get(); expect(text, 'teste'); }); test('replace binds', () { - final boolean = modularTracker.injector.get().value; + final boolean = modularTracker.injector.get(); expect(boolean, false); }); test('replace binds with interface', () { - final result = modularTracker.injector.get().value; + final result = modularTracker.injector.get(); expect(result, isA()); expect(result.name, 'RepoImpl2'); }); From 65d84903beac82a750668c020b5353aa7695eac6 Mon Sep 17 00:00:00 2001 From: Jacob Moura Date: Thu, 12 May 2022 14:04:43 -0300 Subject: [PATCH 3/3] fix: flutter_modular test --- .../test/src/infra/services/bind_service_impl_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_modular/test/src/infra/services/bind_service_impl_test.dart b/flutter_modular/test/src/infra/services/bind_service_impl_test.dart index d28bfd77..e45f67d8 100644 --- a/flutter_modular/test/src/infra/services/bind_service_impl_test.dart +++ b/flutter_modular/test/src/infra/services/bind_service_impl_test.dart @@ -18,7 +18,7 @@ void main() { expect(service.getBind().map((r) => r.value).getOrElse((left) => ''), 'test'); }); test('should throw error not found bind', () { - when(() => injector.get()).thenThrow(BindNotFound('String')); + when(() => injector.getBind()).thenThrow(BindNotFound('String')); expect(service.getBind().fold(id, id), isA()); }); });