Skip to content

Commit 43c4a84

Browse files
committed
Upgraded to rx2
1 parent 45f037d commit 43c4a84

File tree

4 files changed

+220
-41
lines changed

4 files changed

+220
-41
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
/.idea
22
/build
3+
*.iml
4+
/out

build.gradle

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ group 'mvcoding'
22
version '1.0.0'
33

44
buildscript {
5-
ext.kotlin_version = '1.0.2-1'
5+
ext.kotlin_version = '1.2.30'
66
repositories {
77
mavenCentral()
88
}
@@ -14,15 +14,16 @@ buildscript {
1414
repositories {
1515
mavenCentral()
1616
jcenter()
17+
maven { url 'https://jitpack.io' }
1718
}
1819

1920
apply plugin: 'kotlin'
2021

2122
dependencies {
2223
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
23-
compile "io.reactivex:rxkotlin:0.55.0"
24+
compile "io.reactivex.rxjava2:rxkotlin:2.2.0"
2425

2526
testCompile "junit:junit:4.12"
2627
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
27-
testCompile "com.nhaarman:mockito-kotlin:0.5.0"
28+
testCompile "com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0-alpha03"
2829
}

src/main/kotlin/com/mvcoding/mvp/Presenter.kt

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,65 @@
11
package com.mvcoding.mvp
22

3-
import rx.Observable
4-
import rx.Subscription
5-
import rx.subscriptions.CompositeSubscription
3+
import io.reactivex.Flowable
4+
import io.reactivex.Maybe
5+
import io.reactivex.Observable
6+
import io.reactivex.Single
7+
import io.reactivex.disposables.CompositeDisposable
8+
import io.reactivex.disposables.Disposable
69

7-
abstract class Presenter<VIEW : Presenter.View> {
8-
private lateinit var viewSubscriptions: CompositeSubscription
10+
abstract class Presenter<in VIEW : Presenter.View> {
11+
private lateinit var compositeDisposable: CompositeDisposable
912
private var view: View? = null
1013

1114
infix fun attach(view: VIEW) {
1215
ensureViewIsNotAttached(view)
1316
this.view = view
14-
this.viewSubscriptions = CompositeSubscription()
17+
this.compositeDisposable = CompositeDisposable()
1518
onViewAttached(view)
1619
}
1720

1821
infix fun detach(view: VIEW) {
1922
ensureGivenViewIsAttached(view)
2023
this.view = null
21-
this.viewSubscriptions.unsubscribe()
24+
this.compositeDisposable.dispose()
2225
onViewDetached(view)
2326
}
2427

25-
open protected fun onViewAttached(view: VIEW) {
28+
protected open fun onViewAttached(view: VIEW) {
2629
}
2730

28-
open protected fun onViewDetached(view: VIEW) {
31+
protected open fun onViewDetached(view: VIEW) {
2932
}
3033

31-
protected fun unsubscribeOnDetach(subscription: Subscription) {
32-
viewSubscriptions.add(subscription)
34+
private fun disposeOnDetach(disposable: Disposable) {
35+
compositeDisposable.add(disposable)
3336
}
3437

35-
protected fun <T> Observable<T>.subscribeUntilDetached() = subscribe().apply { unsubscribeOnDetach(this) }
38+
protected fun <T> Observable<T>.subscribeUntilDetached(): Disposable = subscribe().apply { disposeOnDetach(this) }
39+
protected fun <T> Flowable<T>.subscribeUntilDetached(): Disposable = subscribe().apply { disposeOnDetach(this) }
40+
protected fun <T> Single<T>.subscribeUntilDetached(): Disposable = subscribe().apply { disposeOnDetach(this) }
41+
protected fun <T> Maybe<T>.subscribeUntilDetached(): Disposable = subscribe().apply { disposeOnDetach(this) }
3642

37-
protected fun <T> Observable<T>.subscribeUntilDetached(onNext: (T) -> Unit) = subscribe(onNext).apply { unsubscribeOnDetach(this) }
43+
protected fun <T> Observable<T>.subscribeUntilDetached(onNext: (T) -> Unit): Disposable = subscribe(onNext).apply { disposeOnDetach(this) }
44+
protected fun <T> Flowable<T>.subscribeUntilDetached(onNext: (T) -> Unit): Disposable = subscribe(onNext).apply { disposeOnDetach(this) }
45+
protected fun <T> Single<T>.subscribeUntilDetached(onSuccess: (T) -> Unit): Disposable = subscribe(onSuccess).apply { disposeOnDetach(this) }
46+
protected fun <T> Maybe<T>.subscribeUntilDetached(onSuccess: (T) -> Unit): Disposable = subscribe(onSuccess).apply { disposeOnDetach(this) }
3847

39-
protected fun <T> Observable<T>.subscribeUntilDetached(onNext: (T) -> Unit, onError: (Throwable) -> Unit) =
40-
subscribe(onNext, onError).apply { unsubscribeOnDetach(this) }
48+
protected fun <T> Observable<T>.subscribeUntilDetached(onNext: (T) -> Unit, onError: (Throwable) -> Unit): Disposable =
49+
subscribe(onNext, onError).apply { disposeOnDetach(this) }
50+
protected fun <T> Flowable<T>.subscribeUntilDetached(onNext: (T) -> Unit, onError: (Throwable) -> Unit): Disposable =
51+
subscribe(onNext, onError).apply { disposeOnDetach(this) }
52+
protected fun <T> Single<T>.subscribeUntilDetached(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit): Disposable =
53+
subscribe(onSuccess, onError).apply { disposeOnDetach(this) }
54+
protected fun <T> Maybe<T>.subscribeUntilDetached(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit): Disposable =
55+
subscribe(onSuccess, onError).apply { disposeOnDetach(this) }
4156

42-
protected fun <T> Observable<T>.subscribeUntilDetached(onNext: (T) -> Unit, onError: (Throwable) -> Unit, onComplete: () -> Unit) =
43-
subscribe(onNext, onError, onComplete).apply { unsubscribeOnDetach(this) }
57+
protected fun <T> Observable<T>.subscribeUntilDetached(onNext: (T) -> Unit, onError: (Throwable) -> Unit, onComplete: () -> Unit): Disposable =
58+
subscribe(onNext, onError, onComplete).apply { disposeOnDetach(this) }
59+
protected fun <T> Flowable<T>.subscribeUntilDetached(onNext: (T) -> Unit, onError: (Throwable) -> Unit, onComplete: () -> Unit): Disposable =
60+
subscribe(onNext, onError, onComplete).apply { disposeOnDetach(this) }
61+
protected fun <T> Maybe<T>.subscribeUntilDetached(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit, onComplete: () -> Unit): Disposable =
62+
subscribe(onSuccess, onError, onComplete).apply { disposeOnDetach(this) }
4463

4564
private fun ensureViewIsNotAttached(view: VIEW) {
4665
if (this.view != null) throw IllegalStateException("Cannot attach $view, because ${this.view} is already attached")
Lines changed: 178 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,220 @@
11
package com.mvcoding.mvp
22

3-
import com.nhaarman.mockito_kotlin.mock
4-
import com.nhaarman.mockito_kotlin.whenever
3+
import com.nhaarman.mockitokotlin2.mock
4+
import com.nhaarman.mockitokotlin2.whenever
5+
import io.reactivex.*
6+
import org.junit.Before
57
import org.junit.Test
6-
import rx.Observable
7-
import rx.Observable.just
88
import kotlin.test.assertFalse
9+
import kotlin.test.assertTrue
910

1011
class PresenterTest {
11-
val view = mock<Presenter.View>()
12-
val presenter: Presenter<Presenter.View> = object : Presenter<Presenter.View>() {
12+
private val view = mock<Presenter.View>()
13+
private val presenter: Presenter<Presenter.View> = object : Presenter<Presenter.View>() {}
14+
private val viewForTest = mock<ViewForTest>()
15+
16+
@Before
17+
fun setUp() {
18+
whenever(viewForTest.observable()).thenReturn(Observable.never())
19+
whenever(viewForTest.observableOnNext()).thenReturn(Observable.never())
20+
whenever(viewForTest.observableOnNextOnError()).thenReturn(Observable.never())
21+
whenever(viewForTest.observableOnNextOnErrorOnComplete()).thenReturn(Observable.never())
22+
whenever(viewForTest.flowable()).thenReturn(Flowable.never())
23+
whenever(viewForTest.flowableOnNext()).thenReturn(Flowable.never())
24+
whenever(viewForTest.flowableOnNextOnError()).thenReturn(Flowable.never())
25+
whenever(viewForTest.flowableOnNextOnErrorOnComplete()).thenReturn(Flowable.never())
26+
whenever(viewForTest.single()).thenReturn(Single.never())
27+
whenever(viewForTest.singleOnSuccess()).thenReturn(Single.never())
28+
whenever(viewForTest.singleOnSuccessOnError()).thenReturn(Single.never())
29+
whenever(viewForTest.maybe()).thenReturn(Maybe.never())
30+
whenever(viewForTest.maybeOnSuccess()).thenReturn(Maybe.never())
31+
whenever(viewForTest.maybeOnSuccessOnError()).thenReturn(Maybe.never())
32+
whenever(viewForTest.maybeOnSuccessOnErrorOnComplete()).thenReturn(Maybe.never())
1333
}
1434

1535
@Test(expected = IllegalStateException::class)
16-
fun throwsIllegalStateExceptionWhenViewIsAlreadyAttached() {
36+
fun `throws error when view is already attached`() {
1737
presenter attach view
1838
presenter attach view
1939
}
2040

2141
@Test(expected = IllegalStateException::class)
22-
fun throwsIllegalStateExceptionWhenViewWasNotAttached() {
42+
fun `throws error when view was not attached`() {
2343
presenter detach view
2444
}
2545

2646
@Test(expected = IllegalStateException::class)
27-
fun throwsIllegalStateExceptionWhenViewWasAlreadyDetached() {
47+
fun `throws error when view was already detached`() {
2848
presenter attach view
2949
presenter detach view
3050
presenter detach view
3151
}
3252

3353
@Test(expected = IllegalStateException::class)
34-
fun throwsIllegalStateExceptionWhenTryingToDetachDifferentView() {
54+
fun `throws error when trying to detach different view`() {
3555
presenter attach view
36-
presenter detach mock<Presenter.View>()
56+
presenter detach mock()
3757
}
3858

3959
@Test
40-
fun subscribeUntilDetachUnsubscribesAfterDetach() {
60+
fun `subscribe until detach disposes observable after detach`() {
4161
val presenter = PresenterForTest()
42-
val view = mock<ViewForTest>()
43-
var isSubscribed = false
44-
val events = just(Unit).doOnSubscribe { isSubscribed = true }.doOnUnsubscribe { isSubscribed = false }
45-
whenever(view.events()).thenReturn(events)
4662

47-
presenter attach view
48-
presenter detach view
63+
var observableIsDisposed = true
64+
var observableOnNextIsDisposed = true
65+
var observableOnNextOnErrorIsDisposed = true
66+
var observableOnNextOnErrorOnCompleteIsDisposed = true
67+
68+
val observable = Observable.create<Unit> { }.doOnSubscribe { observableIsDisposed = false }.doFinally { observableIsDisposed = true }
69+
val observableOnNext = Observable.create<Unit> { }.doOnSubscribe { observableOnNextIsDisposed = false }.doFinally { observableOnNextIsDisposed = true }
70+
val observableOnNextOnError = Observable.create<Unit> { }.doOnSubscribe { observableOnNextOnErrorIsDisposed = false }.doFinally { observableOnNextOnErrorIsDisposed = true }
71+
val observableOnNextOnErrorOnComplete = Observable.create<Unit> { }.doOnSubscribe { observableOnNextOnErrorOnCompleteIsDisposed = false }.doFinally { observableOnNextOnErrorOnCompleteIsDisposed = true }
72+
73+
whenever(viewForTest.observable()).thenReturn(observable)
74+
whenever(viewForTest.observableOnNext()).thenReturn(observableOnNext)
75+
whenever(viewForTest.observableOnNextOnError()).thenReturn(observableOnNextOnError)
76+
whenever(viewForTest.observableOnNextOnErrorOnComplete()).thenReturn(observableOnNextOnErrorOnComplete)
77+
78+
presenter attach viewForTest
79+
assertFalse { observableIsDisposed }
80+
assertFalse { observableOnNextIsDisposed }
81+
assertFalse { observableOnNextOnErrorIsDisposed }
82+
assertFalse { observableOnNextOnErrorOnCompleteIsDisposed }
83+
84+
presenter detach viewForTest
85+
assertTrue { observableIsDisposed }
86+
assertTrue { observableOnNextIsDisposed }
87+
assertTrue { observableOnNextOnErrorIsDisposed }
88+
assertTrue { observableOnNextOnErrorOnCompleteIsDisposed }
89+
}
90+
91+
@Test
92+
fun `subscribe until detach disposes flowable after detach`() {
93+
val presenter = PresenterForTest()
94+
95+
var flowableIsDisposed = true
96+
var flowableOnNextIsDisposed = true
97+
var flowableOnNextOnErrorIsDisposed = true
98+
var flowableOnNextOnErrorOnCompleteIsDisposed = true
99+
100+
val flowable = Flowable.create<Unit>({}, BackpressureStrategy.DROP).doOnSubscribe { flowableIsDisposed = false }.doFinally { flowableIsDisposed = true }
101+
val flowableOnNext = Flowable.create<Unit>({}, BackpressureStrategy.DROP).doOnSubscribe { flowableOnNextIsDisposed = false }.doFinally { flowableOnNextIsDisposed = true }
102+
val flowableOnNextOnError = Flowable.create<Unit>({}, BackpressureStrategy.DROP).doOnSubscribe { flowableOnNextOnErrorIsDisposed = false }.doFinally { flowableOnNextOnErrorIsDisposed = true }
103+
val flowableOnNextOnErrorOnComplete = Flowable.create<Unit>({}, BackpressureStrategy.DROP).doOnSubscribe { flowableOnNextOnErrorOnCompleteIsDisposed = false }.doFinally { flowableOnNextOnErrorOnCompleteIsDisposed = true }
104+
105+
whenever(viewForTest.flowable()).thenReturn(flowable)
106+
whenever(viewForTest.flowableOnNext()).thenReturn(flowableOnNext)
107+
whenever(viewForTest.flowableOnNextOnError()).thenReturn(flowableOnNextOnError)
108+
whenever(viewForTest.flowableOnNextOnErrorOnComplete()).thenReturn(flowableOnNextOnErrorOnComplete)
109+
110+
presenter attach viewForTest
111+
assertFalse { flowableIsDisposed }
112+
assertFalse { flowableOnNextIsDisposed }
113+
assertFalse { flowableOnNextOnErrorIsDisposed }
114+
assertFalse { flowableOnNextOnErrorOnCompleteIsDisposed }
115+
116+
presenter detach viewForTest
117+
assertTrue { flowableIsDisposed }
118+
assertTrue { flowableOnNextIsDisposed }
119+
assertTrue { flowableOnNextOnErrorIsDisposed }
120+
assertTrue { flowableOnNextOnErrorOnCompleteIsDisposed }
121+
}
122+
123+
@Test
124+
fun `subscribe until detach disposes single after detach`() {
125+
val presenter = PresenterForTest()
126+
127+
var singleIsDisposed = true
128+
var singleOnNextIsDisposed = true
129+
var singleOnNextOnErrorIsDisposed = true
130+
131+
val single = Single.create<Unit> { }.doOnSubscribe { singleIsDisposed = false }.doFinally { singleIsDisposed = true }
132+
val singleOnNext = Single.create<Unit> { }.doOnSubscribe { singleOnNextIsDisposed = false }.doFinally { singleOnNextIsDisposed = true }
133+
val singleOnNextOnError = Single.create<Unit> { }.doOnSubscribe { singleOnNextOnErrorIsDisposed = false }.doFinally { singleOnNextOnErrorIsDisposed = true }
134+
135+
whenever(viewForTest.single()).thenReturn(single)
136+
whenever(viewForTest.singleOnSuccess()).thenReturn(singleOnNext)
137+
whenever(viewForTest.singleOnSuccessOnError()).thenReturn(singleOnNextOnError)
138+
139+
presenter attach viewForTest
140+
assertFalse { singleIsDisposed }
141+
assertFalse { singleOnNextIsDisposed }
142+
assertFalse { singleOnNextOnErrorIsDisposed }
143+
144+
presenter detach viewForTest
145+
assertTrue { singleIsDisposed }
146+
assertTrue { singleOnNextIsDisposed }
147+
assertTrue { singleOnNextOnErrorIsDisposed }
148+
}
149+
150+
@Test
151+
fun `subscribe until detach disposes maybe after detach`() {
152+
val presenter = PresenterForTest()
153+
154+
var maybeIsDisposed = true
155+
var maybeOnNextIsDisposed = true
156+
var maybeOnNextOnErrorIsDisposed = true
157+
var maybeOnNextOnErrorOnCompleteIsDisposed = true
158+
159+
val maybe = Maybe.create<Unit> { }.doOnSubscribe { maybeIsDisposed = false }.doFinally { maybeIsDisposed = true }
160+
val maybeOnNext = Maybe.create<Unit> { }.doOnSubscribe { maybeOnNextIsDisposed = false }.doFinally { maybeOnNextIsDisposed = true }
161+
val maybeOnNextOnError = Maybe.create<Unit> { }.doOnSubscribe { maybeOnNextOnErrorIsDisposed = false }.doFinally { maybeOnNextOnErrorIsDisposed = true }
162+
val maybeOnNextOnErrorOnComplete = Maybe.create<Unit> { }.doOnSubscribe { maybeOnNextOnErrorOnCompleteIsDisposed = false }.doFinally { maybeOnNextOnErrorOnCompleteIsDisposed = true }
163+
164+
whenever(viewForTest.maybe()).thenReturn(maybe)
165+
whenever(viewForTest.maybeOnSuccess()).thenReturn(maybeOnNext)
166+
whenever(viewForTest.maybeOnSuccessOnError()).thenReturn(maybeOnNextOnError)
167+
whenever(viewForTest.maybeOnSuccessOnErrorOnComplete()).thenReturn(maybeOnNextOnErrorOnComplete)
168+
169+
presenter attach viewForTest
170+
assertFalse { maybeIsDisposed }
171+
assertFalse { maybeOnNextIsDisposed }
172+
assertFalse { maybeOnNextOnErrorIsDisposed }
173+
assertFalse { maybeOnNextOnErrorOnCompleteIsDisposed }
49174

50-
assertFalse { isSubscribed }
175+
presenter detach viewForTest
176+
assertTrue { maybeIsDisposed }
177+
assertTrue { maybeOnNextIsDisposed }
178+
assertTrue { maybeOnNextOnErrorIsDisposed }
179+
assertTrue { maybeOnNextOnErrorOnCompleteIsDisposed }
51180
}
52181

53182
interface ViewForTest : Presenter.View {
54-
fun events(): Observable<Unit>
183+
fun observable(): Observable<Unit>
184+
fun observableOnNext(): Observable<Unit>
185+
fun observableOnNextOnError(): Observable<Unit>
186+
fun observableOnNextOnErrorOnComplete(): Observable<Unit>
187+
fun flowable(): Flowable<Unit>
188+
fun flowableOnNext(): Flowable<Unit>
189+
fun flowableOnNextOnError(): Flowable<Unit>
190+
fun flowableOnNextOnErrorOnComplete(): Flowable<Unit>
191+
fun single(): Single<Unit>
192+
fun singleOnSuccess(): Single<Unit>
193+
fun singleOnSuccessOnError(): Single<Unit>
194+
fun maybe(): Maybe<Unit>
195+
fun maybeOnSuccess(): Maybe<Unit>
196+
fun maybeOnSuccessOnError(): Maybe<Unit>
197+
fun maybeOnSuccessOnErrorOnComplete(): Maybe<Unit>
55198
}
56199

57200
class PresenterForTest : Presenter<ViewForTest>() {
58201
override fun onViewAttached(view: ViewForTest) {
59202
super.onViewAttached(view)
60-
view.events().subscribeUntilDetached { }
203+
view.observable().subscribeUntilDetached()
204+
view.observableOnNext().subscribeUntilDetached()
205+
view.observableOnNextOnError().subscribeUntilDetached()
206+
view.observableOnNextOnErrorOnComplete().subscribeUntilDetached()
207+
view.flowable().subscribeUntilDetached()
208+
view.flowableOnNext().subscribeUntilDetached()
209+
view.flowableOnNextOnError().subscribeUntilDetached()
210+
view.flowableOnNextOnErrorOnComplete().subscribeUntilDetached()
211+
view.single().subscribeUntilDetached()
212+
view.singleOnSuccess().subscribeUntilDetached()
213+
view.singleOnSuccessOnError().subscribeUntilDetached()
214+
view.maybe().subscribeUntilDetached()
215+
view.maybeOnSuccess().subscribeUntilDetached()
216+
view.maybeOnSuccessOnError().subscribeUntilDetached()
217+
view.maybeOnSuccessOnErrorOnComplete().subscribeUntilDetached()
61218
}
62219
}
63220
}

0 commit comments

Comments
 (0)