From bd383239cd3991a385e912ea0d96d0d363eb8b49 Mon Sep 17 00:00:00 2001 From: Romain BOISSELLE Date: Sun, 13 Nov 2022 22:05:24 +0100 Subject: [PATCH] implement new set binding API (#384) --- .../kodein/di/jxinject/jxInjectorModule.kt | 7 +- .../src/commonMain/kotlin/org/kodein/di/DI.kt | 112 +++++++ .../kotlin/org/kodein/di/SetBindings.kt | 81 ++++- .../kotlin/org/kodein/di/bindings/set.kt | 5 +- .../org/kodein/di/internal/DIBuilderImpl.kt | 155 ++++++++- .../org/kodein/di/Tests_18_MultiBindings.kt | 232 ++++++++++++- .../di/GenericJvmTests_18_MultiBindings.kt | 309 +++++++++++++++++- 7 files changed, 872 insertions(+), 29 deletions(-) diff --git a/kodein-di-jxinject-jvm/src/main/kotlin/org/kodein/di/jxinject/jxInjectorModule.kt b/kodein-di-jxinject-jvm/src/main/kotlin/org/kodein/di/jxinject/jxInjectorModule.kt index b7666285..daa75770 100644 --- a/kodein-di-jxinject-jvm/src/main/kotlin/org/kodein/di/jxinject/jxInjectorModule.kt +++ b/kodein-di-jxinject-jvm/src/main/kotlin/org/kodein/di/jxinject/jxInjectorModule.kt @@ -30,7 +30,9 @@ public val DirectDI.jx: JxInjector get() = JxInjector(this, Instance(erased(), n /** @suppress */ @Suppress("UNCHECKED_CAST") public fun DI.Builder.jxQualifier(cls: Class, tagProvider: (T) -> Any) { - Bind(erased()).InSet(erasedSet()) with InstanceBinding(erased(), JxInjectorContainer.Qualifier(cls, tagProvider as (Annotation) -> Any)) + BindInSet(type = erased()) { + add { InstanceBinding(erased(), JxInjectorContainer.Qualifier(cls, tagProvider as (Annotation) -> Any)) } + } } /** @@ -40,7 +42,8 @@ public fun DI.Builder.jxQualifier(cls: Class, tagProvider: (T * @receiver DI Builder. * @param tagProvider A function that transforms an annotation of type `T` into a tag. */ -internal inline fun DI.Builder.jxQualifier(noinline tagProvider: (T) -> Any) = jxQualifier(T::class.java, tagProvider) +internal inline fun DI.Builder.jxQualifier(noinline tagProvider: (T) -> Any) = + jxQualifier(T::class.java, tagProvider) /** * Utility function that eases the retrieval of a [JxInjector]. diff --git a/kodein-di/src/commonMain/kotlin/org/kodein/di/DI.kt b/kodein-di/src/commonMain/kotlin/org/kodein/di/DI.kt index cc14265c..432adc25 100644 --- a/kodein-di/src/commonMain/kotlin/org/kodein/di/DI.kt +++ b/kodein-di/src/commonMain/kotlin/org/kodein/di/DI.kt @@ -277,6 +277,46 @@ public interface DI : DIAware { public fun With(valueType: TypeToken, value: T) } + /** + * Manage multiple bindings in a [Set] + */ + public interface SetBinder { + /** + * Add a binding in the [Set] of type [T] + * + * @param createBinding The builder that should add binding in the set. + */ + public fun add(createBinding: () -> DIBinding<*, *, out T>) + /** + * Add a binding in the [Set] of type [T], and also in the DI container + * + * @param tag The tag to bind. + * @param overrides Whether this bind **must** or **must not** override an existing binding. + * @param createBinding The builder that should add binding in the set. + */ + public fun bind(tag: Any? = null, overrides: Boolean? = null, createBinding: () -> DIBinding<*, *, out T>) + } + + /** + * Manage multiple bindings, with type argument, in a [Set] + */ + public interface ArgSetBinder { + /** + * Add a binding in the [Set] of type [T] + * + * @param createBinding The builder that should add binding in the set. + */ + public fun add(createBinding: () -> DIBinding<*, in A, out T>) + /** + * Add a binding in the [Set] of type [T], and also in the DI container + * + * @param tag The tag to bind. + * @param overrides Whether this bind **must** or **must not** override an existing binding. + * @param createBinding The builder that should add binding in the set. + */ + public fun bind(tag: Any? = null, overrides: Boolean? = null, createBinding: () -> DIBinding<*, in A, out T>) + } + /** * Attaches the binding of a given type with a given tag. * @@ -286,6 +326,64 @@ public interface DI : DIAware { */ public fun Bind(tag: Any? = null, overrides: Boolean? = null, binding: DIBinding<*, *, T>) + /** + * Creates a Set binding of a given type with a given tag and attaches multiple bindings to it. + * + * @param T The type of value to bind. + * @param tag The tag to bind. + * @param overrides Whether this bind **must** or **must not** override an existing binding. + */ + public fun BindInSet( + tag: Any? = null, + overrides: Boolean? = null, + type: TypeToken, + creator: SetBinder.() -> Unit + ) + + /** + * Attaches multiple bindings in a Set binding of a given type with a given tag. + * + * @param T The type of value to bind. + * @param tag The tag to bind. + * @param overrides Whether this bind **must** or **must not** override an existing binding. + */ + public fun InBindSet( + tag: Any? = null, + overrides: Boolean? = null, + type: TypeToken, + creator: SetBinder.() -> Unit + ) + + /** + * Creates a Set binding of a given type with a given tag and attaches multiple bindings to it. + * + * @param T The type of value to bind. + * @param tag The tag to bind. + * @param overrides Whether this bind **must** or **must not** override an existing binding. + */ + public fun BindInArgSet( + tag: Any? = null, + overrides: Boolean? = null, + argType: TypeToken, + type: TypeToken, + creator: ArgSetBinder.() -> Unit + ) + + /** + * Attaches multiple bindings in a Set binding of a given type with a given tag. + * + * @param T The type of value to bind. + * @param tag The tag to bind. + * @param overrides Whether this bind **must** or **must not** override an existing binding. + */ + public fun InBindArgSet( + tag: Any? = null, + overrides: Boolean? = null, + argType: TypeToken, + type: TypeToken, + creator: ArgSetBinder.() -> Unit + ) + /** * Attaches the binding of a given type with a given tag. * @@ -293,12 +391,26 @@ public interface DI : DIAware { * @param tag The tag to bind. * @param overrides Whether this bind **must** or **must not** override an existing binding. */ + @Deprecated("Use AddBindInSet instead.", ReplaceWith("AddBindInSet")) public fun BindSet( tag: Any? = null, overrides: Boolean? = null, binding: DIBinding<*, *, T>, ) + /** + * Attaches multiple bindings in a Set binding of a given type with a given tag. + * + * @param T The type of value to bind. + * @param tag The tag to bind. + * @param overrides Whether this bind **must** or **must not** override an existing binding. + */ + public fun AddBindInSet( + tag: Any? = null, + overrides: Boolean? = null, + binding: DIBinding<*, *, T>, + ) + /** * Starts the binding of a given type with a given tag. * diff --git a/kodein-di/src/commonMain/kotlin/org/kodein/di/SetBindings.kt b/kodein-di/src/commonMain/kotlin/org/kodein/di/SetBindings.kt index b8c9300d..86e2bb23 100644 --- a/kodein-di/src/commonMain/kotlin/org/kodein/di/SetBindings.kt +++ b/kodein-di/src/commonMain/kotlin/org/kodein/di/SetBindings.kt @@ -1,4 +1,4 @@ -@file:Suppress("unused", "UNCHECKED_CAST") +@file:Suppress("unused", "unchecked_cast", "deprecation") package org.kodein.di @@ -23,9 +23,41 @@ import org.kodein.type.generic public inline fun DI.Builder.bindSet(tag: Any? = null, overrides: Boolean? = null): Unit = Bind( tag = tag, overrides = overrides, - binding = SetBinding(TypeToken.Any, generic(), erasedComp(Set::class, generic()) as TypeToken>) + binding = SetBinding( + contextType = TypeToken.Any, + _elementType = generic(), + createdType = erasedComp(Set::class, generic()) as TypeToken> + ) ) +/** + * Creates a set and add multiple bindings to it. + * + * T generics will be erased! + * + * @param T The created type. + * @param creator The builder that should add binding in the set. + */ +public inline fun DI.Builder.bindSet( + tag: Any? = null, + overrides: Boolean? = null, + noinline creator: DI.Builder.SetBinder.() -> Unit +): Unit = BindInSet(tag = tag, overrides = overrides, type = generic(), creator = creator) + +/** + * Add multiple bindings in an existing set + * + * T generics will be erased! + * + * @param T The binding type of the targeted set. + * @param creator The builder that should add binding in the set. + */ +public inline fun DI.Builder.inBindSet( + tag: Any? = null, + overrides: Boolean? = null, + noinline creator: DI.Builder.SetBinder.() -> Unit +): Unit = InBindSet(tag = tag, overrides = overrides, type = generic(), creator = creator) + /** * Creates a set: multiple bindings can be added in this set. * @@ -33,7 +65,6 @@ public inline fun DI.Builder.bindSet(tag: Any? = null, overrid * * @param A The argument type. * @param T The created type. - * @return A set binding ready to be bound. */ @Suppress("RemoveExplicitTypeArguments") public inline fun DI.Builder.bindArgSet( @@ -43,13 +74,28 @@ public inline fun DI.Builder.bindArgSet( tag = tag, overrides = overrides, binding = ArgSetBinding( - TypeToken.Any, - generic(), - generic(), - erasedComp(Set::class, generic()) as TypeToken> + contextType = TypeToken.Any, + argType = generic(), + _elementType = generic(), + createdType = erasedComp(Set::class, generic()) as TypeToken> ) ) +/** + * Creates a set and add multiple bindings to it. + * + * T generics will be erased! + * + * @param A The argument type. + * @param T The created type. + * @param creator The builder that should add binding in the set. + */ +public inline fun DI.Builder.bindArgSet( + tag: Any? = null, + overrides: Boolean? = null, + noinline creator: DI.Builder.ArgSetBinder.() -> Unit +): Unit = BindInArgSet(tag = tag, overrides = overrides, argType = generic(), type = generic(), creator = creator) + /** * Defines that the binding will be saved in a set binding. * @@ -57,8 +103,9 @@ public inline fun DI.Builder.bindArgSet( * * @param T The type of the binding. */ +@Deprecated("Use addInBindSet instead") public inline fun DI.Builder.TypeBinder.inSet(): TypeBinderInSet> = - InSet(erasedComp(Set::class, generic()) as TypeToken>) + InSet(setTypeToken = erasedComp(Set::class, generic()) as TypeToken>) /** * Defines that the binding will be saved in a set binding. @@ -67,10 +114,22 @@ public inline fun DI.Builder.TypeBinder.inSet(): TypeBinder * * @param T The type of the binding. */ +@Deprecated("Use addInBindSet instead", ReplaceWith("addInBindSet")) public inline fun DI.Builder.inSet( tag: Any? = null, overrides: Boolean? = null, creator: () -> DIBinding<*, *, T> -): Unit { - BindSet(tag = tag, overrides = overrides, creator()) -} +): Unit = BindSet(tag = tag, overrides = overrides, creator()) + +/** + * Defines that the binding will be saved in a set binding. + * + * T generics will be erased! + * + * @param T The type of the binding. + */ +public inline fun DI.Builder.addInBindSet( + tag: Any? = null, + overrides: Boolean? = null, + creator: () -> DIBinding<*, *, T> +): Unit = AddBindInSet(tag = tag, overrides = overrides, binding = creator()) diff --git a/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/set.kt b/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/set.kt index 8a08a0dd..ce3dc105 100644 --- a/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/set.kt +++ b/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/set.kt @@ -69,7 +69,6 @@ public class SetBinding( override val createdType: TypeToken> ) : NoArgDIBinding>, BaseMultiBinding() { - @Suppress("UNCHECKED_CAST") override val set = LinkedHashSet>() override fun getFactory(key: DI.Key>, di: BindingDI): (Unit) -> Set { @@ -96,6 +95,7 @@ public class SetBinding( * * @param T The type of the binding in the set. */ +@Deprecated("TypeBinderInSet must be replaced by the use of bindSet / inBindSet / addInBindSet builders.") public class TypeBinderInSet internal constructor( private val _binder: DI.Builder.TypeBinder, private val _colTypeToken: TypeToken @@ -129,6 +129,7 @@ public class TypeBinderInSet internal constructor( * @param T The provided type of all bindings in the set. * @param setTypeToken The type of the bound set. */ -@Suppress("FunctionName") +@Suppress("FunctionName", "deprecation") +@Deprecated("InSet must be replaced by the use of bindSet / inBindSet / addInBindSet builders.") public fun DI.Builder.TypeBinder.InSet(setTypeToken: TypeToken>): TypeBinderInSet> = TypeBinderInSet(this, setTypeToken) diff --git a/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIBuilderImpl.kt b/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIBuilderImpl.kt index 5c16b2e1..c6adf11e 100644 --- a/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIBuilderImpl.kt +++ b/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIBuilderImpl.kt @@ -5,6 +5,7 @@ package org.kodein.di.internal import org.kodein.di.Copy import org.kodein.di.DI import org.kodein.di.DirectDI +import org.kodein.di.bindings.ArgSetBinding import org.kodein.di.bindings.BaseMultiBinding import org.kodein.di.bindings.ContextTranslator import org.kodein.di.bindings.DIBinding @@ -13,6 +14,7 @@ import org.kodein.di.bindings.InstanceBinding import org.kodein.di.bindings.NoScope import org.kodein.di.bindings.Provider import org.kodein.di.bindings.Scope +import org.kodein.di.bindings.SetBinding import org.kodein.type.TypeToken import org.kodein.type.erasedComp @@ -70,6 +72,98 @@ internal open class DIBuilderImpl internal constructor( Bind(tag = _tag, overrides = _overrides, binding = InstanceBinding(valueType, value)) } + @Suppress("unchecked_cast") + inner class SetBinder internal constructor( + private val setBindingTag: Any? = null, + private val setBindingType: TypeToken, + setBindingOverrides: Boolean? = null, + addSetBindingToContainer: Boolean = true, + ) : DI.Builder.SetBinder { + + private val setBinding: BaseMultiBinding<*, *, T> by lazy { + val setType = erasedComp(Set::class, setBindingType) as TypeToken> + val setKey = DI.Key(TypeToken.Any, TypeToken.Unit, setType, setBindingTag) + + val setBinding = containerBuilder.bindingsMap[setKey]?.first() + ?: throw IllegalStateException("No set binding to $setKey") + setBinding.binding as? BaseMultiBinding<*, *, T> + ?: throw IllegalStateException("$setKey is associated to a ${setBinding.binding.factoryName()} while it should be associated with bindingSet") + } + + init { + if (addSetBindingToContainer) { + Bind( + tag = setBindingTag, + overrides = setBindingOverrides, + binding = SetBinding( + contextType = TypeToken.Any, + _elementType = setBindingType, + createdType = erasedComp(Set::class, setBindingType) as TypeToken> + ) + ) + } + } + + override fun add(createBinding: () -> DIBinding<*, *, out T>) { + val binding = createBinding() + (setBinding.set as MutableSet>).add(binding) + } + + override fun bind(tag: Any?, overrides: Boolean?, createBinding: () -> DIBinding<*, *, out T>) { + val binding = createBinding() + (setBinding.set as MutableSet>).add(binding) + + Bind(tag = tag, overrides = overrides, binding = binding) + } + } + + @Suppress("unchecked_cast") + inner class ArgSetBinder( + private val setBindingTag: Any? = null, + setBindingOverrides: Boolean? = null, + private val setBindingArgType: TypeToken, + private val setBindingType: TypeToken, + addSetBindingToContainer: Boolean = true, + ) : DI.Builder.ArgSetBinder { + + private val setBinding: BaseMultiBinding<*, in A, out T> by lazy { + val setType = erasedComp(Set::class, setBindingType) as TypeToken> + val setKey = DI.Key(TypeToken.Any, setBindingArgType, setType, setBindingTag) + + val setBinding = containerBuilder.bindingsMap[setKey]?.first() + ?: throw IllegalStateException("No set binding to $setKey") + setBinding.binding as? BaseMultiBinding<*, A, T> + ?: throw IllegalStateException("$setKey is associated to a ${setBinding.binding.factoryName()} while it should be associated with bindingSet") + } + + init { + if (addSetBindingToContainer) { + Bind( + tag = setBindingTag, + overrides = setBindingOverrides, + binding = ArgSetBinding( + TypeToken.Any, + setBindingArgType, + setBindingType, + erasedComp(Set::class, setBindingType) as TypeToken> + ) + ) + } + } + + override fun add(createBinding: () -> DIBinding<*, in A, out T>) { + val binding = createBinding() + (setBinding.set as MutableSet>).add(binding) + } + + override fun bind(tag: Any?, overrides: Boolean?, createBinding: () -> DIBinding<*, in A, out T>) { + val binding = createBinding() + (setBinding.set as MutableSet>).add(binding) + + Bind(tag = tag, overrides = overrides, binding = binding) + } + } + @Suppress("FunctionName") override fun Bind( type: TypeToken, @@ -91,11 +185,70 @@ internal open class DIBuilderImpl internal constructor( ) } - @Suppress("unchecked_cast") + override fun BindInSet( + tag: Any?, + overrides: Boolean?, + type: TypeToken, + creator: DI.Builder.SetBinder.() -> Unit + ) = creator(SetBinder(tag, type, overrides)) + + override fun InBindSet( + tag: Any?, + overrides: Boolean?, + type: TypeToken, + creator: DI.Builder.SetBinder.() -> Unit + ) = creator( + SetBinder( + setBindingTag = tag, + setBindingType = type, + setBindingOverrides = overrides, + addSetBindingToContainer = false + ) + ) + + override fun BindInArgSet( + tag: Any?, + overrides: Boolean?, + argType: TypeToken, + type: TypeToken, + creator: DI.Builder.ArgSetBinder.() -> Unit + ) = creator( + ArgSetBinder( + setBindingTag = tag, + setBindingOverrides = overrides, + setBindingArgType = argType, + setBindingType = type + ) + ) + + override fun InBindArgSet( + tag: Any?, + overrides: Boolean?, + argType: TypeToken, + type: TypeToken, + creator: DI.Builder.ArgSetBinder.() -> Unit + ) = creator( + ArgSetBinder( + setBindingTag = tag, + setBindingOverrides = overrides, + setBindingArgType = argType, + setBindingType = type, + addSetBindingToContainer = false + ) + ) + + @Deprecated("Use AddBindInSet instead.", ReplaceWith("AddBindInSet")) override fun BindSet( tag: Any?, overrides: Boolean?, binding: DIBinding<*, *, T>, + ) = AddBindInSet(tag = tag, overrides = overrides, binding = binding) + + @Suppress("unchecked_cast") + override fun AddBindInSet( + tag: Any?, + overrides: Boolean?, + binding: DIBinding<*, *, T>, ) { val setType = erasedComp(Set::class, binding.createdType) as TypeToken> val setKey = DI.Key(binding.contextType, binding.argType, setType, tag) diff --git a/kodein-di/src/commonTest/kotlin/org/kodein/di/Tests_18_MultiBindings.kt b/kodein-di/src/commonTest/kotlin/org/kodein/di/Tests_18_MultiBindings.kt index 405b1504..9b32b530 100644 --- a/kodein-di/src/commonTest/kotlin/org/kodein/di/Tests_18_MultiBindings.kt +++ b/kodein-di/src/commonTest/kotlin/org/kodein/di/Tests_18_MultiBindings.kt @@ -1,7 +1,8 @@ +@file:Suppress("deprecation") + package org.kodein.di import org.kodein.di.test.* -import org.kodein.type.generic import kotlin.test.* @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -39,7 +40,75 @@ class Tests_18_MultiBindings { } @Test - fun test_01_MultiMap() { + fun test_01_MultiSet_with_builder() { + val di = DI { + bindSet { + add { singleton { Person("Salomon") } } + add { provider { Person("Laila") } } + } + + bind { provider { instance>().toList() } } + } + + val persons1: Set by di.instance() + + assertTrue(Person("Salomon") in persons1) + assertTrue(Person("Laila") in persons1) + + val persons2: Set by di.instance() + + val salomon1 = persons1.first { it.name == "Salomon" } + val salomon2 = persons2.first { it.name == "Salomon" } + + val laila1 = persons1.first { it.name == "Laila" } + val laila2 = persons2.first { it.name == "Laila" } + + assertSame(salomon1, salomon2) + assertNotSame(laila1, laila2) + + val list: List by di.instance() + assertEquals(persons1.toList(), list) + } + + @Test + fun test_02_MultiSet_with_modules() { + val m1 by DI.Module { + bindSet() + inBindSet { + add { singleton { Person("Salomon") } } + } + } + val m2 by DI.Module { + addInBindSet { provider { Person("Laila") } } + } + + val di = DI { + importAll(m1, m2) + bind { provider { instance>().toList() } } + } + + val persons1: Set by di.instance() + + assertTrue(Person("Salomon") in persons1) + assertTrue(Person("Laila") in persons1) + + val persons2: Set by di.instance() + + val salomon1 = persons1.first { it.name == "Salomon" } + val salomon2 = persons2.first { it.name == "Salomon" } + + val laila1 = persons1.first { it.name == "Laila" } + val laila2 = persons2.first { it.name == "Laila" } + + assertSame(salomon1, salomon2) + assertNotSame(laila1, laila2) + + val list: List by di.instance() + assertEquals(persons1.toList(), list) + } + + @Test + fun test_03_MultiMap() { val di = DI { bindSet() @@ -56,7 +125,24 @@ class Tests_18_MultiBindings { } @Test - fun test_02_MultiSetWithArg() { + fun test_04_MultiMap_with_builder() { + val di = DI { + bindSet { + add { singleton { "so" to Person("Salomon") } } + add { provider { "loulou" to Person("Laila") }} + } + + bind>() with provider { instance().toMap() } + } + + val persons: Map = di.direct.instance() + + assertEquals(Person("Salomon"), persons["so"]) + assertEquals(Person("Laila"), persons["loulou"]) + } + + @Test + fun test_05_MultiSetWithArg() { val di = DI { bindArgSet() bind().inSet() with multiton { lastName: String -> Person("Salomon $lastName") } @@ -69,7 +155,21 @@ class Tests_18_MultiBindings { } @Test - fun test_03_SimpleMultiSet() { + fun test_06_MultiSetWithArg_with_builder() { + val di = DI { + bindArgSet { + add { multiton { lastName: String -> Person("Salomon $lastName") } } + add { factory { lastName: String -> Person("Laila $lastName") } } + } + } + + val persons: Set by di.instance(arg = "BRYS") + assertTrue(Person("Salomon BRYS") in persons) + assertTrue(Person("Laila BRYS") in persons) + } + + @Test + fun test_07_SimpleMultiSet() { val di = DI { bindSet() @@ -100,7 +200,38 @@ class Tests_18_MultiBindings { } @Test - fun test_04_SimpleMultiMap() { + fun test_08_SimpleMultiSet_with_builder() { + val di = DI { + bindSet { + add { singleton { Person("Salomon") } } + add { provider { Person("Laila") } } + } + + bind>() with provider { instance>().toList() } + } + + val persons1: Set by di.instance() + + assertTrue(Person("Salomon") in persons1) + assertTrue(Person("Laila") in persons1) + + val persons2: Set by di.instance() + + val salomon1 = persons1.first { it.name == "Salomon" } + val salomon2 = persons2.first { it.name == "Salomon" } + + val laila1 = persons1.first { it.name == "Laila" } + val laila2 = persons2.first { it.name == "Laila" } + + assertSame(salomon1, salomon2) + assertNotSame(laila1, laila2) + + val list: List by di.instance() + assertEquals(persons1.toList(), list) + } + + @Test + fun test_09_SimpleMultiMap() { val di = DI { bindSet() @@ -117,7 +248,24 @@ class Tests_18_MultiBindings { } @Test - fun test_05_SimpleMultiSetWithArg() { + fun test_10_SimpleMultiMap_with_builder() { + val di = DI { + bindSet { + add { singleton { "so" to Person("Salomon") } } + add { provider { "loulou" to Person("Laila") } } + } + + bind>() with provider { instance().toMap() } + } + + val persons: Map = di.direct.instance() + + assertEquals(Person("Salomon"), persons["so"]) + assertEquals(Person("Laila"), persons["loulou"]) + } + + @Test + fun test_11_SimpleMultiSetWithArg() { val di = DI { bindArgSet() inSet { factory { lastName: String -> Person("Salomon $lastName") } } @@ -129,4 +277,76 @@ class Tests_18_MultiBindings { assertTrue(Person("Laila BRYS") in persons) } + @Test + fun test_12_SimpleMultiSetWithArg_with_builder() { + val di = DI { + bindArgSet { + add { factory { lastName: String -> Person("Salomon $lastName") } } + add { factory { lastName: String -> Person("Laila $lastName") } } + } + } + + val persons: Set by di.instance(arg = "BRYS") + assertTrue(Person("Salomon BRYS") in persons) + assertTrue(Person("Laila BRYS") in persons) + } + + @Test + fun test_13_MultiSet_with_delegate() { + val di = DI { + bind { singleton { Person("Salomon") } } + + bindSet { + add { singleton { Person("Laila") } } + add { singleton { instance() } } + } + } + + val persons: Set by di.instance() + val salomon: IPerson by di.instance() + + assertTrue(Person("Salomon") in persons) + assertTrue(Person("Laila") in persons) + + assertSame(persons.last(), salomon) + } + + @Test + fun test_14_MultiSet_with_tag() { + val di = DI { + bindSet { + add { singleton { Person("Laila") } } + bind("ANOTHER_TAG") { singleton { Person("Salomon") } } + } + } + + val persons: Set by di.instance() + val salomon: Person by di.instance("ANOTHER_TAG") + + assertTrue(Person("Salomon") in persons) + assertTrue(Person("Laila") in persons) + + assertSame(persons.last(), salomon) + } + + @Test + fun test_15_MultiSet_with_overrides() { + val di = DI { + bind { singleton { Person("Romain") } } + + bindSet { + add { singleton { Person("Laila") } } + bind(overrides = true) { singleton { Person("Salomon") } } + } + } + + val persons: Set by di.instance() + val salomon: Person by di.instance() + + assertTrue(Person("Romain") !in persons) + assertTrue(Person("Salomon") in persons) + assertTrue(Person("Laila") in persons) + + assertSame(persons.last(), salomon) + } } diff --git a/kodein-di/src/jvmTest/kotlin/org/kodein/di/GenericJvmTests_18_MultiBindings.kt b/kodein-di/src/jvmTest/kotlin/org/kodein/di/GenericJvmTests_18_MultiBindings.kt index 3f450aa9..ca4925c3 100644 --- a/kodein-di/src/jvmTest/kotlin/org/kodein/di/GenericJvmTests_18_MultiBindings.kt +++ b/kodein-di/src/jvmTest/kotlin/org/kodein/di/GenericJvmTests_18_MultiBindings.kt @@ -1,3 +1,5 @@ +@file:Suppress("deprecation") + package org.kodein.di import org.kodein.di.test.* @@ -8,7 +10,7 @@ class GenericJvmTests_18_MultiBindings { @Test fun test_00_MultiSet() { - val kodein = DI { + val di = DI { bindSet() bind().inSet() with singleton { Person("Salomon") } @@ -17,12 +19,43 @@ class GenericJvmTests_18_MultiBindings { bind>() with provider { instance>().toList() } } - val persons1: Set by kodein.instance() + val persons1: Set by di.instance() + + assertTrue(Person("Salomon") in persons1) + assertTrue(Person("Laila") in persons1) + + val persons2: Set by di.instance() + + val salomon1 = persons1.first { it.name == "Salomon" } + val salomon2 = persons2.first { it.name == "Salomon" } + + val laila1 = persons1.first { it.name == "Laila" } + val laila2 = persons2.first { it.name == "Laila" } + + assertSame(salomon1, salomon2) + assertNotSame(laila1, laila2) + + val list: List by di.instance() + assertEquals(persons1.toList(), list) + } + + @Test + fun test_01_MultiSet_with_builder() { + val di = DI { + bindSet { + add { singleton { Person("Salomon") } } + add { provider { Person("Laila") } } + } + + bind { provider { instance>().toList() } } + } + + val persons1: Set by di.instance() assertTrue(Person("Salomon") in persons1) assertTrue(Person("Laila") in persons1) - val persons2: Set by kodein.instance() + val persons2: Set by di.instance() val salomon1 = persons1.first { it.name == "Salomon" } val salomon2 = persons2.first { it.name == "Salomon" } @@ -33,13 +66,50 @@ class GenericJvmTests_18_MultiBindings { assertSame(salomon1, salomon2) assertNotSame(laila1, laila2) - val list: List by kodein.instance() + val list: List by di.instance() assertEquals(persons1.toList(), list) } @Test - fun test_01_MultiMap() { - val kodein = DI { + fun test_02_MultiSet_with_modules() { + val m1 by DI.Module { + bindSet() + inBindSet { + add { singleton { Person("Salomon") } } + } + } + val m2 by DI.Module { + addInBindSet { provider { Person("Laila") } } + } + + val di = DI { + importAll(m1, m2) + bind { provider { instance>().toList() } } + } + + val persons1: Set by di.instance() + + assertTrue(Person("Salomon") in persons1) + assertTrue(Person("Laila") in persons1) + + val persons2: Set by di.instance() + + val salomon1 = persons1.first { it.name == "Salomon" } + val salomon2 = persons2.first { it.name == "Salomon" } + + val laila1 = persons1.first { it.name == "Laila" } + val laila2 = persons2.first { it.name == "Laila" } + + assertSame(salomon1, salomon2) + assertNotSame(laila1, laila2) + + val list: List by di.instance() + assertEquals(persons1.toList(), list) + } + + @Test + fun test_03_MultiMap() { + val di = DI { bindSet() bind().inSet() with singleton { "so" to Person("Salomon") } @@ -48,10 +118,235 @@ class GenericJvmTests_18_MultiBindings { bind>() with provider { instance().toMap() } } - val persons: Map = kodein.direct.instance() + val persons: Map = di.direct.instance() + + assertEquals(Person("Salomon"), persons["so"]) + assertEquals(Person("Laila"), persons["loulou"]) + } + + @Test + fun test_04_MultiMap_with_builder() { + val di = DI { + bindSet { + add { singleton { "so" to Person("Salomon") } } + add { provider { "loulou" to Person("Laila") }} + } + + bind>() with provider { instance().toMap() } + } + + val persons: Map = di.direct.instance() + + assertEquals(Person("Salomon"), persons["so"]) + assertEquals(Person("Laila"), persons["loulou"]) + } + + @Test + fun test_05_MultiSetWithArg() { + val di = DI { + bindArgSet() + bind().inSet() with multiton { lastName: String -> Person("Salomon $lastName") } + bind().inSet() with factory { lastName: String -> Person("Laila $lastName") } + } + + val persons: Set by di.instance(arg = "BRYS") + assertTrue(Person("Salomon BRYS") in persons) + assertTrue(Person("Laila BRYS") in persons) + } + + @Test + fun test_06_MultiSetWithArg_with_builder() { + val di = DI { + bindArgSet { + add { multiton { lastName: String -> Person("Salomon $lastName") } } + add { factory { lastName: String -> Person("Laila $lastName") } } + } + } + + val persons: Set by di.instance(arg = "BRYS") + assertTrue(Person("Salomon BRYS") in persons) + assertTrue(Person("Laila BRYS") in persons) + } + + @Test + fun test_07_SimpleMultiSet() { + val di = DI { + bindSet() + + inSet { singleton { Person("Salomon") } } + inSet { provider { Person("Laila") } } + + bind>() with provider { instance>().toList() } + } + + val persons1: Set by di.instance() + + assertTrue(Person("Salomon") in persons1) + assertTrue(Person("Laila") in persons1) + + val persons2: Set by di.instance() + + val salomon1 = persons1.first { it.name == "Salomon" } + val salomon2 = persons2.first { it.name == "Salomon" } + + val laila1 = persons1.first { it.name == "Laila" } + val laila2 = persons2.first { it.name == "Laila" } + + assertSame(salomon1, salomon2) + assertNotSame(laila1, laila2) + + val list: List by di.instance() + assertEquals(persons1.toList(), list) + } + + @Test + fun test_08_SimpleMultiSet_with_builder() { + val di = DI { + bindSet { + add { singleton { Person("Salomon") } } + add { provider { Person("Laila") } } + } + + bind>() with provider { instance>().toList() } + } + + val persons1: Set by di.instance() + + assertTrue(Person("Salomon") in persons1) + assertTrue(Person("Laila") in persons1) + + val persons2: Set by di.instance() + + val salomon1 = persons1.first { it.name == "Salomon" } + val salomon2 = persons2.first { it.name == "Salomon" } + + val laila1 = persons1.first { it.name == "Laila" } + val laila2 = persons2.first { it.name == "Laila" } + + assertSame(salomon1, salomon2) + assertNotSame(laila1, laila2) + + val list: List by di.instance() + assertEquals(persons1.toList(), list) + } + + @Test + fun test_09_SimpleMultiMap() { + val di = DI { + bindSet() + + inSet { singleton { "so" to Person("Salomon") } } + inSet { provider { "loulou" to Person("Laila") } } + + bind>() with provider { instance().toMap() } + } + + val persons: Map = di.direct.instance() assertEquals(Person("Salomon"), persons["so"]) assertEquals(Person("Laila"), persons["loulou"]) } + @Test + fun test_10_SimpleMultiMap_with_builder() { + val di = DI { + bindSet { + add { singleton { "so" to Person("Salomon") } } + add { provider { "loulou" to Person("Laila") } } + } + + bind>() with provider { instance().toMap() } + } + + val persons: Map = di.direct.instance() + + assertEquals(Person("Salomon"), persons["so"]) + assertEquals(Person("Laila"), persons["loulou"]) + } + + @Test + fun test_11_SimpleMultiSetWithArg() { + val di = DI { + bindArgSet() + inSet { factory { lastName: String -> Person("Salomon $lastName") } } + inSet { factory { lastName: String -> Person("Laila $lastName") } } + } + + val persons: Set by di.instance(arg = "BRYS") + assertTrue(Person("Salomon BRYS") in persons) + assertTrue(Person("Laila BRYS") in persons) + } + + @Test + fun test_12_SimpleMultiSetWithArg_with_builder() { + val di = DI { + bindArgSet { + add { factory { lastName: String -> Person("Salomon $lastName") } } + add { factory { lastName: String -> Person("Laila $lastName") } } + } + } + + val persons: Set by di.instance(arg = "BRYS") + assertTrue(Person("Salomon BRYS") in persons) + assertTrue(Person("Laila BRYS") in persons) + } + + @Test + fun test_13_MultiSet_with_delegate() { + val di = DI { + bind { singleton { Person("Salomon") } } + + bindSet { + add { singleton { Person("Laila") } } + add { singleton { instance() } } + } + } + + val persons: Set by di.instance() + val salomon: IPerson by di.instance() + + assertTrue(Person("Salomon") in persons) + assertTrue(Person("Laila") in persons) + + assertSame(persons.last(), salomon) + } + + @Test + fun test_14_MultiSet_with_tag() { + val di = DI { + bindSet { + add { singleton { Person("Laila") } } + bind("ANOTHER_TAG") { singleton { Person("Salomon") } } + } + } + + val persons: Set by di.instance() + val salomon: Person by di.instance("ANOTHER_TAG") + + assertTrue(Person("Salomon") in persons) + assertTrue(Person("Laila") in persons) + + assertSame(persons.last(), salomon) + } + + @Test + fun test_15_MultiSet_with_overrides() { + val di = DI { + bind { singleton { Person("Romain") } } + + bindSet { + add { singleton { Person("Laila") } } + bind(overrides = true) { singleton { Person("Salomon") } } + } + } + + val persons: Set by di.instance() + val salomon: Person by di.instance() + + assertTrue(Person("Romain") !in persons) + assertTrue(Person("Salomon") in persons) + assertTrue(Person("Laila") in persons) + + assertSame(persons.last(), salomon) + } }