Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
32 changes: 17 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ Let's start by looking at what a fully configured Store looks like. We will then
```kotlin
StoreBuilder
.from(
fetcher = nonFlowValueFetcher { api.fetchSubreddit(it, "10").data.children.map(::toPosts) },
sourceOfTruth = SourceOfTrue.from(
reader = db.postDao()::loadPosts,
fetcher = Fetcher.of { api.fetchSubreddit(it, "10").data.children.map(::toPosts) },
sourceOfTruth = SourceOfTrue.of(
flowReader = db.postDao()::loadPosts,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still not sure how discoverable it is to use parameter names do distinguish between the 2 functions. what happens if you call it without parameter names?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

without names this does work:

sourceOfTruth = SourceOfTruth.of(
                    db.postDao()::loadPosts,
                    db.postDao()::insertPosts,
                    db.postDao()::clearFeedBySubredditName,
                    db.postDao()::clearAllFeeds
                )

While this doesn't:

sourceOfTruth = SourceOfTruth.of(
                    {
                        db.postDao().loadPosts(it)
                    },
                    db.postDao()::insertPosts,
                    db.postDao()::clearFeedBySubredditName,
                    db.postDao()::clearAllFeeds
                )

re-naming: part of the challange is that this receives 4 methods, only 1 parameter's type is different so ofFlow wouldn't look very good imo either :/.

writer = db.postDao()::insertPosts,
delete = db.postDao()::clearFeed,
deleteAll = db.postDao()::clearAllFeeds
Expand All @@ -81,7 +81,7 @@ You create a Store using a builder. The only requirement is to include a `Fetche

```kotlin
val store = StoreBuilder
.from(valueFetcher { articleId -> api.getArticle(articleId) }) // api returns Flow<Article>
.from(Fetcher.ofFlow { articleId -> api.getArticle(articleId) }) // api returns Flow<Article>
.build()
```

Expand Down Expand Up @@ -186,9 +186,9 @@ allows you to create offline first applications that can be used without an acti
```kotlin
StoreBuilder
.from(
fetcher = nonFlowValueFetcher { api.fetchSubreddit(it, "10").data.children.map(::toPosts) },
sourceOfTruth = SourceOfTrue.from(
reader = db.postDao()::loadPosts,
fetcher = Fetcher.of { api.fetchSubreddit(it, "10").data.children.map(::toPosts) },
sourceOfTruth = SourceOfTrue.of(
flowReader = db.postDao()::loadPosts,
writer = db.postDao()::insertPosts,
delete = db.postDao()::clearFeed,
deleteAll = db.postDao()::clearAllFeeds
Expand All @@ -215,9 +215,9 @@ You can configure in-memory cache with the `MemoryPolicy`:
```kotlin
StoreBuilder
.from(
fetcher = nonFlowValueFetcher { api.fetchSubreddit(it, "10").data.children.map(::toPosts) },
sourceOfTruth = SourceOfTrue.from(
reader = db.postDao()::loadPosts,
fetcher = Fetcher.of { api.fetchSubreddit(it, "10").data.children.map(::toPosts) },
sourceOfTruth = SourceOfTrue.of(
flowReader = db.postDao()::loadPosts,
writer = db.postDao()::insertPosts,
delete = db.postDao()::clearFeed,
deleteAll = db.postDao()::clearAllFeeds
Expand All @@ -244,8 +244,10 @@ You can delete a specific entry by key from a store, or clear all entries in a s

```kotlin
val store = StoreBuilder
.from(nonFlowValueFetcher { api.fetchData(key) })
.build()
.from(
fetcher = Fetcher.of { key: String ->
api.fetchData(key)
}).build()
```

The following will clear the entry associated with the key from the in-memory cache:
Expand All @@ -267,9 +269,9 @@ When store has a sourceOfTruth, you'll need to provide the `delete` and `deleteA
```kotlin
StoreBuilder
.from(
fetcher = nonFlowValueFetcher { api.fetchData(key) },
sourceOfTruth = SourceOfTrue.from(
reader = dao::loadData,
fetcher = Fetcher.of { api.fetchData(key) },
sourceOfTruth = SourceOfTrue.of(
flowReader = dao::loadData,
writer = dao::writeData,
delete = dao::clearDataByKey,
deleteAll = dao::clearAllData
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/com/dropbox/android/sample/Graph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import com.dropbox.android.external.fs3.FileSystemPersister
import com.dropbox.android.external.fs3.PathResolver
import com.dropbox.android.external.fs3.SourcePersisterFactory
import com.dropbox.android.external.fs3.filesystem.FileSystemFactory
import com.dropbox.android.external.store4.Fetcher
import com.dropbox.android.external.store4.StoreBuilder
import com.dropbox.android.external.store4.MemoryPolicy
import com.dropbox.android.external.store4.Persister
import com.dropbox.android.external.store4.Store
import com.dropbox.android.external.store4.SourceOfTruth
import com.dropbox.android.external.store4.legacy.BarCode
import com.dropbox.android.external.store4.nonFlowValueFetcher
import com.squareup.moshi.Moshi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand Down Expand Up @@ -46,11 +46,11 @@ object Graph {
val db = provideRoom(context)
return StoreBuilder
.from(
nonFlowValueFetcher { key: String ->
Fetcher.of { key: String ->
provideRetrofit().fetchSubreddit(key, 10).data.children.map(::toPosts)
},
sourceOfTruth = SourceOfTruth.from(
reader = db.postDao()::loadPosts,
sourceOfTruth = SourceOfTruth.of(
flowReader = db.postDao()::loadPosts,
writer = db.postDao()::insertPosts,
delete = db.postDao()::clearFeedBySubredditName,
deleteAll = db.postDao()::clearAllFeeds
Expand All @@ -63,12 +63,12 @@ object Graph {
val db = provideRoom(context)
return StoreBuilder
.from<Pair<String, RedditConfig>, List<Post>, List<Post>>(
nonFlowValueFetcher { (query, config) ->
Fetcher.of { (query, config) ->
provideRetrofit().fetchSubreddit(query, config.limit)
.data.children.map(::toPosts)
},
sourceOfTruth = SourceOfTruth.from(
reader = { (query, _) -> db.postDao().loadPosts(query) },
sourceOfTruth = SourceOfTruth.of(
flowReader = { (query, _) -> db.postDao().loadPosts(query) },
writer = { (query, _), posts -> db.postDao().insertPosts(query, posts) },
delete = { (query, _) -> db.postDao().clearFeedBySubredditName(query) },
deleteAll = db.postDao()::clearAllFeeds
Expand Down Expand Up @@ -99,11 +99,11 @@ object Graph {
val adapter = moshi.adapter<RedditConfig>(RedditConfig::class.java)
return StoreBuilder
.from<Unit, RedditConfig, RedditConfig>(
nonFlowValueFetcher {
Fetcher.of {
delay(500)
RedditConfig(10)
},
sourceOfTruth = SourceOfTruth.fromNonFlow(
sourceOfTruth = SourceOfTruth.of(
reader = {
runCatching {
val source = fileSystemPersister.read(Unit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.dropbox.android.external.store4.Fetcher
import com.dropbox.android.external.store4.MemoryPolicy
import com.dropbox.android.external.store4.StoreBuilder
import com.dropbox.android.external.store4.StoreRequest
import com.dropbox.android.external.store4.fresh
import com.dropbox.android.external.store4.get
import com.dropbox.android.external.store4.nonFlowValueFetcher
import com.dropbox.android.sample.R
import kotlinx.android.synthetic.main.fragment_stream.*
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -51,7 +51,7 @@ class StreamFragment : Fragment(), CoroutineScope {
var counter = 0

val store = StoreBuilder
.from(nonFlowValueFetcher { key: Int ->
.from(Fetcher.of { key: Int ->
(key * 1000 + counter++).also { delay(1_000) }
})
.cachePolicy(
Expand Down
16 changes: 8 additions & 8 deletions store-rx2/api/store-rx2.api
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
public final class com/dropbox/store/rx2/RxFetcherKt {
public static final fun flowableFetcher (Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1;
public static final fun flowableValueFetcher (Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1;
public static final fun singleFetcher (Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1;
public static final fun singleValueFetcher (Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1;
public static final fun ofFlowable (Lcom/dropbox/android/external/store4/Fetcher$Companion;Lkotlin/jvm/functions/Function1;)Lcom/dropbox/android/external/store4/Fetcher;
public static final fun ofResultFlowable (Lcom/dropbox/android/external/store4/Fetcher$Companion;Lkotlin/jvm/functions/Function1;)Lcom/dropbox/android/external/store4/Fetcher;
public static final fun ofResultSingle (Lcom/dropbox/android/external/store4/Fetcher$Companion;Lkotlin/jvm/functions/Function1;)Lcom/dropbox/android/external/store4/Fetcher;
public static final fun ofSingle (Lcom/dropbox/android/external/store4/Fetcher$Companion;Lkotlin/jvm/functions/Function1;)Lcom/dropbox/android/external/store4/Fetcher;
}

public final class com/dropbox/store/rx2/RxSourceOfTruthKt {
public static final fun fromFlowable (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Lcom/dropbox/android/external/store4/SourceOfTruth;
public static synthetic fun fromFlowable$default (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/dropbox/android/external/store4/SourceOfTruth;
public static final fun fromMaybe (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Lcom/dropbox/android/external/store4/SourceOfTruth;
public static synthetic fun fromMaybe$default (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/dropbox/android/external/store4/SourceOfTruth;
public static final fun ofFlowable (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Lcom/dropbox/android/external/store4/SourceOfTruth;
public static synthetic fun ofFlowable$default (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/dropbox/android/external/store4/SourceOfTruth;
public static final fun ofMaybe (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Lcom/dropbox/android/external/store4/SourceOfTruth;
public static synthetic fun ofMaybe$default (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/dropbox/android/external/store4/SourceOfTruth;
}

public final class com/dropbox/store/rx2/RxStoreBuilderKt {
Expand Down
17 changes: 8 additions & 9 deletions store-rx2/src/main/kotlin/com/dropbox/store/rx2/RxFetcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.dropbox.store.rx2
import com.dropbox.android.external.store4.Fetcher
import com.dropbox.android.external.store4.FetcherResult
import com.dropbox.android.external.store4.Store
import com.dropbox.android.external.store4.valueFetcher
import io.reactivex.Flowable
import io.reactivex.Single
import kotlinx.coroutines.reactive.asFlow
Expand All @@ -19,9 +18,9 @@ import kotlinx.coroutines.reactive.asFlow
*
* @param flowableFactory a factory for a [Flowable] source of network records.
*/
fun <Key : Any, Output : Any> flowableFetcher(
fun <Key : Any, Output : Any> Fetcher.Companion.ofResultFlowable(
flowableFactory: (key: Key) -> Flowable<FetcherResult<Output>>
): Fetcher<Key, Output> = { key: Key -> flowableFactory(key).asFlow() }
): Fetcher<Key, Output> = Fetcher.ofResultFlow { key: Key -> flowableFactory(key).asFlow() }

/**
* "Creates" a [Fetcher] from a [singleFactory].
Expand All @@ -34,9 +33,9 @@ fun <Key : Any, Output : Any> flowableFetcher(
*
* @param singleFactory a factory for a [Single] source of network records.
*/
fun <Key : Any, Output : Any> singleFetcher(
fun <Key : Any, Output : Any> Fetcher.Companion.ofResultSingle(
singleFactory: (key: Key) -> Single<FetcherResult<Output>>
): Fetcher<Key, Output> = { key: Key -> singleFactory(key).toFlowable().asFlow() }
): Fetcher<Key, Output> = Fetcher.ofResultFlow { key: Key -> singleFactory(key).toFlowable().asFlow() }

/**
* "Creates" a [Fetcher] from a [flowableFactory] and translate the results to a [FetcherResult].
Expand All @@ -50,9 +49,9 @@ fun <Key : Any, Output : Any> singleFetcher(
*
* @param flowFactory a factory for a [Flowable] source of network records.
*/
fun <Key : Any, Output : Any> flowableValueFetcher(
fun <Key : Any, Output : Any> Fetcher.Companion.ofFlowable(
flowableFactory: (key: Key) -> Flowable<Output>
): Fetcher<Key, Output> = valueFetcher { key: Key -> flowableFactory(key).asFlow() }
): Fetcher<Key, Output> = Fetcher.ofFlow { key: Key -> flowableFactory(key).asFlow() }

/**
* Creates a new [Fetcher] from a [singleFactory] and translate the results to a [FetcherResult].
Expand All @@ -66,6 +65,6 @@ fun <Key : Any, Output : Any> flowableValueFetcher(
*
* @param singleFactory a factory for a [Single] source of network records.
*/
fun <Key : Any, Output : Any> singleValueFetcher(
fun <Key : Any, Output : Any> Fetcher.Companion.ofSingle(
singleFactory: (key: Key) -> Single<Output>
): Fetcher<Key, Output> = flowableValueFetcher { key: Key -> singleFactory(key).toFlowable() }
): Fetcher<Key, Output> = ofFlowable { key: Key -> singleFactory(key).toFlowable() }
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import kotlinx.coroutines.rx2.await
* @param deleteAll function for deleting all records in the source of truth
*
*/
fun <Key : Any, Input : Any, Output : Any> SourceOfTruth.Companion.fromMaybe(
fun <Key : Any, Input : Any, Output : Any> SourceOfTruth.Companion.ofMaybe(
reader: (Key) -> Maybe<Output>,
writer: (Key, Input) -> Completable,
delete: ((Key) -> Completable)? = null,
Expand All @@ -26,7 +26,7 @@ fun <Key : Any, Input : Any, Output : Any> SourceOfTruth.Companion.fromMaybe(
val deleteFun: (suspend (Key) -> Unit)? =
if (delete != null) { key -> delete(key).await() } else null
val deleteAllFun: (suspend () -> Unit)? = deleteAll?.let { { deleteAll().await() } }
return fromNonFlow(
return of(
reader = { key -> reader.invoke(key).await() },
writer = { key, output -> writer.invoke(key, output).await() },
delete = deleteFun,
Expand All @@ -44,7 +44,7 @@ fun <Key : Any, Input : Any, Output : Any> SourceOfTruth.Companion.fromMaybe(
* @param deleteAll function for deleting all records in the source of truth
*
*/
fun <Key : Any, Input : Any, Output : Any> SourceOfTruth.Companion.fromFlowable(
fun <Key : Any, Input : Any, Output : Any> SourceOfTruth.Companion.ofFlowable(
reader: (Key) -> Flowable<Output>,
writer: (Key, Input) -> Completable,
delete: ((Key) -> Completable)? = null,
Expand All @@ -53,8 +53,8 @@ fun <Key : Any, Input : Any, Output : Any> SourceOfTruth.Companion.fromFlowable(
val deleteFun: (suspend (Key) -> Unit)? =
if (delete != null) { key -> delete(key).await() } else null
val deleteAllFun: (suspend () -> Unit)? = deleteAll?.let { { deleteAll().await() } }
return from(
reader = { key -> reader.invoke(key).asFlow() },
return of(
flowReader = { key -> reader.invoke(key).asFlow() },
writer = { key, output -> writer.invoke(key, output).await() },
delete = deleteFun,
deleteAll = deleteAllFun
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.dropbox.android.external.store4.ResponseOrigin
import com.dropbox.android.external.store4.StoreBuilder
import com.dropbox.android.external.store4.StoreRequest
import com.dropbox.android.external.store4.StoreResponse
import com.dropbox.store.rx2.singleFetcher
import com.dropbox.store.rx2.ofResultSingle
import com.google.common.truth.Truth.assertThat
import io.reactivex.Single
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand All @@ -30,7 +30,7 @@ class HotRxSingleStoreTest {
3 to FetcherResult.Data("three-1"),
3 to FetcherResult.Data("three-2")
)
val pipeline = StoreBuilder.from(singleFetcher<Int, String> { fetcher.fetch(it) })
val pipeline = StoreBuilder.from(Fetcher.ofResultSingle<Int, String> { fetcher.fetch(it) })
.scope(testScope)
.build()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.dropbox.store.rx2.test

import com.dropbox.android.external.store4.Fetcher
import com.dropbox.android.external.store4.FetcherResult
import com.dropbox.android.external.store4.ResponseOrigin
import com.dropbox.android.external.store4.SourceOfTruth
import com.dropbox.android.external.store4.StoreBuilder
import com.dropbox.android.external.store4.StoreRequest
import com.dropbox.android.external.store4.StoreResponse
import com.dropbox.store.rx2.flowableFetcher
import com.dropbox.store.rx2.fromFlowable
import com.dropbox.store.rx2.ofResultFlowable
import com.dropbox.store.rx2.ofFlowable
import com.dropbox.store.rx2.observe
import io.reactivex.BackpressureStrategy
import io.reactivex.Completable
Expand All @@ -30,7 +31,7 @@ class RxFlowableStoreTest {
private val fakeDisk = mutableMapOf<Int, String>()
private val store =
StoreBuilder.from<Int, String, String>(
flowableFetcher {
Fetcher.ofResultFlowable {
Flowable.create({ emitter ->
emitter.onNext(
FetcherResult.Data("$it ${atomicInteger.incrementAndGet()} occurrence")
Expand All @@ -41,7 +42,7 @@ class RxFlowableStoreTest {
emitter.onComplete()
}, BackpressureStrategy.LATEST)
},
sourceOfTruth = SourceOfTruth.fromFlowable(
sourceOfTruth = SourceOfTruth.ofFlowable(
reader = {
if (fakeDisk[it] != null)
Flowable.fromCallable { fakeDisk[it]!! }
Expand All @@ -64,12 +65,12 @@ class RxFlowableStoreTest {
testSubscriber
.awaitCount(3)
.assertValues(
StoreResponse.Loading<String>(ResponseOrigin.Fetcher),
StoreResponse.Loading(ResponseOrigin.Fetcher),
StoreResponse.Data("3 1 occurrence", ResponseOrigin.Fetcher),
StoreResponse.Data("3 2 occurrence", ResponseOrigin.Fetcher)
)

testSubscriber = TestSubscriber<StoreResponse<String>>()
testSubscriber = TestSubscriber()
store.observe(StoreRequest.cached(3, false))
.subscribeOn(testScheduler)
.subscribe(testSubscriber)
Expand All @@ -81,20 +82,20 @@ class RxFlowableStoreTest {
StoreResponse.Data("3 2 occurrence", ResponseOrigin.SourceOfTruth)
)

testSubscriber = TestSubscriber<StoreResponse<String>>()
testSubscriber = TestSubscriber()
store.observe(StoreRequest.fresh(3))
.subscribeOn(testScheduler)
.subscribe(testSubscriber)
testScheduler.triggerActions()
testSubscriber
.awaitCount(3)
.assertValues(
StoreResponse.Loading<String>(ResponseOrigin.Fetcher),
StoreResponse.Loading(ResponseOrigin.Fetcher),
StoreResponse.Data("3 3 occurrence", ResponseOrigin.Fetcher),
StoreResponse.Data("3 4 occurrence", ResponseOrigin.Fetcher)
)

testSubscriber = TestSubscriber<StoreResponse<String>>()
testSubscriber = TestSubscriber()
store.observe(StoreRequest.cached(3, false))
.subscribeOn(testScheduler)
.subscribe(testSubscriber)
Expand Down
Loading