Skip to content

Commit

Permalink
Merge 1ff78ca into 211a0d9
Browse files Browse the repository at this point in the history
  • Loading branch information
dlemures committed Aug 29, 2019
2 parents 211a0d9 + 1ff78ca commit 6b97e39
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 81 deletions.
24 changes: 24 additions & 0 deletions ktp/src/main/kotlin/toothpick/TPInjectorReplace.kt
@@ -0,0 +1,24 @@
/*
* Copyright 2019 Stephane Nicolas
* Copyright 2019 Daniel Molinero Reguera
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package toothpick

/**
* Bridge that makes it possible to modify the injector used by Toothpick.
*/
fun setToothpickInjector(injector: Injector) {
Toothpick.injector = injector
}
41 changes: 20 additions & 21 deletions ktp/src/main/kotlin/toothpick/ktp/KTP.kt
Expand Up @@ -21,36 +21,35 @@ import toothpick.Scope
import toothpick.Toothpick
import toothpick.configuration.Configuration
import toothpick.ktp.delegate.DelegateNotifier
import toothpick.setToothpickInjector

/**
* Main Toothpick API entry point for Kotlin.
*/
class KTP : Toothpick() {

companion object TP {
val delegateNotifier = DelegateNotifier()

init {
injector = object : InjectorImpl() {
override fun <T : Any> inject(obj: T, scope: Scope) =
if (delegateNotifier.hasDelegates(obj)) {
delegateNotifier.notifyDelegates(obj, scope)
} else {
super.inject(obj, scope)
}
object KTP {

val delegateNotifier = DelegateNotifier()

init {
setToothpickInjector(object : InjectorImpl() {
override fun <T : Any> inject(obj: T, scope: Scope) {
if (delegateNotifier.hasDelegates(obj)) {
delegateNotifier.notifyDelegates(obj, scope)
}
super.inject(obj, scope)
}
}
})
}

fun openScope(name: Any): Scope = Toothpick.openScope(name)
fun openScope(name: Any): Scope = Toothpick.openScope(name)

fun openScope(name: Any, scopeConfig: Scope.ScopeConfig): Scope = Toothpick.openScope(name, scopeConfig)
fun openScope(name: Any, scopeConfig: Scope.ScopeConfig): Scope = Toothpick.openScope(name, scopeConfig)

fun openScopes(vararg names: Any): Scope = Toothpick.openScopes(*names)
fun openScopes(vararg names: Any): Scope = Toothpick.openScopes(*names)

fun closeScope(name: Any) = Toothpick.closeScope(name)
fun closeScope(name: Any) = Toothpick.closeScope(name)

fun isScopeOpen(name: Any) = Toothpick.isScopeOpen(name)
fun isScopeOpen(name: Any) = Toothpick.isScopeOpen(name)

fun setConfiguration(configuration: Configuration) = Toothpick.setConfiguration(configuration)
}
fun setConfiguration(configuration: Configuration) = Toothpick.setConfiguration(configuration)
}
131 changes: 85 additions & 46 deletions ktp/src/main/kotlin/toothpick/ktp/binding/BindingExtension.kt
Expand Up @@ -41,62 +41,101 @@ fun module(bindings: Module.() -> Unit): Module = Module().apply { bindings() }
* @return a binding statement for this key, that can be further customized.
* @see module(Module.() -> Unit)
*/
fun <T : Any> Module.bind(key: KClass<T>): Binding<T>.CanBeNamed = bind(key.java)
fun <T : Any> Module.bind(key: KClass<T>): CanBeNamed<T> = CanBeNamed(bind(key.java))

/**
* DSL method to start a binding statement, using reified types for a more Kotlin friendly syntax.
* @param T the {@link KClass} used as a key for this binding.
* @return a binding statement for this key, that can be further customized.
* @see toothpick.config.Module.bind(Class)
*/
inline fun <reified T> Module.bind(): Binding<T>.CanBeNamed = bind(T::class.java)
inline fun <reified T : Any> Module.bind(): CanBeNamed<T> = CanBeNamed(bind(T::class.java))

/**
* DSL method to give a name (annotation class) to a binding.
* @param T the {@link KClass} used as a key for this binding.
* @param annotationClassWithQualifierAnnotation the {@link KClass} used as a name for this binding.
* @return a binding statement for this key, with the name {@code annotationClassWithQualifierAnnotation}
* that can be further customized.
*/
infix fun <T : Any> Binding<T>.CanBeNamed.withName(annotationClassWithQualifierAnnotation: KClass<out Annotation>): Binding<T>.CanBeBound = withName(annotationClassWithQualifierAnnotation.java)
open class CanBeBound<T : Any>(open val delegate: Binding<T>.CanBeBound) {
/**
* DSL method to make a binding Singleton.
* @return a binding statement for this key, with the implementation class {@code implClass}
* that can be further customized.
*/
fun singleton(): Binding<T>.CanBeReleasable = delegate.singleton()

// https://youtrack.jetbrains.com/issue/KT-17061
// inline fun <T, reified A : Annotation> Binding<T>.CanBeNamed.withName() = withName(A::class.java)
/**
* DSL method to associate an implementation class to a binding.
* @param implClass the {@link KClass} used as an implementation class for this binding.
* @return a binding statement for this key, with the implementation class {@code implClass}
* that can be further customized.
*/
fun toClass(implClass: KClass<out T>): Binding<T>.CanBeSingleton = delegate.to(implClass.java)

/**
* DSL method to associate an implementation class to a binding.
* @param T the {@link KClass} used as a key for this binding.
* @param implClass the {@link KClass} used as an implementation class for this binding.
* @return a binding statement for this key, with the implementation class {@code implClass}
* that can be further customized.
*/
infix fun <T : Any> Binding<T>.CanBeBound.toClass(implClass: KClass<out T>): Binding<T>.CanBeSingleton = to(implClass.java)
/**
* DSL method to associate an implementation class to a binding, more Kotlin friendly.
* @return a binding statement for this key, with the implementation class {@code T}
* that can be further customized.
* @see Binding.CanBeBound.toClass(KClass<T>)
*/
inline fun <reified P : T> toClass(): Binding<T>.CanBeSingleton = delegate.to(P::class.java)

/**
* DSL method to associate an implementation class to a binding, more Kotlin friendly.
* @param T the {@link KClass} used as an implementation class for this binding.
* @return a binding statement for this key, with the implementation class {@code T}
* that can be further customized.
* @see Binding.CanBeBound.toClass(KClass<T>)
*/
inline fun <reified T> Binding<in T>.CanBeBound.toClass(): Binding<in T>.CanBeSingleton = to(T::class.java)
/**
* DSL method to associate an instance to a binding, more Kotlin friendly.
* @param instance to be used
* @return a binding statement for this key, with the instance provided by the lambda
* that can be further customized.
*/
fun toInstance(instance: T) = delegate.toInstance(instance)

/**
* DSL method to associate an instance to a binding, more Kotlin friendly.
* @param T the {@link KClass} used as a key for this binding.
* @param instanceProvider a lambda that creates the instance
* @return a binding statement for this key, with the instance provided by the lambda
* that can be further customized.
*/
fun <T> Binding<T>.CanBeBound.toInstance(instanceProvider: () -> T) = toInstance(instanceProvider())
/**
* DSL method to associate an instance to a binding, more Kotlin friendly.
* @param instanceProvider a lambda that creates the instance
* @return a binding statement for this key, with the instance provided by the lambda
* that can be further customized.
*/
fun toInstance(instanceProvider: () -> T) = delegate.toInstance(instanceProvider())

/**
* DSL method to associate an implto a binding, more Kotlin friendly.
* @param T the {@link KClass} used as a key for this binding.
* @param providerClass the {@link KClass} used as a provider class for this binding.
* @return a binding statement for this key, with the provider class
* that can be further customized.
*/
fun <T : Any> Binding<T>.CanBeBound.toProvider(providerClass: KClass<out Provider<out T>>): Binding<T>.CanProvideSingletonOrSingleton = toProvider(providerClass.java)
// https://youtrack.jetbrains.com/issue/KT-17061
// inline fun <T, reified P : Provider<out T>> Binding<T>.CanBeBound.toProvider(): Binding<T>.CanProvideSingletonOrSingleton = toProvider(P::class.java)
/**
* DSL method to associate an provider to a binding, more Kotlin friendly.
* @param providerClass the {@link KClass} used as a provider class for this binding.
* @return a binding statement for this key, with the provider class
* that can be further customized.
*/
fun toProvider(providerClass: KClass<out Provider<out T>>): Binding<T>.CanProvideSingletonOrSingleton = delegate.toProvider(providerClass.java)

// https://youtrack.jetbrains.com/issue/KT-17061
// inline fun <T, reified P : Provider<out T>> Binding<T>.CanBeBound.toProvider(): Binding<T>.CanProvideSingletonOrSingleton = toProvider(P::class.java)

/**
* DSL method to associate an provider impl to a binding, more Kotlin friendly.
* @param providerInstance the provider instance to be used for this binding.
* @return a binding statement for this key, with the provider class
* that can be further customized.
*/
fun toProviderInstance(providerInstance: Provider<out T>): Binding<T>.CanProvideSingleton = delegate.toProviderInstance(providerInstance)

/**
* DSL method to associate an provider impl to a binding, more Kotlin friendly.
* @param providerInstanceProvider a lambda used as a provider for this binding.
* @return a binding statement for this key, with the provider class
* that can be further customized.
*/
fun toProviderInstance(providerInstanceProvider: () -> T): Binding<T>.CanProvideSingleton = delegate.toProviderInstance(providerInstanceProvider)
}

class CanBeNamed<T : Any>(override val delegate: Binding<T>.CanBeNamed) : CanBeBound<T>(delegate) {
/**
* DSL method to give a name (String) to a binding.
* @param name the {@link String} used as a name for this binding.
* @return a binding statement for this key, with the name {@code name}
* that can be further customized.
*/
fun withName(name: String): CanBeBound<T> = CanBeBound(delegate.withName(name))

/**
* DSL method to give a name (annotation class) to a binding.
* @param annotationClassWithQualifierAnnotation the {@link KClass} used as a name for this binding.
* @return a binding statement for this key, with the name {@code annotationClassWithQualifierAnnotation}
* that can be further customized.
*/
fun withName(annotationClassWithQualifierAnnotation: KClass<out Annotation>): CanBeBound<T> = CanBeBound(delegate.withName(annotationClassWithQualifierAnnotation.java))

// https://youtrack.jetbrains.com/issue/KT-17061
// inline fun <T, reified A : Annotation> Binding<T>.CanBeNamed.withName() = withName(A::class.java)
}
Expand Up @@ -43,7 +43,7 @@ class BindingExtensionTest {
bind<CharSequence>().toInstance("")
bind<CharSequence>().toInstance { "" } // equivalent to previous one
bind<CharSequence>().withName("").toInstance { "" }
bind<CharSequence>().withName(QualifierName::class).toClass<String>()
bind<CharSequence>().withName(QualifierName::class).toInstance { "" }

// toProvider
bind<CharSequence>().toProvider(StringProvider::class)
Expand Down
Expand Up @@ -31,7 +31,6 @@ import toothpick.Toothpick.isScopeOpen
import toothpick.ktp.KTP
import toothpick.ktp.binding.bind
import toothpick.ktp.binding.module
import toothpick.ktp.binding.toInstance

@RunWith(RobolectricTestRunner::class)
class ViewModelUtilExtensionTest {
Expand All @@ -44,7 +43,7 @@ class ViewModelUtilExtensionTest {

// WHEN
KTP.openScopes(application, activity)
.closeOnViewModelCleared(activity)
.closeOnViewModelCleared(activity)
activityController.destroy()

// THEN
Expand All @@ -60,7 +59,7 @@ class ViewModelUtilExtensionTest {

// WHEN
KTP.openScopes(application, activity)
.closeOnViewModelCleared(activity)
.closeOnViewModelCleared(activity)
activityController.configurationChange(Configuration())

// THEN
Expand All @@ -75,12 +74,12 @@ class ViewModelUtilExtensionTest {
val application: Context = ApplicationProvider.getApplicationContext()
// WHEN
val scope = KTP.openScopes(application, ViewModelScope::class.java)
.installViewModelBinding<TestViewModel>(activity)
.closeOnViewModelCleared(activity)
.installModules(module {
bind<String>().withName("name").toInstance { "dependency" }
})
.openSubScope(activity)
.installViewModelBinding<TestViewModel>(activity)
.closeOnViewModelCleared(activity)
.installModules(module {
bind<String>().withName("name").toInstance { "dependency" }
})
.openSubScope(activity)

val viewModelBeforeRotation = scope.getInstance(TestViewModel::class.java)
activityController.configurationChange(Configuration())
Expand Down
2 changes: 1 addition & 1 deletion toothpick-runtime/src/main/java/toothpick/Toothpick.java
Expand Up @@ -39,7 +39,7 @@ public class Toothpick {
// its transformation
private static final ConcurrentHashMap<Object, Scope> MAP_KEY_TO_SCOPE =
new ConcurrentHashMap<>();
protected static Injector injector = new InjectorImpl();
static Injector injector = new InjectorImpl();

protected Toothpick() {
throw new RuntimeException("Constructor can't be invoked even via reflection.");
Expand Down
Expand Up @@ -6,7 +6,6 @@ import toothpick.Scope
import toothpick.ktp.KTP
import toothpick.ktp.binding.bind
import toothpick.ktp.binding.module
import toothpick.ktp.binding.toInstance

class BackpackApplication : Application() {

Expand Down
Expand Up @@ -20,7 +20,6 @@ import toothpick.Scope
import toothpick.ktp.KTP
import toothpick.ktp.binding.bind
import toothpick.ktp.binding.module
import toothpick.ktp.binding.toClass
import toothpick.ktp.delegate.inject
import toothpick.ktp.delegate.lazy
import toothpick.smoothie.lifecycle.closeOnDestroy
Expand Down
Expand Up @@ -18,7 +18,6 @@ import com.example.toothpick.model.Backpack
import toothpick.ktp.KTP
import toothpick.ktp.binding.bind
import toothpick.ktp.binding.module
import toothpick.ktp.binding.toClass
import toothpick.ktp.delegate.inject
import toothpick.ktp.delegate.lazy
import toothpick.smoothie.lifecycle.closeOnDestroy
Expand Down

0 comments on commit 6b97e39

Please sign in to comment.