From 1aa35550e6fd2c00bb3f48d66bb7324184d58422 Mon Sep 17 00:00:00 2001 From: stepango Date: Tue, 6 Sep 2016 09:29:41 +0800 Subject: [PATCH 01/27] Dependencies updated --- build.gradle | 14 +++++++------- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 2f468e4..ed53c06 100755 --- a/build.gradle +++ b/build.gradle @@ -1,24 +1,24 @@ buildscript { - ext.kotlin_version = '1.0.2' + ext.kotlin_version = '1.0.3' repositories { jcenter() } - dependencies { classpath 'com.netflix.nebula:gradle-rxjava-project-plugin:2.+', + dependencies { classpath 'com.netflix.nebula:gradle-rxjava-project-plugin:3.+', "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } -apply plugin: 'rxjava-project' +apply plugin: 'nebula.rxjava-project' apply plugin: 'kotlin' dependencies { - compile 'io.reactivex:rxjava:1.1.5' + compile 'io.reactivex:rxjava:1.1.10' compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - testCompile 'org.funktionale:funktionale:0.8' + testCompile 'org.funktionale:funktionale:0.9' testCompile 'junit:junit:4.12' - testCompile 'org.mockito:mockito-core:1.8.5' + testCompile 'org.mockito:mockito-core:1.10.19' examplesCompile 'com.squareup.retrofit:retrofit:1.9.+' } task wrapper(type: Wrapper) { - gradleVersion = '2.2.1' + gradleVersion = '2.10' } // support for snapshot/final releases with the various branches RxJava uses diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8e63c7e..28a37ab 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Mar 07 02:11:57 MSK 2015 +#Mon Sep 05 19:30:11 SGT 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip From 3f2e99a58ad30d589ba6031d215366044aaab0df Mon Sep 17 00:00:00 2001 From: stepango Date: Sat, 7 Jan 2017 18:59:27 +0200 Subject: [PATCH 02/27] Initial implementation of rxKotlin for rxJava2 --- LICENSE | 2 +- build.gradle | 12 +- .../rx/lang/kotlin/examples/examples.kt | 118 ++++++------ .../lang/kotlin/examples/retrofit/retrofit.kt | 2 +- src/main/kotlin/rx/lang/kotlin/completable.kt | 16 +- src/main/kotlin/rx/lang/kotlin/observables.kt | 107 +++++------ src/main/kotlin/rx/lang/kotlin/single.kt | 24 +-- src/main/kotlin/rx/lang/kotlin/subjects.kt | 20 +- src/main/kotlin/rx/lang/kotlin/subscribers.kt | 120 ++++++------ .../kotlin/rx/lang/kotlin/subscription.kt | 14 +- .../kotlin/rx/lang/kotlin/BasicKotlinTests.kt | 132 ++++++------- .../kotlin/rx/lang/kotlin/CompletableTest.kt | 24 +-- .../kotlin/rx/lang/kotlin/ExtensionTests.kt | 59 +++--- src/test/kotlin/rx/lang/kotlin/KotlinTests.kt | 4 +- .../kotlin/rx/lang/kotlin/ObservablesTest.kt | 176 ++++++++---------- src/test/kotlin/rx/lang/kotlin/SingleTest.kt | 6 +- .../kotlin/rx/lang/kotlin/SubscribersTest.kt | 147 +++++++-------- .../rx/lang/kotlin/SubscriptionTests.kt | 20 +- 18 files changed, 475 insertions(+), 528 deletions(-) diff --git a/LICENSE b/LICENSE index 7f8ced0..59f7b92 100644 --- a/LICENSE +++ b/LICENSE @@ -180,7 +180,7 @@ To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include + replaced with your own identifying information. (Don'value include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the diff --git a/build.gradle b/build.gradle index ed53c06..678111e 100755 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,17 @@ buildscript { - ext.kotlin_version = '1.0.3' - repositories { jcenter() } - dependencies { classpath 'com.netflix.nebula:gradle-rxjava-project-plugin:3.+', - "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } + ext.kotlin_version = '1.0.6' + repositories { jcenter() } + dependencies { + classpath 'com.netflix.nebula:gradle-rxjava-project-plugin:3.+', + "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } } apply plugin: 'nebula.rxjava-project' apply plugin: 'kotlin' dependencies { - compile 'io.reactivex:rxjava:1.1.10' + compile 'io.reactivex.rxjava2:rxjava:2.0.3' compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" testCompile 'org.funktionale:funktionale:0.9' testCompile 'junit:junit:4.12' diff --git a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt b/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt index 5148d6c..6f1a0a4 100644 --- a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt +++ b/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt @@ -1,115 +1,119 @@ package rx.lang.kotlin.examples -import rx.Observable -import rx.lang.kotlin.* -import rx.subscriptions.CompositeSubscription +import io.reactivex.Observable +import io.reactivex.disposables.CompositeDisposable +import rx.lang.kotlin.FunctionSubscriber +import rx.lang.kotlin.addTo +import rx.lang.kotlin.combineLatest +import rx.lang.kotlin.observable +import rx.lang.kotlin.plusAssign +import rx.lang.kotlin.subscribeBy +import rx.lang.kotlin.toObservable +import rx.lang.kotlin.zip import java.net.URL -import java.util.* +import java.util.Scanner import java.util.concurrent.TimeUnit import kotlin.concurrent.thread fun main(args: Array) { - - val subscription = CompositeSubscription() - + + val subscription = CompositeDisposable() + val printArticle = { art: String -> println("--- Article ---\n${art.substring(0, 125)}") } - + val printIt = { it: String -> println(it) } - + subscription += asyncObservable().subscribe(printIt) - + subscription += syncObservable().subscribe(printIt) - + subscription.clear() - + simpleComposition() - + asyncWiki("Tiger", "Elephant").subscribe(printArticle) - + asyncWikiWithErrorHandling("Tiger", "Elephant").subscribe(printArticle) { e -> println("--- Error ---\n${e.message}") } - + combineLatest(listOfObservables()) - + zip(listOfObservables()) - + simpleObservable().subscribe(FunctionSubscriber() .onNext { s -> println("1st onNext => $s") } .onNext { s -> println("2nd onNext => $s") }) - + addToCompositeSubscription() } -private fun URL.toScannerObservable() = observable { s -> +private fun URL.toScannerObservable() = observable({ s -> this.openStream().use { stream -> - Scanner(stream).useDelimiter("\\A").toObservable().subscribe(s) + Scanner(stream).useDelimiter("\\A").toObservable().subscribeBy { s } } -} +}) fun syncObservable(): Observable = - observable { subscriber -> - (0..75).toObservable() - .map { "Sync value_$it" } - .subscribe(subscriber) - } - -fun asyncObservable(): Observable = - observable { subscriber -> - thread { + observable { subscriber -> (0..75).toObservable() - .map { "Async value_$it" } - .subscribe(subscriber) + .map { "Sync value_$it" } + .subscribe { subscriber.onNext(it) } } + +fun asyncObservable(): Observable = observable { subscriber -> + thread { + (0..75).toObservable() + .map { "Async value_$it" } + .subscribe { subscriber.onNext(it) } } +} -fun asyncWiki(vararg articleNames: String): Observable = - observable { subscriber -> - thread { - articleNames.toObservable() - .flatMap { name -> URL("http://en.wikipedia.org/wiki/$name").toScannerObservable().first() } - .subscribe(subscriber) - } +fun asyncWiki(vararg articleNames: String): Observable = observable { subscriber -> + thread { + articleNames.toObservable() + .flatMapMaybe { name: String -> URL("http://en.wikipedia.org/wiki/$name").toScannerObservable().firstElement() } + .subscribe { subscriber.onNext(it) } } +} -fun asyncWikiWithErrorHandling(vararg articleNames: String): Observable = - observable { subscriber -> - thread { - articleNames.toObservable() - .flatMap { name -> URL("http://en.wikipedia.org/wiki/$name").toScannerObservable().first() } - .onError { e -> - subscriber.onError(e) } - .subscribe(subscriber) - } +fun asyncWikiWithErrorHandling(vararg articleNames: String): Observable = observable { subscriber -> + thread { + articleNames.toObservable() + .flatMapMaybe { name -> URL("http://en.wikipedia.org/wiki/$name").toScannerObservable().firstElement() } + .subscribe({ subscriber.onNext(it) }, { subscriber.onError(it) }) } +} fun simpleComposition() { - asyncObservable().skip(10).take(5) - .map { s -> "${s}_xform" } - .subscribe { s -> println("onNext => $s") } + asyncObservable() + .skip(10) + .take(5) + .map { "${it}_xform" } + .subscribe { println("onNext => $it") } } fun listOfObservables(): List> = listOf(syncObservable(), syncObservable()) fun combineLatest(observables: List>) { - observables.combineLatest { it.reduce { one, two -> one + two } }.subscribe { println(it) } + observables.combineLatest { it.reduce { one, two -> one + two } }.subscribe(::println) } fun zip(observables: List>) { - observables.zip { it.reduce { one, two -> one + two } }.subscribe { println(it) } + observables.zip { it.reduce { one, two -> one + two } }.subscribe(::println) } fun simpleObservable(): Observable = (0..17).toObservable().map { "Simple $it" } fun addToCompositeSubscription() { - val compositeSubscription = CompositeSubscription() - + val compositeSubscription = CompositeDisposable() + Observable.just("test") .delay(100, TimeUnit.MILLISECONDS) .subscribe() .addTo(compositeSubscription) - - compositeSubscription.unsubscribe() + + compositeSubscription.dispose() } \ No newline at end of file diff --git a/src/examples/kotlin/rx/lang/kotlin/examples/retrofit/retrofit.kt b/src/examples/kotlin/rx/lang/kotlin/examples/retrofit/retrofit.kt index 01b3fef..5e77a44 100644 --- a/src/examples/kotlin/rx/lang/kotlin/examples/retrofit/retrofit.kt +++ b/src/examples/kotlin/rx/lang/kotlin/examples/retrofit/retrofit.kt @@ -1,9 +1,9 @@ package rx.lang.kotlin.examples.retrofit +import io.reactivex.Observable import retrofit.RestAdapter import retrofit.http.GET import retrofit.http.Query -import rx.Observable data class SearchResultEntry(val id : String, val latestVersion : String) data class SearchResults(val docs : List) diff --git a/src/main/kotlin/rx/lang/kotlin/completable.kt b/src/main/kotlin/rx/lang/kotlin/completable.kt index f9b3b28..ac413f0 100644 --- a/src/main/kotlin/rx/lang/kotlin/completable.kt +++ b/src/main/kotlin/rx/lang/kotlin/completable.kt @@ -1,13 +1,13 @@ package rx.lang.kotlin -import rx.Completable -import rx.Single -import rx.functions.Action0 +import io.reactivex.Completable +import io.reactivex.Single +import io.reactivex.functions.Action import java.util.concurrent.Callable import java.util.concurrent.Future -fun Action0.toCompletable(): Completable = Completable.fromAction(this) -fun completableOf(f: Function0): Completable = Completable.fromAction { f.invoke() } -fun Callable.toCompletable(): Completable = Completable.fromCallable { this.call() } -fun Future.toCompletable(): Completable = Completable.fromFuture(this) -fun Single.toCompletable(): Completable = Completable.fromSingle(this) \ No newline at end of file +fun Action.toCompletable(): Completable = Completable.fromAction(this) +inline fun completableOf(crossinline f: () -> T): Completable = Completable.fromAction { f() } +fun Callable.toCompletable(): Completable = Completable.fromCallable { this.call() } +fun Future.toCompletable(): Completable = Completable.fromFuture(this) +fun Single.toCompletable(): Completable = Completable.fromSingle(this) \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/observables.kt b/src/main/kotlin/rx/lang/kotlin/observables.kt index 8655012..0246fcd 100644 --- a/src/main/kotlin/rx/lang/kotlin/observables.kt +++ b/src/main/kotlin/rx/lang/kotlin/observables.kt @@ -1,72 +1,61 @@ package rx.lang.kotlin -import rx.Observable -import rx.Subscriber -import rx.Subscription -import rx.observables.BlockingObservable +import io.reactivex.Observable +import io.reactivex.ObservableEmitter +import io.reactivex.Single +import io.reactivex.disposables.Disposable +import io.reactivex.functions.BiFunction -fun emptyObservable() : Observable = Observable.empty() -fun observable(body : (s : Subscriber) -> Unit) : Observable = Observable.create(body) /** - * Create deferred observable - * @see [rx.Observable.defer] and [http://reactivex.io/documentation/operators/defer.html] + * [Observable.empty] alias */ -fun deferredObservable(body : () -> Observable) : Observable = Observable.defer(body) -private fun Iterator.toIterable() = object : Iterable { - override fun iterator(): Iterator = this@toIterable -} +fun emptyObservable(): Observable = Observable.empty() -fun BooleanArray.toObservable() : Observable = this.toList().toObservable() -fun ByteArray.toObservable() : Observable = this.toList().toObservable() -fun ShortArray.toObservable() : Observable = this.toList().toObservable() -fun IntArray.toObservable() : Observable = this.toList().toObservable() -fun LongArray.toObservable() : Observable = this.toList().toObservable() -fun FloatArray.toObservable() : Observable = this.toList().toObservable() -fun DoubleArray.toObservable() : Observable = this.toList().toObservable() -fun Array.toObservable() : Observable = Observable.from(this) - -fun IntProgression.toObservable() : Observable = - if (step == 1 && last.toLong() - first < Integer.MAX_VALUE) Observable.range(first, Math.max(0, last - first + 1)) - else Observable.from(this) +/** + * [Observable.defer] alias + */ +fun Observable.defer() = Observable.defer { this } -fun Iterator.toObservable() : Observable = toIterable().toObservable() -fun Iterable.toObservable() : Observable = Observable.from(this) -fun Sequence.toObservable() : Observable = Observable.from(object : Iterable { - override fun iterator(): Iterator = this@toObservable.iterator() -}) +fun observable(body: (ObservableEmitter) -> Unit): Observable = Observable.create(body) -fun T.toSingletonObservable() : Observable = Observable.just(this) -fun Throwable.toObservable() : Observable = Observable.error(this) +private fun Iterator.toIterable() = object : Iterable { + override fun iterator(): Iterator = this@toIterable +} -fun Iterable>.merge() : Observable = Observable.merge(this.toObservable()) -fun Iterable>.mergeDelayError() : Observable = Observable.mergeDelayError(this.toObservable()) +fun BooleanArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) +fun ByteArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) +fun ShortArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) +fun IntArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) +fun LongArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) +fun FloatArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) +fun DoubleArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) +fun Array.toObservable(): Observable = Observable.fromArray(*this) +fun IntProgression.toObservable(): Observable = + if (step == 1 && last.toLong() - first < Integer.MAX_VALUE) Observable.range(first, Math.max(0, last - first + 1)) + else Observable.fromIterable(this) -fun Observable.fold(initial : R, body : (R, T) -> R) : Observable = reduce(initial, {a, e -> body(a, e)}) -fun Observable.onError(block : (Throwable) -> Unit) : Observable = doOnError(block) -@Suppress("BASE_WITH_NULLABLE_UPPER_BOUND") fun Observable.firstOrNull() : Observable = firstOrDefault(null) -fun BlockingObservable.firstOrNull() : T = firstOrDefault(null) +fun Iterator.toObservable(): Observable = toIterable().toObservable() +fun Iterable.toObservable(): Observable = Observable.fromIterable(this) +fun Sequence.toObservable(): Observable = Observable.fromIterable(iterator().toIterable()) -@Suppress("BASE_WITH_NULLABLE_UPPER_BOUND") fun Observable.onErrorReturnNull() : Observable = onErrorReturn {null} +fun T.toSingletonObservable(): Observable = Observable.just(this) +fun Throwable.toObservable(): Observable = Observable.error(this) -fun Observable.lift(operator : (Subscriber) -> Subscriber) : Observable = lift { t1 -> operator(t1!!) } +fun Iterable>.merge(): Observable = Observable.merge(this.toObservable()) +fun Iterable>.mergeDelayError(): Observable = Observable.mergeDelayError(this.toObservable()) -/** - * Returns [Observable] that requires all objects to be non null. Raising [NullPointerException] in case of null object - */ -fun Observable.requireNoNulls() : Observable = map { it ?: throw NullPointerException("null element found in rx observable") } +inline fun Observable.fold(initial: R, crossinline body: (R, T) -> R): Single + = reduce(initial) { a, e -> body(a, e) } -/** - * Returns [Observable] with non-null generic type T. Returned observable filter out null values - */ -@Suppress("CAST_NEVER_SUCCEEDS") fun Observable.filterNotNull(): Observable = filter { it != null } as Observable +fun Observable.onError(block: (Throwable) -> Unit): Observable = doOnError(block) /** * Returns Observable that wrap all values into [IndexedValue] and populates corresponding index value. * Works similar to [kotlin.withIndex] */ -fun Observable.withIndex() : Observable> = - zipWith(Observable.range(0, Int.MAX_VALUE)) { value, index -> IndexedValue(index,value) } +fun Observable.withIndex(): Observable> + = zipWith(Observable.range(0, Int.MAX_VALUE), BiFunction { value, index -> IndexedValue(index, value) }) /** * Returns Observable that emits objects from kotlin [Sequence] returned by function you provided by parameter [body] for @@ -74,34 +63,36 @@ fun Observable.withIndex() : Observable> = * Works similar to [Observable.flatMap] and [Observable.flatMapIterable] but with [Sequence] * * @param body is a function that applied for each item emitted by source observable that returns [Sequence] - * @returns Observable that merges all [Sequence]s produced by [body] functions + * @returns Observable that merges all [Sequence]s produced by [body] functions */ -fun Observable.flatMapSequence( body : (T) -> Sequence ) : Observable = flatMap { body(it).toObservable() } +inline fun Observable.flatMapSequence(crossinline body: (T) -> Sequence): Observable + = flatMap { body(it).toObservable() } /** * Subscribe with a subscriber that is configured inside body */ -inline fun Observable.subscribeWith( body : FunctionSubscriberModifier.() -> Unit ) : Subscription { +inline fun Observable.subscribeBy(body: FunctionSubscriberModifier.() -> Unit): Disposable { val modifier = FunctionSubscriberModifier(subscriber()) modifier.body() - return subscribe(modifier.subscriber) + subscribe(modifier.subscriber) + return modifier.subscriber.origin!! } -fun Observable>.switchOnNext(): Observable = Observable.switchOnNext(this) +fun Observable>.switchOnNext(): Observable = Observable.switchOnNext(this) /** * Observable.combineLatest(List> sources, FuncN combineFunction) */ @Suppress("UNCHECKED_CAST") -fun List>.combineLatest(combineFunction: (args: List) -> R): Observable = - Observable.combineLatest(this, { combineFunction(it.asList() as List) }) +inline fun List>.combineLatest(crossinline combineFunction: (args: List) -> R): Observable + = Observable.combineLatest(this) { combineFunction(it.asList().map { it as T }) } /** * Observable.zip(List> sources, FuncN combineFunction) */ @Suppress("UNCHECKED_CAST") -fun List>.zip(zipFunction: (args: List) -> R): Observable = - Observable.zip(this, { zipFunction(it.asList() as List) }) +inline fun List>.zip(crossinline zipFunction: (args: List) -> R): Observable = + Observable.zip(this) { zipFunction(it.asList().map { it as T }) } /** * Returns an Observable that emits the items emitted by the source Observable, converted to the specified type. diff --git a/src/main/kotlin/rx/lang/kotlin/single.kt b/src/main/kotlin/rx/lang/kotlin/single.kt index 6f1b785..1be5473 100644 --- a/src/main/kotlin/rx/lang/kotlin/single.kt +++ b/src/main/kotlin/rx/lang/kotlin/single.kt @@ -1,22 +1,24 @@ package rx.lang.kotlin -import rx.Single -import rx.SingleSubscriber -import rx.Subscription +import io.reactivex.Single +import io.reactivex.SingleEmitter +import io.reactivex.disposables.Disposable import java.util.concurrent.Callable import java.util.concurrent.Future -fun single(body: (s: SingleSubscriber) -> Unit): Single = Single.create(body) -fun singleOf(value: T): Single = Single.just(value) -fun Future.toSingle(): Single = Single.from(this) -fun Callable.toSingle(): Single = Single.fromCallable { this.call() } -fun Throwable.toSingle(): Single = Single.error(this) +inline fun single(crossinline body: (s: SingleEmitter) -> Unit): Single = Single.create { body(it) } +fun T.toSingle(): Single = Single.just(this) +fun singleOf(value: T): Single = Single.just(value) +fun Future.toSingle(): Single = Single.fromFuture(this) +fun Callable.toSingle(): Single = Single.fromCallable { this.call() } +fun Throwable.toSingle(): Single = Single.error(this) /** * Subscribe with a subscriber that is configured inside body */ -inline fun Single.subscribeWith(body: FunctionSingleSubscriberModifier.() -> Unit): Subscription { - val modifier = FunctionSingleSubscriberModifier(singleSubscriber()) +inline fun Single.subscribeBy(body: FunctionSubscriberModifier.() -> Unit): Disposable { + val modifier = FunctionSubscriberModifier(subscriber()) modifier.body() - return subscribe(modifier.subscriber) + subscribe(modifier.subscriber) + return modifier.subscriber.origin!! } diff --git a/src/main/kotlin/rx/lang/kotlin/subjects.kt b/src/main/kotlin/rx/lang/kotlin/subjects.kt index b6d7764..7dbd24a 100644 --- a/src/main/kotlin/rx/lang/kotlin/subjects.kt +++ b/src/main/kotlin/rx/lang/kotlin/subjects.kt @@ -1,13 +1,13 @@ package rx.lang.kotlin -import rx.schedulers.TestScheduler -import rx.subjects.* +import io.reactivex.Observable +import io.reactivex.subjects.AsyncSubject +import io.reactivex.subjects.BehaviorSubject +import io.reactivex.subjects.PublishSubject +import io.reactivex.subjects.ReplaySubject -fun BehaviorSubject() : BehaviorSubject = BehaviorSubject.create() -fun BehaviorSubject(default : T) : BehaviorSubject = BehaviorSubject.create(default) -fun AsyncSubject() : AsyncSubject = AsyncSubject.create() -fun PublishSubject() : PublishSubject = PublishSubject.create() -fun ReplaySubject(capacity : Int = 16) : ReplaySubject = ReplaySubject.create(capacity) - -fun Subject.synchronized() : Subject = SerializedSubject(this) -fun TestSubject(scheduler: TestScheduler) : TestSubject = TestSubject.create(scheduler) +fun BehaviorSubject(): BehaviorSubject = BehaviorSubject.create() +fun BehaviorSubject(default: T): BehaviorSubject = BehaviorSubject.createDefault(default) +fun AsyncSubject(): AsyncSubject = AsyncSubject.create() +fun PublishSubject(): PublishSubject = PublishSubject.create() +fun ReplaySubject(capacity: Int = Observable.bufferSize()): ReplaySubject = ReplaySubject.create(capacity) diff --git a/src/main/kotlin/rx/lang/kotlin/subscribers.kt b/src/main/kotlin/rx/lang/kotlin/subscribers.kt index 6c0930c..62adf24 100644 --- a/src/main/kotlin/rx/lang/kotlin/subscribers.kt +++ b/src/main/kotlin/rx/lang/kotlin/subscribers.kt @@ -1,20 +1,22 @@ package rx.lang.kotlin -import rx.SingleSubscriber -import rx.Subscriber -import rx.exceptions.OnErrorNotImplementedException -import rx.observers.SerializedSubscriber -import rx.subscriptions.Subscriptions -import java.util.* - -class FunctionSubscriber() : Subscriber() { - private val onCompletedFunctions = ArrayList<() -> Unit>() +import io.reactivex.CompletableObserver +import io.reactivex.MaybeObserver +import io.reactivex.Observer +import io.reactivex.SingleObserver +import io.reactivex.disposables.Disposable +import java.util.ArrayList + +class FunctionSubscriber : Observer, MaybeObserver, SingleObserver, CompletableObserver { + + private val onCompleteFunctions = ArrayList<() -> Unit>() private val onErrorFunctions = ArrayList<(e: Throwable) -> Unit>() private val onNextFunctions = ArrayList<(value: T) -> Unit>() private val onStartFunctions = ArrayList<() -> Unit>() - - override fun onCompleted() = onCompletedFunctions.forEach { it() } - + var origin: Disposable? = null + + override fun onComplete() = onCompleteFunctions.forEach { it() } + override fun onError(e: Throwable?) = (e ?: RuntimeException("exception is unknown")).let { ex -> if (onErrorFunctions.isEmpty()) { throw OnErrorNotImplementedException(ex) @@ -22,76 +24,58 @@ class FunctionSubscriber() : Subscriber() { onErrorFunctions.forEach { it(ex) } } } - - override fun onNext(t: T) = onNextFunctions.forEach { it(t) } - - override fun onStart() = onStartFunctions.forEach { it() } - - fun onCompleted(onCompletedFunction: () -> Unit): FunctionSubscriber = copy { onCompletedFunctions.add(onCompletedFunction) } + + override fun onNext(value: T) = onNextFunctions.forEach { it(value) } + override fun onSuccess(value: T) = onNext(value) + + override fun onSubscribe(d: Disposable?) { + origin = d + onStartFunctions.forEach { it() } + } + + fun onCompleted(onCompletedFunction: () -> Unit): FunctionSubscriber = copy { onCompleteFunctions.add(onCompletedFunction) } fun onError(onErrorFunction: (t: Throwable) -> Unit): FunctionSubscriber = copy { onErrorFunctions.add(onErrorFunction) } fun onNext(onNextFunction: (t: T) -> Unit): FunctionSubscriber = copy { onNextFunctions.add(onNextFunction) } - fun onStart(onStartFunction : () -> Unit) : FunctionSubscriber = copy { onStartFunctions.add(onStartFunction) } - + fun onStart(onStartFunction: () -> Unit): FunctionSubscriber = copy { onStartFunctions.add(onStartFunction) } + private fun copy(block: FunctionSubscriber.() -> Unit): FunctionSubscriber { val newSubscriber = FunctionSubscriber() - newSubscriber.onCompletedFunctions.addAll(onCompletedFunctions) + newSubscriber.onCompleteFunctions.addAll(onCompleteFunctions) newSubscriber.onErrorFunctions.addAll(onErrorFunctions) newSubscriber.onNextFunctions.addAll(onNextFunctions) newSubscriber.onStartFunctions.addAll(onStartFunctions) - + newSubscriber.block() - + return newSubscriber } } -class FunctionSingleSubscriber() : SingleSubscriber() { - private val onSuccessFunctions = ArrayList<(value: T) -> Unit>() - private val onErrorFunctions = ArrayList<(e: Throwable) -> Unit>() - - override fun onSuccess(t: T) = onSuccessFunctions.forEach { it(t) } - - override fun onError(e: Throwable?) = (e ?: RuntimeException("exception is unknown")).let { ex -> - if (onErrorFunctions.isEmpty()) { - throw OnErrorNotImplementedException(ex) - } else { - onErrorFunctions.forEach { it(ex) } - } - } +class OnErrorNotImplementedException(ex: Throwable) : RuntimeException(ex) - fun onSuccess(onSuccessFunction: (t: T) -> Unit): FunctionSingleSubscriber = copy { onSuccessFunctions.add(onSuccessFunction) } - fun onError(onErrorFunction: (e: Throwable) -> Unit): FunctionSingleSubscriber = copy { onErrorFunctions.add(onErrorFunction) } - - private fun copy(block: FunctionSingleSubscriber.() -> Unit): FunctionSingleSubscriber { - val newSubscriber = FunctionSingleSubscriber() - newSubscriber.onSuccessFunctions.addAll(onSuccessFunctions) - newSubscriber.onErrorFunctions.addAll(onErrorFunctions) - - newSubscriber.block() - - return newSubscriber - } -} - -class FunctionSubscriberModifier(init: FunctionSubscriber = subscriber()) { +class FunctionSubscriberModifier(init: FunctionSubscriber = subscriber()) { var subscriber: FunctionSubscriber = init private set - - fun onCompleted(onCompletedFunction: () -> Unit) : Unit { subscriber = subscriber.onCompleted(onCompletedFunction) } - fun onError(onErrorFunction: (t : Throwable) -> Unit) : Unit { subscriber = subscriber.onError(onErrorFunction) } - fun onNext(onNextFunction: (t : T) -> Unit) : Unit { subscriber = subscriber.onNext(onNextFunction) } - fun onStart(onStartFunction : () -> Unit) : Unit { subscriber = subscriber.onStart(onStartFunction) } -} - -class FunctionSingleSubscriberModifier(init: FunctionSingleSubscriber = singleSubscriber()) { - var subscriber: FunctionSingleSubscriber = init - private set - - fun onSuccess(onSuccessFunction: (t: T) -> Unit): Unit { subscriber = subscriber.onSuccess(onSuccessFunction) } - fun onError(onErrorFunction: (r: Throwable) -> Unit): Unit {subscriber = subscriber.onError(onErrorFunction) } + + fun onComplete(onCompletedFunction: () -> Unit): Unit { + subscriber = subscriber.onCompleted(onCompletedFunction) + } + + fun onError(onErrorFunction: (t: Throwable) -> Unit): Unit { + subscriber = subscriber.onError(onErrorFunction) + } + + fun onNext(onNextFunction: (t: T) -> Unit): Unit { + subscriber = subscriber.onNext(onNextFunction) + } + + fun onStart(onStartFunction: () -> Unit): Unit { + subscriber = subscriber.onStart(onStartFunction) + } + + fun onSuccess(onSuccessFunction: (t: T) -> Unit): Unit { + subscriber = subscriber.onNext(onSuccessFunction) + } } -fun subscriber(): FunctionSubscriber = FunctionSubscriber() -fun singleSubscriber(): FunctionSingleSubscriber = FunctionSingleSubscriber() -fun Subscriber.synchronized(): Subscriber = SerializedSubscriber(this) -fun Subscriber<*>.add(action: () -> Unit) = add(Subscriptions.create(action)) +fun subscriber(): FunctionSubscriber = FunctionSubscriber() \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/subscription.kt b/src/main/kotlin/rx/lang/kotlin/subscription.kt index 776ae5e..8dd4703 100644 --- a/src/main/kotlin/rx/lang/kotlin/subscription.kt +++ b/src/main/kotlin/rx/lang/kotlin/subscription.kt @@ -1,19 +1,19 @@ package rx.lang.kotlin -import rx.Subscription -import rx.subscriptions.CompositeSubscription +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable /** * subscription += observable.subscribe{} */ -operator fun CompositeSubscription.plusAssign(subscription: Subscription) = add(subscription) +operator fun CompositeDisposable.plusAssign(subscription: Disposable) { + add(subscription) +} /** * Add the subscription to a CompositeSubscription. * @param compositeSubscription CompositeSubscription to add this subscription to * @return this instance */ -fun Subscription.addTo(compositeSubscription: CompositeSubscription) : Subscription { - compositeSubscription.add(this) - return this -} \ No newline at end of file +fun Disposable.addTo(compositeSubscription: CompositeDisposable): Disposable + = apply { compositeSubscription.add(this) } \ No newline at end of file diff --git a/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt b/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt index 35705a6..20dba91 100644 --- a/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt @@ -16,14 +16,18 @@ package rx.lang.kotlin +import io.reactivex.Notification +import io.reactivex.Observable +import io.reactivex.ObservableEmitter +import io.reactivex.ObservableOnSubscribe +import io.reactivex.functions.BiFunction +import io.reactivex.functions.Function3 import org.junit.Assert.assertEquals import org.junit.Assert.fail import org.junit.Test -import org.mockito.Mockito.* -import rx.Notification -import rx.Observable -import rx.Observable.OnSubscribe -import rx.Subscriber +import org.mockito.Mockito.any +import org.mockito.Mockito.times +import org.mockito.Mockito.verify import kotlin.concurrent.thread /** @@ -33,10 +37,10 @@ class BasicKotlinTests : KotlinTests() { @Test fun testCreate() { - Observable.create(OnSubscribe { subscriber -> - subscriber.onNext("Hello") - subscriber.onCompleted() - }).subscribe { result -> + Observable.create { onSubscribe -> + onSubscribe.onNext("Hello") + onSubscribe.onComplete() + }.subscribe { result -> a.received(result) } @@ -44,18 +48,14 @@ class BasicKotlinTests : KotlinTests() { } @Test fun testFilter() { - Observable.from(listOf(1, 2, 3)).filter { it >= 2 }.subscribe(received()) - verify(a, times(0)).received(1); - verify(a, times(1)).received(2); - verify(a, times(1)).received(3); + Observable.fromIterable(listOf(1, 2, 3)).filter { it >= 2 }.subscribe(received()) + verify(a, times(0)).received(1) + verify(a, times(1)).received(2) + verify(a, times(1)).received(3) } @Test fun testLast() { - assertEquals("three", Observable.from(listOf("one", "two", "three")).toBlocking().last()) - } - - @Test fun testLastWithPredicate() { - assertEquals("two", Observable.from(listOf("one", "two", "three")).toBlocking().last { x -> x.length == 3 }) + assertEquals("three", Observable.fromIterable(listOf("one", "two", "three")).blockingLast()) } @Test fun testMap1() { @@ -64,27 +64,27 @@ class BasicKotlinTests : KotlinTests() { } @Test fun testMap2() { - Observable.from(listOf(1, 2, 3)).map { v -> "hello_$v" }.subscribe(received()) + Observable.fromIterable(listOf(1, 2, 3)).map { v -> "hello_$v" }.subscribe(received()) verify(a, times(1)).received("hello_1") verify(a, times(1)).received("hello_2") verify(a, times(1)).received("hello_3") } @Test fun testMaterialize() { - Observable.from(listOf(1, 2, 3)).materialize().subscribe(received()) + Observable.fromIterable(listOf(1, 2, 3)).materialize().subscribe(received()) verify(a, times(4)).received(any(Notification::class.java)) verify(a, times(0)).error(any(Exception::class.java)) } @Test fun testMerge() { Observable.merge( - Observable.from(listOf(1, 2, 3)), + Observable.fromIterable(listOf(1, 2, 3)), Observable.merge( Observable.just(6), Observable.error(NullPointerException()), Observable.just(7) ), - Observable.from(listOf(4, 5)) + Observable.fromIterable(listOf(4, 5)) ).subscribe(received()) { e -> a.error(e) } verify(a, times(1)).received(1) verify(a, times(1)).received(2) @@ -110,19 +110,19 @@ class BasicKotlinTests : KotlinTests() { @Test fun testFromWithIterable() { val list = listOf(1, 2, 3, 4, 5) - assertEquals(5, Observable.from(list).count().toBlocking().single()) + assertEquals(5, Observable.fromIterable(list).count().blockingGet()) } @Test fun testFromWithObjects() { val list = listOf(1, 2, 3, 4, 5) - assertEquals(2, Observable.from(listOf(list, 6)).count().toBlocking().single()) + assertEquals(2, Observable.fromIterable(listOf(list, 6)).count().blockingGet()) } @Test fun testStartWith() { val list = listOf(10, 11, 12, 13, 14) val startList = listOf(1, 2, 3, 4, 5) - assertEquals(6, Observable.from(list).startWith(0).count().toBlocking().single()) - assertEquals(10, Observable.from(list).startWith(startList).count().toBlocking().single()) + assertEquals(6, Observable.fromIterable(list).startWith(0).count().blockingGet()) + assertEquals(10, Observable.fromIterable(list).startWith(startList).count().blockingGet()) } @Test fun testScriptWithOnNext() { @@ -131,21 +131,21 @@ class BasicKotlinTests : KotlinTests() { } @Test fun testSkipTake() { - Observable.from(listOf(1, 2, 3)).skip(1).take(1).subscribe(received()) + Observable.fromIterable(listOf(1, 2, 3)).skip(1).take(1).subscribe(received()) verify(a, times(0)).received(1) verify(a, times(1)).received(2) verify(a, times(0)).received(3) } @Test fun testSkip() { - Observable.from(listOf(1, 2, 3)).skip(2).subscribe(received()) + Observable.fromIterable(listOf(1, 2, 3)).skip(2).subscribe(received()) verify(a, times(0)).received(1) verify(a, times(0)).received(2) verify(a, times(1)).received(3) } @Test fun testTake() { - Observable.from(listOf(1, 2, 3)).take(2).subscribe(received()) + Observable.fromIterable(listOf(1, 2, 3)).take(2).subscribe(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(0)).received(3) @@ -157,14 +157,17 @@ class BasicKotlinTests : KotlinTests() { } @Test fun testTakeWhile() { - Observable.from(listOf(1, 2, 3)).takeWhile { x -> x < 3 }.subscribe(received()) + Observable.fromIterable(listOf(1, 2, 3)).takeWhile { x -> x < 3 }.subscribe(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(0)).received(3) } @Test fun testTakeWhileWithIndex() { - Observable.from(listOf(1, 2, 3)).takeWhile { x -> x < 3 }.zipWith(Observable.range(0,Integer.MAX_VALUE)){ x, i -> x }.subscribe(received()) + Observable.fromIterable(listOf(1, 2, 3)) + .takeWhile { x -> x < 3 } + .zipWith(Observable.range(0, Integer.MAX_VALUE), BiFunction { x, i -> x }) + .subscribe(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(0)).received(3) @@ -176,56 +179,56 @@ class BasicKotlinTests : KotlinTests() { } @Test fun testForEach() { - Observable.create(AsyncObservable()).toBlocking().forEach(received()) + Observable.create(AsyncObservable()).blockingForEach(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(1)).received(3) } @Test(expected = RuntimeException::class) fun testForEachWithError() { - Observable.create(AsyncObservable()).toBlocking().forEach { throw RuntimeException("err") } + Observable.create(AsyncObservable()).blockingForEach { throw RuntimeException("err") } fail("we expect an exception to be thrown") } @Test fun testLastOrDefault() { - assertEquals("two", Observable.from(listOf("one", "two")).toBlocking().lastOrDefault("default") { x -> x.length == 3 }) - assertEquals("default", Observable.from(listOf("one", "two")).toBlocking().lastOrDefault("default") { x -> x.length > 3 }) + assertEquals("two", Observable.fromIterable(listOf("one", "two")).blockingLast("default")) + assertEquals("default", Observable.fromIterable(listOf("one", "two")).filter { it.length > 3 }.blockingLast("default")) } @Test(expected = IllegalArgumentException::class) fun testSingle() { - assertEquals("one", Observable.just("one").toBlocking().single { x -> x.length == 3 }) - Observable.from(listOf("one", "two")).toBlocking().single { x -> x.length == 3 } + assertEquals("one", Observable.just("one").blockingSingle()) + Observable.fromIterable(listOf("one", "two")).blockingSingle() fail() } @Test fun testDefer() { - Observable.defer { Observable.from(listOf(1, 2)) }.subscribe(received()) + Observable.defer { Observable.fromIterable(listOf(1, 2)) }.subscribe(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) } @Test fun testAll() { - Observable.from(listOf(1, 2, 3)).all { x -> x > 0 }.subscribe(received()) + Observable.fromIterable(listOf(1, 2, 3)).all { x -> x > 0 }.subscribe(received()) verify(a, times(1)).received(true) } @Test fun testZip() { - val o1 = Observable.from(listOf(1, 2, 3)) - val o2 = Observable.from(listOf(4, 5, 6)) - val o3 = Observable.from(listOf(7, 8, 9)) + val o1 = Observable.fromIterable(listOf(1, 2, 3)) + val o2 = Observable.fromIterable(listOf(4, 5, 6)) + val o3 = Observable.fromIterable(listOf(7, 8, 9)) - val values = Observable.zip(o1, o2, o3) { a, b, c -> listOf(a, b, c) }.toList().toBlocking().single() + val values = Observable.zip(o1, o2, o3, Function3> { a, b, c -> listOf(a, b, c) }).toList().blockingGet() assertEquals(listOf(1, 4, 7), values[0]) assertEquals(listOf(2, 5, 8), values[1]) assertEquals(listOf(3, 6, 9), values[2]) } @Test fun testZipWithIterable() { - val o1 = Observable.from(listOf(1, 2, 3)) - val o2 = Observable.from(listOf(4, 5, 6)) - val o3 = Observable.from(listOf(7, 8, 9)) + val o1 = Observable.fromIterable(listOf(1, 2, 3)) + val o2 = Observable.fromIterable(listOf(4, 5, 6)) + val o3 = Observable.fromIterable(listOf(7, 8, 9)) - val values = Observable.zip(listOf(o1, o2, o3)) { args -> listOf(*args) }.toList().toBlocking().single() + val values = Observable.zip(listOf(o1, o2, o3), { args -> listOf(*args) }).toList().blockingGet() assertEquals(listOf(1, 4, 7), values[0]) assertEquals(listOf(2, 5, 8), values[1]) assertEquals(listOf(3, 6, 9), values[2]) @@ -234,13 +237,13 @@ class BasicKotlinTests : KotlinTests() { @Test fun testGroupBy() { var count = 0 - Observable.from(listOf("one", "two", "three", "four", "five", "six")) - .groupBy { s -> s.length } - .flatMap { groupObervable -> - groupObervable.map { s -> - "Value: $s Group ${groupObervable.key}" + Observable.fromIterable(listOf("one", "two", "three", "four", "five", "six")) + .groupBy(String::length) + .flatMap { groupObservable -> + groupObservable.map { s -> + "Value: $s Group ${groupObservable.key}" } - }.toBlocking().forEach { s -> + }.blockingForEach { s -> println(s) count++ } @@ -249,45 +252,42 @@ class BasicKotlinTests : KotlinTests() { } - class TestFactory() { var counter = 1 val numbers: Observable - get(){ - return Observable.from(listOf(1, 3, 2, 5, 4)) + get() { + return Observable.fromIterable(listOf(1, 3, 2, 5, 4)) } val onSubscribe: TestOnSubscribe - get(){ + get() { return TestOnSubscribe(counter++) } val observable: Observable - get(){ + get() { return Observable.create(onSubscribe) } } - class AsyncObservable : OnSubscribe { - override fun call(op: Subscriber) { + class AsyncObservable : ObservableOnSubscribe { + override fun subscribe(op: ObservableEmitter) { thread { Thread.sleep(50) op.onNext(1) op.onNext(2) op.onNext(3) - op.onCompleted() + op.onComplete() } - } } - class TestOnSubscribe(val count: Int) : OnSubscribe { - override fun call(op: Subscriber) { + class TestOnSubscribe(val count: Int) : ObservableOnSubscribe { + override fun subscribe(op: ObservableEmitter) { op.onNext("hello_$count") - op.onCompleted() + op.onComplete() } - } } diff --git a/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt b/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt index 85278f8..6f1a3d8 100644 --- a/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt @@ -1,18 +1,18 @@ package rx.lang.kotlin +import io.reactivex.Single +import io.reactivex.functions.Action import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull -import rx.Single -import rx.functions.Action0 -import java.util.* +import org.junit.Test +import java.util.NoSuchElementException import java.util.concurrent.Callable -import org.junit.Test as test class CompletableTest { - @test fun testCreateFromAction() { + @Test fun testCreateFromAction() { var count = 0 - val c1 = Action0 { count++ }.toCompletable() + val c1 = Action { count++ }.toCompletable() assertNotNull(c1) c1.subscribe() assertEquals(1, count) @@ -24,7 +24,7 @@ class CompletableTest { assertEquals(1, count) } - @test fun testCreateFromCallable() { + @Test fun testCreateFromCallable() { var count = 0 val c1 = Callable { count++ }.toCompletable() assertNotNull(c1) @@ -32,16 +32,16 @@ class CompletableTest { assertEquals(1, count) } - @test(expected = NoSuchElementException::class) fun testCreateFromFuture() { - val c1 = 1.toSingletonObservable().toBlocking().toFuture().toCompletable() + @Test(expected = NoSuchElementException::class) fun testCreateFromFuture() { + val c1 = 1.toSingletonObservable().single(0).toCompletable() assertNotNull(c1) - c1.toObservable().toBlocking().first() + c1.toObservable().blockingFirst() } - @test(expected = NoSuchElementException::class) fun testCreateFromSingle() { + @Test(expected = NoSuchElementException::class) fun testCreateFromSingle() { val c1 = Single.just("Hello World!").toCompletable() assertNotNull(c1) - c1.toObservable().toBlocking().first() + c1.toObservable().blockingFirst() } } diff --git a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt index ead462e..1f312a2 100644 --- a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt @@ -16,15 +16,20 @@ package rx.lang.kotlin +import io.reactivex.Notification +import io.reactivex.Observable +import io.reactivex.ObservableEmitter +import io.reactivex.functions.BiFunction +import io.reactivex.functions.Function3 +import io.reactivex.schedulers.TestScheduler import org.funktionale.partials.invoke import org.junit.Assert.assertEquals import org.junit.Assert.fail import org.junit.Test -import org.mockito.Mockito.* -import rx.Notification -import rx.Observable -import rx.Subscriber -import rx.schedulers.TestScheduler +import org.mockito.Mockito.any +import org.mockito.Mockito.inOrder +import org.mockito.Mockito.times +import org.mockito.Mockito.verify import java.util.concurrent.TimeUnit import kotlin.concurrent.thread @@ -35,10 +40,9 @@ class ExtensionTests : KotlinTests() { @Test fun testCreate() { - observable { subscriber -> subscriber.onNext("Hello") - subscriber.onCompleted() + subscriber.onComplete() }.subscribe { result -> a.received(result) } @@ -55,11 +59,11 @@ class ExtensionTests : KotlinTests() { @Test fun testLast() { - assertEquals("three", listOf("one", "two", "three").toObservable().toBlocking().last()) + assertEquals("three", listOf("one", "two", "three").toObservable().blockingLast()) } @Test fun testLastWithPredicate() { - assertEquals("two", listOf("one", "two", "three").toObservable().toBlocking().last { x -> x.length == 3 }) + assertEquals("two", listOf("one", "two", "three").toObservable().filter { it.length == 3 }.blockingLast()) } @Test fun testMap1() { @@ -113,14 +117,14 @@ class ExtensionTests : KotlinTests() { @Test fun testFromWithIterable() { - assertEquals(5, listOf(1, 2, 3, 4, 5).toObservable().count().toBlocking().single()) + assertEquals(5, listOf(1, 2, 3, 4, 5).toObservable().count().blockingGet()) } @Test fun testStartWith() { val list = listOf(10, 11, 12, 13, 14) val startList = listOf(1, 2, 3, 4, 5) - assertEquals(6, list.toObservable().startWith(0).count().toBlocking().single()) - assertEquals(10, list.toObservable().startWith(startList).count().toBlocking().single()) + assertEquals(6, list.toObservable().startWith(0).count().blockingGet()) + assertEquals(10, list.toObservable().startWith(startList).count().blockingGet()) } @Test fun testScriptWithOnNext() { @@ -162,7 +166,7 @@ class ExtensionTests : KotlinTests() { } @Test fun testTakeWhileWithIndex() { - listOf(1, 2, 3).toObservable().takeWhile { x -> x < 3 }.zipWith((0..Integer.MAX_VALUE).toObservable()) { x, i -> x }.subscribe(received()) + listOf(1, 2, 3).toObservable().takeWhile { x -> x < 3 }.zipWith((0..Integer.MAX_VALUE).toObservable(), BiFunction { x, i -> x }).subscribe(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(0)).received(3) @@ -174,24 +178,24 @@ class ExtensionTests : KotlinTests() { } @Test fun testForEach() { - observable(asyncObservable).toBlocking().forEach(received()) + observable(asyncObservable).blockingForEach(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(1)).received(3) } @Test(expected = RuntimeException::class) fun testForEachWithError() { - observable(asyncObservable).toBlocking().forEach { throw RuntimeException("err") } + observable(asyncObservable).blockingForEach { throw RuntimeException("err") } fail("we expect an exception to be thrown") } @Test fun testLastOrDefault() { - assertEquals("two", listOf("one", "two").toObservable().toBlocking().lastOrDefault("default") { x -> x.length == 3 }) - assertEquals("default", listOf("one", "two").toObservable().toBlocking().lastOrDefault("default") { x -> x.length > 3 }) + assertEquals("two", listOf("one", "two").toObservable().blockingLast("default")) + assertEquals("default", listOf("one", "two").toObservable().filter { it.length > 3 }.blockingLast("default")) } @Test fun testDefer() { - deferredObservable { listOf(1, 2).toObservable() }.subscribe(received()) + listOf(1, 2).toObservable().defer().subscribe(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) } @@ -206,7 +210,7 @@ class ExtensionTests : KotlinTests() { val o2 = listOf(4, 5, 6).toObservable() val o3 = listOf(7, 8, 9).toObservable() - val values = Observable.zip(o1, o2, o3) { a, b, c -> listOf(a, b, c) }.toList().toBlocking().single() + val values = Observable.zip(o1, o2, o3, Function3 > { a, b, c -> listOf(a, b, c) }).toList().blockingGet() assertEquals(listOf(1, 4, 7), values[0]) assertEquals(listOf(2, 5, 8), values[1]) assertEquals(listOf(3, 6, 9), values[2]) @@ -217,7 +221,7 @@ class ExtensionTests : KotlinTests() { val worker = testScheduler.createWorker() val observable = observable> { s -> - fun at(delay: Long, func : () -> Unit){ + fun at(delay: Long, func: () -> Unit) { worker.schedule({ func() }, delay, TimeUnit.MILLISECONDS) @@ -229,7 +233,7 @@ class ExtensionTests : KotlinTests() { val second = Observable.interval(5, TimeUnit.MILLISECONDS, testScheduler).take(3) at(11, { s.onNext(second) }) - at(40, { s.onCompleted() }) + at(40, { s.onComplete() }) } observable.switchOnNext().subscribe(received()) @@ -246,30 +250,29 @@ class ExtensionTests : KotlinTests() { inOrder.verifyNoMoreInteractions() } - val funOnSubscribe: (Int, Subscriber) -> Unit = { counter, subscriber -> + val funOnSubscribe: (Int, ObservableEmitter) -> Unit = { counter, subscriber -> subscriber.onNext("hello_$counter") - subscriber.onCompleted() + subscriber.onComplete() } - val asyncObservable: (Subscriber) -> Unit = { subscriber -> + val asyncObservable: (ObservableEmitter) -> Unit = { subscriber -> thread { Thread.sleep(50) subscriber.onNext(1) subscriber.onNext(2) subscriber.onNext(3) - subscriber.onCompleted() + subscriber.onComplete() } } - - inner class TestFactory() { + inner class TestFactory { var counter = 1 val numbers: Observable get() = listOf(1, 3, 2, 5, 4).toObservable() - val onSubscribe: (Subscriber) -> Unit + val onSubscribe: (ObservableEmitter) -> Unit get() = funOnSubscribe(p1 = counter++) // partial applied function val observable: Observable diff --git a/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt b/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt index d58d1b2..75f8e6e 100644 --- a/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt @@ -16,10 +16,10 @@ package rx.lang.kotlin +import io.reactivex.Observable import org.junit.Before import org.mockito.Mock import org.mockito.MockitoAnnotations -import rx.Observable abstract class KotlinTests { @Mock var a: ScriptAssertion = uninitialized() @@ -30,7 +30,7 @@ abstract class KotlinTests { } @Suppress("BASE_WITH_NULLABLE_UPPER_BOUND") - fun received() = {result: T? -> a.received(result) } + fun received() = { result: T? -> a.received(result) } interface ScriptAssertion { fun error(e: Throwable?) diff --git a/src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt b/src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt index 165a7e3..155ae4e 100644 --- a/src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt @@ -1,26 +1,30 @@ package rx.lang.kotlin -import org.junit.Assert.* +import io.reactivex.Observable +import io.reactivex.observers.TestObserver +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull import org.junit.Ignore -import rx.Observable -import rx.observers.TestSubscriber import java.util.concurrent.atomic.AtomicInteger import org.junit.Test as test class ObservablesTest { @test fun testCreation() { - val o0 : Observable = emptyObservable() - observable { s -> s.onNext(1); s.onNext(777); s.onCompleted() }.toList().forEach { - assertEquals(listOf(1, 777), it) - } - val o1 : Observable = listOf(1, 2, 3).toObservable() - val o2 : Observable> = listOf(1, 2, 3).toSingletonObservable() - - val o3 : Observable = deferredObservable { observable { s -> s.onNext(1) } } - val o4 : Observable = Array(3) {0}.toObservable() - val o5 : Observable = IntArray(3).toObservable() - + val o0: Observable = emptyObservable() + val list = observable { s -> + s.onNext(1) + s.onNext(777) + s.onComplete() + }.toList().blockingGet() + assertEquals(listOf(1, 777), list) + val o1: Observable = listOf(1, 2, 3).toObservable() + val o2: Observable> = listOf(1, 2, 3).toSingletonObservable() + + val o3: Observable = observable { s -> s.onNext(1) }.defer() + val o4: Observable = Array(3) { 0 }.toObservable() + val o5: Observable = IntArray(3).toObservable() + assertNotNull(o0) assertNotNull(o1) assertNotNull(o2) @@ -28,7 +32,7 @@ class ObservablesTest { assertNotNull(o4) assertNotNull(o5) } - + @test fun testExampleFromReadme() { val result = observable { subscriber -> subscriber.onNext("H") @@ -37,138 +41,114 @@ class ObservablesTest { subscriber.onNext("") subscriber.onNext("l") subscriber.onNext("o") - subscriber.onCompleted() - }.filter { it.isNotEmpty() }. - fold (StringBuilder()) { sb, e -> sb.append(e) }. - map { it.toString() }. - toBlocking().single() - + subscriber.onComplete() + }.filter(String::isNotEmpty). + fold(StringBuilder(), StringBuilder::append). + map { it.toString() }. + blockingGet() + assertEquals("Hello", result) } - + @test fun iteratorObservable() { - assertEquals(listOf(1,2,3), listOf(1,2,3).iterator().toObservable().toList().toBlocking().single()) + assertEquals(listOf(1, 2, 3), listOf(1, 2, 3).iterator().toObservable().toList().blockingGet()) } - + @test fun intProgressionStep1Empty() { - assertEquals(listOf(1), (1..1).toObservable().toList().toBlocking().first()) + assertEquals(listOf(1), (1..1).toObservable().toList().blockingGet()) } + @test fun intProgressionStep1() { - assertEquals((1..10).toList(), (1..10).toObservable().toList().toBlocking().first()) + assertEquals((1..10).toList(), (1..10).toObservable().toList().blockingGet()) } - + @test fun intProgressionDownTo() { - assertEquals((1 downTo 10).toList(), (1 downTo 10).toObservable().toList().toBlocking().first()) + assertEquals((1 downTo 10).toList(), (1 downTo 10).toObservable().toList().blockingGet()) } - - @Ignore + + @Ignore("Too slow") @test fun intProgressionOverflow() { - // too slow - assertEquals((0..10).toList().reversed(), (-10 .. Integer.MAX_VALUE).toObservable().skip(Integer.MAX_VALUE).map{Integer.MAX_VALUE - it}.toList().toBlocking().first()) + assertEquals((0..10).toList().reversed(), (-10..Integer.MAX_VALUE).toObservable().skip(Integer.MAX_VALUE.toLong()).map { Integer.MAX_VALUE - it }.toList().blockingGet()) } - - @test fun filterNotNull() { - val o : Observable = listOf(1, null).toObservable().filterNotNull() - o.toList().forEach { - assertEquals(listOf(1), it) - } - } - - @test fun requireNoNullsWithoutNulls() { - (listOf(1,2) as List).toObservable().requireNoNulls().subscribe() - } - - @test fun requireNoNulls() { - try { - val o : Observable = listOf(1, null).toObservable().requireNoNulls() - - o.subscribe() - fail("shouldn't reach here") - } catch (expected : Throwable) { - } - } - + @test fun testWithIndex() { - listOf("a", "b", "c").toObservable(). - withIndex(). - toList(). - forEach { - assertEquals(listOf(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c")), it) - } + listOf("a", "b", "c").toObservable() + .withIndex() + .toList() + .test() + .assertValues(listOf(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c"))) } - + @test fun `withIndex() shouldn't share index between multiple subscribers`() { val o = listOf("a", "b", "c").toObservable().withIndex() - - val subscriber1 = TestSubscriber>() - val subscriber2 = TestSubscriber>() - + + val subscriber1 = TestObserver.create>() + val subscriber2 = TestObserver.create>() + o.subscribe(subscriber1) o.subscribe(subscriber2) - + subscriber1.awaitTerminalEvent() subscriber1.assertValues(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c")) - + subscriber2.awaitTerminalEvent() subscriber2.assertValues(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c")) } - + @test fun testFold() { - listOf(1, 2, 3).toObservable().fold(0) {acc, e -> acc + e}.single().forEach { - assertEquals(6, it) - } + val result = listOf(1, 2, 3).toObservable().fold(0) { acc, e -> acc + e }.blockingGet() + assertEquals(6, result) } - + @test fun `kotlin sequence should produce expected items and observable be able to handle em`() { - kotlin.sequences.generateSequence(0) {it + 1}.toObservable().take(3).toList().forEach { - assertEquals(listOf(0, 1, 2), it) - } + generateSequence(0) { it + 1 }.toObservable() + .take(3) + .toList() + .test() + .assertValues(listOf(0, 1, 2)) } - + @test fun `infinite iterable should not hang or produce too many elements`() { val generated = AtomicInteger() - kotlin.sequences.generateSequence { generated.incrementAndGet() }.toObservable(). + generateSequence { generated.incrementAndGet() }.toObservable(). take(100). toList(). subscribe() - + assertEquals(100, generated.get()) } - + @test fun testFlatMapSequence() { assertEquals( listOf(1, 2, 3, 2, 3, 4, 3, 4, 5), - listOf(1,2,3).toObservable().flatMapSequence { listOf(it, it + 1, it + 2).asSequence() }.toList().toBlocking().single() + listOf(1, 2, 3).toObservable().flatMapSequence { listOf(it, it + 1, it + 2).asSequence() }.toList().blockingGet() ) } - + @test fun testCombineLatest() { - val list = listOf(1,2,3,2,3,4,3,4,5) - assertEquals(list, list.map { it.toSingletonObservable() }.combineLatest { it }.toBlocking().first()) + val list = listOf(1, 2, 3, 2, 3, 4, 3, 4, 5) + assertEquals(list, list.map { it.toSingletonObservable() }.combineLatest { it }.blockingFirst()) } - + @test fun testZip() { - val list = listOf(1,2,3,2,3,4,3,4,5) - assertEquals(list, list.map { it.toSingletonObservable() }.zip { it }.toBlocking().first()) + val list = listOf(1, 2, 3, 2, 3, 4, 3, 4, 5) + assertEquals(list, list.map { it.toSingletonObservable() }.zip { it }.blockingFirst()) } - + @test fun testCast() { val source = Observable.just(1, 2) val observable = source.cast() - val subscriber = TestSubscriber() - observable.subscribe(subscriber) - subscriber.apply { - assertValues(1, 2) - assertNoErrors() - assertCompleted() - } + observable.test() + .await() + .assertValues(1, 2) + .assertNoErrors() + .assertComplete() } - + @test fun testCastWithWrongType() { val source = Observable.just(1, 2) val observable = source.cast() - val subscriber = TestSubscriber() - observable.subscribe(subscriber) - subscriber.assertError(ClassCastException::class.java) + observable.test() + .assertError(ClassCastException::class.java) } } \ No newline at end of file diff --git a/src/test/kotlin/rx/lang/kotlin/SingleTest.kt b/src/test/kotlin/rx/lang/kotlin/SingleTest.kt index a77e0e7..5bce1b2 100644 --- a/src/test/kotlin/rx/lang/kotlin/SingleTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/SingleTest.kt @@ -1,10 +1,10 @@ package rx.lang.kotlin import org.mockito.Mockito +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify import java.util.concurrent.Callable import org.junit.Test as test -import org.mockito.Mockito.verify -import org.mockito.Mockito.mock class SingleTest : KotlinTests() { @test fun testCreate() { @@ -17,7 +17,7 @@ class SingleTest : KotlinTests() { } @test fun testCreateFromFuture() { - val future = "Hello World!".toSingletonObservable().toBlocking().toFuture() + val future = "Hello World!".toSingletonObservable().toFuture() val single = future.toSingle() single.subscribe { result -> a.received(result) diff --git a/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt b/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt index d049a49..051937d 100644 --- a/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt @@ -1,10 +1,10 @@ package rx.lang.kotlin +import io.reactivex.Observer +import io.reactivex.disposables.Disposables import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue -import rx.Subscriber -import rx.exceptions.OnErrorNotImplementedException -import java.util.* +import java.util.ArrayList import org.junit.Test as test class SubscribersTest { @@ -12,169 +12,150 @@ class SubscribersTest { val s = subscriber() callSubscriberMethods(false, s) } - + @test fun testSubscriberConstruction() { val events = ArrayList() - + callSubscriberMethods(false, subscriber(). - onNext {events.add("onNext($it)")} + onNext { events.add("onNext($it)") } ) - + assertEquals(listOf("onNext(1)"), events) events.clear() - + callSubscriberMethods(true, subscriber(). - onNext {events.add("onNext($it)")}. + onNext { events.add("onNext($it)") }. onError { events.add(it.javaClass.simpleName) } ) - + assertEquals(listOf("onNext(1)", "RuntimeException"), events) events.clear() - + callSubscriberMethods(true, subscriber(). - onNext {events.add("onNext($it)")}. + onNext { events.add("onNext($it)") }. onError { events.add(it.javaClass.simpleName) }. onCompleted { events.add("onCompleted") } ) - + assertEquals(listOf("onNext(1)", "RuntimeException", "onCompleted"), events) events.clear() } - - @test(expected = OnErrorNotImplementedException::class) + + @test(expected = Exception::class) fun testNoErrorHandlers() { subscriber().onError(Exception("expected")) } - + @test fun testErrorHandlers() { var errorsCaught = 0 - + subscriber(). onError { errorsCaught++ }. onError { errorsCaught++ }. onError(Exception("expected")) - + assertEquals(2, errorsCaught) } - + @test fun testMultipleOnNextHandlers() { var nextCaught = 0 - + subscriber(). - onNext { nextCaught ++ }. - onNext { nextCaught ++ }. + onNext { nextCaught++ }. + onNext { nextCaught++ }. onNext(1) - + assertEquals(2, nextCaught) } - + @test fun testOnStart() { var started = false - subscriber().onStart { started = true }.onStart() + "".toSingletonObservable().subscribeBy { + onStart { started = true } + } assertTrue(started) } - - private fun callSubscriberMethods(hasOnError : Boolean, s: Subscriber) { - s.onStart() + + private fun callSubscriberMethods(hasOnError: Boolean, s: Observer) { + s.onSubscribe(Disposables.empty()) s.onNext(1) try { s.onError(RuntimeException()) - } catch (t : Throwable) { - if (hasOnError) { - throw t - } + } catch (t: Throwable) { + if (hasOnError) throw t } - s.onCompleted() + s.onComplete() } - + @test fun testSubscribeWith() { val completeObservable = observable { it.onNext(1) - it.onCompleted() + it.onComplete() } - + val events = ArrayList() - - completeObservable.subscribeWith { + + completeObservable.subscribeBy { onNext { events.add("onNext($it)") } } - + assertEquals(listOf("onNext(1)"), events) events.clear() - - completeObservable.subscribeWith { + + completeObservable.subscribeBy { onNext { events.add("onNext($it)") } - onCompleted { events.add("onCompleted") } + onComplete { events.add("onCompleted") } } - + assertEquals(listOf("onNext(1)", "onCompleted"), events) events.clear() - + val errorObservable = observable { it.onNext(1) it.onError(RuntimeException()) } - - errorObservable.subscribeWith { + + errorObservable.subscribeBy { onNext { events.add("onNext($it)") } onError { events.add("onError(${it.javaClass.simpleName})") } } - + assertEquals(listOf("onNext(1)", "onError(RuntimeException)"), events) - events.clear() - - try { - errorObservable.subscribeWith { - onNext { events.add("onNext($it)") } - } - } catch (t: Throwable) { - events.add("catch(${t.javaClass.simpleName})") - } - - assertEquals(listOf("onNext(1)", "catch(OnErrorNotImplementedException)"), events) - events.clear() } - + @test fun testSingleSubscribeWith() { val events = ArrayList() val successSingle = singleOf(1) - - successSingle.subscribeWith { + + successSingle.subscribeBy { onSuccess { events.add("onSuccess($it)") } } - + assertEquals(listOf("onSuccess(1)"), events) events.clear() - + val errorSingle = RuntimeException().toSingle() - - errorSingle.subscribeWith { + + errorSingle.subscribeBy { onSuccess { events.add("onSuccess($it)") } onError { events.add("onError(${it.javaClass.simpleName})") } } - + assertEquals(listOf("onError(RuntimeException)"), events) - events.clear() - + } + + @test fun testSubscribeByErrorNotImplemented() { + val events = ArrayList() + val errorSingle = RuntimeException().toSingle() try { - errorSingle.subscribeWith { + errorSingle.subscribeBy { onSuccess { events.add("onSuccess($it)") } } } catch (e: Throwable) { - events.add("catch(${e.javaClass.simpleName})") + val name = e.cause?.javaClass?.simpleName + events.add("catch($name)") } - + assertEquals(listOf("catch(OnErrorNotImplementedException)"), events) - events.clear() - } - - @test fun testIdiomaticAdd() { - var subscriptionCalled = false - val s = subscriber() - - s.add { subscriptionCalled = true } - s.unsubscribe() - - assertTrue(subscriptionCalled) } } diff --git a/src/test/kotlin/rx/lang/kotlin/SubscriptionTests.kt b/src/test/kotlin/rx/lang/kotlin/SubscriptionTests.kt index 2dfe412..01c528d 100644 --- a/src/test/kotlin/rx/lang/kotlin/SubscriptionTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/SubscriptionTests.kt @@ -1,29 +1,29 @@ package rx.lang.kotlin +import io.reactivex.Observable +import io.reactivex.disposables.CompositeDisposable import org.junit.Test -import rx.Observable -import rx.subscriptions.CompositeSubscription import java.util.concurrent.TimeUnit class SubscriptionTest { @Test fun testSubscriptionAddTo() { - val compositeSubscription = CompositeSubscription() + val compositeSubscription = CompositeDisposable() // Create an asynchronous subscription // The delay ensures that we don't automatically unsubscribe because data finished emitting val subscription = Observable.just("test") .delay(100, TimeUnit.MILLISECONDS) - .subscribe(); + .subscribe() - assert(!subscription.isUnsubscribed) + assert(!subscription.isDisposed) - subscription.addTo(compositeSubscription); + subscription.addTo(compositeSubscription) - assert(compositeSubscription.hasSubscriptions()); - assert(!subscription.isUnsubscribed); + assert(compositeSubscription.size() > 0) + assert(!subscription.isDisposed) - compositeSubscription.unsubscribe() + compositeSubscription.dispose() - assert(compositeSubscription.isUnsubscribed); + assert(compositeSubscription.isDisposed) } } \ No newline at end of file From 0f2ee085d10bcd948b9beef0ec22ce072801a765 Mon Sep 17 00:00:00 2001 From: stepango Date: Sun, 8 Jan 2017 12:41:44 +0200 Subject: [PATCH 03/27] Code cleaning. Code review related fixes. --- LICENSE | 2 +- RELEASING.md | 19 ++++ build.gradle | 2 +- .../rx/lang/kotlin/examples/examples.kt | 2 +- src/main/kotlin/rx/lang/kotlin/observables.kt | 12 +-- src/main/kotlin/rx/lang/kotlin/subscribers.kt | 28 +++--- .../kotlin/rx/lang/kotlin/BasicKotlinTests.kt | 3 +- .../kotlin/rx/lang/kotlin/CompletableTest.kt | 6 -- .../kotlin/rx/lang/kotlin/ExtensionTests.kt | 13 +-- .../kotlin/rx/lang/kotlin/ObservablesTest.kt | 56 ++++++------ src/test/kotlin/rx/lang/kotlin/SingleTest.kt | 13 +-- .../kotlin/rx/lang/kotlin/SubscribersTest.kt | 90 ++++++++++--------- 12 files changed, 124 insertions(+), 122 deletions(-) create mode 100644 RELEASING.md diff --git a/LICENSE b/LICENSE index 59f7b92..7f8ced0 100644 --- a/LICENSE +++ b/LICENSE @@ -180,7 +180,7 @@ To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don'value include + replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..773245a --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,19 @@ +Release Process +=============== + + 1. Ensure `VERSION_NAME` in `gradle.properties` is set to the version you want to release. + 2. Add an entry in `CHANGELOG.md` with the changes for the release. + 3. Update `README.md` with the version about to be released. Also update the RxJava version in + this file to its latest. + 4. Update the RxJava version in `rxkotlin/build.gradle` to its latest. (We tell people that we + won't be tracking RxJava releases, and we don't, but we do it anyway when we are releasing for + those who ignore the advice.) + 5. Commit: `git commit -am "Prepare version X.Y.X"` + 6. Tag: `git tag -a X.Y.Z -m "Version X.Y.Z"` + 7. Update `VERSION_NAME` in `gradle.properties` to the next development version. For example, if + you just tagged version 1.0.4 you would set this value to 1.0.5. Do NOT append "-SNAPSHOT" to + this value, it will be added automatically. + 8. Commit: `git commit -am "Prepare next development version."` + 9. Push: `git push && git push --tags` + 10. Paste the `CHANGELOG.md` contents for this version into a Release on GitHub along with the + Groovy for depending on the new version (https://github.com/ReactiveX/RxKotlin/releases). diff --git a/build.gradle b/build.gradle index 678111e..34d7c57 100755 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ apply plugin: 'nebula.rxjava-project' apply plugin: 'kotlin' dependencies { - compile 'io.reactivex.rxjava2:rxjava:2.0.3' + compile 'io.reactivex.rxjava2:rxjava:2.0.4' compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" testCompile 'org.funktionale:funktionale:0.9' testCompile 'junit:junit:4.12' diff --git a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt b/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt index 6f1a0a4..f23f211 100644 --- a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt +++ b/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt @@ -74,7 +74,7 @@ fun asyncObservable(): Observable = observable { subscriber -> fun asyncWiki(vararg articleNames: String): Observable = observable { subscriber -> thread { articleNames.toObservable() - .flatMapMaybe { name: String -> URL("http://en.wikipedia.org/wiki/$name").toScannerObservable().firstElement() } + .flatMapMaybe { name -> URL("http://en.wikipedia.org/wiki/$name").toScannerObservable().firstElement() } .subscribe { subscriber.onNext(it) } } } diff --git a/src/main/kotlin/rx/lang/kotlin/observables.kt b/src/main/kotlin/rx/lang/kotlin/observables.kt index 0246fcd..6a356b9 100644 --- a/src/main/kotlin/rx/lang/kotlin/observables.kt +++ b/src/main/kotlin/rx/lang/kotlin/observables.kt @@ -11,11 +11,6 @@ import io.reactivex.functions.BiFunction */ fun emptyObservable(): Observable = Observable.empty() -/** - * [Observable.defer] alias - */ -fun Observable.defer() = Observable.defer { this } - fun observable(body: (ObservableEmitter) -> Unit): Observable = Observable.create(body) private fun Iterator.toIterable() = object : Iterable { @@ -39,9 +34,6 @@ fun Iterator.toObservable(): Observable = toIterable().toObserva fun Iterable.toObservable(): Observable = Observable.fromIterable(this) fun Sequence.toObservable(): Observable = Observable.fromIterable(iterator().toIterable()) -fun T.toSingletonObservable(): Observable = Observable.just(this) -fun Throwable.toObservable(): Observable = Observable.error(this) - fun Iterable>.merge(): Observable = Observable.merge(this.toObservable()) fun Iterable>.mergeDelayError(): Observable = Observable.mergeDelayError(this.toObservable()) @@ -91,8 +83,8 @@ inline fun List>.combineLatest(crossinline combineFunction: * Observable.zip(List> sources, FuncN combineFunction) */ @Suppress("UNCHECKED_CAST") -inline fun List>.zip(crossinline zipFunction: (args: List) -> R): Observable = - Observable.zip(this) { zipFunction(it.asList().map { it as T }) } +inline fun List>.zip(crossinline zipFunction: (args: List) -> R): Observable + = Observable.zip(this) { zipFunction(it.asList().map { it as T }) } /** * Returns an Observable that emits the items emitted by the source Observable, converted to the specified type. diff --git a/src/main/kotlin/rx/lang/kotlin/subscribers.kt b/src/main/kotlin/rx/lang/kotlin/subscribers.kt index 62adf24..d595547 100644 --- a/src/main/kotlin/rx/lang/kotlin/subscribers.kt +++ b/src/main/kotlin/rx/lang/kotlin/subscribers.kt @@ -8,15 +8,15 @@ import io.reactivex.disposables.Disposable import java.util.ArrayList class FunctionSubscriber : Observer, MaybeObserver, SingleObserver, CompletableObserver { - + private val onCompleteFunctions = ArrayList<() -> Unit>() private val onErrorFunctions = ArrayList<(e: Throwable) -> Unit>() private val onNextFunctions = ArrayList<(value: T) -> Unit>() private val onStartFunctions = ArrayList<() -> Unit>() var origin: Disposable? = null - + override fun onComplete() = onCompleteFunctions.forEach { it() } - + override fun onError(e: Throwable?) = (e ?: RuntimeException("exception is unknown")).let { ex -> if (onErrorFunctions.isEmpty()) { throw OnErrorNotImplementedException(ex) @@ -24,29 +24,29 @@ class FunctionSubscriber : Observer, MaybeObserver, SingleObserve onErrorFunctions.forEach { it(ex) } } } - + override fun onNext(value: T) = onNextFunctions.forEach { it(value) } override fun onSuccess(value: T) = onNext(value) - + override fun onSubscribe(d: Disposable?) { origin = d onStartFunctions.forEach { it() } } - + fun onCompleted(onCompletedFunction: () -> Unit): FunctionSubscriber = copy { onCompleteFunctions.add(onCompletedFunction) } fun onError(onErrorFunction: (t: Throwable) -> Unit): FunctionSubscriber = copy { onErrorFunctions.add(onErrorFunction) } fun onNext(onNextFunction: (t: T) -> Unit): FunctionSubscriber = copy { onNextFunctions.add(onNextFunction) } fun onStart(onStartFunction: () -> Unit): FunctionSubscriber = copy { onStartFunctions.add(onStartFunction) } - + private fun copy(block: FunctionSubscriber.() -> Unit): FunctionSubscriber { val newSubscriber = FunctionSubscriber() newSubscriber.onCompleteFunctions.addAll(onCompleteFunctions) newSubscriber.onErrorFunctions.addAll(onErrorFunctions) newSubscriber.onNextFunctions.addAll(onNextFunctions) newSubscriber.onStartFunctions.addAll(onStartFunctions) - + newSubscriber.block() - + return newSubscriber } } @@ -56,23 +56,23 @@ class OnErrorNotImplementedException(ex: Throwable) : RuntimeException(ex) class FunctionSubscriberModifier(init: FunctionSubscriber = subscriber()) { var subscriber: FunctionSubscriber = init private set - + fun onComplete(onCompletedFunction: () -> Unit): Unit { subscriber = subscriber.onCompleted(onCompletedFunction) } - + fun onError(onErrorFunction: (t: Throwable) -> Unit): Unit { subscriber = subscriber.onError(onErrorFunction) } - + fun onNext(onNextFunction: (t: T) -> Unit): Unit { subscriber = subscriber.onNext(onNextFunction) } - + fun onStart(onStartFunction: () -> Unit): Unit { subscriber = subscriber.onStart(onStartFunction) } - + fun onSuccess(onSuccessFunction: (t: T) -> Unit): Unit { subscriber = subscriber.onNext(onSuccessFunction) } diff --git a/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt b/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt index 20dba91..dc74cf1 100644 --- a/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt @@ -35,9 +35,8 @@ import kotlin.concurrent.thread */ class BasicKotlinTests : KotlinTests() { - @Test fun testCreate() { - Observable.create { onSubscribe -> + Observable.create { onSubscribe -> onSubscribe.onNext("Hello") onSubscribe.onComplete() }.subscribe { result -> diff --git a/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt b/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt index 6f1a3d8..1264af2 100644 --- a/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt @@ -32,12 +32,6 @@ class CompletableTest { assertEquals(1, count) } - @Test(expected = NoSuchElementException::class) fun testCreateFromFuture() { - val c1 = 1.toSingletonObservable().single(0).toCompletable() - assertNotNull(c1) - c1.toObservable().blockingFirst() - } - @Test(expected = NoSuchElementException::class) fun testCreateFromSingle() { val c1 = Single.just("Hello World!").toCompletable() assertNotNull(c1) diff --git a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt index 1f312a2..c41b09c 100644 --- a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt @@ -66,11 +66,6 @@ class ExtensionTests : KotlinTests() { assertEquals("two", listOf("one", "two", "three").toObservable().filter { it.length == 3 }.blockingLast()) } - @Test fun testMap1() { - 1.toSingletonObservable().map { v -> "hello_$v" }.subscribe(received()) - verify(a, times(1)).received("hello_1") - } - @Test fun testMap2() { listOf(1, 2, 3).toObservable().map { v -> "hello_$v" }.subscribe(received()) verify(a, times(1)).received("hello_1") @@ -87,9 +82,9 @@ class ExtensionTests : KotlinTests() { @Test fun testMerge() { listOf(listOf(1, 2, 3).toObservable(), - listOf(6.toSingletonObservable(), - NullPointerException().toObservable(), - 7.toSingletonObservable() + listOf(Observable.just(6), + Observable.error(NullPointerException()), + Observable.just(7) ).merge(), listOf(4, 5).toObservable() ).merge().subscribe(received(), { e -> a.error(e) }) @@ -195,7 +190,7 @@ class ExtensionTests : KotlinTests() { } @Test fun testDefer() { - listOf(1, 2).toObservable().defer().subscribe(received()) + Observable.defer { listOf(1, 2).toObservable() }.subscribe(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) } diff --git a/src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt b/src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt index 155ae4e..263bc51 100644 --- a/src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt @@ -19,12 +19,12 @@ class ObservablesTest { }.toList().blockingGet() assertEquals(listOf(1, 777), list) val o1: Observable = listOf(1, 2, 3).toObservable() - val o2: Observable> = listOf(1, 2, 3).toSingletonObservable() - - val o3: Observable = observable { s -> s.onNext(1) }.defer() + val o2: Observable> = Observable.just(listOf(1, 2, 3)) + + val o3: Observable = Observable.defer { observable { s -> s.onNext(1) } } val o4: Observable = Array(3) { 0 }.toObservable() val o5: Observable = IntArray(3).toObservable() - + assertNotNull(o0) assertNotNull(o1) assertNotNull(o2) @@ -32,7 +32,7 @@ class ObservablesTest { assertNotNull(o4) assertNotNull(o5) } - + @test fun testExampleFromReadme() { val result = observable { subscriber -> subscriber.onNext("H") @@ -46,31 +46,31 @@ class ObservablesTest { fold(StringBuilder(), StringBuilder::append). map { it.toString() }. blockingGet() - + assertEquals("Hello", result) } - + @test fun iteratorObservable() { assertEquals(listOf(1, 2, 3), listOf(1, 2, 3).iterator().toObservable().toList().blockingGet()) } - + @test fun intProgressionStep1Empty() { assertEquals(listOf(1), (1..1).toObservable().toList().blockingGet()) } - + @test fun intProgressionStep1() { assertEquals((1..10).toList(), (1..10).toObservable().toList().blockingGet()) } - + @test fun intProgressionDownTo() { assertEquals((1 downTo 10).toList(), (1 downTo 10).toObservable().toList().blockingGet()) } - + @Ignore("Too slow") @test fun intProgressionOverflow() { assertEquals((0..10).toList().reversed(), (-10..Integer.MAX_VALUE).toObservable().skip(Integer.MAX_VALUE.toLong()).map { Integer.MAX_VALUE - it }.toList().blockingGet()) } - + @test fun testWithIndex() { listOf("a", "b", "c").toObservable() .withIndex() @@ -78,28 +78,28 @@ class ObservablesTest { .test() .assertValues(listOf(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c"))) } - + @test fun `withIndex() shouldn't share index between multiple subscribers`() { val o = listOf("a", "b", "c").toObservable().withIndex() - + val subscriber1 = TestObserver.create>() val subscriber2 = TestObserver.create>() - + o.subscribe(subscriber1) o.subscribe(subscriber2) - + subscriber1.awaitTerminalEvent() subscriber1.assertValues(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c")) - + subscriber2.awaitTerminalEvent() subscriber2.assertValues(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c")) } - + @test fun testFold() { val result = listOf(1, 2, 3).toObservable().fold(0) { acc, e -> acc + e }.blockingGet() assertEquals(6, result) } - + @test fun `kotlin sequence should produce expected items and observable be able to handle em`() { generateSequence(0) { it + 1 }.toObservable() .take(3) @@ -107,34 +107,34 @@ class ObservablesTest { .test() .assertValues(listOf(0, 1, 2)) } - + @test fun `infinite iterable should not hang or produce too many elements`() { val generated = AtomicInteger() generateSequence { generated.incrementAndGet() }.toObservable(). take(100). toList(). subscribe() - + assertEquals(100, generated.get()) } - + @test fun testFlatMapSequence() { assertEquals( listOf(1, 2, 3, 2, 3, 4, 3, 4, 5), listOf(1, 2, 3).toObservable().flatMapSequence { listOf(it, it + 1, it + 2).asSequence() }.toList().blockingGet() ) } - + @test fun testCombineLatest() { val list = listOf(1, 2, 3, 2, 3, 4, 3, 4, 5) - assertEquals(list, list.map { it.toSingletonObservable() }.combineLatest { it }.blockingFirst()) + assertEquals(list, list.map { Observable.just(it) }.combineLatest { it }.blockingFirst()) } - + @test fun testZip() { val list = listOf(1, 2, 3, 2, 3, 4, 3, 4, 5) - assertEquals(list, list.map { it.toSingletonObservable() }.zip { it }.blockingFirst()) + assertEquals(list, list.map { Observable.just(it) }.zip { it }.blockingFirst()) } - + @test fun testCast() { val source = Observable.just(1, 2) val observable = source.cast() @@ -144,7 +144,7 @@ class ObservablesTest { .assertNoErrors() .assertComplete() } - + @test fun testCastWithWrongType() { val source = Observable.just(1, 2) val observable = source.cast() diff --git a/src/test/kotlin/rx/lang/kotlin/SingleTest.kt b/src/test/kotlin/rx/lang/kotlin/SingleTest.kt index 5bce1b2..01017d3 100644 --- a/src/test/kotlin/rx/lang/kotlin/SingleTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/SingleTest.kt @@ -1,13 +1,14 @@ package rx.lang.kotlin +import io.reactivex.Observable +import org.junit.Test import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify import java.util.concurrent.Callable -import org.junit.Test as test class SingleTest : KotlinTests() { - @test fun testCreate() { + @Test fun testCreate() { single { s -> s.onSuccess("Hello World!"); }.subscribe { result -> @@ -16,8 +17,8 @@ class SingleTest : KotlinTests() { verify(a, Mockito.times(1)).received("Hello World!") } - @test fun testCreateFromFuture() { - val future = "Hello World!".toSingletonObservable().toFuture() + @Test fun testCreateFromFuture() { + val future = Observable.just("Hello World!").toFuture() val single = future.toSingle() single.subscribe { result -> a.received(result) @@ -25,7 +26,7 @@ class SingleTest : KotlinTests() { verify(a, Mockito.times(1)).received("Hello World!") } - @test fun testCreateFromCallable() { + @Test fun testCreateFromCallable() { val callable = mock(Callable::class.java) Mockito.`when`(callable.call()).thenReturn("value") @@ -37,7 +38,7 @@ class SingleTest : KotlinTests() { verify(a, Mockito.times(1)).received("value") } - @test fun testCreateFromJust() { + @Test fun testCreateFromJust() { singleOf("Hello World!").subscribe { result -> a.received(result) } diff --git a/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt b/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt index 051937d..b4390cf 100644 --- a/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt @@ -1,81 +1,83 @@ package rx.lang.kotlin +import io.reactivex.Observable import io.reactivex.Observer import io.reactivex.disposables.Disposables import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue +import org.junit.Test import java.util.ArrayList -import org.junit.Test as test class SubscribersTest { - @test fun testEmptySubscriber() { + + @Test fun testEmptySubscriber() { val s = subscriber() callSubscriberMethods(false, s) } - - @test fun testSubscriberConstruction() { + + @Test fun testSubscriberConstruction() { val events = ArrayList() - + callSubscriberMethods(false, subscriber(). onNext { events.add("onNext($it)") } ) - + assertEquals(listOf("onNext(1)"), events) events.clear() - + callSubscriberMethods(true, subscriber(). onNext { events.add("onNext($it)") }. onError { events.add(it.javaClass.simpleName) } ) - + assertEquals(listOf("onNext(1)", "RuntimeException"), events) events.clear() - + callSubscriberMethods(true, subscriber(). onNext { events.add("onNext($it)") }. onError { events.add(it.javaClass.simpleName) }. onCompleted { events.add("onCompleted") } ) - + assertEquals(listOf("onNext(1)", "RuntimeException", "onCompleted"), events) events.clear() } - - @test(expected = Exception::class) + + @Test(expected = Exception::class) fun testNoErrorHandlers() { subscriber().onError(Exception("expected")) } - - @test fun testErrorHandlers() { + + @Test fun testErrorHandlers() { var errorsCaught = 0 - + subscriber(). onError { errorsCaught++ }. onError { errorsCaught++ }. onError(Exception("expected")) - + assertEquals(2, errorsCaught) } - - @test fun testMultipleOnNextHandlers() { + + @Test fun testMultipleOnNextHandlers() { var nextCaught = 0 - + subscriber(). onNext { nextCaught++ }. onNext { nextCaught++ }. onNext(1) - + assertEquals(2, nextCaught) } - - @test fun testOnStart() { + + @Test fun testOnStart() { var started = false - "".toSingletonObservable().subscribeBy { + Observable.just("").subscribeBy { onStart { started = true } } assertTrue(started) } - + private fun callSubscriberMethods(hasOnError: Boolean, s: Observer) { s.onSubscribe(Disposables.empty()) s.onNext(1) @@ -86,65 +88,65 @@ class SubscribersTest { } s.onComplete() } - - @test fun testSubscribeWith() { + + @Test fun testSubscribeWith() { val completeObservable = observable { it.onNext(1) it.onComplete() } - + val events = ArrayList() - + completeObservable.subscribeBy { onNext { events.add("onNext($it)") } } - + assertEquals(listOf("onNext(1)"), events) events.clear() - + completeObservable.subscribeBy { onNext { events.add("onNext($it)") } onComplete { events.add("onCompleted") } } - + assertEquals(listOf("onNext(1)", "onCompleted"), events) events.clear() - + val errorObservable = observable { it.onNext(1) it.onError(RuntimeException()) } - + errorObservable.subscribeBy { onNext { events.add("onNext($it)") } onError { events.add("onError(${it.javaClass.simpleName})") } } - + assertEquals(listOf("onNext(1)", "onError(RuntimeException)"), events) } - - @test fun testSingleSubscribeWith() { + + @Test fun testSingleSubscribeWith() { val events = ArrayList() val successSingle = singleOf(1) - + successSingle.subscribeBy { onSuccess { events.add("onSuccess($it)") } } - + assertEquals(listOf("onSuccess(1)"), events) events.clear() - + val errorSingle = RuntimeException().toSingle() - + errorSingle.subscribeBy { onSuccess { events.add("onSuccess($it)") } onError { events.add("onError(${it.javaClass.simpleName})") } } - + assertEquals(listOf("onError(RuntimeException)"), events) } - - @test fun testSubscribeByErrorNotImplemented() { + + @Test fun testSubscribeByErrorNotImplemented() { val events = ArrayList() val errorSingle = RuntimeException().toSingle() try { @@ -155,7 +157,7 @@ class SubscribersTest { val name = e.cause?.javaClass?.simpleName events.add("catch($name)") } - + assertEquals(listOf("catch(OnErrorNotImplementedException)"), events) } } From 54b37af08b548f454e0677e4f43219112b7ebac3 Mon Sep 17 00:00:00 2001 From: stepango Date: Sun, 8 Jan 2017 12:44:08 +0200 Subject: [PATCH 04/27] Whitespaces removed. --- .../rx/lang/kotlin/examples/examples.kt | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt b/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt index f23f211..1c59cb0 100644 --- a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt +++ b/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt @@ -16,37 +16,37 @@ import java.util.concurrent.TimeUnit import kotlin.concurrent.thread fun main(args: Array) { - + val subscription = CompositeDisposable() - + val printArticle = { art: String -> println("--- Article ---\n${art.substring(0, 125)}") } - + val printIt = { it: String -> println(it) } - + subscription += asyncObservable().subscribe(printIt) - + subscription += syncObservable().subscribe(printIt) - + subscription.clear() - + simpleComposition() - + asyncWiki("Tiger", "Elephant").subscribe(printArticle) - + asyncWikiWithErrorHandling("Tiger", "Elephant").subscribe(printArticle) { e -> println("--- Error ---\n${e.message}") } - + combineLatest(listOfObservables()) - + zip(listOfObservables()) - + simpleObservable().subscribe(FunctionSubscriber() .onNext { s -> println("1st onNext => $s") } .onNext { s -> println("2nd onNext => $s") }) - + addToCompositeSubscription() } @@ -109,11 +109,11 @@ fun simpleObservable(): Observable = (0..17).toObservable().map { "Simpl fun addToCompositeSubscription() { val compositeSubscription = CompositeDisposable() - + Observable.just("test") .delay(100, TimeUnit.MILLISECONDS) .subscribe() .addTo(compositeSubscription) - + compositeSubscription.dispose() } \ No newline at end of file From ef24fa4aa790771dc881a539dce13c69bdd386aa Mon Sep 17 00:00:00 2001 From: stepango Date: Sun, 8 Jan 2017 12:54:26 +0200 Subject: [PATCH 05/27] gitignore update --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fb5b676..5a21392 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,4 @@ bin/ # Scala build *.cache /.nb-gradle/private/ +local.properties From 2adeec25417dff5dee881dc901d7a70734a72460 Mon Sep 17 00:00:00 2001 From: stepango Date: Fri, 13 Jan 2017 11:46:29 +0200 Subject: [PATCH 06/27] subscribers replaced by named args extension --- .../rx/lang/kotlin/examples/examples.kt | 31 ++-- .../{completable.kt => completables.kt} | 0 src/main/kotlin/rx/lang/kotlin/observables.kt | 13 -- .../rx/lang/kotlin/{single.kt => singles.kt} | 10 -- src/main/kotlin/rx/lang/kotlin/subscribers.kt | 81 --------- .../kotlin/rx/lang/kotlin/subscription.kt | 19 -- .../kotlin/rx/lang/kotlin/subscriptions.kt | 57 ++++++ .../kotlin/rx/lang/kotlin/SubscribersTest.kt | 163 ------------------ 8 files changed, 74 insertions(+), 300 deletions(-) rename src/main/kotlin/rx/lang/kotlin/{completable.kt => completables.kt} (100%) rename src/main/kotlin/rx/lang/kotlin/{single.kt => singles.kt} (62%) delete mode 100644 src/main/kotlin/rx/lang/kotlin/subscribers.kt delete mode 100644 src/main/kotlin/rx/lang/kotlin/subscription.kt create mode 100644 src/main/kotlin/rx/lang/kotlin/subscriptions.kt delete mode 100644 src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt diff --git a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt b/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt index 1c59cb0..22dd9d4 100644 --- a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt +++ b/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt @@ -2,7 +2,6 @@ package rx.lang.kotlin.examples import io.reactivex.Observable import io.reactivex.disposables.CompositeDisposable -import rx.lang.kotlin.FunctionSubscriber import rx.lang.kotlin.addTo import rx.lang.kotlin.combineLatest import rx.lang.kotlin.observable @@ -23,6 +22,7 @@ fun main(args: Array) { println("--- Article ---\n${art.substring(0, 125)}") } + @Suppress("ConvertLambdaToReference") val printIt = { it: String -> println(it) } subscription += asyncObservable().subscribe(printIt) @@ -43,25 +43,26 @@ fun main(args: Array) { zip(listOfObservables()) - simpleObservable().subscribe(FunctionSubscriber() - .onNext { s -> println("1st onNext => $s") } - .onNext { s -> println("2nd onNext => $s") }) + simpleObservable().subscribeBy( + onNext = { s: String -> println("1st onNext => $s") } andThen { println("2nd onNext => $it") } + ) addToCompositeSubscription() } -private fun URL.toScannerObservable() = observable({ s -> +private fun URL.toScannerObservable() = observable { s -> this.openStream().use { stream -> - Scanner(stream).useDelimiter("\\A").toObservable().subscribeBy { s } + Scanner(stream).useDelimiter("\\A") + .toObservable() + .subscribe { s.onNext(it) } } -}) +} -fun syncObservable(): Observable = - observable { subscriber -> - (0..75).toObservable() - .map { "Sync value_$it" } - .subscribe { subscriber.onNext(it) } - } +fun syncObservable(): Observable = observable { subscriber -> + (0..75).toObservable() + .map { "Sync value_$it" } + .subscribe { subscriber.onNext(it) } +} fun asyncObservable(): Observable = observable { subscriber -> thread { @@ -116,4 +117,6 @@ fun addToCompositeSubscription() { .addTo(compositeSubscription) compositeSubscription.dispose() -} \ No newline at end of file +} + +infix inline fun ((T) -> Unit).andThen(crossinline block: (T) -> Unit): (T) -> Unit = { this(it); block(it) } \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/completable.kt b/src/main/kotlin/rx/lang/kotlin/completables.kt similarity index 100% rename from src/main/kotlin/rx/lang/kotlin/completable.kt rename to src/main/kotlin/rx/lang/kotlin/completables.kt diff --git a/src/main/kotlin/rx/lang/kotlin/observables.kt b/src/main/kotlin/rx/lang/kotlin/observables.kt index 6a356b9..b7f408c 100644 --- a/src/main/kotlin/rx/lang/kotlin/observables.kt +++ b/src/main/kotlin/rx/lang/kotlin/observables.kt @@ -3,7 +3,6 @@ package rx.lang.kotlin import io.reactivex.Observable import io.reactivex.ObservableEmitter import io.reactivex.Single -import io.reactivex.disposables.Disposable import io.reactivex.functions.BiFunction /** @@ -40,8 +39,6 @@ fun Iterable>.mergeDelayError(): Observable = Obs inline fun Observable.fold(initial: R, crossinline body: (R, T) -> R): Single = reduce(initial) { a, e -> body(a, e) } -fun Observable.onError(block: (Throwable) -> Unit): Observable = doOnError(block) - /** * Returns Observable that wrap all values into [IndexedValue] and populates corresponding index value. * Works similar to [kotlin.withIndex] @@ -60,16 +57,6 @@ fun Observable.withIndex(): Observable> inline fun Observable.flatMapSequence(crossinline body: (T) -> Sequence): Observable = flatMap { body(it).toObservable() } -/** - * Subscribe with a subscriber that is configured inside body - */ -inline fun Observable.subscribeBy(body: FunctionSubscriberModifier.() -> Unit): Disposable { - val modifier = FunctionSubscriberModifier(subscriber()) - modifier.body() - subscribe(modifier.subscriber) - return modifier.subscriber.origin!! -} - fun Observable>.switchOnNext(): Observable = Observable.switchOnNext(this) /** diff --git a/src/main/kotlin/rx/lang/kotlin/single.kt b/src/main/kotlin/rx/lang/kotlin/singles.kt similarity index 62% rename from src/main/kotlin/rx/lang/kotlin/single.kt rename to src/main/kotlin/rx/lang/kotlin/singles.kt index 1be5473..0cd9442 100644 --- a/src/main/kotlin/rx/lang/kotlin/single.kt +++ b/src/main/kotlin/rx/lang/kotlin/singles.kt @@ -2,7 +2,6 @@ package rx.lang.kotlin import io.reactivex.Single import io.reactivex.SingleEmitter -import io.reactivex.disposables.Disposable import java.util.concurrent.Callable import java.util.concurrent.Future @@ -13,12 +12,3 @@ fun Future.toSingle(): Single = Single.fromFuture(this) fun Callable.toSingle(): Single = Single.fromCallable { this.call() } fun Throwable.toSingle(): Single = Single.error(this) -/** - * Subscribe with a subscriber that is configured inside body - */ -inline fun Single.subscribeBy(body: FunctionSubscriberModifier.() -> Unit): Disposable { - val modifier = FunctionSubscriberModifier(subscriber()) - modifier.body() - subscribe(modifier.subscriber) - return modifier.subscriber.origin!! -} diff --git a/src/main/kotlin/rx/lang/kotlin/subscribers.kt b/src/main/kotlin/rx/lang/kotlin/subscribers.kt deleted file mode 100644 index d595547..0000000 --- a/src/main/kotlin/rx/lang/kotlin/subscribers.kt +++ /dev/null @@ -1,81 +0,0 @@ -package rx.lang.kotlin - -import io.reactivex.CompletableObserver -import io.reactivex.MaybeObserver -import io.reactivex.Observer -import io.reactivex.SingleObserver -import io.reactivex.disposables.Disposable -import java.util.ArrayList - -class FunctionSubscriber : Observer, MaybeObserver, SingleObserver, CompletableObserver { - - private val onCompleteFunctions = ArrayList<() -> Unit>() - private val onErrorFunctions = ArrayList<(e: Throwable) -> Unit>() - private val onNextFunctions = ArrayList<(value: T) -> Unit>() - private val onStartFunctions = ArrayList<() -> Unit>() - var origin: Disposable? = null - - override fun onComplete() = onCompleteFunctions.forEach { it() } - - override fun onError(e: Throwable?) = (e ?: RuntimeException("exception is unknown")).let { ex -> - if (onErrorFunctions.isEmpty()) { - throw OnErrorNotImplementedException(ex) - } else { - onErrorFunctions.forEach { it(ex) } - } - } - - override fun onNext(value: T) = onNextFunctions.forEach { it(value) } - override fun onSuccess(value: T) = onNext(value) - - override fun onSubscribe(d: Disposable?) { - origin = d - onStartFunctions.forEach { it() } - } - - fun onCompleted(onCompletedFunction: () -> Unit): FunctionSubscriber = copy { onCompleteFunctions.add(onCompletedFunction) } - fun onError(onErrorFunction: (t: Throwable) -> Unit): FunctionSubscriber = copy { onErrorFunctions.add(onErrorFunction) } - fun onNext(onNextFunction: (t: T) -> Unit): FunctionSubscriber = copy { onNextFunctions.add(onNextFunction) } - fun onStart(onStartFunction: () -> Unit): FunctionSubscriber = copy { onStartFunctions.add(onStartFunction) } - - private fun copy(block: FunctionSubscriber.() -> Unit): FunctionSubscriber { - val newSubscriber = FunctionSubscriber() - newSubscriber.onCompleteFunctions.addAll(onCompleteFunctions) - newSubscriber.onErrorFunctions.addAll(onErrorFunctions) - newSubscriber.onNextFunctions.addAll(onNextFunctions) - newSubscriber.onStartFunctions.addAll(onStartFunctions) - - newSubscriber.block() - - return newSubscriber - } -} - -class OnErrorNotImplementedException(ex: Throwable) : RuntimeException(ex) - -class FunctionSubscriberModifier(init: FunctionSubscriber = subscriber()) { - var subscriber: FunctionSubscriber = init - private set - - fun onComplete(onCompletedFunction: () -> Unit): Unit { - subscriber = subscriber.onCompleted(onCompletedFunction) - } - - fun onError(onErrorFunction: (t: Throwable) -> Unit): Unit { - subscriber = subscriber.onError(onErrorFunction) - } - - fun onNext(onNextFunction: (t: T) -> Unit): Unit { - subscriber = subscriber.onNext(onNextFunction) - } - - fun onStart(onStartFunction: () -> Unit): Unit { - subscriber = subscriber.onStart(onStartFunction) - } - - fun onSuccess(onSuccessFunction: (t: T) -> Unit): Unit { - subscriber = subscriber.onNext(onSuccessFunction) - } -} - -fun subscriber(): FunctionSubscriber = FunctionSubscriber() \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/subscription.kt b/src/main/kotlin/rx/lang/kotlin/subscription.kt deleted file mode 100644 index 8dd4703..0000000 --- a/src/main/kotlin/rx/lang/kotlin/subscription.kt +++ /dev/null @@ -1,19 +0,0 @@ -package rx.lang.kotlin - -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable - -/** - * subscription += observable.subscribe{} - */ -operator fun CompositeDisposable.plusAssign(subscription: Disposable) { - add(subscription) -} - -/** - * Add the subscription to a CompositeSubscription. - * @param compositeSubscription CompositeSubscription to add this subscription to - * @return this instance - */ -fun Disposable.addTo(compositeSubscription: CompositeDisposable): Disposable - = apply { compositeSubscription.add(this) } \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/subscriptions.kt b/src/main/kotlin/rx/lang/kotlin/subscriptions.kt new file mode 100644 index 0000000..13b214b --- /dev/null +++ b/src/main/kotlin/rx/lang/kotlin/subscriptions.kt @@ -0,0 +1,57 @@ +package rx.lang.kotlin + +import io.reactivex.Completable +import io.reactivex.Maybe +import io.reactivex.Observable +import io.reactivex.Single +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable + +/** + * subscription += observable.subscribe() + */ +operator fun CompositeDisposable.plusAssign(subscription: Disposable) { + add(subscription) +} + +/** + * Add the subscription to a CompositeSubscription. + * @param compositeSubscription CompositeSubscription to add this subscription to + * @return this instance + */ +fun Disposable.addTo(compositeSubscription: CompositeDisposable): Disposable + = apply { compositeSubscription.add(this) } + +/** + * Overloaded subscribe function that allow passing named parameters + */ +fun Observable.subscribeBy( + onNext: ((T) -> Unit)? = null, + onError: ((Throwable) -> Unit)? = null, + onComplete: (() -> Unit)? = null +): Disposable = subscribe(onNext, onError, onComplete) + +/** + * Overloaded subscribe function that allow passing named parameters + */ +fun Single.subscribeBy( + onSuccess: ((T) -> Unit)? = null, + onError: ((Throwable) -> Unit)? = null +): Disposable = subscribe(onSuccess, onError) + +/** + * Overloaded subscribe function that allow passing named parameters + */ +fun Maybe.subscribeBy( + onSuccess: ((T) -> Unit)? = null, + onError: ((Throwable) -> Unit)? = null, + onComplete: (() -> Unit)? = null +): Disposable = subscribe(onSuccess, onError, onComplete) + +/** + * Overloaded subscribe function that allow passing named parameters + */ +fun Completable.subscribeBy( + onError: ((Throwable) -> Unit)? = null, + onComplete: (() -> Unit)? = null +): Disposable = subscribe(onComplete, onError) \ No newline at end of file diff --git a/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt b/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt deleted file mode 100644 index b4390cf..0000000 --- a/src/test/kotlin/rx/lang/kotlin/SubscribersTest.kt +++ /dev/null @@ -1,163 +0,0 @@ -package rx.lang.kotlin - -import io.reactivex.Observable -import io.reactivex.Observer -import io.reactivex.disposables.Disposables -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test -import java.util.ArrayList - -class SubscribersTest { - - @Test fun testEmptySubscriber() { - val s = subscriber() - callSubscriberMethods(false, s) - } - - @Test fun testSubscriberConstruction() { - val events = ArrayList() - - callSubscriberMethods(false, subscriber(). - onNext { events.add("onNext($it)") } - ) - - assertEquals(listOf("onNext(1)"), events) - events.clear() - - callSubscriberMethods(true, subscriber(). - onNext { events.add("onNext($it)") }. - onError { events.add(it.javaClass.simpleName) } - ) - - assertEquals(listOf("onNext(1)", "RuntimeException"), events) - events.clear() - - callSubscriberMethods(true, subscriber(). - onNext { events.add("onNext($it)") }. - onError { events.add(it.javaClass.simpleName) }. - onCompleted { events.add("onCompleted") } - ) - - assertEquals(listOf("onNext(1)", "RuntimeException", "onCompleted"), events) - events.clear() - } - - @Test(expected = Exception::class) - fun testNoErrorHandlers() { - subscriber().onError(Exception("expected")) - } - - @Test fun testErrorHandlers() { - var errorsCaught = 0 - - subscriber(). - onError { errorsCaught++ }. - onError { errorsCaught++ }. - onError(Exception("expected")) - - assertEquals(2, errorsCaught) - } - - @Test fun testMultipleOnNextHandlers() { - var nextCaught = 0 - - subscriber(). - onNext { nextCaught++ }. - onNext { nextCaught++ }. - onNext(1) - - assertEquals(2, nextCaught) - } - - @Test fun testOnStart() { - var started = false - Observable.just("").subscribeBy { - onStart { started = true } - } - assertTrue(started) - } - - private fun callSubscriberMethods(hasOnError: Boolean, s: Observer) { - s.onSubscribe(Disposables.empty()) - s.onNext(1) - try { - s.onError(RuntimeException()) - } catch (t: Throwable) { - if (hasOnError) throw t - } - s.onComplete() - } - - @Test fun testSubscribeWith() { - val completeObservable = observable { - it.onNext(1) - it.onComplete() - } - - val events = ArrayList() - - completeObservable.subscribeBy { - onNext { events.add("onNext($it)") } - } - - assertEquals(listOf("onNext(1)"), events) - events.clear() - - completeObservable.subscribeBy { - onNext { events.add("onNext($it)") } - onComplete { events.add("onCompleted") } - } - - assertEquals(listOf("onNext(1)", "onCompleted"), events) - events.clear() - - val errorObservable = observable { - it.onNext(1) - it.onError(RuntimeException()) - } - - errorObservable.subscribeBy { - onNext { events.add("onNext($it)") } - onError { events.add("onError(${it.javaClass.simpleName})") } - } - - assertEquals(listOf("onNext(1)", "onError(RuntimeException)"), events) - } - - @Test fun testSingleSubscribeWith() { - val events = ArrayList() - val successSingle = singleOf(1) - - successSingle.subscribeBy { - onSuccess { events.add("onSuccess($it)") } - } - - assertEquals(listOf("onSuccess(1)"), events) - events.clear() - - val errorSingle = RuntimeException().toSingle() - - errorSingle.subscribeBy { - onSuccess { events.add("onSuccess($it)") } - onError { events.add("onError(${it.javaClass.simpleName})") } - } - - assertEquals(listOf("onError(RuntimeException)"), events) - } - - @Test fun testSubscribeByErrorNotImplemented() { - val events = ArrayList() - val errorSingle = RuntimeException().toSingle() - try { - errorSingle.subscribeBy { - onSuccess { events.add("onSuccess($it)") } - } - } catch (e: Throwable) { - val name = e.cause?.javaClass?.simpleName - events.add("catch($name)") - } - - assertEquals(listOf("catch(OnErrorNotImplementedException)"), events) - } -} From 5478cec5a12f30d7c4f3af4a7dd5dd6a1e6de530 Mon Sep 17 00:00:00 2001 From: stepango Date: Fri, 13 Jan 2017 17:43:23 +0200 Subject: [PATCH 07/27] subscribeBy non-null params --- .../kotlin/rx/lang/kotlin/subscriptions.kt | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/rx/lang/kotlin/subscriptions.kt b/src/main/kotlin/rx/lang/kotlin/subscriptions.kt index 13b214b..529c000 100644 --- a/src/main/kotlin/rx/lang/kotlin/subscriptions.kt +++ b/src/main/kotlin/rx/lang/kotlin/subscriptions.kt @@ -22,36 +22,40 @@ operator fun CompositeDisposable.plusAssign(subscription: Disposable) { fun Disposable.addTo(compositeSubscription: CompositeDisposable): Disposable = apply { compositeSubscription.add(this) } +private val onNextStub: (Any) -> Unit = {} +private val onErrorStub: (Throwable) -> Unit = {} +private val onCompleteStub: () -> Unit = {} + /** * Overloaded subscribe function that allow passing named parameters */ fun Observable.subscribeBy( - onNext: ((T) -> Unit)? = null, - onError: ((Throwable) -> Unit)? = null, - onComplete: (() -> Unit)? = null + onNext: (T) -> Unit = onNextStub, + onError: (Throwable) -> Unit = onErrorStub, + onComplete: () -> Unit = onCompleteStub ): Disposable = subscribe(onNext, onError, onComplete) /** * Overloaded subscribe function that allow passing named parameters */ fun Single.subscribeBy( - onSuccess: ((T) -> Unit)? = null, - onError: ((Throwable) -> Unit)? = null + onSuccess: (T) -> Unit = onNextStub, + onError: (Throwable) -> Unit = onErrorStub ): Disposable = subscribe(onSuccess, onError) /** * Overloaded subscribe function that allow passing named parameters */ fun Maybe.subscribeBy( - onSuccess: ((T) -> Unit)? = null, - onError: ((Throwable) -> Unit)? = null, - onComplete: (() -> Unit)? = null + onSuccess: (T) -> Unit = onNextStub, + onError: (Throwable) -> Unit = onErrorStub, + onComplete: () -> Unit = onCompleteStub ): Disposable = subscribe(onSuccess, onError, onComplete) /** * Overloaded subscribe function that allow passing named parameters */ fun Completable.subscribeBy( - onError: ((Throwable) -> Unit)? = null, - onComplete: (() -> Unit)? = null -): Disposable = subscribe(onComplete, onError) \ No newline at end of file + onError: (Throwable) -> Unit = onErrorStub, + onComplete: () -> Unit = onCompleteStub +): Disposable = subscribe(onComplete, onError) From 06f3f0381614c3bc2ad8082bcac20b8bf1600db0 Mon Sep 17 00:00:00 2001 From: stepango Date: Tue, 24 Jan 2017 14:45:28 +0200 Subject: [PATCH 08/27] empty* methods removed flowable extensions added minor fixes --- src/main/kotlin/rx/lang/kotlin/completable.kt | 11 ++ .../kotlin/rx/lang/kotlin/completables.kt | 13 -- src/main/kotlin/rx/lang/kotlin/disposable.kt | 19 +++ src/main/kotlin/rx/lang/kotlin/flowable.kt | 83 ++++++++++ src/main/kotlin/rx/lang/kotlin/maybe.kt | 13 ++ .../kotlin/{observables.kt => observable.kt} | 10 +- .../rx/lang/kotlin/{singles.kt => single.kt} | 6 +- .../lang/kotlin/{subjects.kt => subject.kt} | 0 .../{subscriptions.kt => subscription.kt} | 16 -- .../kotlin/rx/lang/kotlin/CompletableTest.kt | 12 +- .../kotlin/rx/lang/kotlin/FlowableTest.kt | 148 ++++++++++++++++++ .../{ObservablesTest.kt => ObservableTest.kt} | 40 ++--- src/test/kotlin/rx/lang/kotlin/SingleTest.kt | 12 +- 13 files changed, 315 insertions(+), 68 deletions(-) create mode 100644 src/main/kotlin/rx/lang/kotlin/completable.kt delete mode 100644 src/main/kotlin/rx/lang/kotlin/completables.kt create mode 100644 src/main/kotlin/rx/lang/kotlin/disposable.kt create mode 100644 src/main/kotlin/rx/lang/kotlin/flowable.kt create mode 100644 src/main/kotlin/rx/lang/kotlin/maybe.kt rename src/main/kotlin/rx/lang/kotlin/{observables.kt => observable.kt} (95%) rename src/main/kotlin/rx/lang/kotlin/{singles.kt => single.kt} (74%) rename src/main/kotlin/rx/lang/kotlin/{subjects.kt => subject.kt} (100%) rename src/main/kotlin/rx/lang/kotlin/{subscriptions.kt => subscription.kt} (74%) create mode 100644 src/test/kotlin/rx/lang/kotlin/FlowableTest.kt rename src/test/kotlin/rx/lang/kotlin/{ObservablesTest.kt => ObservableTest.kt} (84%) diff --git a/src/main/kotlin/rx/lang/kotlin/completable.kt b/src/main/kotlin/rx/lang/kotlin/completable.kt new file mode 100644 index 0000000..8fe11e1 --- /dev/null +++ b/src/main/kotlin/rx/lang/kotlin/completable.kt @@ -0,0 +1,11 @@ +package rx.lang.kotlin + +import io.reactivex.Completable +import io.reactivex.functions.Action +import java.util.concurrent.Callable +import java.util.concurrent.Future + +fun Action.toCompletable(): Completable = Completable.fromAction(this) +fun Callable.toCompletable(): Completable = Completable.fromCallable(this) +fun Future.toCompletable(): Completable = Completable.fromFuture(this) +fun (() -> Any).toCompletable(): Completable = Completable.fromCallable(this) diff --git a/src/main/kotlin/rx/lang/kotlin/completables.kt b/src/main/kotlin/rx/lang/kotlin/completables.kt deleted file mode 100644 index ac413f0..0000000 --- a/src/main/kotlin/rx/lang/kotlin/completables.kt +++ /dev/null @@ -1,13 +0,0 @@ -package rx.lang.kotlin - -import io.reactivex.Completable -import io.reactivex.Single -import io.reactivex.functions.Action -import java.util.concurrent.Callable -import java.util.concurrent.Future - -fun Action.toCompletable(): Completable = Completable.fromAction(this) -inline fun completableOf(crossinline f: () -> T): Completable = Completable.fromAction { f() } -fun Callable.toCompletable(): Completable = Completable.fromCallable { this.call() } -fun Future.toCompletable(): Completable = Completable.fromFuture(this) -fun Single.toCompletable(): Completable = Completable.fromSingle(this) \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/disposable.kt b/src/main/kotlin/rx/lang/kotlin/disposable.kt new file mode 100644 index 0000000..ca85585 --- /dev/null +++ b/src/main/kotlin/rx/lang/kotlin/disposable.kt @@ -0,0 +1,19 @@ +package rx.lang.kotlin + +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable + +/** + * subscription += observable.subscribe() + */ +operator fun CompositeDisposable.plusAssign(subscription: Disposable) { + add(subscription) +} + +/** + * Add the subscription to a CompositeSubscription. + * @param compositeSubscription CompositeSubscription to add this subscription to + * @return this instance + */ +fun Disposable.addTo(compositeSubscription: CompositeDisposable): Disposable + = apply { compositeSubscription.add(this) } \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/flowable.kt b/src/main/kotlin/rx/lang/kotlin/flowable.kt new file mode 100644 index 0000000..1a348ee --- /dev/null +++ b/src/main/kotlin/rx/lang/kotlin/flowable.kt @@ -0,0 +1,83 @@ +package rx.lang.kotlin + +import io.reactivex.BackpressureStrategy +import io.reactivex.Flowable +import io.reactivex.FlowableEmitter +import io.reactivex.Single +import io.reactivex.functions.BiFunction + +fun flowable( + strategy: BackpressureStrategy = BackpressureStrategy.BUFFER, + body: (FlowableEmitter) -> Unit +): Flowable = Flowable.create(body, strategy) + +private fun Iterator.toIterable() = object : Iterable { + override fun iterator(): Iterator = this@toIterable +} + +fun BooleanArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) +fun ByteArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) +fun ShortArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) +fun IntArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) +fun LongArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) +fun FloatArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) +fun DoubleArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) +fun Array.toFlowable(): Flowable = Flowable.fromArray(*this) + +fun IntProgression.toFlowable(): Flowable = + if (step == 1 && last.toLong() - first < Integer.MAX_VALUE) Flowable.range(first, Math.max(0, last - first + 1)) + else Flowable.fromIterable(this) + +fun Iterator.toFlowable(): Flowable = toIterable().toFlowable() +fun Iterable.toFlowable(): Flowable = Flowable.fromIterable(this) +fun Sequence.toFlowable(): Flowable = Flowable.fromIterable(iterator().toIterable()) + +fun Iterable>.merge(): Flowable = Flowable.merge(this.toFlowable()) +fun Iterable>.mergeDelayError(): Flowable = Flowable.mergeDelayError(this.toFlowable()) + +inline fun Flowable.fold(initial: R, crossinline body: (R, T) -> R): Single + = reduce(initial) { a, e -> body(a, e) } + +/** + * Returns Flowable that wrap all values into [IndexedValue] and populates corresponding index value. + * Works similar to [kotlin.withIndex] + */ +fun Flowable.withIndex(): Flowable> + = zipWith(Flowable.range(0, Int.MAX_VALUE), BiFunction { value, index -> IndexedValue(index, value) }) + +/** + * Returns Flowable that emits objects from kotlin [Sequence] returned by function you provided by parameter [body] for + * each input object and merges all produced elements into one flowable. + * Works similar to [Flowable.flatMap] and [Flowable.flatMapIterable] but with [Sequence] + * + * @param body is a function that applied for each item emitted by source flowable that returns [Sequence] + * @returns Flowable that merges all [Sequence]s produced by [body] functions + */ +inline fun Flowable.flatMapSequence(crossinline body: (T) -> Sequence): Flowable + = flatMap { body(it).toFlowable() } + +fun Flowable>.switchOnNext(): Flowable = Flowable.switchOnNext(this) + +/** + * Flowable.combineLatest(List> sources, FuncN combineFunction) + */ +@Suppress("UNCHECKED_CAST") +inline fun List>.combineLatest(crossinline combineFunction: (args: List) -> R): Flowable + = Flowable.combineLatest(this) { combineFunction(it.asList().map { it as T }) } + +/** + * Flowable.zip(List> sources, FuncN combineFunction) + */ +@Suppress("UNCHECKED_CAST") +inline fun List>.zip(crossinline zipFunction: (args: List) -> R): Flowable + = Flowable.zip(this) { zipFunction(it.asList().map { it as T }) } + +/** + * Returns an Flowable that emits the items emitted by the source Flowable, converted to the specified type. + */ +inline fun Flowable<*>.cast(): Flowable = cast(R::class.java) + +/** + * Filters the items emitted by an Observable, only emitting those of the specified type. + */ +inline fun Flowable<*>.ofType(): Flowable = ofType(R::class.java) \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/maybe.kt b/src/main/kotlin/rx/lang/kotlin/maybe.kt new file mode 100644 index 0000000..35479bf --- /dev/null +++ b/src/main/kotlin/rx/lang/kotlin/maybe.kt @@ -0,0 +1,13 @@ +package rx.lang.kotlin + +import io.reactivex.Maybe +import java.util.concurrent.Callable +import java.util.concurrent.Future + +fun T?.toMaybe(): Maybe = Maybe.create { s -> if (this != null) s.onSuccess(this); s.onComplete() } +fun Future.toMaybe(): Maybe = Maybe.fromFuture(this) +fun Callable.toMaybe(): Maybe = Maybe.fromCallable(this) +fun (() -> T).toMaybe(): Maybe = Maybe.fromCallable(this) + +inline fun Maybe<*>.cast(): Maybe = cast(R::class.java) +inline fun Maybe<*>.ofType(): Maybe = ofType(R::class.java) \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/observables.kt b/src/main/kotlin/rx/lang/kotlin/observable.kt similarity index 95% rename from src/main/kotlin/rx/lang/kotlin/observables.kt rename to src/main/kotlin/rx/lang/kotlin/observable.kt index b7f408c..0c30616 100644 --- a/src/main/kotlin/rx/lang/kotlin/observables.kt +++ b/src/main/kotlin/rx/lang/kotlin/observable.kt @@ -5,11 +5,6 @@ import io.reactivex.ObservableEmitter import io.reactivex.Single import io.reactivex.functions.BiFunction -/** - * [Observable.empty] alias - */ -fun emptyObservable(): Observable = Observable.empty() - fun observable(body: (ObservableEmitter) -> Unit): Observable = Observable.create(body) private fun Iterator.toIterable() = object : Iterable { @@ -77,3 +72,8 @@ inline fun List>.zip(crossinline zipFunction: (args: List Observable<*>.cast(): Observable = cast(R::class.java) + +/** + * Filters the items emitted by an Observable, only emitting those of the specified type. + */ +inline fun Observable<*>.ofType(): Observable = ofType(R::class.java) diff --git a/src/main/kotlin/rx/lang/kotlin/singles.kt b/src/main/kotlin/rx/lang/kotlin/single.kt similarity index 74% rename from src/main/kotlin/rx/lang/kotlin/singles.kt rename to src/main/kotlin/rx/lang/kotlin/single.kt index 0cd9442..c2e2de4 100644 --- a/src/main/kotlin/rx/lang/kotlin/singles.kt +++ b/src/main/kotlin/rx/lang/kotlin/single.kt @@ -7,8 +7,8 @@ import java.util.concurrent.Future inline fun single(crossinline body: (s: SingleEmitter) -> Unit): Single = Single.create { body(it) } fun T.toSingle(): Single = Single.just(this) -fun singleOf(value: T): Single = Single.just(value) fun Future.toSingle(): Single = Single.fromFuture(this) -fun Callable.toSingle(): Single = Single.fromCallable { this.call() } -fun Throwable.toSingle(): Single = Single.error(this) +fun Callable.toSingle(): Single = Single.fromCallable(this) +fun (() -> T).toSingle(): Single = Single.fromCallable(this) +inline fun Single<*>.cast(): Single = cast(R::class.java) diff --git a/src/main/kotlin/rx/lang/kotlin/subjects.kt b/src/main/kotlin/rx/lang/kotlin/subject.kt similarity index 100% rename from src/main/kotlin/rx/lang/kotlin/subjects.kt rename to src/main/kotlin/rx/lang/kotlin/subject.kt diff --git a/src/main/kotlin/rx/lang/kotlin/subscriptions.kt b/src/main/kotlin/rx/lang/kotlin/subscription.kt similarity index 74% rename from src/main/kotlin/rx/lang/kotlin/subscriptions.kt rename to src/main/kotlin/rx/lang/kotlin/subscription.kt index 529c000..1c77878 100644 --- a/src/main/kotlin/rx/lang/kotlin/subscriptions.kt +++ b/src/main/kotlin/rx/lang/kotlin/subscription.kt @@ -4,24 +4,8 @@ import io.reactivex.Completable import io.reactivex.Maybe import io.reactivex.Observable import io.reactivex.Single -import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.Disposable -/** - * subscription += observable.subscribe() - */ -operator fun CompositeDisposable.plusAssign(subscription: Disposable) { - add(subscription) -} - -/** - * Add the subscription to a CompositeSubscription. - * @param compositeSubscription CompositeSubscription to add this subscription to - * @return this instance - */ -fun Disposable.addTo(compositeSubscription: CompositeDisposable): Disposable - = apply { compositeSubscription.add(this) } - private val onNextStub: (Any) -> Unit = {} private val onErrorStub: (Throwable) -> Unit = {} private val onCompleteStub: () -> Unit = {} diff --git a/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt b/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt index 1264af2..d179edc 100644 --- a/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt @@ -16,12 +16,6 @@ class CompletableTest { assertNotNull(c1) c1.subscribe() assertEquals(1, count) - - count = 0 - val c2 = completableOf { count++ } - assertNotNull(c2) - c2.subscribe() - assertEquals(1, count) } @Test fun testCreateFromCallable() { @@ -30,6 +24,12 @@ class CompletableTest { assertNotNull(c1) c1.subscribe() assertEquals(1, count) + + count = 0 + val c2 = { count++ }.toCompletable() + assertNotNull(c2) + c2.subscribe() + assertEquals(1, count) } @Test(expected = NoSuchElementException::class) fun testCreateFromSingle() { diff --git a/src/test/kotlin/rx/lang/kotlin/FlowableTest.kt b/src/test/kotlin/rx/lang/kotlin/FlowableTest.kt new file mode 100644 index 0000000..2f9f981 --- /dev/null +++ b/src/test/kotlin/rx/lang/kotlin/FlowableTest.kt @@ -0,0 +1,148 @@ +package rx.lang.kotlin + +import io.reactivex.Flowable +import org.junit.Assert +import org.junit.Ignore +import org.junit.Test +import java.util.concurrent.atomic.AtomicInteger + +class FlowableTest { + + @Test fun testCreation() { + val o0: Flowable = Flowable.empty() + val list = flowable { s -> + s.onNext(1) + s.onNext(777) + s.onComplete() + }.toList().blockingGet() + Assert.assertEquals(listOf(1, 777), list) + val o1: Flowable = listOf(1, 2, 3).toFlowable() + val o2: Flowable> = Flowable.just(listOf(1, 2, 3)) + + val o3: Flowable = Flowable.defer { flowable { s -> s.onNext(1) } } + val o4: Flowable = Array(3) { 0 }.toFlowable() + val o5: Flowable = IntArray(3).toFlowable() + + Assert.assertNotNull(o0) + Assert.assertNotNull(o1) + Assert.assertNotNull(o2) + Assert.assertNotNull(o3) + Assert.assertNotNull(o4) + Assert.assertNotNull(o5) + } + + @Test fun testExampleFromReadme() { + val result = flowable { subscriber -> + subscriber.onNext("H") + subscriber.onNext("e") + subscriber.onNext("l") + subscriber.onNext("") + subscriber.onNext("l") + subscriber.onNext("o") + subscriber.onComplete() + }.filter(String::isNotEmpty). + fold(StringBuilder(), StringBuilder::append). + map { it.toString() }. + blockingGet() + + Assert.assertEquals("Hello", result) + } + + @Test fun iteratorFlowable() { + Assert.assertEquals(listOf(1, 2, 3), listOf(1, 2, 3).iterator().toFlowable().toList().blockingGet()) + } + + @Test fun intProgressionStep1Empty() { + Assert.assertEquals(listOf(1), (1..1).toFlowable().toList().blockingGet()) + } + + @Test fun intProgressionStep1() { + Assert.assertEquals((1..10).toList(), (1..10).toFlowable().toList().blockingGet()) + } + + @Test fun intProgressionDownTo() { + Assert.assertEquals((1 downTo 10).toList(), (1 downTo 10).toFlowable().toList().blockingGet()) + } + + @Ignore("Too slow") + @Test fun intProgressionOverflow() { + Assert.assertEquals((0..10).toList().reversed(), (-10..Integer.MAX_VALUE).toFlowable().skip(Integer.MAX_VALUE.toLong()).map { Integer.MAX_VALUE - it }.toList().blockingGet()) + } + + @Test fun testWithIndex() { + listOf("a", "b", "c").toFlowable() + .withIndex() + .toList() + .test() + .assertValues(listOf(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c"))) + } + + @Test fun `withIndex() shouldn't share index between multiple subscribers`() { + val o = listOf("a", "b", "c").toFlowable().withIndex() + + o.test() + .await() + .assertValues(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c")) + + o.test() + .await() + .assertValues(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c")) + } + + @Test fun testFold() { + val result = listOf(1, 2, 3).toFlowable().fold(0) { acc, e -> acc + e }.blockingGet() + Assert.assertEquals(6, result) + } + + @Test fun `kotlin sequence should produce expected items and flowable be able to handle em`() { + generateSequence(0) { it + 1 }.toFlowable() + .take(3) + .toList() + .test() + .assertValues(listOf(0, 1, 2)) + } + + @Test fun `infinite iterable should not hang or produce too many elements`() { + val generated = AtomicInteger() + generateSequence { generated.incrementAndGet() }.toFlowable(). + take(100). + toList(). + subscribe() + + Assert.assertEquals(100, generated.get()) + } + + @Test fun testFlatMapSequence() { + Assert.assertEquals( + listOf(1, 2, 3, 2, 3, 4, 3, 4, 5), + listOf(1, 2, 3).toFlowable().flatMapSequence { listOf(it, it + 1, it + 2).asSequence() }.toList().blockingGet() + ) + } + + @Test fun testCombineLatest() { + val list = listOf(1, 2, 3, 2, 3, 4, 3, 4, 5) + Assert.assertEquals(list, list.map { Flowable.just(it) }.combineLatest { it }.blockingFirst()) + } + + @Test fun testZip() { + val list = listOf(1, 2, 3, 2, 3, 4, 3, 4, 5) + Assert.assertEquals(list, list.map { Flowable.just(it) }.zip { it }.blockingFirst()) + } + + @Test fun testCast() { + val source = Flowable.just(1, 2) + val flowable = source.cast() + flowable.test() + .await() + .assertValues(1, 2) + .assertNoErrors() + .assertComplete() + } + + @Test fun testCastWithWrongType() { + val source = Flowable.just(1, 2) + val flowable = source.cast() + flowable.test() + .assertError(ClassCastException::class.java) + } +} \ No newline at end of file diff --git a/src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt b/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt similarity index 84% rename from src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt rename to src/test/kotlin/rx/lang/kotlin/ObservableTest.kt index 263bc51..bbd2e64 100644 --- a/src/test/kotlin/rx/lang/kotlin/ObservablesTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt @@ -5,13 +5,13 @@ import io.reactivex.observers.TestObserver import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Ignore +import org.junit.Test import java.util.concurrent.atomic.AtomicInteger -import org.junit.Test as test +class ObservableTest { -class ObservablesTest { - @test fun testCreation() { - val o0: Observable = emptyObservable() + @Test fun testCreation() { + val o0: Observable = Observable.empty() val list = observable { s -> s.onNext(1) s.onNext(777) @@ -33,7 +33,7 @@ class ObservablesTest { assertNotNull(o5) } - @test fun testExampleFromReadme() { + @Test fun testExampleFromReadme() { val result = observable { subscriber -> subscriber.onNext("H") subscriber.onNext("e") @@ -50,28 +50,28 @@ class ObservablesTest { assertEquals("Hello", result) } - @test fun iteratorObservable() { + @Test fun iteratorObservable() { assertEquals(listOf(1, 2, 3), listOf(1, 2, 3).iterator().toObservable().toList().blockingGet()) } - @test fun intProgressionStep1Empty() { + @Test fun intProgressionStep1Empty() { assertEquals(listOf(1), (1..1).toObservable().toList().blockingGet()) } - @test fun intProgressionStep1() { + @Test fun intProgressionStep1() { assertEquals((1..10).toList(), (1..10).toObservable().toList().blockingGet()) } - @test fun intProgressionDownTo() { + @Test fun intProgressionDownTo() { assertEquals((1 downTo 10).toList(), (1 downTo 10).toObservable().toList().blockingGet()) } @Ignore("Too slow") - @test fun intProgressionOverflow() { + @Test fun intProgressionOverflow() { assertEquals((0..10).toList().reversed(), (-10..Integer.MAX_VALUE).toObservable().skip(Integer.MAX_VALUE.toLong()).map { Integer.MAX_VALUE - it }.toList().blockingGet()) } - @test fun testWithIndex() { + @Test fun testWithIndex() { listOf("a", "b", "c").toObservable() .withIndex() .toList() @@ -79,7 +79,7 @@ class ObservablesTest { .assertValues(listOf(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c"))) } - @test fun `withIndex() shouldn't share index between multiple subscribers`() { + @Test fun `withIndex() shouldn't share index between multiple subscribers`() { val o = listOf("a", "b", "c").toObservable().withIndex() val subscriber1 = TestObserver.create>() @@ -95,12 +95,12 @@ class ObservablesTest { subscriber2.assertValues(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c")) } - @test fun testFold() { + @Test fun testFold() { val result = listOf(1, 2, 3).toObservable().fold(0) { acc, e -> acc + e }.blockingGet() assertEquals(6, result) } - @test fun `kotlin sequence should produce expected items and observable be able to handle em`() { + @Test fun `kotlin sequence should produce expected items and observable be able to handle em`() { generateSequence(0) { it + 1 }.toObservable() .take(3) .toList() @@ -108,7 +108,7 @@ class ObservablesTest { .assertValues(listOf(0, 1, 2)) } - @test fun `infinite iterable should not hang or produce too many elements`() { + @Test fun `infinite iterable should not hang or produce too many elements`() { val generated = AtomicInteger() generateSequence { generated.incrementAndGet() }.toObservable(). take(100). @@ -118,24 +118,24 @@ class ObservablesTest { assertEquals(100, generated.get()) } - @test fun testFlatMapSequence() { + @Test fun testFlatMapSequence() { assertEquals( listOf(1, 2, 3, 2, 3, 4, 3, 4, 5), listOf(1, 2, 3).toObservable().flatMapSequence { listOf(it, it + 1, it + 2).asSequence() }.toList().blockingGet() ) } - @test fun testCombineLatest() { + @Test fun testCombineLatest() { val list = listOf(1, 2, 3, 2, 3, 4, 3, 4, 5) assertEquals(list, list.map { Observable.just(it) }.combineLatest { it }.blockingFirst()) } - @test fun testZip() { + @Test fun testZip() { val list = listOf(1, 2, 3, 2, 3, 4, 3, 4, 5) assertEquals(list, list.map { Observable.just(it) }.zip { it }.blockingFirst()) } - @test fun testCast() { + @Test fun testCast() { val source = Observable.just(1, 2) val observable = source.cast() observable.test() @@ -145,7 +145,7 @@ class ObservablesTest { .assertComplete() } - @test fun testCastWithWrongType() { + @Test fun testCastWithWrongType() { val source = Observable.just(1, 2) val observable = source.cast() observable.test() diff --git a/src/test/kotlin/rx/lang/kotlin/SingleTest.kt b/src/test/kotlin/rx/lang/kotlin/SingleTest.kt index 01017d3..7fc571c 100644 --- a/src/test/kotlin/rx/lang/kotlin/SingleTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/SingleTest.kt @@ -10,7 +10,7 @@ import java.util.concurrent.Callable class SingleTest : KotlinTests() { @Test fun testCreate() { single { s -> - s.onSuccess("Hello World!"); + s.onSuccess("Hello World!") }.subscribe { result -> a.received(result) } @@ -39,9 +39,11 @@ class SingleTest : KotlinTests() { } @Test fun testCreateFromJust() { - singleOf("Hello World!").subscribe { result -> - a.received(result) - } - Mockito.verify(a, Mockito.times(1)).received("Hello World!") + "Hello World!".toSingle() + .subscribe { result -> + a.received(result) + } + Mockito.verify(a, Mockito.times(1)) + .received("Hello World!") } } \ No newline at end of file From 8b1bbd6e20c04299c7cb31c750636f1fe4e30997 Mon Sep 17 00:00:00 2001 From: stepango Date: Sun, 12 Feb 2017 16:36:03 +0800 Subject: [PATCH 09/27] dependencies updated --- build.gradle | 6 +- gradle/wrapper/gradle-wrapper.jar | Bin 51018 -> 54208 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 74 +++++++++++++---------- gradlew.bat | 14 ++--- 5 files changed, 50 insertions(+), 48 deletions(-) diff --git a/build.gradle b/build.gradle index 34d7c57..dd4ee5d 100755 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext.kotlin_version = '1.0.6' repositories { jcenter() } dependencies { - classpath 'com.netflix.nebula:gradle-rxjava-project-plugin:3.+', + classpath 'com.netflix.nebula:gradle-rxjava-project-plugin:4.+', "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -11,7 +11,7 @@ apply plugin: 'nebula.rxjava-project' apply plugin: 'kotlin' dependencies { - compile 'io.reactivex.rxjava2:rxjava:2.0.4' + compile 'io.reactivex.rxjava2:rxjava:2.0.5' compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" testCompile 'org.funktionale:funktionale:0.9' testCompile 'junit:junit:4.12' @@ -20,7 +20,7 @@ dependencies { } task wrapper(type: Wrapper) { - gradleVersion = '2.10' + gradleVersion = '2.14' } // support for snapshot/final releases with the various branches RxJava uses diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c97a8bdb9088d370da7e88784a7a093b971aa23a..cc0ac2e1c507c335a5b15f3c1fbcfd2450e58e68 100644 GIT binary patch delta 33042 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 delta 29904 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≫7?%4+5O~cL2qw0XdoCy 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec9973..e95643d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line From 02492c254ddf91b1f9b76f4616e990601bfb3a85 Mon Sep 17 00:00:00 2001 From: stepango Date: Sun, 12 Feb 2017 16:56:07 +0800 Subject: [PATCH 10/27] subscribeBy method for Flowable --- src/main/kotlin/rx/lang/kotlin/subscription.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/kotlin/rx/lang/kotlin/subscription.kt b/src/main/kotlin/rx/lang/kotlin/subscription.kt index 1c77878..69f0e2e 100644 --- a/src/main/kotlin/rx/lang/kotlin/subscription.kt +++ b/src/main/kotlin/rx/lang/kotlin/subscription.kt @@ -1,6 +1,7 @@ package rx.lang.kotlin import io.reactivex.Completable +import io.reactivex.Flowable import io.reactivex.Maybe import io.reactivex.Observable import io.reactivex.Single @@ -19,6 +20,15 @@ fun Observable.subscribeBy( onComplete: () -> Unit = onCompleteStub ): Disposable = subscribe(onNext, onError, onComplete) +/** + * Overloaded subscribe function that allow passing named parameters + */ +fun Flowable.subscribeBy( + onNext: (T) -> Unit = onNextStub, + onError: (Throwable) -> Unit = onErrorStub, + onComplete: () -> Unit = onCompleteStub +): Disposable = subscribe(onNext, onError, onComplete) + /** * Overloaded subscribe function that allow passing named parameters */ From 484cbab043d1f0508b9907d7b7fc357d009312a5 Mon Sep 17 00:00:00 2001 From: stepango Date: Sun, 12 Feb 2017 17:33:32 +0800 Subject: [PATCH 11/27] tests updated --- .../kotlin/rx/lang/kotlin/ObservableTest.kt | 86 +++++++++++++++---- 1 file changed, 67 insertions(+), 19 deletions(-) diff --git a/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt b/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt index bbd2e64..90e727b 100644 --- a/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt @@ -6,18 +6,23 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Ignore import org.junit.Test +import java.math.BigDecimal import java.util.concurrent.atomic.AtomicInteger class ObservableTest { @Test fun testCreation() { + val observable = observable { s -> + s.apply { + onNext(1) + onNext(777) + onComplete() + } + } + + assertEquals(listOf(1, 777), observable.toList().blockingGet()) + val o0: Observable = Observable.empty() - val list = observable { s -> - s.onNext(1) - s.onNext(777) - s.onComplete() - }.toList().blockingGet() - assertEquals(listOf(1, 777), list) val o1: Observable = listOf(1, 2, 3).toObservable() val o2: Observable> = Observable.just(listOf(1, 2, 3)) @@ -34,18 +39,22 @@ class ObservableTest { } @Test fun testExampleFromReadme() { - val result = observable { subscriber -> - subscriber.onNext("H") - subscriber.onNext("e") - subscriber.onNext("l") - subscriber.onNext("") - subscriber.onNext("l") - subscriber.onNext("o") - subscriber.onComplete() - }.filter(String::isNotEmpty). - fold(StringBuilder(), StringBuilder::append). - map { it.toString() }. - blockingGet() + val observable = observable { s -> + s.apply { + onNext("H") + onNext("e") + onNext("l") + onNext("") + onNext("l") + onNext("o") + onComplete() + } + } + val result = observable + .filter(String::isNotEmpty) + .fold(StringBuilder(), StringBuilder::append) + .map { it.toString() } + .blockingGet() assertEquals("Hello", result) } @@ -68,7 +77,12 @@ class ObservableTest { @Ignore("Too slow") @Test fun intProgressionOverflow() { - assertEquals((0..10).toList().reversed(), (-10..Integer.MAX_VALUE).toObservable().skip(Integer.MAX_VALUE.toLong()).map { Integer.MAX_VALUE - it }.toList().blockingGet()) + val result = (-10..Integer.MAX_VALUE).toObservable() + .skip(Integer.MAX_VALUE.toLong()) + .map { Integer.MAX_VALUE - it } + .toList() + .blockingGet() + assertEquals((0..10).toList().reversed(), result) } @Test fun testWithIndex() { @@ -151,4 +165,38 @@ class ObservableTest { observable.test() .assertError(ClassCastException::class.java) } + + @Test fun testOfType() { + val source = Observable.just(BigDecimal.valueOf(15, 1), 2, BigDecimal.valueOf(42), 15) + + source.ofType() + .test() + .await() + .assertValues(2, 15) + .assertNoErrors() + .assertComplete() + + source.ofType() + .test() + .await() + .assertValues(BigDecimal.valueOf(15, 1), BigDecimal.valueOf(42)) + .assertNoErrors() + .assertComplete() + + source.ofType() + .test() + .await() + .assertNoValues() + .assertNoErrors() + .assertComplete() + + source.ofType>() + .test() + .await() + .assertValues(BigDecimal.valueOf(15, 1), 2, BigDecimal.valueOf(42), 15) + .assertNoErrors() + .assertComplete() + + } + } \ No newline at end of file From e117048ddf039c9bf7753c21aa255c2cf578663d Mon Sep 17 00:00:00 2001 From: stepango Date: Sun, 12 Feb 2017 17:46:03 +0800 Subject: [PATCH 12/27] minor tests refactoring --- .../kotlin/rx/lang/kotlin/BasicKotlinTests.kt | 28 +++++++++++++------ .../kotlin/rx/lang/kotlin/CompletableTest.kt | 4 ++- src/test/kotlin/rx/lang/kotlin/KotlinTests.kt | 4 +-- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt b/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt index dc74cf1..80fcd0d 100644 --- a/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt @@ -20,6 +20,7 @@ import io.reactivex.Notification import io.reactivex.Observable import io.reactivex.ObservableEmitter import io.reactivex.ObservableOnSubscribe +import io.reactivex.Single import io.reactivex.functions.BiFunction import io.reactivex.functions.Function3 import org.junit.Assert.assertEquals @@ -39,15 +40,17 @@ class BasicKotlinTests : KotlinTests() { Observable.create { onSubscribe -> onSubscribe.onNext("Hello") onSubscribe.onComplete() - }.subscribe { result -> - a.received(result) - } + }.subscribeBy( + onNext = { a.received(it) } + ) verify(a, times(1)).received("Hello") } @Test fun testFilter() { - Observable.fromIterable(listOf(1, 2, 3)).filter { it >= 2 }.subscribe(received()) + Observable.fromIterable(listOf(1, 2, 3)) + .filter { it >= 2 } + .subscribeBy(onNext = received()) verify(a, times(0)).received(1) verify(a, times(1)).received(2) verify(a, times(1)).received(3) @@ -58,7 +61,9 @@ class BasicKotlinTests : KotlinTests() { } @Test fun testMap1() { - Observable.just(1).map { v -> "hello_$v" }.subscribe(received()) + Single.just(1) + .map { v -> "hello_$v" } + .subscribeBy(onSuccess = received()) verify(a, times(1)).received("hello_1") } @@ -84,7 +89,10 @@ class BasicKotlinTests : KotlinTests() { Observable.just(7) ), Observable.fromIterable(listOf(4, 5)) - ).subscribe(received()) { e -> a.error(e) } + ).subscribeBy( + onNext = received(), + onError = { a.error(it) } + ) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(1)).received(3) @@ -96,13 +104,17 @@ class BasicKotlinTests : KotlinTests() { } @Test fun testScriptWithMaterialize() { - TestFactory().observable.materialize().subscribe(received()) + TestFactory() + .observable + .materialize() + .subscribeBy(onNext = received()) verify(a, times(2)).received(any(Notification::class.java)) } @Test fun testScriptWithMerge() { val factory = TestFactory() - Observable.merge(factory.observable, factory.observable).subscribe(received()) + Observable.merge(factory.observable, factory.observable) + .subscribeBy(onNext = received()) verify(a, times(1)).received("hello_1") verify(a, times(1)).received("hello_2") } diff --git a/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt b/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt index d179edc..110714a 100644 --- a/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt @@ -24,8 +24,10 @@ class CompletableTest { assertNotNull(c1) c1.subscribe() assertEquals(1, count) + } - count = 0 + @Test fun createFromLambda() { + var count = 0 val c2 = { count++ }.toCompletable() assertNotNull(c2) c2.subscribe() diff --git a/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt b/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt index 75f8e6e..1f3cb39 100644 --- a/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt @@ -16,21 +16,19 @@ package rx.lang.kotlin -import io.reactivex.Observable import org.junit.Before import org.mockito.Mock import org.mockito.MockitoAnnotations abstract class KotlinTests { @Mock var a: ScriptAssertion = uninitialized() - @Mock var w: Observable = uninitialized() @Before fun before() { MockitoAnnotations.initMocks(this) } @Suppress("BASE_WITH_NULLABLE_UPPER_BOUND") - fun received() = { result: T? -> a.received(result) } + fun received() = { result: T -> a.received(result) } interface ScriptAssertion { fun error(e: Throwable?) From 564b0437a5323e64a195051eb055a7b34034f1ab Mon Sep 17 00:00:00 2001 From: stepango Date: Tue, 7 Mar 2017 21:58:19 +0800 Subject: [PATCH 13/27] * JoinToString method and tests ported from rxKotlin 1.0 * Subjects removed * Operators added --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +-- src/main/kotlin/rx/lang/kotlin/operators.kt | 36 +++++++++++++++++++ src/main/kotlin/rx/lang/kotlin/subject.kt | 13 ------- .../{subscription.kt => subscribers.kt} | 0 .../kotlin/rx/lang/kotlin/ExtensionTests.kt | 23 ++++++++++-- 6 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 src/main/kotlin/rx/lang/kotlin/operators.kt delete mode 100644 src/main/kotlin/rx/lang/kotlin/subject.kt rename src/main/kotlin/rx/lang/kotlin/{subscription.kt => subscribers.kt} (100%) diff --git a/build.gradle b/build.gradle index dd4ee5d..e12f734 100755 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.0.6' + ext.kotlin_version = '1.1.0' repositories { jcenter() } dependencies { classpath 'com.netflix.nebula:gradle-rxjava-project-plugin:4.+', diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b080e76..9bc0a69 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Feb 12 16:33:43 SGT 2017 +#Sun Mar 05 07:47:21 SGT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-all.zip diff --git a/src/main/kotlin/rx/lang/kotlin/operators.kt b/src/main/kotlin/rx/lang/kotlin/operators.kt new file mode 100644 index 0000000..01dc3b4 --- /dev/null +++ b/src/main/kotlin/rx/lang/kotlin/operators.kt @@ -0,0 +1,36 @@ +package rx.lang.kotlin + +import io.reactivex.Observable +import io.reactivex.Single + +/** + * Merges the emissions of an Observable>. Same as calling `flatMap { it }`. + */ +fun Observable>.mergeAll() = flatMap { it } + +/** + * Concatenates the emissions of an Observable>. Same as calling `concatMap { it }`. + */ +fun Observable>.concatAll() = concatMap { it } + +/** + * Emits the latest `Observable` emitted through an `Observable>`. Same as calling `switchMap { it }`. + */ +fun Observable>.switchLatest() = switchMap { it } + + +/** + * Joins the emissions of a finite `Observable` into a `String`. + * + * @param separator is the dividing character(s) between each element in the concatenated `String` + * + * @param prefix is the preceding `String` before the concatenated elements (optional) + * + * @param postfix is the succeeding `String` after the concatenated elements (optional) + */ +fun Observable.joinToString(separator: String? = null, + prefix: String? = null, + postfix: String? = null +): Single = collect({ StringBuilder(prefix ?: "") }) { builder: StringBuilder, next: T -> + builder.append(if (builder.length == prefix?.length ?: 0) "" else separator ?: "").append(next) +}.map { it.append(postfix ?: "").toString() } \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/subject.kt b/src/main/kotlin/rx/lang/kotlin/subject.kt deleted file mode 100644 index 7dbd24a..0000000 --- a/src/main/kotlin/rx/lang/kotlin/subject.kt +++ /dev/null @@ -1,13 +0,0 @@ -package rx.lang.kotlin - -import io.reactivex.Observable -import io.reactivex.subjects.AsyncSubject -import io.reactivex.subjects.BehaviorSubject -import io.reactivex.subjects.PublishSubject -import io.reactivex.subjects.ReplaySubject - -fun BehaviorSubject(): BehaviorSubject = BehaviorSubject.create() -fun BehaviorSubject(default: T): BehaviorSubject = BehaviorSubject.createDefault(default) -fun AsyncSubject(): AsyncSubject = AsyncSubject.create() -fun PublishSubject(): PublishSubject = PublishSubject.create() -fun ReplaySubject(capacity: Int = Observable.bufferSize()): ReplaySubject = ReplaySubject.create(capacity) diff --git a/src/main/kotlin/rx/lang/kotlin/subscription.kt b/src/main/kotlin/rx/lang/kotlin/subscribers.kt similarity index 100% rename from src/main/kotlin/rx/lang/kotlin/subscription.kt rename to src/main/kotlin/rx/lang/kotlin/subscribers.kt diff --git a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt index c41b09c..9561374 100644 --- a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt @@ -52,9 +52,9 @@ class ExtensionTests : KotlinTests() { @Test fun testFilter() { listOf(1, 2, 3).toObservable().filter { it >= 2 }.subscribe(received()) - verify(a, times(0)).received(1); - verify(a, times(1)).received(2); - verify(a, times(1)).received(3); + verify(a, times(0)).received(1) + verify(a, times(1)).received(2) + verify(a, times(1)).received(3) } @@ -260,6 +260,23 @@ class ExtensionTests : KotlinTests() { } } + @Test + fun testJoinToString1() { + Observable.range(1, 5) + .joinToString(separator = ",") + .test() + .await() + .assertResult("1,2,3,4,5") + } + + @Test + fun testJoinToString2() { + Observable.range(1, 5) + .joinToString(separator = ",", prefix = "(", postfix = ")") + .test() + .await() + .assertResult("(1,2,3,4,5)") + } inner class TestFactory { var counter = 1 From 48c057051db9477d8bc148e191546f775ac4d97a Mon Sep 17 00:00:00 2001 From: stepango Date: Tue, 7 Mar 2017 22:07:48 +0800 Subject: [PATCH 14/27] Minor formatting fix --- src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt index 922dc3f..9f4366b 100644 --- a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt @@ -270,14 +270,16 @@ class ExtensionTests : KotlinTests() { } -@Test + @Test fun testJoinToString2() { Observable.range(1, 5) .joinToString(separator = ",", prefix = "(", postfix = ")") .test() .await() .assertResult("(1,2,3,4,5)") - } inner class TestFactory { + } + + inner class TestFactory { var counter = 1 val numbers: Observable From b677898c912aeef72d8e34a9fa71d56217297fb9 Mon Sep 17 00:00:00 2001 From: stepango Date: Tue, 7 Mar 2017 22:10:27 +0800 Subject: [PATCH 15/27] More formatting fixes --- .../kotlin/rx/lang/kotlin/ExtensionTests.kt | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt index 9f4366b..fc93a33 100644 --- a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt @@ -57,7 +57,6 @@ class ExtensionTests : KotlinTests() { verify(a, times(1)).received(3) } - @Test fun testLast() { assertEquals("three", listOf("one", "two", "three").toObservable().blockingLast()) } @@ -79,7 +78,6 @@ class ExtensionTests : KotlinTests() { verify(a, times(0)).error(any(Exception::class.java)) } - @Test fun testMerge() { listOf(listOf(1, 2, 3).toObservable(), listOf(Observable.just(6), @@ -110,7 +108,6 @@ class ExtensionTests : KotlinTests() { verify(a, times(1)).received("hello_2") } - @Test fun testFromWithIterable() { assertEquals(5, listOf(1, 2, 3, 4, 5).toObservable().count().blockingGet()) } @@ -123,45 +120,64 @@ class ExtensionTests : KotlinTests() { } @Test fun testScriptWithOnNext() { - TestFactory().observable.subscribe(received()) + TestFactory().observable + .subscribe(received()) verify(a, times(1)).received("hello_1") } @Test fun testSkipTake() { - listOf(1, 2, 3).toObservable().skip(1).take(1).subscribe(received()) + listOf(1, 2, 3) + .toObservable() + .skip(1) + .take(1) + .subscribe(received()) verify(a, times(0)).received(1) verify(a, times(1)).received(2) verify(a, times(0)).received(3) } @Test fun testSkip() { - listOf(1, 2, 3).toObservable().skip(2).subscribe(received()) + listOf(1, 2, 3) + .toObservable() + .skip(2) + .subscribe(received()) verify(a, times(0)).received(1) verify(a, times(0)).received(2) verify(a, times(1)).received(3) } @Test fun testTake() { - listOf(1, 2, 3).toObservable().take(2).subscribe(received()) + listOf(1, 2, 3) + .toObservable() + .take(2) + .subscribe(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(0)).received(3) } @Test fun testTakeLast() { - TestFactory().observable.takeLast(1).subscribe(received()) + TestFactory().observable + .takeLast(1) + .subscribe(received()) verify(a, times(1)).received("hello_1") } @Test fun testTakeWhile() { - listOf(1, 2, 3).toObservable().takeWhile { x -> x < 3 }.subscribe(received()) + listOf(1, 2, 3) + .toObservable() + .takeWhile { x -> x < 3 } + .subscribe(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(0)).received(3) } @Test fun testTakeWhileWithIndex() { - listOf(1, 2, 3).toObservable().takeWhile { x -> x < 3 }.zipWith((0..Integer.MAX_VALUE).toObservable(), BiFunction { x, i -> x }).subscribe(received()) + listOf(1, 2, 3).toObservable() + .takeWhile { x -> x < 3 } + .zipWith((0..Integer.MAX_VALUE).toObservable(), BiFunction { x: Int, i: Int -> x }) + .subscribe(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(0)).received(3) @@ -269,7 +285,6 @@ class ExtensionTests : KotlinTests() { .assertResult("1,2,3,4,5") } - @Test fun testJoinToString2() { Observable.range(1, 5) @@ -290,6 +305,5 @@ class ExtensionTests : KotlinTests() { val observable: Observable get() = observable(onSubscribe) - } } From c5d5aa6c759bdb4f5a74b43ae20adc48dac9a381 Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Tue, 7 Mar 2017 19:01:39 -0600 Subject: [PATCH 16/27] Refactor "subscription" references to "disposable" --- src/main/kotlin/rx/lang/kotlin/disposable.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/rx/lang/kotlin/disposable.kt b/src/main/kotlin/rx/lang/kotlin/disposable.kt index ca85585..d442e90 100644 --- a/src/main/kotlin/rx/lang/kotlin/disposable.kt +++ b/src/main/kotlin/rx/lang/kotlin/disposable.kt @@ -4,16 +4,16 @@ import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.Disposable /** - * subscription += observable.subscribe() + * disposable += observable.subscribe() */ -operator fun CompositeDisposable.plusAssign(subscription: Disposable) { - add(subscription) +operator fun CompositeDisposable.plusAssign(disposable: Disposable) { + add(disposable) } /** * Add the subscription to a CompositeSubscription. - * @param compositeSubscription CompositeSubscription to add this subscription to + * @param compositeDisposable CompositeDisposable to add this subscription to * @return this instance */ -fun Disposable.addTo(compositeSubscription: CompositeDisposable): Disposable - = apply { compositeSubscription.add(this) } \ No newline at end of file +fun Disposable.addTo(compositeDisposable: CompositeDisposable): Disposable + = apply { compositeDisposable.add(this) } \ No newline at end of file From 816c227a39df88f0930b0ba0cf1c87fb03387766 Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Tue, 7 Mar 2017 19:10:20 -0600 Subject: [PATCH 17/27] refactor `flowable.kt` and `observable.kt` to match 1.x changes --- src/main/kotlin/rx/lang/kotlin/flowable.kt | 43 ++++++++------------ src/main/kotlin/rx/lang/kotlin/observable.kt | 32 +++++++-------- 2 files changed, 30 insertions(+), 45 deletions(-) diff --git a/src/main/kotlin/rx/lang/kotlin/flowable.kt b/src/main/kotlin/rx/lang/kotlin/flowable.kt index 1a348ee..12ba34e 100644 --- a/src/main/kotlin/rx/lang/kotlin/flowable.kt +++ b/src/main/kotlin/rx/lang/kotlin/flowable.kt @@ -1,28 +1,16 @@ package rx.lang.kotlin -import io.reactivex.BackpressureStrategy import io.reactivex.Flowable -import io.reactivex.FlowableEmitter -import io.reactivex.Single import io.reactivex.functions.BiFunction -fun flowable( - strategy: BackpressureStrategy = BackpressureStrategy.BUFFER, - body: (FlowableEmitter) -> Unit -): Flowable = Flowable.create(body, strategy) -private fun Iterator.toIterable() = object : Iterable { - override fun iterator(): Iterator = this@toIterable -} - -fun BooleanArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) -fun ByteArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) -fun ShortArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) -fun IntArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) -fun LongArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) -fun FloatArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) -fun DoubleArray.toFlowable(): Flowable = Flowable.fromArray(*this.toTypedArray()) -fun Array.toFlowable(): Flowable = Flowable.fromArray(*this) +fun BooleanArray.toFlowable(): Flowable = this.asIterable().toFlowable() +fun ByteArray.toFlowable(): Flowable = this.asIterable().toFlowable() +fun ShortArray.toFlowable(): Flowable = this.asIterable().toFlowable() +fun IntArray.toFlowable(): Flowable = this.asIterable().toFlowable() +fun LongArray.toFlowable(): Flowable = this.asIterable().toFlowable() +fun FloatArray.toFlowable(): Flowable = this.asIterable().toFlowable() +fun DoubleArray.toFlowable(): Flowable = this.asIterable().toFlowable() fun IntProgression.toFlowable(): Flowable = if (step == 1 && last.toLong() - first < Integer.MAX_VALUE) Flowable.range(first, Math.max(0, last - first + 1)) @@ -30,14 +18,11 @@ fun IntProgression.toFlowable(): Flowable = fun Iterator.toFlowable(): Flowable = toIterable().toFlowable() fun Iterable.toFlowable(): Flowable = Flowable.fromIterable(this) -fun Sequence.toFlowable(): Flowable = Flowable.fromIterable(iterator().toIterable()) +fun Sequence.toFlowable(): Flowable = asIterable().toFlowable() fun Iterable>.merge(): Flowable = Flowable.merge(this.toFlowable()) fun Iterable>.mergeDelayError(): Flowable = Flowable.mergeDelayError(this.toFlowable()) -inline fun Flowable.fold(initial: R, crossinline body: (R, T) -> R): Single - = reduce(initial) { a, e -> body(a, e) } - /** * Returns Flowable that wrap all values into [IndexedValue] and populates corresponding index value. * Works similar to [kotlin.withIndex] @@ -62,14 +47,14 @@ fun Flowable>.switchOnNext(): Flowable = Flowable.switc * Flowable.combineLatest(List> sources, FuncN combineFunction) */ @Suppress("UNCHECKED_CAST") -inline fun List>.combineLatest(crossinline combineFunction: (args: List) -> R): Flowable +inline fun Iterable>.combineLatest(crossinline combineFunction: (args: List) -> R): Flowable = Flowable.combineLatest(this) { combineFunction(it.asList().map { it as T }) } /** * Flowable.zip(List> sources, FuncN combineFunction) */ @Suppress("UNCHECKED_CAST") -inline fun List>.zip(crossinline zipFunction: (args: List) -> R): Flowable +inline fun Iterable>.zip(crossinline zipFunction: (args: List) -> R): Flowable = Flowable.zip(this) { zipFunction(it.asList().map { it as T }) } /** @@ -78,6 +63,10 @@ inline fun List>.zip(crossinline zipFunction: (args: List) inline fun Flowable<*>.cast(): Flowable = cast(R::class.java) /** - * Filters the items emitted by an Observable, only emitting those of the specified type. + * Filters the items emitted by an Flowable, only emitting those of the specified type. */ -inline fun Flowable<*>.ofType(): Flowable = ofType(R::class.java) \ No newline at end of file +inline fun Flowable<*>.ofType(): Flowable = ofType(R::class.java) + +private fun Iterator.toIterable() = object : Iterable { + override fun iterator(): Iterator = this@toIterable +} \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/observable.kt b/src/main/kotlin/rx/lang/kotlin/observable.kt index 0c30616..eefd51e 100644 --- a/src/main/kotlin/rx/lang/kotlin/observable.kt +++ b/src/main/kotlin/rx/lang/kotlin/observable.kt @@ -5,19 +5,14 @@ import io.reactivex.ObservableEmitter import io.reactivex.Single import io.reactivex.functions.BiFunction -fun observable(body: (ObservableEmitter) -> Unit): Observable = Observable.create(body) -private fun Iterator.toIterable() = object : Iterable { - override fun iterator(): Iterator = this@toIterable -} - -fun BooleanArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) -fun ByteArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) -fun ShortArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) -fun IntArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) -fun LongArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) -fun FloatArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) -fun DoubleArray.toObservable(): Observable = Observable.fromArray(*this.toTypedArray()) +fun BooleanArray.toObservable(): Observable = this.asIterable().toObservable() +fun ByteArray.toObservable(): Observable = this.asIterable().toObservable() +fun ShortArray.toObservable(): Observable = this.asIterable().toObservable() +fun IntArray.toObservable(): Observable = this.asIterable().toObservable() +fun LongArray.toObservable(): Observable = this.asIterable().toObservable() +fun FloatArray.toObservable(): Observable = this.asIterable().toObservable() +fun DoubleArray.toObservable(): Observable = this.asIterable().toObservable() fun Array.toObservable(): Observable = Observable.fromArray(*this) fun IntProgression.toObservable(): Observable = @@ -26,14 +21,11 @@ fun IntProgression.toObservable(): Observable = fun Iterator.toObservable(): Observable = toIterable().toObservable() fun Iterable.toObservable(): Observable = Observable.fromIterable(this) -fun Sequence.toObservable(): Observable = Observable.fromIterable(iterator().toIterable()) +fun Sequence.toObservable(): Observable = asIterable().toObservable() fun Iterable>.merge(): Observable = Observable.merge(this.toObservable()) fun Iterable>.mergeDelayError(): Observable = Observable.mergeDelayError(this.toObservable()) -inline fun Observable.fold(initial: R, crossinline body: (R, T) -> R): Single - = reduce(initial) { a, e -> body(a, e) } - /** * Returns Observable that wrap all values into [IndexedValue] and populates corresponding index value. * Works similar to [kotlin.withIndex] @@ -58,14 +50,14 @@ fun Observable>.switchOnNext(): Observable = Observab * Observable.combineLatest(List> sources, FuncN combineFunction) */ @Suppress("UNCHECKED_CAST") -inline fun List>.combineLatest(crossinline combineFunction: (args: List) -> R): Observable +inline fun Iterable>.combineLatest(crossinline combineFunction: (args: List) -> R): Observable = Observable.combineLatest(this) { combineFunction(it.asList().map { it as T }) } /** * Observable.zip(List> sources, FuncN combineFunction) */ @Suppress("UNCHECKED_CAST") -inline fun List>.zip(crossinline zipFunction: (args: List) -> R): Observable +inline fun Iterable>.zip(crossinline zipFunction: (args: List) -> R): Observable = Observable.zip(this) { zipFunction(it.asList().map { it as T }) } /** @@ -77,3 +69,7 @@ inline fun Observable<*>.cast(): Observable = cast(R::class * Filters the items emitted by an Observable, only emitting those of the specified type. */ inline fun Observable<*>.ofType(): Observable = ofType(R::class.java) + +private fun Iterator.toIterable() = object : Iterable { + override fun iterator(): Iterator = this@toIterable +} \ No newline at end of file From d8881f79f31f6e1efcf1b8a0e45586d4569dce47 Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Tue, 7 Mar 2017 19:13:49 -0600 Subject: [PATCH 18/27] add `Flowable` support for operators --- src/main/kotlin/rx/lang/kotlin/operators.kt | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main/kotlin/rx/lang/kotlin/operators.kt b/src/main/kotlin/rx/lang/kotlin/operators.kt index 01dc3b4..eda0e67 100644 --- a/src/main/kotlin/rx/lang/kotlin/operators.kt +++ b/src/main/kotlin/rx/lang/kotlin/operators.kt @@ -1,5 +1,6 @@ package rx.lang.kotlin +import io.reactivex.Flowable import io.reactivex.Observable import io.reactivex.Single @@ -8,17 +9,35 @@ import io.reactivex.Single */ fun Observable>.mergeAll() = flatMap { it } +/** + * Merges the emissions of a Flowable>. Same as calling `flatMap { it }`. + */ +fun Flowable>.mergeAll() = flatMap { it } + + /** * Concatenates the emissions of an Observable>. Same as calling `concatMap { it }`. */ fun Observable>.concatAll() = concatMap { it } +/** + * Concatenates the emissions of an Flowable>. Same as calling `concatMap { it }`. + */ +fun Flowable>.concatAll() = concatMap { it } + + /** * Emits the latest `Observable` emitted through an `Observable>`. Same as calling `switchMap { it }`. */ fun Observable>.switchLatest() = switchMap { it } +/** + * Emits the latest `Flowable` emitted through an `Flowable>`. Same as calling `switchMap { it }`. + */ +fun Flowable>.switchLatest() = switchMap { it } + + /** * Joins the emissions of a finite `Observable` into a `String`. * @@ -33,4 +52,22 @@ fun Observable.joinToString(separator: String? = null, postfix: String? = null ): Single = collect({ StringBuilder(prefix ?: "") }) { builder: StringBuilder, next: T -> builder.append(if (builder.length == prefix?.length ?: 0) "" else separator ?: "").append(next) +}.map { it.append(postfix ?: "").toString() } + + + +/** + * Joins the emissions of a finite `Flowable` into a `String`. + * + * @param separator is the dividing character(s) between each element in the concatenated `String` + * + * @param prefix is the preceding `String` before the concatenated elements (optional) + * + * @param postfix is the succeeding `String` after the concatenated elements (optional) + */ +fun Flowable.joinToString(separator: String? = null, + prefix: String? = null, + postfix: String? = null +): Single = collect({ StringBuilder(prefix ?: "") }) { builder: StringBuilder, next: T -> + builder.append(if (builder.length == prefix?.length ?: 0) "" else separator ?: "").append(next) }.map { it.append(postfix ?: "").toString() } \ No newline at end of file From 307b2282326d5fe7329908ab976db35cde177411 Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Tue, 7 Mar 2017 19:14:49 -0600 Subject: [PATCH 19/27] update `single` to match 1.x changes --- src/main/kotlin/rx/lang/kotlin/single.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/rx/lang/kotlin/single.kt b/src/main/kotlin/rx/lang/kotlin/single.kt index c2e2de4..6f6f7be 100644 --- a/src/main/kotlin/rx/lang/kotlin/single.kt +++ b/src/main/kotlin/rx/lang/kotlin/single.kt @@ -5,7 +5,6 @@ import io.reactivex.SingleEmitter import java.util.concurrent.Callable import java.util.concurrent.Future -inline fun single(crossinline body: (s: SingleEmitter) -> Unit): Single = Single.create { body(it) } fun T.toSingle(): Single = Single.just(this) fun Future.toSingle(): Single = Single.fromFuture(this) fun Callable.toSingle(): Single = Single.fromCallable(this) From 2ca695a7b3d8d53cae8d42a20bd4fa1ffa0be6fe Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Tue, 7 Mar 2017 19:15:14 -0600 Subject: [PATCH 20/27] rid Subject functions --- src/main/kotlin/rx/lang/kotlin/subject.kt | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 src/main/kotlin/rx/lang/kotlin/subject.kt diff --git a/src/main/kotlin/rx/lang/kotlin/subject.kt b/src/main/kotlin/rx/lang/kotlin/subject.kt deleted file mode 100644 index 7dbd24a..0000000 --- a/src/main/kotlin/rx/lang/kotlin/subject.kt +++ /dev/null @@ -1,13 +0,0 @@ -package rx.lang.kotlin - -import io.reactivex.Observable -import io.reactivex.subjects.AsyncSubject -import io.reactivex.subjects.BehaviorSubject -import io.reactivex.subjects.PublishSubject -import io.reactivex.subjects.ReplaySubject - -fun BehaviorSubject(): BehaviorSubject = BehaviorSubject.create() -fun BehaviorSubject(default: T): BehaviorSubject = BehaviorSubject.createDefault(default) -fun AsyncSubject(): AsyncSubject = AsyncSubject.create() -fun PublishSubject(): PublishSubject = PublishSubject.create() -fun ReplaySubject(capacity: Int = Observable.bufferSize()): ReplaySubject = ReplaySubject.create(capacity) From c5837a1aee8efb76ee5aaae3c8e43568bf5eccb6 Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Tue, 7 Mar 2017 19:24:00 -0600 Subject: [PATCH 21/27] update readme to reflect `onComplete` --- README.md | 90 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 291ba06..8241804 100755 --- a/README.md +++ b/README.md @@ -1,30 +1,38 @@ -# Kotlin Adaptor for RxJava +# RxKotlin + +## Kotlin Extensions for RxJava + +RxKotlin is a lightweight library that adds convenient extension functions to [RxJava](https://github.com/ReactiveX/RxJava). You can use RxJava with Kotlin out-of-the-box, but Kotlin has language features (such as [extension functions](https://kotlinlang.org/docs/reference/extensions.html)) that can streamline usage of RxJava even more. RxKotlin aims to conservatively collect these conveniences in one centralized library. -This adaptor exposes a set of Extension functions that allow a more idiomatic Kotlin usage ```kotlin -observable { subscriber -> - subscriber.onNext("H") - subscriber.onNext("e") - subscriber.onNext("l") - subscriber.onNext("") - subscriber.onNext("l") - subscriber.onNext("o") - subscriber.onCompleted() -}.filter { it.isNotEmpty() } -.fold (StringBuilder()) { sb, e -> sb.append(e) } -.map { it.toString() } -.subscribe { a.received(it) } - -verify(a, times(1)).received("Hello") +package rx.lang.kotlin + +import rx.Observable + +fun main(args: Array) { + + val list = listOf("Alpha", "Beta", "Gamma", "Delta", "Epsilon") + + list.toObservable() // extension function for Iterables + .filter { it.length >= 5 } + .subscribeBy( // named arguments for lambda Subscribers + onNext = { println(it) }, + onError = { it.printStackTrace() }, + onComplete = { println("Done!") } + ) + +} ``` ## Build [![Build Status](https://travis-ci.org/ReactiveX/RxKotlin.svg?branch=0.x)](https://travis-ci.org/ReactiveX/RxKotlin) + ## Binaries + Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Crxkotlin). Example for Maven: @@ -37,14 +45,54 @@ Example for Maven: ``` -and for Ivy: +and for Gradle: -```xml - +```groovy +compile 'io.reactivex:rxkotlin:x.y.z' ``` -and for Gradle: +You can also use Gradle or Maven with [JitPack](https://jitpack.io/) to build directly off a snapshot, branch, or commit of this repository. + +For example, to build off the 1.x branch, use this setup for Gradle: ```groovy -compile 'io.reactivex:rxkotlin:x.y.z' +repositories { + maven { url 'https://jitpack.io' } +} + +dependencies { + compile 'com.github.ReactiveX:RxKotlin:1.x-SNAPSHOT' +} +``` + +Use this setup for Maven: + +```xml + + + jitpack.io + https://jitpack.io + + + + + com.github.ReactiveX + RxKotlin + 1.x-SNAPSHOT + ``` + +Learn more about building this project with JitPack [here](https://jitpack.io/#ReactiveX/RxKotlin). + +## Kotlin Slack Channel + +Join us on the #rx channel in Kotlin Slack! + +https://kotlinlang.slack.com/messages/rx + +## Contributing + +We welcome contributions and discussion for new features. It is recommended to file an issue first to prevent unnecessary efforts, but feel free to put in pull requests. The vision is to keep this library lightweight, with a tight and focused scope applicable to all platforms (including Android, server, and desktop). Anything specific to a particular domain (for example, [JavaFX](https://github.com/thomasnield/RxKotlinFX), [Math](https://github.com/thomasnield/rxkotlin-math), or [JDBC](https://github.com/davidmoten/rxjava-jdbc)) might be better suited as a separate project. Feel free to open discussion and we will help figure out where your functionality may belong. + + + From 1d81d6f4869488568927743177d3180444eb9d32 Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Wed, 8 Mar 2017 10:39:58 -0600 Subject: [PATCH 22/27] Fix 2.x extension tests --- src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt index fc93a33..ab46445 100644 --- a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt @@ -1,5 +1,5 @@ /** - * Copyright 2013 Netflix, Inc. + * Copyright 2017 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ class ExtensionTests : KotlinTests() { @Test fun testCreate() { - observable { subscriber -> + Observable.create { subscriber -> subscriber.onNext("Hello") subscriber.onComplete() }.subscribe { result -> @@ -189,14 +189,14 @@ class ExtensionTests : KotlinTests() { } @Test fun testForEach() { - observable(asyncObservable).blockingForEach(received()) + Observable.create(asyncObservable).blockingForEach(received()) verify(a, times(1)).received(1) verify(a, times(1)).received(2) verify(a, times(1)).received(3) } @Test(expected = RuntimeException::class) fun testForEachWithError() { - observable(asyncObservable).blockingForEach { throw RuntimeException("err") } + Observable.create(asyncObservable).blockingForEach { throw RuntimeException("err") } fail("we expect an exception to be thrown") } @@ -231,7 +231,7 @@ class ExtensionTests : KotlinTests() { val testScheduler = TestScheduler() val worker = testScheduler.createWorker() - val observable = observable> { s -> + val observable = Observable.create> { s -> fun at(delay: Long, func: () -> Unit) { worker.schedule({ func() From 6d62302b2f12f1536e9df7285f3f597de9823e38 Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Wed, 8 Mar 2017 10:45:42 -0600 Subject: [PATCH 23/27] add Array.toFlowable() --- src/main/kotlin/rx/lang/kotlin/flowable.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/rx/lang/kotlin/flowable.kt b/src/main/kotlin/rx/lang/kotlin/flowable.kt index 12ba34e..c9c9890 100644 --- a/src/main/kotlin/rx/lang/kotlin/flowable.kt +++ b/src/main/kotlin/rx/lang/kotlin/flowable.kt @@ -1,6 +1,7 @@ package rx.lang.kotlin import io.reactivex.Flowable +import io.reactivex.Observable import io.reactivex.functions.BiFunction @@ -11,6 +12,7 @@ fun IntArray.toFlowable(): Flowable = this.asIterable().toFlowable() fun LongArray.toFlowable(): Flowable = this.asIterable().toFlowable() fun FloatArray.toFlowable(): Flowable = this.asIterable().toFlowable() fun DoubleArray.toFlowable(): Flowable = this.asIterable().toFlowable() +fun Array.toFlowable(): Flowable = Flowable.fromArray(*this) fun IntProgression.toFlowable(): Flowable = if (step == 1 && last.toLong() - first < Integer.MAX_VALUE) Flowable.range(first, Math.max(0, last - first + 1)) From 2310f73a32b32abbfda2f2b1470d6ea3a8db449e Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Wed, 8 Mar 2017 10:49:54 -0600 Subject: [PATCH 24/27] update and fix compile errors for tests in 2.x --- .../kotlin/rx/lang/kotlin/examples/examples.kt | 11 +++++------ src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt | 2 +- src/test/kotlin/rx/lang/kotlin/FlowableTest.kt | 15 ++++++++++----- src/test/kotlin/rx/lang/kotlin/ObservableTest.kt | 12 ++++++------ src/test/kotlin/rx/lang/kotlin/SingleTest.kt | 3 ++- 5 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt b/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt index 22dd9d4..7ab9e15 100644 --- a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt +++ b/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt @@ -4,7 +4,6 @@ import io.reactivex.Observable import io.reactivex.disposables.CompositeDisposable import rx.lang.kotlin.addTo import rx.lang.kotlin.combineLatest -import rx.lang.kotlin.observable import rx.lang.kotlin.plusAssign import rx.lang.kotlin.subscribeBy import rx.lang.kotlin.toObservable @@ -50,7 +49,7 @@ fun main(args: Array) { addToCompositeSubscription() } -private fun URL.toScannerObservable() = observable { s -> +private fun URL.toScannerObservable() = Observable.create { s -> this.openStream().use { stream -> Scanner(stream).useDelimiter("\\A") .toObservable() @@ -58,13 +57,13 @@ private fun URL.toScannerObservable() = observable { s -> } } -fun syncObservable(): Observable = observable { subscriber -> +fun syncObservable(): Observable = Observable.create { subscriber -> (0..75).toObservable() .map { "Sync value_$it" } .subscribe { subscriber.onNext(it) } } -fun asyncObservable(): Observable = observable { subscriber -> +fun asyncObservable(): Observable = Observable.create { subscriber -> thread { (0..75).toObservable() .map { "Async value_$it" } @@ -72,7 +71,7 @@ fun asyncObservable(): Observable = observable { subscriber -> } } -fun asyncWiki(vararg articleNames: String): Observable = observable { subscriber -> +fun asyncWiki(vararg articleNames: String): Observable = Observable.create { subscriber -> thread { articleNames.toObservable() .flatMapMaybe { name -> URL("http://en.wikipedia.org/wiki/$name").toScannerObservable().firstElement() } @@ -80,7 +79,7 @@ fun asyncWiki(vararg articleNames: String): Observable = observable { su } } -fun asyncWikiWithErrorHandling(vararg articleNames: String): Observable = observable { subscriber -> +fun asyncWikiWithErrorHandling(vararg articleNames: String): Observable = Observable.create { subscriber -> thread { articleNames.toObservable() .flatMapMaybe { name -> URL("http://en.wikipedia.org/wiki/$name").toScannerObservable().firstElement() } diff --git a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt index ab46445..ec32c26 100644 --- a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt +++ b/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt @@ -304,6 +304,6 @@ class ExtensionTests : KotlinTests() { get() = funOnSubscribe(p1 = counter++) // partial applied function val observable: Observable - get() = observable(onSubscribe) + get() = Observable.create(onSubscribe) } } diff --git a/src/test/kotlin/rx/lang/kotlin/FlowableTest.kt b/src/test/kotlin/rx/lang/kotlin/FlowableTest.kt index 2f9f981..1b614d9 100644 --- a/src/test/kotlin/rx/lang/kotlin/FlowableTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/FlowableTest.kt @@ -1,6 +1,8 @@ package rx.lang.kotlin +import io.reactivex.BackpressureStrategy import io.reactivex.Flowable +import io.reactivex.FlowableEmitter import org.junit.Assert import org.junit.Ignore import org.junit.Test @@ -8,9 +10,12 @@ import java.util.concurrent.atomic.AtomicInteger class FlowableTest { + private fun bufferedFlowable(source: (FlowableEmitter) -> Unit) = + Flowable.create(source, BackpressureStrategy.BUFFER) + @Test fun testCreation() { val o0: Flowable = Flowable.empty() - val list = flowable { s -> + val list = bufferedFlowable { s -> s.onNext(1) s.onNext(777) s.onComplete() @@ -19,7 +24,7 @@ class FlowableTest { val o1: Flowable = listOf(1, 2, 3).toFlowable() val o2: Flowable> = Flowable.just(listOf(1, 2, 3)) - val o3: Flowable = Flowable.defer { flowable { s -> s.onNext(1) } } + val o3: Flowable = Flowable.defer { bufferedFlowable { s -> s.onNext(1) } } val o4: Flowable = Array(3) { 0 }.toFlowable() val o5: Flowable = IntArray(3).toFlowable() @@ -32,7 +37,7 @@ class FlowableTest { } @Test fun testExampleFromReadme() { - val result = flowable { subscriber -> + val result = bufferedFlowable { subscriber -> subscriber.onNext("H") subscriber.onNext("e") subscriber.onNext("l") @@ -41,7 +46,7 @@ class FlowableTest { subscriber.onNext("o") subscriber.onComplete() }.filter(String::isNotEmpty). - fold(StringBuilder(), StringBuilder::append). + reduce(StringBuilder(), StringBuilder::append). map { it.toString() }. blockingGet() @@ -90,7 +95,7 @@ class FlowableTest { } @Test fun testFold() { - val result = listOf(1, 2, 3).toFlowable().fold(0) { acc, e -> acc + e }.blockingGet() + val result = listOf(1, 2, 3).toFlowable().reduce(0) { acc, e -> acc + e }.blockingGet() Assert.assertEquals(6, result) } diff --git a/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt b/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt index 90e727b..b8b18e2 100644 --- a/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt @@ -12,7 +12,7 @@ import java.util.concurrent.atomic.AtomicInteger class ObservableTest { @Test fun testCreation() { - val observable = observable { s -> + val observable = Observable.create { s -> s.apply { onNext(1) onNext(777) @@ -26,7 +26,7 @@ class ObservableTest { val o1: Observable = listOf(1, 2, 3).toObservable() val o2: Observable> = Observable.just(listOf(1, 2, 3)) - val o3: Observable = Observable.defer { observable { s -> s.onNext(1) } } + val o3: Observable = Observable.defer { Observable.create { s -> s.onNext(1) } } val o4: Observable = Array(3) { 0 }.toObservable() val o5: Observable = IntArray(3).toObservable() @@ -39,7 +39,7 @@ class ObservableTest { } @Test fun testExampleFromReadme() { - val observable = observable { s -> + val observable = Observable.create { s -> s.apply { onNext("H") onNext("e") @@ -52,7 +52,7 @@ class ObservableTest { } val result = observable .filter(String::isNotEmpty) - .fold(StringBuilder(), StringBuilder::append) + .reduce(StringBuilder(), StringBuilder::append) .map { it.toString() } .blockingGet() @@ -109,8 +109,8 @@ class ObservableTest { subscriber2.assertValues(IndexedValue(0, "a"), IndexedValue(1, "b"), IndexedValue(2, "c")) } - @Test fun testFold() { - val result = listOf(1, 2, 3).toObservable().fold(0) { acc, e -> acc + e }.blockingGet() + @Test fun testReduce() { + val result = listOf(1, 2, 3).toObservable().reduce(0) { acc, e -> acc + e }.blockingGet() assertEquals(6, result) } diff --git a/src/test/kotlin/rx/lang/kotlin/SingleTest.kt b/src/test/kotlin/rx/lang/kotlin/SingleTest.kt index 7fc571c..b0d3f00 100644 --- a/src/test/kotlin/rx/lang/kotlin/SingleTest.kt +++ b/src/test/kotlin/rx/lang/kotlin/SingleTest.kt @@ -1,6 +1,7 @@ package rx.lang.kotlin import io.reactivex.Observable +import io.reactivex.Single import org.junit.Test import org.mockito.Mockito import org.mockito.Mockito.mock @@ -9,7 +10,7 @@ import java.util.concurrent.Callable class SingleTest : KotlinTests() { @Test fun testCreate() { - single { s -> + Single.create { s -> s.onSuccess("Hello World!") }.subscribe { result -> a.received(result) From 15d9e01327c308a60df8d2d1d207894111edcd1c Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Wed, 8 Mar 2017 17:02:05 -0600 Subject: [PATCH 25/27] move swtichOnNext() --- src/main/kotlin/rx/lang/kotlin/flowable.kt | 1 - src/main/kotlin/rx/lang/kotlin/observable.kt | 1 - src/main/kotlin/rx/lang/kotlin/operators.kt | 6 ++++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/rx/lang/kotlin/flowable.kt b/src/main/kotlin/rx/lang/kotlin/flowable.kt index c9c9890..0efd16a 100644 --- a/src/main/kotlin/rx/lang/kotlin/flowable.kt +++ b/src/main/kotlin/rx/lang/kotlin/flowable.kt @@ -43,7 +43,6 @@ fun Flowable.withIndex(): Flowable> inline fun Flowable.flatMapSequence(crossinline body: (T) -> Sequence): Flowable = flatMap { body(it).toFlowable() } -fun Flowable>.switchOnNext(): Flowable = Flowable.switchOnNext(this) /** * Flowable.combineLatest(List> sources, FuncN combineFunction) diff --git a/src/main/kotlin/rx/lang/kotlin/observable.kt b/src/main/kotlin/rx/lang/kotlin/observable.kt index eefd51e..bc4a30e 100644 --- a/src/main/kotlin/rx/lang/kotlin/observable.kt +++ b/src/main/kotlin/rx/lang/kotlin/observable.kt @@ -44,7 +44,6 @@ fun Observable.withIndex(): Observable> inline fun Observable.flatMapSequence(crossinline body: (T) -> Sequence): Observable = flatMap { body(it).toObservable() } -fun Observable>.switchOnNext(): Observable = Observable.switchOnNext(this) /** * Observable.combineLatest(List> sources, FuncN combineFunction) diff --git a/src/main/kotlin/rx/lang/kotlin/operators.kt b/src/main/kotlin/rx/lang/kotlin/operators.kt index eda0e67..456792e 100644 --- a/src/main/kotlin/rx/lang/kotlin/operators.kt +++ b/src/main/kotlin/rx/lang/kotlin/operators.kt @@ -26,6 +26,12 @@ fun Observable>.concatAll() = concatMap { it } fun Flowable>.concatAll() = concatMap { it } +fun Observable>.switchOnNext(): Observable = Observable.switchOnNext(this) + + +fun Flowable>.switchOnNext(): Flowable = Flowable.switchOnNext(this) + + /** * Emits the latest `Observable` emitted through an `Observable>`. Same as calling `switchMap { it }`. */ From 3b623a9f8479e62d17c552e4466ba2c3899a364d Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Fri, 10 Mar 2017 22:24:40 -0600 Subject: [PATCH 26/27] git commit -m 'refactor package domain to io.reactivex.rxkotlin' --- .../reactivex/rxkotlin}/examples/examples.kt | 13 ++++--- .../rxkotlin}/examples/retrofit/retrofit.kt | 2 +- .../reactivex/rxkotlin}/completable.kt | 2 +- .../reactivex/rxkotlin}/disposable.kt | 2 +- .../reactivex/rxkotlin}/flowable.kt | 2 +- .../kotlin => io/reactivex/rxkotlin}/maybe.kt | 2 +- .../reactivex/rxkotlin}/observable.kt | 2 +- .../reactivex/rxkotlin}/operators.kt | 6 ++-- .../reactivex/rxkotlin}/single.kt | 2 +- .../reactivex/rxkotlin}/subscribers.kt | 2 +- .../reactivex/rxkotlin}/BasicKotlinTests.kt | 2 +- .../reactivex/rxkotlin}/CompletableTest.kt | 2 +- .../reactivex/rxkotlin}/ExtensionTests.kt | 2 +- .../reactivex/rxkotlin}/FlowableTest.kt | 36 +++++++++---------- .../reactivex/rxkotlin}/KotlinTests.kt | 2 +- .../reactivex/rxkotlin}/ObservableTest.kt | 2 +- .../reactivex/rxkotlin}/SingleTest.kt | 2 +- .../reactivex/rxkotlin}/SubscriptionTests.kt | 2 +- 18 files changed, 42 insertions(+), 43 deletions(-) rename src/examples/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/examples/examples.kt (93%) rename src/examples/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/examples/retrofit/retrofit.kt (95%) rename src/main/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/completable.kt (93%) rename src/main/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/disposable.kt (94%) rename src/main/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/flowable.kt (99%) rename src/main/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/maybe.kt (95%) rename src/main/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/observable.kt (99%) rename src/main/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/operators.kt (94%) rename src/main/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/single.kt (94%) rename src/main/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/subscribers.kt (98%) rename src/test/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/BasicKotlinTests.kt (99%) rename src/test/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/CompletableTest.kt (97%) rename src/test/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/ExtensionTests.kt (99%) rename src/test/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/FlowableTest.kt (79%) rename src/test/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/KotlinTests.kt (97%) rename src/test/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/ObservableTest.kt (99%) rename src/test/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/SingleTest.kt (97%) rename src/test/kotlin/{rx/lang/kotlin => io/reactivex/rxkotlin}/SubscriptionTests.kt (96%) diff --git a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt b/src/examples/kotlin/io/reactivex/rxkotlin/examples/examples.kt similarity index 93% rename from src/examples/kotlin/rx/lang/kotlin/examples/examples.kt rename to src/examples/kotlin/io/reactivex/rxkotlin/examples/examples.kt index 7ab9e15..eb2a919 100644 --- a/src/examples/kotlin/rx/lang/kotlin/examples/examples.kt +++ b/src/examples/kotlin/io/reactivex/rxkotlin/examples/examples.kt @@ -1,13 +1,12 @@ -package rx.lang.kotlin.examples +package io.reactivex.rxkotlin.examples import io.reactivex.Observable import io.reactivex.disposables.CompositeDisposable -import rx.lang.kotlin.addTo -import rx.lang.kotlin.combineLatest -import rx.lang.kotlin.plusAssign -import rx.lang.kotlin.subscribeBy -import rx.lang.kotlin.toObservable -import rx.lang.kotlin.zip +import io.reactivex.rxkotlin.* +import io.reactivex.rxkotlin.combineLatest +import io.reactivex.rxkotlin.subscribeBy +import io.reactivex.rxkotlin.toObservable +import io.reactivex.rxkotlin.zip import java.net.URL import java.util.Scanner import java.util.concurrent.TimeUnit diff --git a/src/examples/kotlin/rx/lang/kotlin/examples/retrofit/retrofit.kt b/src/examples/kotlin/io/reactivex/rxkotlin/examples/retrofit/retrofit.kt similarity index 95% rename from src/examples/kotlin/rx/lang/kotlin/examples/retrofit/retrofit.kt rename to src/examples/kotlin/io/reactivex/rxkotlin/examples/retrofit/retrofit.kt index 5e77a44..d32b886 100644 --- a/src/examples/kotlin/rx/lang/kotlin/examples/retrofit/retrofit.kt +++ b/src/examples/kotlin/io/reactivex/rxkotlin/examples/retrofit/retrofit.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin.examples.retrofit +package io.reactivex.rxkotlin.examples.retrofit import io.reactivex.Observable import retrofit.RestAdapter diff --git a/src/main/kotlin/rx/lang/kotlin/completable.kt b/src/main/kotlin/io/reactivex/rxkotlin/completable.kt similarity index 93% rename from src/main/kotlin/rx/lang/kotlin/completable.kt rename to src/main/kotlin/io/reactivex/rxkotlin/completable.kt index 8fe11e1..2200e78 100644 --- a/src/main/kotlin/rx/lang/kotlin/completable.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/completable.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Completable import io.reactivex.functions.Action diff --git a/src/main/kotlin/rx/lang/kotlin/disposable.kt b/src/main/kotlin/io/reactivex/rxkotlin/disposable.kt similarity index 94% rename from src/main/kotlin/rx/lang/kotlin/disposable.kt rename to src/main/kotlin/io/reactivex/rxkotlin/disposable.kt index d442e90..b56298b 100644 --- a/src/main/kotlin/rx/lang/kotlin/disposable.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/disposable.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.Disposable diff --git a/src/main/kotlin/rx/lang/kotlin/flowable.kt b/src/main/kotlin/io/reactivex/rxkotlin/flowable.kt similarity index 99% rename from src/main/kotlin/rx/lang/kotlin/flowable.kt rename to src/main/kotlin/io/reactivex/rxkotlin/flowable.kt index 0efd16a..262021b 100644 --- a/src/main/kotlin/rx/lang/kotlin/flowable.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/flowable.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Flowable import io.reactivex.Observable diff --git a/src/main/kotlin/rx/lang/kotlin/maybe.kt b/src/main/kotlin/io/reactivex/rxkotlin/maybe.kt similarity index 95% rename from src/main/kotlin/rx/lang/kotlin/maybe.kt rename to src/main/kotlin/io/reactivex/rxkotlin/maybe.kt index 35479bf..b1c3c5d 100644 --- a/src/main/kotlin/rx/lang/kotlin/maybe.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/maybe.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Maybe import java.util.concurrent.Callable diff --git a/src/main/kotlin/rx/lang/kotlin/observable.kt b/src/main/kotlin/io/reactivex/rxkotlin/observable.kt similarity index 99% rename from src/main/kotlin/rx/lang/kotlin/observable.kt rename to src/main/kotlin/io/reactivex/rxkotlin/observable.kt index bc4a30e..124f904 100644 --- a/src/main/kotlin/rx/lang/kotlin/observable.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/observable.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Observable import io.reactivex.ObservableEmitter diff --git a/src/main/kotlin/rx/lang/kotlin/operators.kt b/src/main/kotlin/io/reactivex/rxkotlin/operators.kt similarity index 94% rename from src/main/kotlin/rx/lang/kotlin/operators.kt rename to src/main/kotlin/io/reactivex/rxkotlin/operators.kt index 456792e..08dd148 100644 --- a/src/main/kotlin/rx/lang/kotlin/operators.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/operators.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Flowable import io.reactivex.Observable @@ -72,8 +72,8 @@ fun Observable.joinToString(separator: String? = null, * @param postfix is the succeeding `String` after the concatenated elements (optional) */ fun Flowable.joinToString(separator: String? = null, - prefix: String? = null, - postfix: String? = null + prefix: String? = null, + postfix: String? = null ): Single = collect({ StringBuilder(prefix ?: "") }) { builder: StringBuilder, next: T -> builder.append(if (builder.length == prefix?.length ?: 0) "" else separator ?: "").append(next) }.map { it.append(postfix ?: "").toString() } \ No newline at end of file diff --git a/src/main/kotlin/rx/lang/kotlin/single.kt b/src/main/kotlin/io/reactivex/rxkotlin/single.kt similarity index 94% rename from src/main/kotlin/rx/lang/kotlin/single.kt rename to src/main/kotlin/io/reactivex/rxkotlin/single.kt index 6f6f7be..69d9701 100644 --- a/src/main/kotlin/rx/lang/kotlin/single.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/single.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Single import io.reactivex.SingleEmitter diff --git a/src/main/kotlin/rx/lang/kotlin/subscribers.kt b/src/main/kotlin/io/reactivex/rxkotlin/subscribers.kt similarity index 98% rename from src/main/kotlin/rx/lang/kotlin/subscribers.kt rename to src/main/kotlin/io/reactivex/rxkotlin/subscribers.kt index 69f0e2e..30e104c 100644 --- a/src/main/kotlin/rx/lang/kotlin/subscribers.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/subscribers.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Completable import io.reactivex.Flowable diff --git a/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt b/src/test/kotlin/io/reactivex/rxkotlin/BasicKotlinTests.kt similarity index 99% rename from src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt rename to src/test/kotlin/io/reactivex/rxkotlin/BasicKotlinTests.kt index 80fcd0d..b512bf0 100644 --- a/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/BasicKotlinTests.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Notification import io.reactivex.Observable diff --git a/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt b/src/test/kotlin/io/reactivex/rxkotlin/CompletableTest.kt similarity index 97% rename from src/test/kotlin/rx/lang/kotlin/CompletableTest.kt rename to src/test/kotlin/io/reactivex/rxkotlin/CompletableTest.kt index 110714a..a749c53 100644 --- a/src/test/kotlin/rx/lang/kotlin/CompletableTest.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/CompletableTest.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Single import io.reactivex.functions.Action diff --git a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt b/src/test/kotlin/io/reactivex/rxkotlin/ExtensionTests.kt similarity index 99% rename from src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt rename to src/test/kotlin/io/reactivex/rxkotlin/ExtensionTests.kt index ec32c26..f650e91 100644 --- a/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/ExtensionTests.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Notification import io.reactivex.Observable diff --git a/src/test/kotlin/rx/lang/kotlin/FlowableTest.kt b/src/test/kotlin/io/reactivex/rxkotlin/FlowableTest.kt similarity index 79% rename from src/test/kotlin/rx/lang/kotlin/FlowableTest.kt rename to src/test/kotlin/io/reactivex/rxkotlin/FlowableTest.kt index 1b614d9..5547062 100644 --- a/src/test/kotlin/rx/lang/kotlin/FlowableTest.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/FlowableTest.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.BackpressureStrategy import io.reactivex.Flowable @@ -10,33 +10,33 @@ import java.util.concurrent.atomic.AtomicInteger class FlowableTest { - private fun bufferedFlowable(source: (FlowableEmitter) -> Unit) = - Flowable.create(source, BackpressureStrategy.BUFFER) + private fun bufferedFlowable(source: (io.reactivex.FlowableEmitter) -> Unit) = + io.reactivex.Flowable.create(source, io.reactivex.BackpressureStrategy.BUFFER) - @Test fun testCreation() { - val o0: Flowable = Flowable.empty() + @org.junit.Test fun testCreation() { + val o0: io.reactivex.Flowable = io.reactivex.Flowable.empty() val list = bufferedFlowable { s -> s.onNext(1) s.onNext(777) s.onComplete() }.toList().blockingGet() - Assert.assertEquals(listOf(1, 777), list) - val o1: Flowable = listOf(1, 2, 3).toFlowable() - val o2: Flowable> = Flowable.just(listOf(1, 2, 3)) + org.junit.Assert.assertEquals(listOf(1, 777), list) + val o1: io.reactivex.Flowable = listOf(1, 2, 3).toFlowable() + val o2: io.reactivex.Flowable> = io.reactivex.Flowable.just(listOf(1, 2, 3)) - val o3: Flowable = Flowable.defer { bufferedFlowable { s -> s.onNext(1) } } - val o4: Flowable = Array(3) { 0 }.toFlowable() - val o5: Flowable = IntArray(3).toFlowable() + val o3: io.reactivex.Flowable = io.reactivex.Flowable.defer { bufferedFlowable { s -> s.onNext(1) } } + val o4: io.reactivex.Flowable = Array(3) { 0 }.toFlowable() + val o5: io.reactivex.Flowable = IntArray(3).toFlowable() - Assert.assertNotNull(o0) - Assert.assertNotNull(o1) - Assert.assertNotNull(o2) - Assert.assertNotNull(o3) - Assert.assertNotNull(o4) - Assert.assertNotNull(o5) + org.junit.Assert.assertNotNull(o0) + org.junit.Assert.assertNotNull(o1) + org.junit.Assert.assertNotNull(o2) + org.junit.Assert.assertNotNull(o3) + org.junit.Assert.assertNotNull(o4) + org.junit.Assert.assertNotNull(o5) } - @Test fun testExampleFromReadme() { + @org.junit.Test fun testExampleFromReadme() { val result = bufferedFlowable { subscriber -> subscriber.onNext("H") subscriber.onNext("e") diff --git a/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt b/src/test/kotlin/io/reactivex/rxkotlin/KotlinTests.kt similarity index 97% rename from src/test/kotlin/rx/lang/kotlin/KotlinTests.kt rename to src/test/kotlin/io/reactivex/rxkotlin/KotlinTests.kt index 1f3cb39..0c9eb40 100644 --- a/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/KotlinTests.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package rx.lang.kotlin +package io.reactivex.rxkotlin import org.junit.Before import org.mockito.Mock diff --git a/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt b/src/test/kotlin/io/reactivex/rxkotlin/ObservableTest.kt similarity index 99% rename from src/test/kotlin/rx/lang/kotlin/ObservableTest.kt rename to src/test/kotlin/io/reactivex/rxkotlin/ObservableTest.kt index b8b18e2..45fac28 100644 --- a/src/test/kotlin/rx/lang/kotlin/ObservableTest.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/ObservableTest.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Observable import io.reactivex.observers.TestObserver diff --git a/src/test/kotlin/rx/lang/kotlin/SingleTest.kt b/src/test/kotlin/io/reactivex/rxkotlin/SingleTest.kt similarity index 97% rename from src/test/kotlin/rx/lang/kotlin/SingleTest.kt rename to src/test/kotlin/io/reactivex/rxkotlin/SingleTest.kt index b0d3f00..e97b470 100644 --- a/src/test/kotlin/rx/lang/kotlin/SingleTest.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/SingleTest.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Observable import io.reactivex.Single diff --git a/src/test/kotlin/rx/lang/kotlin/SubscriptionTests.kt b/src/test/kotlin/io/reactivex/rxkotlin/SubscriptionTests.kt similarity index 96% rename from src/test/kotlin/rx/lang/kotlin/SubscriptionTests.kt rename to src/test/kotlin/io/reactivex/rxkotlin/SubscriptionTests.kt index 01c528d..a1bddab 100644 --- a/src/test/kotlin/rx/lang/kotlin/SubscriptionTests.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/SubscriptionTests.kt @@ -1,4 +1,4 @@ -package rx.lang.kotlin +package io.reactivex.rxkotlin import io.reactivex.Observable import io.reactivex.disposables.CompositeDisposable From ff4327784c931aca39cbdd325633a09a245de362 Mon Sep 17 00:00:00 2001 From: Thomas Nield Date: Fri, 10 Mar 2017 22:28:01 -0600 Subject: [PATCH 27/27] optimize imports --- src/main/kotlin/io/reactivex/rxkotlin/flowable.kt | 1 - src/main/kotlin/io/reactivex/rxkotlin/observable.kt | 2 -- src/main/kotlin/io/reactivex/rxkotlin/single.kt | 1 - src/main/kotlin/io/reactivex/rxkotlin/subscribers.kt | 6 +----- .../kotlin/io/reactivex/rxkotlin/BasicKotlinTests.kt | 10 ++-------- .../kotlin/io/reactivex/rxkotlin/CompletableTest.kt | 2 +- .../kotlin/io/reactivex/rxkotlin/ExtensionTests.kt | 5 +---- src/test/kotlin/io/reactivex/rxkotlin/FlowableTest.kt | 2 -- 8 files changed, 5 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/io/reactivex/rxkotlin/flowable.kt b/src/main/kotlin/io/reactivex/rxkotlin/flowable.kt index 262021b..1ccdd4b 100644 --- a/src/main/kotlin/io/reactivex/rxkotlin/flowable.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/flowable.kt @@ -1,7 +1,6 @@ package io.reactivex.rxkotlin import io.reactivex.Flowable -import io.reactivex.Observable import io.reactivex.functions.BiFunction diff --git a/src/main/kotlin/io/reactivex/rxkotlin/observable.kt b/src/main/kotlin/io/reactivex/rxkotlin/observable.kt index 124f904..2b2c32d 100644 --- a/src/main/kotlin/io/reactivex/rxkotlin/observable.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/observable.kt @@ -1,8 +1,6 @@ package io.reactivex.rxkotlin import io.reactivex.Observable -import io.reactivex.ObservableEmitter -import io.reactivex.Single import io.reactivex.functions.BiFunction diff --git a/src/main/kotlin/io/reactivex/rxkotlin/single.kt b/src/main/kotlin/io/reactivex/rxkotlin/single.kt index 69d9701..6d3a4e9 100644 --- a/src/main/kotlin/io/reactivex/rxkotlin/single.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/single.kt @@ -1,7 +1,6 @@ package io.reactivex.rxkotlin import io.reactivex.Single -import io.reactivex.SingleEmitter import java.util.concurrent.Callable import java.util.concurrent.Future diff --git a/src/main/kotlin/io/reactivex/rxkotlin/subscribers.kt b/src/main/kotlin/io/reactivex/rxkotlin/subscribers.kt index 30e104c..6432591 100644 --- a/src/main/kotlin/io/reactivex/rxkotlin/subscribers.kt +++ b/src/main/kotlin/io/reactivex/rxkotlin/subscribers.kt @@ -1,10 +1,6 @@ package io.reactivex.rxkotlin -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.Maybe -import io.reactivex.Observable -import io.reactivex.Single +import io.reactivex.* import io.reactivex.disposables.Disposable private val onNextStub: (Any) -> Unit = {} diff --git a/src/test/kotlin/io/reactivex/rxkotlin/BasicKotlinTests.kt b/src/test/kotlin/io/reactivex/rxkotlin/BasicKotlinTests.kt index b512bf0..7c930e5 100644 --- a/src/test/kotlin/io/reactivex/rxkotlin/BasicKotlinTests.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/BasicKotlinTests.kt @@ -16,19 +16,13 @@ package io.reactivex.rxkotlin -import io.reactivex.Notification -import io.reactivex.Observable -import io.reactivex.ObservableEmitter -import io.reactivex.ObservableOnSubscribe -import io.reactivex.Single +import io.reactivex.* import io.reactivex.functions.BiFunction import io.reactivex.functions.Function3 import org.junit.Assert.assertEquals import org.junit.Assert.fail import org.junit.Test -import org.mockito.Mockito.any -import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.Mockito.* import kotlin.concurrent.thread /** diff --git a/src/test/kotlin/io/reactivex/rxkotlin/CompletableTest.kt b/src/test/kotlin/io/reactivex/rxkotlin/CompletableTest.kt index a749c53..c61c26b 100644 --- a/src/test/kotlin/io/reactivex/rxkotlin/CompletableTest.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/CompletableTest.kt @@ -5,7 +5,7 @@ import io.reactivex.functions.Action import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Test -import java.util.NoSuchElementException +import java.util.* import java.util.concurrent.Callable class CompletableTest { diff --git a/src/test/kotlin/io/reactivex/rxkotlin/ExtensionTests.kt b/src/test/kotlin/io/reactivex/rxkotlin/ExtensionTests.kt index f650e91..08ebde8 100644 --- a/src/test/kotlin/io/reactivex/rxkotlin/ExtensionTests.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/ExtensionTests.kt @@ -26,10 +26,7 @@ import org.funktionale.partials.invoke import org.junit.Assert.assertEquals import org.junit.Assert.fail import org.junit.Test -import org.mockito.Mockito.any -import org.mockito.Mockito.inOrder -import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.Mockito.* import java.util.concurrent.TimeUnit import kotlin.concurrent.thread diff --git a/src/test/kotlin/io/reactivex/rxkotlin/FlowableTest.kt b/src/test/kotlin/io/reactivex/rxkotlin/FlowableTest.kt index 5547062..8e210b8 100644 --- a/src/test/kotlin/io/reactivex/rxkotlin/FlowableTest.kt +++ b/src/test/kotlin/io/reactivex/rxkotlin/FlowableTest.kt @@ -1,8 +1,6 @@ package io.reactivex.rxkotlin -import io.reactivex.BackpressureStrategy import io.reactivex.Flowable -import io.reactivex.FlowableEmitter import org.junit.Assert import org.junit.Ignore import org.junit.Test