Add lambdas to Store and Filesystem modules (rx2) (#189)

This commit is contained in:
Shane Fitzpatrick 2017-05-01 13:44:44 -07:00 committed by Mike Nakhimovich
parent 20445fff13
commit 47c463ba8a
17 changed files with 72 additions and 299 deletions

View file

@ -19,7 +19,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.google.gms:google-services:3.0.0'
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.5.6'
classpath 'me.tatarka:gradle-retrolambda:3.2.5'
classpath 'me.tatarka:gradle-retrolambda:3.6.1'
classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2'
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.8'
}

View file

@ -19,8 +19,8 @@ dependencies {
buildscript {
tasks.withType(JavaCompile) {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}

View file

@ -8,8 +8,6 @@ import java.io.FileNotFoundException;
import javax.annotation.Nonnull;
import io.reactivex.Maybe;
import io.reactivex.MaybeEmitter;
import io.reactivex.MaybeOnSubscribe;
import okio.BufferedSource;
/**
@ -32,23 +30,20 @@ public class FSReader<T> implements DiskRead<BufferedSource, T> {
@Nonnull
@Override
public Maybe<BufferedSource> read(@Nonnull final T key) {
return Maybe.create(new MaybeOnSubscribe<BufferedSource>() {
@Override
public void subscribe(MaybeEmitter<BufferedSource> emitter) {
String resolvedKey = pathResolver.resolve(key);
boolean exists = fileSystem.exists(resolvedKey);
return Maybe.create(emitter -> {
String resolvedKey = pathResolver.resolve(key);
boolean exists = fileSystem.exists(resolvedKey);
if (exists) {
try {
BufferedSource bufferedSource = fileSystem.read(resolvedKey);
emitter.onSuccess(bufferedSource);
emitter.onComplete();
} catch (FileNotFoundException e) {
emitter.onError(e);
}
} else {
emitter.onError(new FileNotFoundException(ERROR_MESSAGE + resolvedKey));
if (exists) {
try {
BufferedSource bufferedSource = fileSystem.read(resolvedKey);
emitter.onSuccess(bufferedSource);
emitter.onComplete();
} catch (FileNotFoundException e) {
emitter.onError(e);
}
} else {
emitter.onError(new FileNotFoundException(ERROR_MESSAGE + resolvedKey));
}
});
}

View file

@ -3,8 +3,6 @@ package com.nytimes.android.external.fs2;
import com.nytimes.android.external.fs2.filesystem.FileSystem;
import com.nytimes.android.external.store2.base.DiskWrite;
import java.util.concurrent.Callable;
import javax.annotation.Nonnull;
import io.reactivex.Single;
@ -28,14 +26,9 @@ public class FSWriter<T> implements DiskWrite<BufferedSource, T> {
@Nonnull
@Override
public Single<Boolean> write(@Nonnull final T key, @Nonnull final BufferedSource data) {
return Single.fromCallable(new Callable<Boolean>() {
@Nonnull
@Override
@SuppressWarnings("PMD.SignatureDeclareThrowsException")
public Boolean call() throws Exception {
fileSystem.write(pathResolver.resolve(key), data);
return true;
}
return Single.fromCallable(() -> {
fileSystem.write(pathResolver.resolve(key), data);
return true;
});
}
}

View file

@ -15,8 +15,8 @@ dependencies {
buildscript {
tasks.withType(JavaCompile) {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
apply from: rootProject.file("gradle/maven-push.gradle")

View file

@ -8,7 +8,6 @@ import com.nytimes.android.external.store2.base.InternalStore;
import com.nytimes.android.external.store2.base.Persister;
import com.nytimes.android.external.store2.util.KeyParser;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@ -17,15 +16,9 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import io.reactivex.Maybe;
import io.reactivex.MaybeSource;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.annotations.Experimental;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.subjects.BehaviorSubject;
import io.reactivex.subjects.PublishSubject;
@ -133,24 +126,13 @@ final class RealInternalStore<Raw, Parsed, Key> implements InternalStore<Parsed,
*/
private Maybe<Parsed> lazyCache(@Nonnull final Key key) {
return Maybe
.defer(new Callable<MaybeSource<? extends Parsed>>() {
@Override
public MaybeSource<? extends Parsed> call() {
return cache(key);
}
})
.defer(() -> cache(key))
.onErrorResumeNext(Maybe.<Parsed>empty());
}
Maybe<Parsed> cache(@Nonnull final Key key) {
try {
return memCache.get(key, new Callable<Maybe<Parsed>>() {
@Nonnull
@Override
public Maybe<Parsed> call() {
return disk(key);
}
});
return memCache.get(key, () -> disk(key));
} catch (ExecutionException e) {
return Maybe.empty();
}
@ -184,36 +166,22 @@ final class RealInternalStore<Raw, Parsed, Key> implements InternalStore<Parsed,
Maybe<Parsed> readDisk(@Nonnull final Key key) {
return persister().read(key)
.onErrorResumeNext(Maybe.<Raw>empty())
.map(new Function<Raw, Parsed>() {
@Override
public Parsed apply(@NonNull Raw raw) {
return parser.apply(key, raw);
}
})
.doOnSuccess(new Consumer<Parsed>() {
@Override
public void accept(@NonNull Parsed parsed) {
updateMemory(key, parsed);
if (stalePolicy == StalePolicy.REFRESH_ON_STALE
&& StoreUtil.persisterIsStale(key, persister)) {
backfillCache(key);
}
.map(raw -> parser.apply(key, raw))
.doOnSuccess(parsed -> {
updateMemory(key, parsed);
if (stalePolicy == StalePolicy.REFRESH_ON_STALE
&& StoreUtil.persisterIsStale(key, persister)) {
backfillCache(key);
}
}).cache();
}
@SuppressWarnings("CheckReturnValue")
void backfillCache(@Nonnull Key key) {
fetch(key).subscribe(new Consumer<Parsed>() {
@Override
public void accept(@NonNull Parsed parsed) {
// do Nothing we are just backfilling cache
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) {
// do nothing as we are just backfilling cache
}
fetch(key).subscribe(parsed -> {
// do Nothing we are just backfilling cache
}, throwable -> {
// do nothing as we are just backfilling cache
});
}
@ -227,12 +195,7 @@ final class RealInternalStore<Raw, Parsed, Key> implements InternalStore<Parsed,
@Nonnull
@Override
public Single<Parsed> fetch(@Nonnull final Key key) {
return Single.defer(new Callable<SingleSource<? extends Parsed>>() {
@Override
public SingleSource<? extends Parsed> call() {
return fetchAndPersist(key);
}
});
return Single.defer(() -> fetchAndPersist(key));
}
/**
@ -248,13 +211,7 @@ final class RealInternalStore<Raw, Parsed, Key> implements InternalStore<Parsed,
@Nullable
Single<Parsed> fetchAndPersist(@Nonnull final Key key) {
try {
return inFlightRequests.get(key, new Callable<Single<Parsed>>() {
@Nonnull
@Override
public Single<Parsed> call() {
return response(key);
}
});
return inFlightRequests.get(key, () -> response(key));
} catch (ExecutionException e) {
return Single.error(e);
}
@ -264,41 +221,19 @@ final class RealInternalStore<Raw, Parsed, Key> implements InternalStore<Parsed,
Single<Parsed> response(@Nonnull final Key key) {
return fetcher()
.fetch(key)
.flatMap(new Function<Raw, SingleSource<Parsed>>() {
@Override
public SingleSource<Parsed> apply(@NonNull Raw raw) {
return persister().write(key, raw)
.flatMap(new Function<Boolean, SingleSource<Parsed>>() {
@Override
public SingleSource<Parsed> apply(@NonNull Boolean aBoolean) {
return readDisk(key).toSingle();
}
});
}
})
.onErrorResumeNext(new Function<Throwable, SingleSource<? extends Parsed>>() {
@Override
public SingleSource<? extends Parsed> apply(@NonNull Throwable throwable) {
if (stalePolicy == StalePolicy.NETWORK_BEFORE_STALE) {
return readDisk(key)
.switchIfEmpty(Maybe.<Parsed>error(throwable))
.toSingle();
}
return Single.error(throwable);
}
})
.doOnSuccess(new Consumer<Parsed>() {
@Override
public void accept(@NonNull Parsed parsed) {
notifySubscribers(parsed);
}
})
.doAfterTerminate(new Action() {
@Override
public void run() {
inFlightRequests.invalidate(key);
.flatMap(raw -> persister()
.write(key, raw)
.flatMap(aBoolean -> readDisk(key).toSingle()))
.onErrorResumeNext(throwable -> {
if (stalePolicy == StalePolicy.NETWORK_BEFORE_STALE) {
return readDisk(key)
.switchIfEmpty(Maybe.<Parsed>error(throwable))
.toSingle();
}
return Single.error(throwable);
})
.doOnSuccess(this::notifySubscribers)
.doAfterTerminate(() -> inFlightRequests.invalidate(key))
.cache();
}

View file

@ -5,8 +5,6 @@ import javax.annotation.Nonnull;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Function;
import static com.nytimes.android.external.cache.Preconditions.checkNotNull;
@ -29,16 +27,6 @@ final class RepeatWhenEmits<T> implements ObservableTransformer<T, T> {
@Override
public ObservableSource<T> apply(Observable<T> upstream) {
return upstream.repeatWhen(new Function<Observable<Object>, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Observable<Object> objectObservable) {
return objectObservable.switchMap(new Function<Object, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Object o) {
return source;
}
});
}
});
return upstream.repeatWhen(events -> events.switchMap(aVoid -> source));
}
}

View file

@ -8,11 +8,8 @@ import javax.annotation.Nonnull;
import io.reactivex.Observable;
import io.reactivex.ObservableTransformer;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Predicate;
import io.reactivex.subjects.PublishSubject;
import static com.nytimes.android.external.store2.base.RecordState.STALE;
final class StoreUtil {
@ -22,12 +19,7 @@ final class StoreUtil {
@Nonnull
static <Parsed, Key> ObservableTransformer<Parsed, Parsed>
repeatWhenCacheEvicted(PublishSubject<Key> refreshSubject, @Nonnull final Key keyForRepeat) {
Observable<Key> filter = refreshSubject.filter(new Predicate<Key>() {
@Override
public boolean test(@NonNull Key key) throws Exception {
return key.equals(keyForRepeat);
}
});
Observable<Key> filter = refreshSubject.filter(key -> key.equals(keyForRepeat));
return RepeatWhenEmits.from(filter);
}

View file

@ -1,6 +1,5 @@
package com.nytimes.android.external.store2;
import com.nytimes.android.external.store2.base.Fetcher;
import com.nytimes.android.external.store2.base.impl.BarCode;
import com.nytimes.android.external.store2.base.impl.Store;
import com.nytimes.android.external.store2.base.impl.StoreBuilder;
@ -8,10 +7,6 @@ import com.nytimes.android.external.store2.base.impl.StoreBuilder;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.Callable;
import javax.annotation.Nonnull;
import io.reactivex.Single;
import static org.assertj.core.api.Assertions.assertThat;
@ -25,18 +20,7 @@ public class ClearStoreMemoryTest {
public void setUp() {
networkCalls = 0;
store = StoreBuilder.<Integer>barcode()
.fetcher(new Fetcher<Integer, BarCode>() {
@Nonnull
@Override
public Single<Integer> fetch(@Nonnull BarCode barCode) {
return Single.fromCallable(new Callable<Integer>() {
@Override
public Integer call() {
return networkCalls++;
}
});
}
})
.fetcher(barCode -> Single.fromCallable(() -> networkCalls++))
.open();
}

View file

@ -1,6 +1,5 @@
package com.nytimes.android.external.store2;
import com.nytimes.android.external.store2.base.Fetcher;
import com.nytimes.android.external.store2.base.impl.BarCode;
import com.nytimes.android.external.store2.base.impl.Store;
import com.nytimes.android.external.store2.base.impl.StoreBuilder;
@ -11,12 +10,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull;
import io.reactivex.Maybe;
import io.reactivex.Single;
@ -36,18 +31,7 @@ public class ClearStoreTest {
public void setUp() {
networkCalls = new AtomicInteger(0);
store = StoreBuilder.<Integer>barcode()
.fetcher(new Fetcher<Integer, BarCode>() {
@Nonnull
@Override
public Single<Integer> fetch(@Nonnull BarCode barCode) {
return Single.fromCallable(new Callable<Integer>() {
@Override
public Integer call() {
return networkCalls.incrementAndGet();
}
});
}
})
.fetcher(barCode -> Single.fromCallable(() -> networkCalls.incrementAndGet()))
.persister(persister)
.open();
}

View file

@ -1,6 +1,5 @@
package com.nytimes.android.external.store2;
import com.nytimes.android.external.store2.base.Fetcher;
import com.nytimes.android.external.store2.base.impl.BarCode;
import com.nytimes.android.external.store2.base.impl.Store;
import com.nytimes.android.external.store2.base.impl.StoreBuilder;
@ -8,10 +7,6 @@ import com.nytimes.android.external.store2.base.impl.StoreBuilder;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.Callable;
import javax.annotation.Nonnull;
import io.reactivex.Single;
@ -23,22 +18,13 @@ public class DontCacheErrorsTest {
@Before
public void setUp() {
store = StoreBuilder.<Integer>barcode()
.fetcher(new Fetcher<Integer, BarCode>() {
@Nonnull
@Override
public Single<Integer> fetch(@Nonnull BarCode barCode) {
return Single.fromCallable(new Callable<Integer>() {
@Override
public Integer call() {
if (shouldThrow) {
throw new RuntimeException();
} else {
return 0;
}
}
});
.fetcher(barCode -> Single.fromCallable(() -> {
if (shouldThrow) {
throw new RuntimeException();
} else {
return 0;
}
})
}))
.open();
}

View file

@ -1,7 +1,6 @@
package com.nytimes.android.external.store2;
import com.nytimes.android.external.store2.base.Clearable;
import com.nytimes.android.external.store2.base.Fetcher;
import com.nytimes.android.external.store2.base.Persister;
import com.nytimes.android.external.store2.base.impl.BarCode;
import com.nytimes.android.external.store2.base.impl.Store;
@ -13,7 +12,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull;
@ -36,18 +34,7 @@ public class GetRefreshingTest {
public void setUp() {
networkCalls = new AtomicInteger(0);
store = StoreBuilder.<Integer>barcode()
.fetcher(new Fetcher<Integer, BarCode>() {
@Nonnull
@Override
public Single<Integer> fetch(@Nonnull BarCode barCode) {
return Single.fromCallable(new Callable<Integer>() {
@Override
public Integer call() {
return networkCalls.incrementAndGet();
}
});
}
})
.fetcher(barCode -> Single.fromCallable(() -> networkCalls.incrementAndGet()))
.persister(persister)
.open();
}

View file

@ -1,19 +1,14 @@
package com.nytimes.android.external.store2;
import com.nytimes.android.external.store2.base.Fetcher;
import com.nytimes.android.external.store2.base.impl.Store;
import com.nytimes.android.external.store2.base.impl.StoreBuilder;
import com.nytimes.android.external.store2.util.KeyParser;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import javax.annotation.Nonnull;
import io.reactivex.Single;
import io.reactivex.annotations.NonNull;
import io.reactivex.observers.TestObserver;
@ -27,19 +22,9 @@ public class KeyParserTest {
@Before
public void setUp() throws Exception {
store = StoreBuilder.<Integer, String, String>parsedWithKey()
.parser(new KeyParser<Integer, String, String>() {
@Override
public String apply(@NonNull Integer integer, @NonNull String s) {
return s + integer;
}
})
.fetcher(new Fetcher<String, Integer>() {
@Nonnull
@Override
public Single<String> fetch(@Nonnull Integer integer) {
return Single.just(NETWORK);
}
}).open();
.parser((integer, s) -> s + integer)
.fetcher(integer -> Single.just(NETWORK))
.open();
}

View file

@ -1,6 +1,5 @@
package com.nytimes.android.external.store2;
import com.nytimes.android.external.store2.base.Fetcher;
import com.nytimes.android.external.store2.base.impl.BarCode;
import com.nytimes.android.external.store2.base.impl.Store;
import com.nytimes.android.external.store2.base.impl.StoreBuilder;
@ -8,10 +7,6 @@ import com.nytimes.android.external.store2.base.impl.StoreBuilder;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.Callable;
import javax.annotation.Nonnull;
import io.reactivex.Single;
import static org.assertj.core.api.Assertions.assertThat;
@ -25,18 +20,7 @@ public class SequentialTest {
public void setUp() {
networkCalls = 0;
store = StoreBuilder.<Integer>barcode()
.fetcher(new Fetcher<Integer, BarCode>() {
@Nonnull
@Override
public Single<Integer> fetch(@Nonnull BarCode barCode) {
return Single.fromCallable(new Callable<Integer>() {
@Override
public Integer call() {
return networkCalls++;
}
});
}
})
.fetcher(barcode -> Single.fromCallable(() -> networkCalls++))
.open();
}

View file

@ -2,7 +2,6 @@ package com.nytimes.android.external.store2;
import com.nytimes.android.external.store2.base.Fetcher;
import com.nytimes.android.external.store2.base.Parser;
import com.nytimes.android.external.store2.base.Persister;
import com.nytimes.android.external.store2.base.impl.BarCode;
import com.nytimes.android.external.store2.base.impl.Store;
@ -16,7 +15,6 @@ import javax.annotation.Nonnull;
import io.reactivex.Maybe;
import io.reactivex.Single;
import io.reactivex.annotations.NonNull;
import static org.assertj.core.api.Assertions.assertThat;
@ -28,13 +26,7 @@ public class StoreBuilderTest {
public void testBuildersBuildWithCorrectTypes() {
//test is checking whether types are correct in builders
Store<Date, Integer> store = StoreBuilder.<Integer, String, Date>parsedWithKey()
.fetcher(new Fetcher<String, Integer>() {
@Nonnull
@Override
public Single<String> fetch(@Nonnull Integer key) {
return Single.just(String.valueOf(key));
}
})
.fetcher(key -> Single.just(String.valueOf(key)))
.persister(new Persister<String, Integer>() {
@Nonnull
@Override
@ -48,12 +40,7 @@ public class StoreBuilderTest {
return Single.just(true);
}
})
.parser(new Parser<String, Date>() {
@Override
public Date apply(@NonNull String s) {
return DATE;
}
})
.parser(s -> DATE)
.open();

View file

@ -20,10 +20,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import io.reactivex.Maybe;
import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.BiFunction;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.spy;
@ -85,14 +81,11 @@ public class StoreTest {
.open();
Single<String> networkSingle =
Single.create(new SingleOnSubscribe<String>() {
@Override
public void subscribe(SingleEmitter<String> emitter) {
if (counter.incrementAndGet() == 1) {
emitter.onSuccess(NETWORK);
} else {
emitter.onError(new RuntimeException("Yo Dawg your inflight is broken"));
}
Single.create(emitter -> {
if (counter.incrementAndGet() == 1) {
emitter.onSuccess(NETWORK);
} else {
emitter.onError(new RuntimeException("Yo Dawg your inflight is broken"));
}
});
@ -108,13 +101,8 @@ public class StoreTest {
.thenReturn(Single.just(true));
String response = simpleStore.get(barCode).zipWith(simpleStore.get(barCode),
new BiFunction<String, String, String>() {
@Override
public String apply(@NonNull String s, @NonNull String s2) {
return "hello";
}
})
String response = simpleStore.get(barCode)
.zipWith(simpleStore.get(barCode), (s, s2) -> "hello")
.blockingGet();
assertThat(response).isEqualTo("hello");
verify(fetcher, times(1)).fetch(barCode);

View file

@ -17,26 +17,11 @@ import static org.junit.Assert.assertNotNull;
public class MultiParserTest {
private static final Parser<Integer, String> PARSER_1 = new Parser<Integer, String>() {
@Override
public String apply(Integer value) {
return String.valueOf(value);
}
};
private static final Parser<Integer, String> PARSER_1 = String::valueOf;
private static final Parser<String, BarCode> PARSER_2 = new Parser<String, BarCode>() {
@Override
public BarCode apply(String value) {
return new BarCode(value, "KEY");
}
};
private static final Parser<String, BarCode> PARSER_2 = value -> new BarCode(value, "KEY");
private static final Parser<BarCode, UUID> PARSER_3 = new Parser<BarCode, UUID>() {
@Override
public UUID apply(BarCode barCode) {
return UUID.randomUUID();
}
};
private static final Parser<BarCode, UUID> PARSER_3 = barCode -> UUID.randomUUID();
@Rule
public ExpectedException expectedException = ExpectedException.none();