From e781090f369b671b1b94ad8752df080f6f3a140b Mon Sep 17 00:00:00 2001 From: Oleksandr Leushchenko Date: Thu, 25 Aug 2022 18:48:13 +0300 Subject: [PATCH 1/6] Add scopeName parameter to popScope --- lib/get_it.dart | 2 +- lib/get_it_impl.dart | 14 ++++++++++---- test/scope_test.dart | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/lib/get_it.dart b/lib/get_it.dart index d7aa6a6..b1f3adf 100644 --- a/lib/get_it.dart +++ b/lib/get_it.dart @@ -401,7 +401,7 @@ abstract class GetIt { /// if you passed a dispose function when you pushed this scope it will be /// called before the scope is popped. /// As dispose functions can be async, you should await this function. - Future popScope(); + Future popScope({String? scopeName}); /// if you have a lot of scopes with names you can pop (see [popScope]) all /// scopes above the scope with [name] including that scope unless [inclusive]= false diff --git a/lib/get_it_impl.dart b/lib/get_it_impl.dart index 7b99d77..cb8faa9 100644 --- a/lib/get_it_impl.dart +++ b/lib/get_it_impl.dart @@ -775,14 +775,20 @@ class _GetItImplementation implements GetIt { /// called before the scope is popped. /// As dispose functions can be async, you should await this function. @override - Future popScope() async { + Future popScope({String? scopeName}) async { throwIfNot( _scopes.length > 1, StateError( "GetIt: You are already on the base scope. you can't pop this one")); - await _currentScope.dispose(); - await _currentScope.reset(dispose: true); - _scopes.removeLast(); + final scope = scopeName != null + ? _scopes.firstWhere( + (s) => s.name == scopeName, + orElse: () => throw ArgumentError("Scope $scopeName not found"), + ) + : _currentScope; + await scope.dispose(); + await scope.reset(dispose: true); + _scopes.remove(scope); onScopeChanged?.call(false); } diff --git a/test/scope_test.dart b/test/scope_test.dart index 8162ab6..623b125 100644 --- a/test/scope_test.dart +++ b/test/scope_test.dart @@ -342,7 +342,45 @@ void main() { expect(() => getIt.get(), throwsA(const TypeMatcher())); }); + test('popscope named', () async { + final getIt = GetIt.instance; + constructorCounter = 0; + + getIt.registerSingleton(TestClass('Basescope')); + + getIt.pushNewScope(scopeName: 'scope2'); + getIt.registerSingleton(TestClass('2. scope')); + getIt.registerSingleton(TestClass2('2. scope')); + + getIt.pushNewScope(); + getIt.registerSingleton(TestClass3()); + + final instanceTestClassScope2 = getIt.get(); + + expect(instanceTestClassScope2 is TestClass, true); + expect(instanceTestClassScope2.id, '2. scope'); + await getIt.popScope(scopeName: 'scope2'); + + final instanceTestClassScope1 = getIt.get(); + + expect(instanceTestClassScope1.id, 'Basescope'); + expect(() => getIt.get(), + throwsA(const TypeMatcher())); + + final instanceTestClass3Scope3 = getIt.get(); + expect(instanceTestClass3Scope3 is TestClass3, true); + }); + test('popscope throws if scope with name not found', () async { + final getIt = GetIt.instance; + constructorCounter = 0; + + getIt.pushNewScope(scopeName: 'scope2'); + await expectLater( + () => getIt.popScope(scopeName: 'scope'), + throwsA(const TypeMatcher()), + ); + }); test('popscopeuntil inclusive=true', () async { final getIt = GetIt.instance; constructorCounter = 0; From c206635910b85eafd14d15e688671072f98fca69 Mon Sep 17 00:00:00 2001 From: Oleksandr Leushchenko Date: Thu, 25 Aug 2022 21:41:33 +0300 Subject: [PATCH 2/6] Add documentation --- lib/get_it.dart | 1 + lib/get_it_impl.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/get_it.dart b/lib/get_it.dart index b1f3adf..f50eab4 100644 --- a/lib/get_it.dart +++ b/lib/get_it.dart @@ -401,6 +401,7 @@ abstract class GetIt { /// if you passed a dispose function when you pushed this scope it will be /// called before the scope is popped. /// As dispose functions can be async, you should await this function. + /// If [scopeName] is provided, only the scope with that name will be popped. Future popScope({String? scopeName}); /// if you have a lot of scopes with names you can pop (see [popScope]) all diff --git a/lib/get_it_impl.dart b/lib/get_it_impl.dart index cb8faa9..ee604e9 100644 --- a/lib/get_it_impl.dart +++ b/lib/get_it_impl.dart @@ -774,6 +774,7 @@ class _GetItImplementation implements GetIt { /// if you passed a dispose function when you pushed this scope it will be /// called before the scope is popped. /// As dispose functions can be async, you should await this function. + /// If [scopeName] is provided, only the scope with that name will be popped. @override Future popScope({String? scopeName}) async { throwIfNot( From f4c940d786e11df57b8fb51cbfcbb7462c6dc073 Mon Sep 17 00:00:00 2001 From: Oleksandr Leushchenko Date: Thu, 25 Aug 2022 21:41:43 +0300 Subject: [PATCH 3/6] Pop the last named scope --- lib/get_it_impl.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/get_it_impl.dart b/lib/get_it_impl.dart index ee604e9..1042bf1 100644 --- a/lib/get_it_impl.dart +++ b/lib/get_it_impl.dart @@ -782,7 +782,7 @@ class _GetItImplementation implements GetIt { StateError( "GetIt: You are already on the base scope. you can't pop this one")); final scope = scopeName != null - ? _scopes.firstWhere( + ? _scopes.lastWhere( (s) => s.name == scopeName, orElse: () => throw ArgumentError("Scope $scopeName not found"), ) From b9ba69c1daf0aa0c08236b0ce0581422108d337e Mon Sep 17 00:00:00 2001 From: Oleksandr Leushchenko Date: Fri, 5 May 2023 11:50:32 +0300 Subject: [PATCH 4/6] Introduce dropScope --- README.md | 9 +++++ lib/get_it.dart | 12 +++++-- lib/get_it_impl.dart | 27 +++++++++++++++ test/scope_test.dart | 78 ++++++++++++++++++++++---------------------- 4 files changed, 85 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 0ff4032..2e5f588 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,15 @@ Another example could be a shopping basket where you want to ensure that not a c /// If no scope with [name] exists, nothing is popped and `false` is returned Future popScopesTill(String name, {bool inclusive = true}); + /// Disposes all registered factories and singletons in the provided scope, + /// then destroys (drops) the scope. If the dropped scope was the last one, + /// the previous scope becomes active again. + /// if you provided dispose functions on registration, they will be called. + /// if you passed a dispose function when you pushed this scope it will be + /// called before the scope is dropped. + /// As dispose functions can be async, you should await this function. + Future dropScope(String scopeName); + /// Clears all registered types for the current scope /// If you provided dispose function when registering they will be called /// [dispose] if `false` it only resets without calling any dispose diff --git a/lib/get_it.dart b/lib/get_it.dart index 0950eca..e7b7e9f 100644 --- a/lib/get_it.dart +++ b/lib/get_it.dart @@ -402,8 +402,7 @@ abstract class GetIt { /// if you passed a dispose function when you pushed this scope it will be /// called before the scope is popped. /// As dispose functions can be async, you should await this function. - /// If [scopeName] is provided, only the scope with that name will be popped. - Future popScope({String? scopeName}); + Future popScope(); /// if you have a lot of scopes with names you can pop (see [popScope]) all /// scopes above the scope with [name] including that scope unless [inclusive]= false @@ -412,6 +411,15 @@ abstract class GetIt { /// If no scope with [name] exists, nothing is popped and `false` is returned Future popScopesTill(String name, {bool inclusive = true}); + /// Disposes all registered factories and singletons in the provided scope, + /// then destroys (drops) the scope. If the dropped scope was the last one, + /// the previous scope becomes active again. + /// if you provided dispose functions on registration, they will be called. + /// if you passed a dispose function when you pushed this scope it will be + /// called before the scope is dropped. + /// As dispose functions can be async, you should await this function. + Future dropScope(String scopeName); + /// Returns the name of the current scope if it has one otherwise null /// if you are already on the baseScope it returns 'baseScope' String? get currentScopeName; diff --git a/lib/get_it_impl.dart b/lib/get_it_impl.dart index b5992f5..11486b8 100644 --- a/lib/get_it_impl.dart +++ b/lib/get_it_impl.dart @@ -842,6 +842,33 @@ class _GetItImplementation implements GetIt { return true; } + /// Disposes all registered factories and singletons in the provided scope, + /// then drops (destroys) the scope. If the dropped scope was the last one, + /// the previous scope becomes active again. + /// if you provided dispose functions on registration, they will be called. + /// if you passed a dispose function when you pushed this scope it will be + /// called before the scope is dropped. + /// As dispose functions can be async, you should await this function. + @override + Future dropScope(String scopeName) async { + if (currentScopeName == scopeName) { + return popScope(); + } + throwIfNot( + _scopes.length > 1, + StateError( + "GetIt: You are already on the base scope. you can't drop this one", + ), + ); + final scope = _scopes.lastWhere( + (s) => s.name == scopeName, + orElse: () => throw ArgumentError("Scope $scopeName not found"), + ); + await scope.dispose(); + await scope.reset(dispose: true); + _scopes.remove(scope); + } + @override String? get currentScopeName => _currentScope.name; diff --git a/test/scope_test.dart b/test/scope_test.dart index 248ad20..da29bdd 100644 --- a/test/scope_test.dart +++ b/test/scope_test.dart @@ -365,45 +365,6 @@ void main() { throwsA(const TypeMatcher()), ); }); - test('popscope named', () async { - final getIt = GetIt.instance; - constructorCounter = 0; - - getIt.registerSingleton(TestClass('Basescope')); - - getIt.pushNewScope(scopeName: 'scope2'); - getIt.registerSingleton(TestClass('2. scope')); - getIt.registerSingleton(TestClass2('2. scope')); - - getIt.pushNewScope(); - getIt.registerSingleton(TestClass3()); - - final instanceTestClassScope2 = getIt.get(); - - expect(instanceTestClassScope2 is TestClass, true); - expect(instanceTestClassScope2.id, '2. scope'); - - await getIt.popScope(scopeName: 'scope2'); - - final instanceTestClassScope1 = getIt.get(); - - expect(instanceTestClassScope1.id, 'Basescope'); - expect(() => getIt.get(), - throwsA(const TypeMatcher())); - - final instanceTestClass3Scope3 = getIt.get(); - expect(instanceTestClass3Scope3 is TestClass3, true); - }); - test('popscope throws if scope with name not found', () async { - final getIt = GetIt.instance; - constructorCounter = 0; - - getIt.pushNewScope(scopeName: 'scope2'); - await expectLater( - () => getIt.popScope(scopeName: 'scope'), - throwsA(const TypeMatcher()), - ); - }); test('popscopeuntil inclusive=true', () async { final getIt = GetIt.instance; constructorCounter = 0; @@ -521,6 +482,45 @@ void main() { expect(() => getIt.popScope(), throwsA(const TypeMatcher())); }); + test('dropScope', () async { + final getIt = GetIt.instance; + + getIt.registerSingleton(TestClass('Basescope')); + + getIt.pushNewScope(scopeName: 'scope2'); + getIt.registerSingleton(TestClass('2. scope')); + getIt.registerSingleton(TestClass2('2. scope')); + + getIt.pushNewScope(); + getIt.registerSingleton(TestClass3()); + + final instanceTestClassScope2 = getIt.get(); + + expect(instanceTestClassScope2 is TestClass, true); + expect(instanceTestClassScope2.id, '2. scope'); + + await getIt.dropScope('scope2'); + + final instanceTestClassScope1 = getIt.get(); + + expect(instanceTestClassScope1.id, 'Basescope'); + expect( + () => getIt.get(), + throwsA(const TypeMatcher()), + ); + + final instanceTestClass3Scope3 = getIt.get(); + expect(instanceTestClass3Scope3 is TestClass3, true); + }); + test('dropScope throws if scope with name not found', () async { + final getIt = GetIt.instance; + + getIt.pushNewScope(scopeName: 'scope2'); + await expectLater( + () => getIt.dropScope('scope'), + throwsA(const TypeMatcher()), + ); + }); test('resetScope', () async { final getIt = GetIt.instance; From 4e370ff477982545d327e011cbaada21caf096c2 Mon Sep 17 00:00:00 2001 From: Oleksandr Leushchenko Date: Fri, 5 May 2023 11:51:52 +0300 Subject: [PATCH 5/6] Code cleanup --- test/scope_test.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/scope_test.dart b/test/scope_test.dart index da29bdd..80dc919 100644 --- a/test/scope_test.dart +++ b/test/scope_test.dart @@ -365,6 +365,7 @@ void main() { throwsA(const TypeMatcher()), ); }); + test('popscopeuntil inclusive=true', () async { final getIt = GetIt.instance; constructorCounter = 0; @@ -482,6 +483,7 @@ void main() { expect(() => getIt.popScope(), throwsA(const TypeMatcher())); }); + test('dropScope', () async { final getIt = GetIt.instance; @@ -512,6 +514,7 @@ void main() { final instanceTestClass3Scope3 = getIt.get(); expect(instanceTestClass3Scope3 is TestClass3, true); }); + test('dropScope throws if scope with name not found', () async { final getIt = GetIt.instance; From 0610f3a8b351c6f4e2749ddbd8b06bb9f87c93ed Mon Sep 17 00:00:00 2001 From: Oleksandr Leushchenko Date: Fri, 5 May 2023 16:05:42 +0300 Subject: [PATCH 6/6] Fix test --- test/scope_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/scope_test.dart b/test/scope_test.dart index f8a8be8..a24fc00 100644 --- a/test/scope_test.dart +++ b/test/scope_test.dart @@ -508,7 +508,7 @@ void main() { expect(instanceTestClassScope1.id, 'Basescope'); expect( () => getIt.get(), - throwsA(const TypeMatcher()), + throwsA(const TypeMatcher()), ); final instanceTestClass3Scope3 = getIt.get();