From 6e4022ca16defab4511b9cb6f416b732f60b6c22 Mon Sep 17 00:00:00 2001 From: Gabriel Peal Date: Tue, 23 Oct 2018 18:26:37 -0700 Subject: [PATCH] Add selectSubscribe for 5-7 properties --- .../com/airbnb/mvrx/BaseMvRxViewModel.kt | 123 ++++++++++++++++++ .../main/kotlin/com/airbnb/mvrx/MvRxTuples.kt | 5 +- .../kotlin/com/airbnb/mvrx/StateContainer.kt | 38 +++--- .../airbnb/mvrx/ViewModelSubscriberTest.kt | 113 ++++++++++++++-- 4 files changed, 248 insertions(+), 31 deletions(-) diff --git a/mvrx/src/main/kotlin/com/airbnb/mvrx/BaseMvRxViewModel.kt b/mvrx/src/main/kotlin/com/airbnb/mvrx/BaseMvRxViewModel.kt index 7d7a7e866..1791956bb 100644 --- a/mvrx/src/main/kotlin/com/airbnb/mvrx/BaseMvRxViewModel.kt +++ b/mvrx/src/main/kotlin/com/airbnb/mvrx/BaseMvRxViewModel.kt @@ -328,6 +328,129 @@ abstract class BaseMvRxViewModel( .distinctUntilChanged() .subscribeLifecycle(owner, uniqueOnly) { (a, b, c, d) -> subscriber(a, b, c, d) } + /** + * Subscribe to state changes for five properties. + */ + protected fun selectSubscribe( + prop1: KProperty1, + prop2: KProperty1, + prop3: KProperty1, + prop4: KProperty1, + prop5: KProperty1, + subscriber: (A, B, C, D, E) -> Unit + ) = selectSubscribeInternal(null, prop1, prop2, prop3, prop4, prop5, false, subscriber) + + @RestrictTo(RestrictTo.Scope.LIBRARY) + fun selectSubscribe( + owner: LifecycleOwner, + prop1: KProperty1, + prop2: KProperty1, + prop3: KProperty1, + prop4: KProperty1, + prop5: KProperty1, + uniqueOnly: Boolean = false, + subscriber: (A, B, C, D, E) -> Unit + ) = selectSubscribeInternal(owner, prop1, prop2, prop3, prop4, prop5, uniqueOnly, subscriber) + + private fun selectSubscribeInternal( + owner: LifecycleOwner?, + prop1: KProperty1, + prop2: KProperty1, + prop3: KProperty1, + prop4: KProperty1, + prop5: KProperty1, + uniqueOnly: Boolean, + subscriber: (A, B, C, D, E) -> Unit + ) = stateStore.observable + .map { MvRxTuple5(prop1.get(it), prop2.get(it), prop3.get(it), prop4.get(it), prop5.get(it)) } + .distinctUntilChanged() + .subscribeLifecycle(owner, uniqueOnly) { (a, b, c, d, e) -> subscriber(a, b, c, d, e) } + + /** + * Subscribe to state changes for six properties. + */ + protected fun selectSubscribe( + prop1: KProperty1, + prop2: KProperty1, + prop3: KProperty1, + prop4: KProperty1, + prop5: KProperty1, + prop6: KProperty1, + subscriber: (A, B, C, D, E, F) -> Unit + ) = selectSubscribeInternal(null, prop1, prop2, prop3, prop4, prop5, prop6, false, subscriber) + + @RestrictTo(RestrictTo.Scope.LIBRARY) + fun selectSubscribe( + owner: LifecycleOwner, + prop1: KProperty1, + prop2: KProperty1, + prop3: KProperty1, + prop4: KProperty1, + prop5: KProperty1, + prop6: KProperty1, + uniqueOnly: Boolean = false, + subscriber: (A, B, C, D, E, F) -> Unit + ) = selectSubscribeInternal(owner, prop1, prop2, prop3, prop4, prop5, prop6, uniqueOnly, subscriber) + + private fun selectSubscribeInternal( + owner: LifecycleOwner?, + prop1: KProperty1, + prop2: KProperty1, + prop3: KProperty1, + prop4: KProperty1, + prop5: KProperty1, + prop6: KProperty1, + uniqueOnly: Boolean, + subscriber: (A, B, C, D, E, F) -> Unit + ) = stateStore.observable + .map { MvRxTuple6(prop1.get(it), prop2.get(it), prop3.get(it), prop4.get(it), prop5.get(it), prop6.get(it)) } + .distinctUntilChanged() + .subscribeLifecycle(owner, uniqueOnly) { (a, b, c, d, e, f) -> subscriber(a, b, c, d, e, f) } + + /** + * Subscribe to state changes for seven properties. + */ + protected fun selectSubscribe( + prop1: KProperty1, + prop2: KProperty1, + prop3: KProperty1, + prop4: KProperty1, + prop5: KProperty1, + prop6: KProperty1, + prop7: KProperty1, + subscriber: (A, B, C, D, E, F, G) -> Unit + ) = selectSubscribeInternal(null, prop1, prop2, prop3, prop4, prop5, prop6, prop7, false, subscriber) + + @RestrictTo(RestrictTo.Scope.LIBRARY) + fun selectSubscribe( + owner: LifecycleOwner, + prop1: KProperty1, + prop2: KProperty1, + prop3: KProperty1, + prop4: KProperty1, + prop5: KProperty1, + prop6: KProperty1, + prop7: KProperty1, + uniqueOnly: Boolean = false, + subscriber: (A, B, C, D, E, F, G) -> Unit + ) = selectSubscribeInternal(owner, prop1, prop2, prop3, prop4, prop5, prop6, prop7, uniqueOnly, subscriber) + + private fun selectSubscribeInternal( + owner: LifecycleOwner?, + prop1: KProperty1, + prop2: KProperty1, + prop3: KProperty1, + prop4: KProperty1, + prop5: KProperty1, + prop6: KProperty1, + prop7: KProperty1, + uniqueOnly: Boolean, + subscriber: (A, B, C, D, E, F, G) -> Unit + ) = stateStore.observable + .map { MvRxTuple7(prop1.get(it), prop2.get(it), prop3.get(it), prop4.get(it), prop5.get(it), prop6.get(it), prop7.get(it)) } + .distinctUntilChanged() + .subscribeLifecycle(owner, uniqueOnly) { (a, b, c, d, e, f, g) -> subscriber(a, b, c, d, e, f, g) } + private fun Observable.subscribeLifecycle( lifecycleOwner: LifecycleOwner? = null, uniqueOnly: Boolean, diff --git a/mvrx/src/main/kotlin/com/airbnb/mvrx/MvRxTuples.kt b/mvrx/src/main/kotlin/com/airbnb/mvrx/MvRxTuples.kt index 99852cb26..992f210f0 100644 --- a/mvrx/src/main/kotlin/com/airbnb/mvrx/MvRxTuples.kt +++ b/mvrx/src/main/kotlin/com/airbnb/mvrx/MvRxTuples.kt @@ -3,4 +3,7 @@ package com.airbnb.mvrx internal data class MvRxTuple1(val a: A) internal data class MvRxTuple2(val a: A, val b: B) internal data class MvRxTuple3(val a: A, val b: B, val c: C) -internal data class MvRxTuple4(val a: A, val b: B, val c: C, val d: D) \ No newline at end of file +internal data class MvRxTuple4(val a: A, val b: B, val c: C, val d: D) +internal data class MvRxTuple5(val a: A, val b: B, val c: C, val d: D, val e: E) +internal data class MvRxTuple6(val a: A, val b: B, val c: C, val d: D, val e: E, val f: F) +internal data class MvRxTuple7(val a: A, val b: B, val c: C, val d: D, val e: E, val f: F, val g: G) \ No newline at end of file diff --git a/mvrx/src/main/kotlin/com/airbnb/mvrx/StateContainer.kt b/mvrx/src/main/kotlin/com/airbnb/mvrx/StateContainer.kt index de2910243..1081764b9 100644 --- a/mvrx/src/main/kotlin/com/airbnb/mvrx/StateContainer.kt +++ b/mvrx/src/main/kotlin/com/airbnb/mvrx/StateContainer.kt @@ -25,37 +25,37 @@ fun , B : MvRxState, C : BaseMvRxViewModel, D : MvRx ) = block(viewModel1.state, viewModel2.state, viewModel3.state) /** - * Accesses ViewModel state from five ViewModels synchronously and returns the result of the block. + * Accesses ViewModel state from four ViewModels synchronously and returns the result of the block. */ fun < A : BaseMvRxViewModel, B : MvRxState, C : BaseMvRxViewModel, D : MvRxState, E : BaseMvRxViewModel, F : MvRxState, G : BaseMvRxViewModel, H : MvRxState, - I : BaseMvRxViewModel, J : MvRxState, - K + I > withState( viewModel1: A, viewModel2: C, viewModel3: E, viewModel4: G, - viewModel5: I, - block: (B, D, F, H, J) -> K -) = block(viewModel1.state, viewModel2.state, viewModel3.state, viewModel4.state, viewModel5.state) + block: (B, D, F, H) -> I +) = block(viewModel1.state, viewModel2.state, viewModel3.state, viewModel4.state) /** - * Accesses ViewModel state from four ViewModels synchronously and returns the result of the block. + * Accesses ViewModel state from five ViewModels synchronously and returns the result of the block. */ fun < - A : BaseMvRxViewModel, B : MvRxState, - C : BaseMvRxViewModel, D : MvRxState, - E : BaseMvRxViewModel, F : MvRxState, - G : BaseMvRxViewModel, H : MvRxState, - I -> withState( - viewModel1: A, - viewModel2: C, - viewModel3: E, - viewModel4: G, - block: (B, D, F, H) -> I -) = block(viewModel1.state, viewModel2.state, viewModel3.state, viewModel4.state) \ No newline at end of file + A : BaseMvRxViewModel, B : MvRxState, + C : BaseMvRxViewModel, D : MvRxState, + E : BaseMvRxViewModel, F : MvRxState, + G : BaseMvRxViewModel, H : MvRxState, + I : BaseMvRxViewModel, J : MvRxState, + K + > withState( + viewModel1: A, + viewModel2: C, + viewModel3: E, + viewModel4: G, + viewModel5: I, + block: (B, D, F, H, J) -> K +) = block(viewModel1.state, viewModel2.state, viewModel3.state, viewModel4.state, viewModel5.state) \ No newline at end of file diff --git a/mvrx/src/test/kotlin/com/airbnb/mvrx/ViewModelSubscriberTest.kt b/mvrx/src/test/kotlin/com/airbnb/mvrx/ViewModelSubscriberTest.kt index b3ec36db6..219e8c7b7 100644 --- a/mvrx/src/test/kotlin/com/airbnb/mvrx/ViewModelSubscriberTest.kt +++ b/mvrx/src/test/kotlin/com/airbnb/mvrx/ViewModelSubscriberTest.kt @@ -11,7 +11,16 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test -data class ViewModelTestState(val foo: Int = 0, val bar: Int = 0, val bam: Int = 0, val list: List = emptyList(), val async: Async = Uninitialized) : MvRxState +data class ViewModelTestState( + val foo: Int = 0, + val bar: Int = 0, + val bam: Int = 0, + val list: List = emptyList(), + val async: Async = Uninitialized, + val prop6: Int = 0, + val prop7: Int = 0 +) : MvRxState + class ViewModelTestViewModel(initialState: ViewModelTestState) : TestMvRxViewModel(initialState) { var subscribeCallCount = 0 @@ -207,10 +216,10 @@ class ViewModelSubscriberTest : BaseTest() { fun testSelectSubscribe3External() { var callCount = 0 viewModel.selectSubscribe( - owner, - ViewModelTestState::foo, - ViewModelTestState::bar, - ViewModelTestState::bam + owner, + ViewModelTestState::foo, + ViewModelTestState::bar, + ViewModelTestState::bam ) { _, _, _ -> callCount++ } assertEquals(1, callCount) viewModel.setFoo(1) @@ -225,11 +234,11 @@ class ViewModelSubscriberTest : BaseTest() { fun testSelectSubscribe4External() { var callCount = 0 viewModel.selectSubscribe( - owner, - ViewModelTestState::foo, - ViewModelTestState::bar, - ViewModelTestState::bam, - ViewModelTestState::list + owner, + ViewModelTestState::foo, + ViewModelTestState::bar, + ViewModelTestState::bam, + ViewModelTestState::list ) { _, _, _, _ -> callCount++ } assertEquals(1, callCount) viewModel.setFoo(1) @@ -238,8 +247,90 @@ class ViewModelSubscriberTest : BaseTest() { assertEquals(3, callCount) viewModel.setBam(2) assertEquals(4, callCount) - viewModel.set{ copy(list = listOf(1, 2, 3)) } + viewModel.set { copy(list = listOf(1, 2, 3)) } + assertEquals(5, callCount) + } + + + @Test + fun testSelectSubscribe5External() { + var callCount = 0 + viewModel.selectSubscribe( + owner, + ViewModelTestState::foo, + ViewModelTestState::bar, + ViewModelTestState::bam, + ViewModelTestState::list, + ViewModelTestState::async + ) { _, _, _, _, _ -> callCount++ } + assertEquals(1, callCount) + viewModel.setFoo(1) + assertEquals(2, callCount) + viewModel.setBar(2) + assertEquals(3, callCount) + viewModel.setBam(2) + assertEquals(4, callCount) + viewModel.set { copy(list = listOf(1, 2, 3)) } + assertEquals(5, callCount) + viewModel.set { copy(async = Loading()) } + assertEquals(6, callCount) + } + + @Test + fun testSelectSubscribe6External() { + var callCount = 0 + viewModel.selectSubscribe( + owner, + ViewModelTestState::foo, + ViewModelTestState::bar, + ViewModelTestState::bam, + ViewModelTestState::list, + ViewModelTestState::async, + ViewModelTestState::prop6 + ) { _, _, _, _, _, _ -> callCount++ } + assertEquals(1, callCount) + viewModel.setFoo(1) + assertEquals(2, callCount) + viewModel.setBar(2) + assertEquals(3, callCount) + viewModel.setBam(2) + assertEquals(4, callCount) + viewModel.set { copy(list = listOf(1, 2, 3)) } + assertEquals(5, callCount) + viewModel.set { copy(async = Loading()) } + assertEquals(6, callCount) + viewModel.set { copy(prop6 = 1) } + assertEquals(7, callCount) + } + + @Test + fun testSelectSubscribe7External() { + var callCount = 0 + viewModel.selectSubscribe( + owner, + ViewModelTestState::foo, + ViewModelTestState::bar, + ViewModelTestState::bam, + ViewModelTestState::list, + ViewModelTestState::async, + ViewModelTestState::prop6, + ViewModelTestState::prop7 + ) { _, _, _, _, _, _, _ -> callCount++ } + assertEquals(1, callCount) + viewModel.setFoo(1) + assertEquals(2, callCount) + viewModel.setBar(2) + assertEquals(3, callCount) + viewModel.setBam(2) + assertEquals(4, callCount) + viewModel.set { copy(list = listOf(1, 2, 3)) } assertEquals(5, callCount) + viewModel.set { copy(async = Loading()) } + assertEquals(6, callCount) + viewModel.set { copy(prop6 = 1) } + assertEquals(7, callCount) + viewModel.set { copy(prop7 = 1) } + assertEquals(8, callCount) } @Test