Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RxLifecycle interop module #118

Merged
merged 16 commits into from
Oct 22, 2017
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed this in my earlier comments, but these two methods should each have docs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c2e63ff

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,24 @@
/*
* 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.
*/

/**
* Interop for RxLifecycle. It could understand RxLifecycle's
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can phrase this better, doesn't have to be too wordy. Let's do

AutoDispose extensions for interop with RxLifecycle. This namely supports {@link LifecycleProvider}.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed - 7f3e1a8

* LifecycleProvider mechanics, which in turn lend support to different
* components such as RxActivity. It does so by providing converters to retrieve ScopeProvider.
*/
@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