Skip to content

Commit

Permalink
RxLifecycle interop module (#118)
Browse files Browse the repository at this point in the history
* RX lifecycle interop module added for autodipose

* Added pom artifact

* Renamed package, fixed javadoc and addressed other review comments

* Fixed lifecycle everywhere and moved rxlifecycle to misc in dependencies.gradle

* Fixed javadoc

* Renamed RXLifecycle to RxLifecycle and method name fix

* Renamed depdencies in dependency.gradle and removed class from javadoc

* Renamed methods in RXLifecycle to from() for consistency

* Added javadoc for individual methods

* Removed double space

* Fixed checkstyle for test

* fixed exception name

* API replaced with implementation to avoid transitive compile time dependencies

* Reverted the incorrect api

* Added more javadoc in package-info.java and license header to all the files

* Fixed javadoc
  • Loading branch information
bangarharshit authored and ZacSweers committed Oct 22, 2017
1 parent 0e543ef commit 60d95bf
Show file tree
Hide file tree
Showing 8 changed files with 420 additions and 1 deletion.
58 changes: 58 additions & 0 deletions autodispose-rxlifecycle/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2017. Uber Technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

buildscript {
repositories {
jcenter()
maven { url deps.build.repositories.plugins }
}
dependencies {
classpath deps.build.gradlePlugins.apt
classpath deps.build.gradlePlugins.errorProne
}
}

apply plugin: 'java-library'
apply plugin: 'net.ltgt.apt'
apply plugin: 'net.ltgt.errorprone'

sourceCompatibility = "1.7"
targetCompatibility = "1.7"

test {
testLogging.showStandardStreams = true
}

dependencies {
apt deps.build.nullAway
testApt deps.build.nullAway

compile project(':autodispose')
compile deps.misc.rxlifecycle
compileOnly deps.misc.errorProneAnnotations
compileOnly deps.misc.javaxExtras

errorprone deps.build.checkerFramework
errorprone deps.build.errorProne

testCompile project(':test-utils')
}

tasks.withType(JavaCompile) {
options.compilerArgs += ["-Xep:NullAway:ERROR", "-XepOpt:NullAway:AnnotatedPackages=com.uber"]
}

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
19 changes: 19 additions & 0 deletions autodispose-rxlifecycle/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#
# Copyright (C) 2017. Uber Technologies
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

POM_NAME=AutoDispose (RxLifecycle Interop)
POM_ARTIFACT_ID=autodispose-rxlifecycle
POM_PACKAGING=jar
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2017. Uber Technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.ubercab.autodispose.rxlifecycle;

import com.trello.rxlifecycle2.LifecycleProvider;
import com.trello.rxlifecycle2.OutsideLifecycleException;
import com.uber.autodispose.AutoDispose.ScopeHandler;
import com.uber.autodispose.LifecycleEndedException;
import com.uber.autodispose.ScopeProvider;
import io.reactivex.Maybe;

/**
* Interop for RxLifecycle. It provides static utility methods to convert {@link
* LifecycleProvider} to {@link ScopeProvider}.
*
* <p>There are several static utility converter
* methods such as {@link #from(LifecycleProvider)} for {@link
* LifecycleProvider#bindToLifecycle()} and {@link #from(LifecycleProvider, Object)} for
* {@link LifecycleProvider#bindUntilEvent(Object)}.
* <p>
*
* <em>Note:</em> RxLifecycle treats the {@link OutsideLifecycleException}
* as normal terminal event. There is no mapping to {@link LifecycleEndedException} and in such
* cases the stream is normally disposed.
*/
public final class RxLifecycleInterop {

private RxLifecycleInterop() {
throw new AssertionError("No Instances");
}

private static final Object DEFAULT_THROWAWAY_OBJECT = new Object();

/**
* Converter for transforming {@link LifecycleProvider} to {@link ScopeProvider}.
* It disposes the source when the next reasonable event occurs.
* <p>
* Example usage:
* <pre><code>
* Observable.just(1)
* .to(RxLifecycleInterop.from(lifecycleProvider))
* .subscribe(...)
* </code></pre>
*
* @param <E> the lifecycle event.
* @param provider the {@link LifecycleProvider} for RxLifecycle.
* @return a {@link ScopeHandler} to create AutoDisposing transformation
*/
public static <E> ScopeProvider from(final LifecycleProvider<E> provider) {
return new ScopeProvider() {
@Override public Maybe<?> requestScope() {
return provider.lifecycle()
.compose(provider.bindToLifecycle())
.ignoreElements()
.toMaybe()
.defaultIfEmpty(DEFAULT_THROWAWAY_OBJECT);
}
};
}

/**
* Converter for transforming {@link LifecycleProvider} to {@link ScopeProvider}.
* It disposes the source when a specific event occurs.
* <p>
* Example usage:
* <pre><code>
* Observable.just(1)
* .to(RxLifecycleInterop.from(lifecycleProvider, event))
* .subscribe(...)
* </code></pre>
*
* @param <E> the lifecycle event.
* @param provider the {@link LifecycleProvider} for RxLifecycle.
* @param event the event at which the source is disposed.
* @return a {@link ScopeHandler} to create AutoDisposing transformation
*/
public static <E> ScopeProvider from(final LifecycleProvider<E> provider, final E event) {
return new ScopeProvider() {
@Override public Maybe<?> requestScope() {
return provider.lifecycle()
.compose(provider.bindUntilEvent(event))
.ignoreElements()
.toMaybe()
.defaultIfEmpty(DEFAULT_THROWAWAY_OBJECT);
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2017. Uber Technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* AutoDispose extensions for interop with RxLifecycle. This namely supports
* {@link com.trello.rxlifecycle2.LifecycleProvider}.
*/
@com.uber.javaxextras.FieldsMethodsAndParametersAreNonNullByDefault
package com.ubercab.autodispose.rxlifecycle;

Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright (c) 2017. Uber Technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.ubercab.autodispose.rxlifecycle;

import com.uber.autodispose.AutoDispose;
import com.uber.autodispose.test.RecordingObserver;
import io.reactivex.disposables.Disposable;
import io.reactivex.observers.TestObserver;
import io.reactivex.subjects.PublishSubject;
import org.junit.Test;

import static com.google.common.truth.Truth.assertThat;

public class RxLifecycleInteropTest {

private static final RecordingObserver.Logger LOGGER = new RecordingObserver.Logger() {
@Override public void log(String message) {
System.out.println(RxLifecycleInteropTest.class.getSimpleName() + ": " + message);
}
};

private TestLifecycleProvider lifecycleProvider = new TestLifecycleProvider();

@Test
public void bindLifecycle_normalTermination_completeTheStream() {
lifecycleProvider.emitCreate();
TestObserver<Integer> o = new TestObserver<>();
PublishSubject<Integer> source = PublishSubject.create();
Disposable d = source.to(AutoDispose.with(
RxLifecycleInterop.from(lifecycleProvider)).<Integer>forObservable())
.subscribeWith(o);
o.assertSubscribed();

assertThat(source.hasObservers()).isTrue();

source.onNext(1);
o.assertValue(1);

source.onNext(2);
source.onComplete();
o.assertValues(1, 2);
o.assertComplete();
assertThat(d.isDisposed()).isFalse(); // Because it completed normally, was not disposed.
assertThat(source.hasObservers()).isFalse();
}

@Test
public void bindLifecycle_normalTermination_unsubscribe() {
lifecycleProvider.emitCreate();
RecordingObserver<Integer> o = new RecordingObserver<>(LOGGER);
PublishSubject<Integer> source = PublishSubject.create();
source.to(AutoDispose.with(
RxLifecycleInterop.from(lifecycleProvider)).<Integer>forObservable())
.subscribe(o);
o.takeSubscribe();

assertThat(source.hasObservers()).isTrue();

source.onNext(1);
assertThat(o.takeNext()).isEqualTo(1);

lifecycleProvider.emitDestroy();
source.onNext(2);
o.assertNoMoreEvents();
assertThat(source.hasObservers()).isFalse();
}

@Test
public void bindLifecycle_outsideLifecycleBound_unsubscribe() {
lifecycleProvider.emitCreate();
RecordingObserver<Integer> o = new RecordingObserver<>(LOGGER);
PublishSubject<Integer> source = PublishSubject.create();
lifecycleProvider.emitDestroy();
source
.to(AutoDispose.with(
RxLifecycleInterop.from(lifecycleProvider)).<Integer>forObservable())
.subscribe(o);

o.takeSubscribe();

source.onNext(2);
o.assertNoMoreEvents();
assertThat(
source.hasObservers()).isFalse(); // Because RxLifecycle
// treats OutsideLifecycleException as terminal event.
}

@Test
public void bindUntilEvent_normalTermination_completeTheStream() {
lifecycleProvider.emitCreate();
TestObserver<Integer> o = new TestObserver<>();
PublishSubject<Integer> source = PublishSubject.create();
Disposable d = source.to(AutoDispose.with(RxLifecycleInterop.from(
lifecycleProvider,
TestLifecycleProvider.Event.DESTROY)).<Integer>forObservable())
.subscribeWith(o);
o.assertSubscribed();

assertThat(source.hasObservers()).isTrue();

source.onNext(1);
o.assertValue(1);

source.onNext(2);
source.onComplete();
o.assertValues(1, 2);
o.assertComplete();
assertThat(d.isDisposed()).isFalse(); // Because it completed normally, was not disposed.
assertThat(source.hasObservers()).isFalse();
}

@Test
public void bindUntilEvent_interruptedTermination_unsubscribe() {
lifecycleProvider.emitCreate();
RecordingObserver<Integer> o = new RecordingObserver<>(LOGGER);
PublishSubject<Integer> source = PublishSubject.create();
source.to(AutoDispose.with(RxLifecycleInterop.from(lifecycleProvider,
TestLifecycleProvider.Event.DESTROY)).<Integer>forObservable())
.subscribe(o);
o.takeSubscribe();

assertThat(source.hasObservers()).isTrue();

source.onNext(1);
assertThat(o.takeNext()).isEqualTo(1);

lifecycleProvider.emitDestroy();
source.onNext(2);
o.assertNoMoreEvents();
assertThat(source.hasObservers()).isFalse();
}
}
Loading

0 comments on commit 60d95bf

Please sign in to comment.