diff --git a/CHANGELOG.md b/CHANGELOG.md index e372942..9d445ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +## [8.0.0-pre-5] - 25.06.2024 adding `changeTypeInstanceName` ## [8.0.0-pre-4] - 03.06.2024 fixing bug in `unregister` that happened if you tried to unregister a named registration by providing an istance instead of the type and the instance name ## [8.0.0-pre-3] - 31.05.2024 releaseInstance will now throw if the instance isn't registered ## [8.0.0-pre-2] - 29.05.2024 fixing negitiv reference count diff --git a/lib/get_it.dart b/lib/get_it.dart index ff59ea8..be624a9 100644 --- a/lib/get_it.dart +++ b/lib/get_it.dart @@ -422,6 +422,24 @@ abstract class GetIt { /// is registered inside GetIt bool isRegistered({Object? instance, String? instanceName}); + /// In some cases it can be necessary to change the name of a registered instance + /// This avoids to unregister and reregister the instance which might cause trouble + /// with disposing functions. + /// IMPORTANT: This will only change the the first instance that is found while + /// searching the scopes. + /// If the new name is already in use in the current scope it will throw a + /// StateError + /// [instanceName] the current name of the instance + /// [newInstanceName] the new name of the instance + /// [instance] the instance itself that can be used instead of + /// providing the type and the name. If [instance] is null the type and the name + /// have to be provided + void changeTypeInstanceName({ + String? instanceName, + required String newInstanceName, + T? instance, + }); + /// Clears all registered types in the reverse order in which they were registered. /// Handy when writing unit tests or when disposing services that depend on each other. /// If you provided dispose function when registering they will be called diff --git a/lib/get_it_impl.dart b/lib/get_it_impl.dart index 9520118..8d17784 100644 --- a/lib/get_it_impl.dart +++ b/lib/get_it_impl.dart @@ -62,7 +62,7 @@ class _ServiceFactory { final DisposingFunc? disposeFunction; /// In case of a named registration the instance name is here stored for easy access - final String? instanceName; + String? instanceName; /// true if one of the async registration functions have been used final bool isAsync; @@ -1034,6 +1034,55 @@ class _GetItImplementation implements GetIt { } } + /// In some cases it can be necessary to change the name of a registered instance + /// This avoids to unregister and reregister the instance which might cause trouble + /// with disposing functions. + /// IMPORTANT: This will only change the the first instance that is found while + /// searching the scopes. + /// If the new name is already in use in the current scope it will throw a + /// StateError + /// [instanceName] the current name of the instance + /// [newInstanceName] the new name of the instance + /// [instance] the instance itself that can be used instead of + /// providing the type and the name. If [instance] is null the type and the name + /// have to be provided + @override + void changeTypeInstanceName({ + String? instanceName, + required String newInstanceName, + T? instance, + }) { + assert(instance != null || instanceName != null, + 'You have to provide either an instance or an instanceName'); + + final factoryToRename = instance != null + ? _findFactoryByInstance(instance) + : _findFactoryByNameAndType(instanceName); + + if (instance != null) { + instanceName = factoryToRename.instanceName; + } + + throwIfNot( + factoryToRename.isNamedRegistration, + StateError( + 'This instance $instance is not registered with a name', + ), + ); + + final typeRegistration = factoryToRename.registeredIn; + throwIf( + typeRegistration.namedFactories.containsKey(newInstanceName), + StateError( + 'There is already an instance of type ${factoryToRename.registrationType} registered with the name $newInstanceName', + ), + ); + + typeRegistration.namedFactories[newInstanceName] = factoryToRename; + typeRegistration.namedFactories.remove(instanceName); + factoryToRename.instanceName = newInstanceName; + } + /// Clears the instance of a lazy singleton, /// being able to call the factory function on the next call /// of [get] on that type again. diff --git a/pubspec.yaml b/pubspec.yaml index 7d447cf..4b5c293 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: get_it description: Simple direct Service Locator that allows to decouple the interface from a concrete implementation and to access the concrete implementation from everywhere in your App" -version: 8.0.0-pre-4 +version: 8.0.0-pre-5 maintainer: Thomas Burkhart (@escamoteur) homepage: https://github.com/fluttercommunity/get_it funding: diff --git a/test/get_it_test.dart b/test/get_it_test.dart index b13eafb..afc6ed9 100644 --- a/test/get_it_test.dart +++ b/test/get_it_test.dart @@ -1019,6 +1019,77 @@ void main() { throwsA(const TypeMatcher()), ); }); + test('change registration name with type and name', () async { + final getIt = GetIt.instance; + disposeCounter = 0; + + getIt.registerSingleton(TestClass(), instanceName: 'instanceName'); + + final TestClass instance1 = getIt.get(instanceName: 'instanceName'); + + expect(instance1 is TestClass, true); + + getIt.changeTypeInstanceName( + instanceName: 'instanceName', + newInstanceName: 'instanceName2', + ); + + expect(disposeCounter, 0); + + expect( + () => getIt(instanceName: 'instanceName'), + throwsA(const TypeMatcher()), + ); + expect( + getIt(instanceName: 'instanceName2'), + const TypeMatcher(), + ); + }); + + test('change registration name with type and name existing name', () async { + final getIt = GetIt.instance; + + getIt.registerSingleton(TestClass(), instanceName: 'instanceName'); + getIt.registerSingleton(TestClass(), instanceName: 'instanceNameExisting'); + + final TestClass instance1 = getIt.get(instanceName: 'instanceName'); + + expect(instance1 is TestClass, true); + + expect(() { + getIt.changeTypeInstanceName( + instanceName: 'instanceName', + newInstanceName: 'instanceNameExisting', + ); + }, throwsA(const TypeMatcher())); + }); + + test('change registration name of instance', () async { + final getIt = GetIt.instance; + disposeCounter = 0; + + getIt.registerSingleton(TestClass(), instanceName: 'instanceName'); + + final TestClass instance1 = getIt.get(instanceName: 'instanceName'); + + expect(instance1 is TestClass, true); + + getIt.changeTypeInstanceName( + instance: instance1, + newInstanceName: 'instanceName2', + ); + + expect(disposeCounter, 0); + + expect( + () => getIt(instanceName: 'instanceName'), + throwsA(const TypeMatcher()), + ); + expect( + getIt(instanceName: 'instanceName2'), + const TypeMatcher(), + ); + }); test( 'can register a singleton with instanceName and retrieve it with generic parameters and instanceName',