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

Separate MutableStoreBuilder from StoreBuilder #542

Merged
merged 4 commits into from
Apr 13, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.mobilenativefoundation.store.store5

import kotlinx.coroutines.CoroutineScope
import org.mobilenativefoundation.store.store5.impl.mutableStoreBuilderFromFetcherAndSourceOfTruth

interface MutableStoreBuilder<Key : Any, Network : Any, Output : Any, Local : Any> {

fun <Response : Any> build(
updater: Updater<Key, Output, Response>,
bookkeeper: Bookkeeper<Key>? = null
): MutableStore<Key, Output>

/**
* A store multicasts same [Output] value to many consumers (Similar to RxJava.share()), by default
* [Store] will open a global scope for management of shared responses, if instead you'd like to control
* the scope that sharing/multicasting happens in you can pass a @param [scope]
*
* @param scope - scope to use for sharing
*/
fun scope(scope: CoroutineScope): MutableStoreBuilder<Key, Network, Output, Local>

/**
* controls eviction policy for a store cache, use [MemoryPolicy.MemoryPolicyBuilder] to configure a TTL
* or size based eviction
* Example: MemoryPolicy.builder().setExpireAfterWrite(10.seconds).build()
*/
fun cachePolicy(memoryPolicy: MemoryPolicy<Key, Output>?): MutableStoreBuilder<Key, Network, Output, Local>

/**
* by default a Store caches in memory with a default policy of max items = 100
*/
fun disableCache(): MutableStoreBuilder<Key, Network, Output, Local>

fun converter(converter: Converter<Network, Output, Local>):
MutableStoreBuilder<Key, Network, Output, Local>

fun validator(validator: Validator<Output>): MutableStoreBuilder<Key, Network, Output, Local>

companion object {
/**
* Creates a new [MutableStoreBuilder] from a [Fetcher] and a [SourceOfTruth].
*
* @param fetcher a function for fetching a flow of network records.
* @param sourceOfTruth a [SourceOfTruth] for the store.
*/
fun <Key : Any, Network : Any, Output : Any, Local : Any> from(
fetcher: Fetcher<Key, Network>,
sourceOfTruth: SourceOfTruth<Key, Local>
): MutableStoreBuilder<Key, Network, Output, Local> =
mutableStoreBuilderFromFetcherAndSourceOfTruth(fetcher = fetcher, sourceOfTruth = sourceOfTruth)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,10 @@ import org.mobilenativefoundation.store.store5.impl.storeBuilderFromFetcherAndSo
/**
* Main entry point for creating a [Store].
*/
interface StoreBuilder<Key : Any, Network : Any, Output : Any, Local : Any> {
interface StoreBuilder<Key : Any, Output : Any> {
fun build(): Store<Key, Output>

fun <Response : Any> build(
updater: Updater<Key, Output, Response>,
bookkeeper: Bookkeeper<Key>? = null
): MutableStore<Key, Output>
fun <Network : Any, Local : Any> toMutableStoreBuilder(): MutableStoreBuilder<Key, Network, Output, Local>

/**
* A store multicasts same [Output] value to many consumers (Similar to RxJava.share()), by default
Expand All @@ -37,46 +34,41 @@ interface StoreBuilder<Key : Any, Network : Any, Output : Any, Local : Any> {
*
* @param scope - scope to use for sharing
*/
fun scope(scope: CoroutineScope): StoreBuilder<Key, Network, Output, Local>
fun scope(scope: CoroutineScope): StoreBuilder<Key, Output>

/**
* controls eviction policy for a store cache, use [MemoryPolicy.MemoryPolicyBuilder] to configure a TTL
* or size based eviction
* Example: MemoryPolicy.builder().setExpireAfterWrite(10.seconds).build()
*/
fun cachePolicy(memoryPolicy: MemoryPolicy<Key, Output>?): StoreBuilder<Key, Network, Output, Local>
fun cachePolicy(memoryPolicy: MemoryPolicy<Key, Output>?): StoreBuilder<Key, Output>

/**
* by default a Store caches in memory with a default policy of max items = 100
*/
fun disableCache(): StoreBuilder<Key, Network, Output, Local>
fun disableCache(): StoreBuilder<Key, Output>

fun converter(converter: Converter<Network, Output, Local>):
StoreBuilder<Key, Network, Output, Local>

fun validator(validator: Validator<Output>): StoreBuilder<Key, Network, Output, Local>
fun validator(validator: Validator<Output>): StoreBuilder<Key, Output>

companion object {

/**
* Creates a new [StoreBuilder] from a [Fetcher].
*
* @param fetcher a [Fetcher] flow of network records.
*/
fun <Key : Any, Network : Any, Output : Any> from(
fetcher: Fetcher<Key, Network>,
): StoreBuilder<Key, Network, Output, *> = storeBuilderFromFetcher(fetcher = fetcher)
fun <Key : Any, Input : Any, Output : Any> from(
fetcher: Fetcher<Key, Input>,
): StoreBuilder<Key, Output> = storeBuilderFromFetcher(fetcher = fetcher)

/**
* Creates a new [StoreBuilder] from a [Fetcher] and a [SourceOfTruth].
*
* @param fetcher a function for fetching a flow of network records.
* @param sourceOfTruth a [SourceOfTruth] for the store.
*/
fun <Key : Any, Network : Any, Output : Any, Local : Any> from(
fetcher: Fetcher<Key, Network>,
sourceOfTruth: SourceOfTruth<Key, Local>
): StoreBuilder<Key, Network, Output, Local> =
storeBuilderFromFetcherAndSourceOfTruth(fetcher = fetcher, sourceOfTruth = sourceOfTruth)
fun <Key : Any, Input : Any, Output : Any> from(
fetcher: Fetcher<Key, Input>,
sourceOfTruth: SourceOfTruth<Key, Input>
): StoreBuilder<Key, Output> = storeBuilderFromFetcherAndSourceOfTruth(fetcher = fetcher, sourceOfTruth = sourceOfTruth)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.mobilenativefoundation.store.store5.impl

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import org.mobilenativefoundation.store.store5.Bookkeeper
import org.mobilenativefoundation.store.store5.Converter
import org.mobilenativefoundation.store.store5.Fetcher
import org.mobilenativefoundation.store.store5.MemoryPolicy
import org.mobilenativefoundation.store.store5.MutableStore
import org.mobilenativefoundation.store.store5.MutableStoreBuilder
import org.mobilenativefoundation.store.store5.SourceOfTruth
import org.mobilenativefoundation.store.store5.Store
import org.mobilenativefoundation.store.store5.StoreDefaults
import org.mobilenativefoundation.store.store5.Updater
import org.mobilenativefoundation.store.store5.Validator
import org.mobilenativefoundation.store.store5.impl.extensions.asMutableStore

fun <Key : Any, Network : Any, Output : Any, Local : Any> mutableStoreBuilderFromFetcher(
fetcher: Fetcher<Key, Network>,
): MutableStoreBuilder<Key, Network, Output, Local> = RealMutableStoreBuilder(fetcher)

fun <Key : Any, Network : Any, Output : Any, Local : Any> mutableStoreBuilderFromFetcherAndSourceOfTruth(
fetcher: Fetcher<Key, Network>,
sourceOfTruth: SourceOfTruth<Key, Local>,
): MutableStoreBuilder<Key, Network, Output, Local> = RealMutableStoreBuilder(fetcher, sourceOfTruth)

internal class RealMutableStoreBuilder<Key : Any, Network : Any, Output : Any, Local : Any>(
private val fetcher: Fetcher<Key, Network>,
private val sourceOfTruth: SourceOfTruth<Key, Local>? = null,
) : MutableStoreBuilder<Key, Network, Output, Local> {
private var scope: CoroutineScope? = null
private var cachePolicy: MemoryPolicy<Key, Output>? = StoreDefaults.memoryPolicy
private var converter: Converter<Network, Output, Local>? = null
private var validator: Validator<Output>? = null

override fun scope(scope: CoroutineScope): MutableStoreBuilder<Key, Network, Output, Local> {
this.scope = scope
return this
}

override fun cachePolicy(memoryPolicy: MemoryPolicy<Key, Output>?): MutableStoreBuilder<Key, Network, Output, Local> {
cachePolicy = memoryPolicy
return this
}

override fun disableCache(): MutableStoreBuilder<Key, Network, Output, Local> {
cachePolicy = null
return this
}

override fun validator(validator: Validator<Output>): MutableStoreBuilder<Key, Network, Output, Local> {
this.validator = validator
return this
}

override fun converter(converter: Converter<Network, Output, Local>): MutableStoreBuilder<Key, Network, Output, Local> {
this.converter = converter
return this
}

fun build(): Store<Key, Output> = RealStore(
scope = scope ?: GlobalScope,
sourceOfTruth = sourceOfTruth,
fetcher = fetcher,
memoryPolicy = cachePolicy,
converter = converter,
validator = validator
)

override fun <UpdaterResult : Any> build(
updater: Updater<Key, Output, UpdaterResult>,
bookkeeper: Bookkeeper<Key>?
): MutableStore<Key, Output> =
build().asMutableStore<Key, Network, Output, Local, UpdaterResult>(
updater = updater,
bookkeeper = bookkeeper
)
}
Original file line number Diff line number Diff line change
@@ -1,64 +1,58 @@
@file:Suppress("UNCHECKED_CAST")

package org.mobilenativefoundation.store.store5.impl

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import org.mobilenativefoundation.store.store5.Bookkeeper
import org.mobilenativefoundation.store.store5.Converter
import org.mobilenativefoundation.store.store5.Fetcher
import org.mobilenativefoundation.store.store5.MemoryPolicy
import org.mobilenativefoundation.store.store5.MutableStore
import org.mobilenativefoundation.store.store5.MutableStoreBuilder
import org.mobilenativefoundation.store.store5.SourceOfTruth
import org.mobilenativefoundation.store.store5.Store
import org.mobilenativefoundation.store.store5.StoreBuilder
import org.mobilenativefoundation.store.store5.StoreDefaults
import org.mobilenativefoundation.store.store5.Updater
import org.mobilenativefoundation.store.store5.Validator
import org.mobilenativefoundation.store.store5.impl.extensions.asMutableStore

fun <Key : Any, Network : Any, Output : Any> storeBuilderFromFetcher(
fetcher: Fetcher<Key, Network>,
fun <Key : Any, Input : Any, Output : Any> storeBuilderFromFetcher(
fetcher: Fetcher<Key, Input>,
sourceOfTruth: SourceOfTruth<Key, *>? = null,
): StoreBuilder<Key, Network, Output, *> = RealStoreBuilder(fetcher, sourceOfTruth)
): StoreBuilder<Key, Output> = RealStoreBuilder(fetcher, sourceOfTruth)

fun <Key : Any, Output : Any, Network : Any, Local : Any> storeBuilderFromFetcherAndSourceOfTruth(
fetcher: Fetcher<Key, Network>,
sourceOfTruth: SourceOfTruth<Key, Local>,
): StoreBuilder<Key, Network, Output, Local> = RealStoreBuilder(fetcher, sourceOfTruth)
fun <Key : Any, Input : Any, Output : Any> storeBuilderFromFetcherAndSourceOfTruth(
fetcher: Fetcher<Key, Input>,
sourceOfTruth: SourceOfTruth<Key, *>,
): StoreBuilder<Key, Output> = RealStoreBuilder(fetcher, sourceOfTruth)

internal class RealStoreBuilder<Key : Any, Network : Any, Output : Any, Local : Any>(
private val fetcher: Fetcher<Key, Network>,
private val sourceOfTruth: SourceOfTruth<Key, Local>? = null
) : StoreBuilder<Key, Network, Output, Local> {
) : StoreBuilder<Key, Output> {
private var scope: CoroutineScope? = null
private var cachePolicy: MemoryPolicy<Key, Output>? = StoreDefaults.memoryPolicy
private var converter: Converter<Network, Output, Local>? = null
private var validator: Validator<Output>? = null

override fun scope(scope: CoroutineScope): StoreBuilder<Key, Network, Output, Local> {
override fun scope(scope: CoroutineScope): StoreBuilder<Key, Output> {
this.scope = scope
return this
}

override fun cachePolicy(memoryPolicy: MemoryPolicy<Key, Output>?): StoreBuilder<Key, Network, Output, Local> {
override fun cachePolicy(memoryPolicy: MemoryPolicy<Key, Output>?): StoreBuilder<Key, Output> {
cachePolicy = memoryPolicy
return this
}

override fun disableCache(): StoreBuilder<Key, Network, Output, Local> {
override fun disableCache(): StoreBuilder<Key, Output> {
cachePolicy = null
return this
}

override fun validator(validator: Validator<Output>): StoreBuilder<Key, Network, Output, Local> {
override fun validator(validator: Validator<Output>): StoreBuilder<Key, Output> {
this.validator = validator
return this
}

override fun converter(converter: Converter<Network, Output, Local>): StoreBuilder<Key, Network, Output, Local> {
this.converter = converter
return this
}

override fun build(): Store<Key, Output> = RealStore(
scope = scope ?: GlobalScope,
sourceOfTruth = sourceOfTruth,
Expand All @@ -68,12 +62,24 @@ internal class RealStoreBuilder<Key : Any, Network : Any, Output : Any, Local :
validator = validator
)

override fun <UpdaterResult : Any> build(
updater: Updater<Key, Output, UpdaterResult>,
bookkeeper: Bookkeeper<Key>?
): MutableStore<Key, Output> =
build().asMutableStore<Key, Network, Output, Local, UpdaterResult>(
updater = updater,
bookkeeper = bookkeeper
)
override fun <Network : Any, Local : Any> toMutableStoreBuilder(): MutableStoreBuilder<Key, Network, Output, Local> {
fetcher as Fetcher<Key, Network>
return if (sourceOfTruth == null) {
mutableStoreBuilderFromFetcher(fetcher)
} else {
mutableStoreBuilderFromFetcherAndSourceOfTruth<Key, Network, Output, Local>(fetcher, sourceOfTruth as SourceOfTruth<Key, Local>)
}.apply {
if ([email protected] != null) {
scope([email protected]!!)
}

if ([email protected] != null) {
cachePolicy([email protected])
}

if ([email protected] != null) {
validator([email protected]!!)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class ClearAllStoreTests {

@Test
fun callingClearAllOnStoreWithPersisterAndNoInMemoryCacheDeletesAllEntriesFromThePersister() = testScope.runTest {
val store = StoreBuilder.from<String, Int, Int, Int>(
val store = StoreBuilder.from<String, Int, Int>(
fetcher = fetcher,
sourceOfTruth = persister.asSourceOfTruth()
).scope(testScope)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ClearStoreByKeyTests {
fun callingClearWithKeyOnStoreWithPersisterWithNoInMemoryCacheDeletesTheEntryAssociatedWithTheKeyFromThePersister() = testScope.runTest {
val key = "key"
val value = 1
val store = StoreBuilder.from<String, Int, Int, Int>(
val store = StoreBuilder.from<String, Int, Int>(
fetcher = Fetcher.of { value },
sourceOfTruth = persister.asSourceOfTruth()
).scope(testScope)
Expand Down Expand Up @@ -107,7 +107,7 @@ class ClearStoreByKeyTests {
val key2 = "key2"
val value1 = 1
val value2 = 2
val store = StoreBuilder.from<String, Int, Int, Int>(
val store = StoreBuilder.from<String, Int, Int>(
fetcher = Fetcher.of { key ->
when (key) {
key1 -> value1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,6 @@ class FetcherResponseTests {
)
}

private fun <Key : Any, Network : Any, Output : Any, Local : Any> StoreBuilder<Key, Network, Output, Local>.buildWithTestScope() =
private fun <Key : Any, Output : Any> StoreBuilder<Key, Output>.buildWithTestScope() =
scope(testScope).build()
}
Loading