Skip to content

Commit 49bc3a6

Browse files
committed
Created Presenter.
1 parent 5c78774 commit 49bc3a6

File tree

4 files changed

+120
-9
lines changed

4 files changed

+120
-9
lines changed

build.gradle

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,18 @@ buildscript {
1111
}
1212
}
1313

14+
repositories {
15+
mavenCentral()
16+
jcenter()
17+
}
18+
1419
apply plugin: 'kotlin'
1520

1621
dependencies {
1722
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
18-
}
23+
compile "io.reactivex:rxkotlin:0.55.0"
1924

20-
repositories {
21-
mavenCentral()
22-
}
23-
24-
sourceSets {
25-
main.java.srcDirs += 'src/main/kotlin'
26-
}
25+
testCompile "junit:junit:4.12"
26+
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
27+
testCompile "com.nhaarman:mockito-kotlin:0.5.0"
28+
}
Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,47 @@
11
package com.mvcoding.mvp
22

3-
class Presenter {
3+
import rx.Observable
4+
import rx.Subscription
5+
import rx.subscriptions.CompositeSubscription
6+
7+
abstract class Presenter<VIEW : View> {
8+
private lateinit var viewSubscriptions: CompositeSubscription
9+
private var view: View? = null
10+
11+
fun attach(view: VIEW) {
12+
ensureViewIsNotAttached(view)
13+
this.view = view
14+
this.viewSubscriptions = CompositeSubscription()
15+
onViewAttached(view)
16+
}
17+
18+
fun detach(view: VIEW) {
19+
ensureGivenViewIsAttached(view)
20+
this.view = null
21+
this.viewSubscriptions.unsubscribe()
22+
onViewDetached(view)
23+
}
24+
25+
open protected fun onViewAttached(view: VIEW) {
26+
}
27+
28+
open protected fun onViewDetached(view: VIEW) {
29+
}
30+
31+
protected fun unsubscribeOnDetach(subscription: Subscription) {
32+
viewSubscriptions.add(subscription)
33+
}
34+
35+
protected fun <T> Observable<T>.subscribeUntilDetached(onNext: (T) -> Unit) = subscribe(onNext).apply { unsubscribeOnDetach(this) }
36+
37+
private fun ensureViewIsNotAttached(view: VIEW) {
38+
if (this.view != null) throw IllegalStateException("Cannot attach $view, because ${this.view} is already attached")
39+
}
40+
41+
private fun ensureGivenViewIsAttached(view: VIEW) {
42+
if (this.view == null)
43+
throw IllegalStateException("View is already detached.")
44+
else if (this.view != view)
45+
throw IllegalStateException("Trying to detach different view. We have view: ${this.view}. Trying to detach view: $view")
46+
}
447
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.mvcoding.mvp
2+
3+
interface View
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.mvcoding.mvp
2+
3+
import com.nhaarman.mockito_kotlin.mock
4+
import com.nhaarman.mockito_kotlin.whenever
5+
import org.junit.Test
6+
import rx.Observable
7+
import rx.Observable.just
8+
import kotlin.test.assertFalse
9+
10+
class PresenterTest {
11+
val view = mock<View>()
12+
val presenter: Presenter<View> = object : Presenter<View>() {
13+
}
14+
15+
@Test(expected = IllegalStateException::class)
16+
fun throwsIllegalStateExceptionWhenViewIsAlreadyAttached() {
17+
presenter.attach(view)
18+
presenter.attach(view)
19+
}
20+
21+
@Test(expected = IllegalStateException::class)
22+
fun throwsIllegalStateExceptionWhenViewWasNotAttached() {
23+
presenter.detach(view)
24+
}
25+
26+
@Test(expected = IllegalStateException::class)
27+
fun throwsIllegalStateExceptionWhenViewWasAlreadyDetached() {
28+
presenter.attach(view)
29+
presenter.detach(view)
30+
presenter.detach(view)
31+
}
32+
33+
@Test(expected = IllegalStateException::class)
34+
fun throwsIllegalStateExceptionWhenTryingToDetachDifferentView() {
35+
presenter.attach(view)
36+
presenter.detach(mock<View>())
37+
}
38+
39+
@Test
40+
fun subscribeUntilDetachUnsubscribesAfterDetach() {
41+
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)
46+
47+
presenter.attach(view)
48+
presenter.detach(view)
49+
50+
assertFalse { isSubscribed }
51+
}
52+
53+
interface ViewForTest : View {
54+
fun events(): Observable<Unit>
55+
}
56+
57+
class PresenterForTest : Presenter<ViewForTest>() {
58+
override fun onViewAttached(view: ViewForTest) {
59+
super.onViewAttached(view)
60+
view.events().subscribeUntilDetached { }
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)