Compare commits

...

36 Commits

Author SHA1 Message Date
59e0e751f1 all android submodules with common mpp template now incldes JVM code 2021-04-20 18:37:42 +06:00
a5ae5e6c2b rename template files 2021-04-20 18:36:53 +06:00
a2b87e63c9 start 0.4.36 2021-04-20 18:36:10 +06:00
d95c283653 crutch for nodejs 2021-04-18 17:29:02 +06:00
6a9ae0c148 Merge pull request #60 from InsanusMokrassar/0.4.35
hotfixes
2021-04-15 14:31:28 +06:00
040dd517d8 hotfixes 2021-04-15 14:30:45 +06:00
9663c1ca64 Merge pull request #59 from InsanusMokrassar/0.4.35
0.4.35
2021-04-15 14:19:31 +06:00
ffc2d23be7 add project typed serializer 2021-04-15 14:15:40 +06:00
49b009e59b update exposed 2021-04-15 13:49:53 +06:00
778b6a555b start 0.4.35 2021-04-15 13:48:52 +06:00
b3730998e9 Merge pull request #58 from InsanusMokrassar/0.4.34
0.4.34
2021-04-13 12:28:25 +06:00
837758aebe update uuid 2021-04-13 12:22:04 +06:00
6ac4149aa1 fixes in crud repos 2021-04-13 12:20:24 +06:00
278584ae6a start 0.4.34 2021-04-13 12:10:55 +06:00
126f9d5f41 Merge pull request #57 from InsanusMokrassar/0.4.33
0.4.33
2021-04-05 16:30:58 +06:00
ce15ff4e0a update ktor 2021-04-05 16:22:39 +06:00
382b956beb make createWeakSubScope private, upfill readme 2021-04-05 16:20:11 +06:00
36deab4909 weak jobs workaround 2021-04-05 16:10:36 +06:00
550fc59d9d start 0.4.33 2021-04-05 15:14:21 +06:00
9100a57458 Merge pull request #56 from InsanusMokrassar/0.4.32
0.4.32
2021-04-03 14:07:01 +06:00
74d9bbccd9 updates 2021-04-02 18:43:56 +06:00
75e602a349 update kotlin exposed 2021-04-02 18:39:56 +06:00
e73644db10 start 0.4.32 2021-04-02 18:26:53 +06:00
148e6bdae7 Merge pull request #55 from InsanusMokrassar/0.4.31
0.4.31
2021-03-29 20:12:11 +06:00
1b540199f0 small update 2021-03-29 19:51:38 +06:00
30b70e9984 replacement of doForAll and getAll 2021-03-29 19:48:37 +06:00
c1557cff27 huge update with crud caching, common doForAll/getAll and deprecations in old ones doForAll 2021-03-29 19:39:10 +06:00
2d662f91b3 updates in coroutines 2021-03-29 17:57:36 +06:00
c4a08e52e5 update kotlin 2021-03-24 13:11:15 +06:00
08c371c142 add repos.cache 2021-03-24 13:01:15 +06:00
8e62dd460c start 0.4.31 2021-03-23 13:46:35 +06:00
1f9302dc94 Merge pull request #54 from InsanusMokrassar/0.4.30
0.4.30
2021-03-17 14:33:15 +06:00
16f445f699 update klock 2021-03-17 13:52:54 +06:00
b4abd564ec update paginations 2021-03-17 13:48:58 +06:00
14ffafb0a7 start 0.4.30 2021-03-17 13:43:52 +06:00
e0cc780887 Merge pull request #52 from InsanusMokrassar/0.4.29
0.4.29
2021-03-05 20:45:34 +06:00
38 changed files with 610 additions and 61 deletions

21
.github/workflows/dokka_push.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Publish KDocs
on:
push:
branches:
- master
jobs:
publishing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build
run: ./gradlew dokkaHtml
- name: Publish KDocs
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dokka/build/dokka/html
publish_branch: kdocs

View File

@@ -1,5 +1,73 @@
# Changelog
## 0.4.36
* All `Android` targets inside common mpp modules now includes JVM code
## 0.4.35
* `Versions`:
* `Kotlin Exposed`: `0.30.1` -> `0.30.2`
* `Serialization`:
* `TypedSerializer`:
* Project has been inited
## 0.4.34
* `Versions`:
* `uuid`: `0.2.3` -> `0.2.4`
* `Repos`:
* `AbstractExposedCRUDRepo` now implements `StandardCRUDRepo`
* `AbstractMutableAndroidCRUDRepo` now implements `StandardCRUDRepo`
## 0.4.33
* `Versions`:
* `Ktor`: `1.5.2` -> `1.5.3`
* `Coroutines`
* Add `WeakJob` workaround:
* `CoroutineScope#weakLaunch`
* `CoroutineScope#weakAsync`
## 0.4.32
* `Versions`:
* `Kotlin Exposed`: `0.29.1` -> `0.30.1`
## 0.4.31
* `Versions`:
* `Kotlin`: `1.4.31` -> `1.4.32`
* `Pagination`:
* New extensions `PaginationResult.changeResultsUnchecked` and `PaginationResult.changeResults` for mapping results
with the same parameters, but different data
* Extension `PaginationResult.thisPageIfNotEmpty` now is typed and will return `PaginationResult?` with the same
generic type as income `PaginationResult`
* New extension `PaginationResult.currentPageIfNotEmpty` - shortcut for `PaginationResult.thisPageIfNotEmpty`
* New common functions. They were created as replacements for currently available for more comfortable work
with repos pagination:
* `doForAll`
* `doForAllWithNextPaging`
* `doForAllWithCurrentPaging`
* `getAll`
* `getAllWithNextPaging`
* `getAllWithCurrentPaging`
* `Coroutines`:
* Rewrite `subscribeSafelyWithoutExceptions`
* Now `subscribeSafelyWithoutExceptions` will use default handler instead of skipping
* New extension `subscribeSafelySkippingExceptions`
* `Repos`
* New subproject `repos.cache` - this subproject will contain repos with data caching mechanisms
* Most old `doForAll` methods have been deprecated
## 0.4.30
* `Versions`:
* `Klock`: `2.0.6` -> `2.0.7`
* `Pagination`:
* New variable `defaultPaginationPageSize` has been added to be able to change default pagination size
* Add new value `firstPageWithOneElementPagination`
## 0.4.29
* `Versions`:

View File

@@ -25,6 +25,15 @@ allprojects {
google()
maven { url "https://kotlin.bintray.com/kotlinx" }
}
// temporal crutch until legacy tests will be stabled or legacy target will be removed
if (it != rootProject.findProject("docs")) {
tasks.whenTaskAdded { task ->
if(task.name == "jsLegacyBrowserTest" || task.name == "jsLegacyNodeTest") {
task.enabled = false
}
}
}
}
apply from: "./extensions.gradle"

View File

@@ -25,13 +25,25 @@ inline fun <T> Flow<T>.subscribeSafely(
}
/**
* Use [subscribeSafelyWithoutExceptions], but all exceptions inside of [safely] will be skipped
* Use [subscribeSafelyWithoutExceptions], but all exceptions will be passed to [defaultSafelyExceptionHandler]
*/
inline fun <T> Flow<T>.subscribeSafelyWithoutExceptions(
scope: CoroutineScope,
noinline block: suspend (T) -> Unit
) = subscribeSafely(
scope,
{},
block
)
) = subscribe(scope) {
safelyWithoutExceptions {
block(it)
}
}
/**
* Use [subscribeSafelyWithoutExceptions], but all exceptions inside of [safely] will be skipped
*/
inline fun <T> Flow<T>.subscribeSafelySkippingExceptions(
scope: CoroutineScope,
noinline block: suspend (T) -> Unit
) = subscribe(scope) {
safelyWithoutExceptions({ /* skip exceptions */ }) {
block(it)
}
}

View File

@@ -0,0 +1,31 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
private fun CoroutineScope.createWeakSubScope() = CoroutineScope(coroutineContext.minusKey(Job)).also { newScope ->
coroutineContext.job.invokeOnCompletion { newScope.cancel() }
}
fun CoroutineScope.weakLaunch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val scope = createWeakSubScope()
val job = scope.launch(context, start, block)
job.invokeOnCompletion { scope.cancel() }
return job
}
fun <T> CoroutineScope.weakAsync(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
val scope = createWeakSubScope()
val deferred = scope.async(context, start, block)
deferred.invokeOnCompletion { scope.cancel() }
return deferred
}

View File

@@ -0,0 +1,40 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
import org.junit.Test
class WeakJob {
@Test
fun `test that weak jobs works correctly`() {
val scope = CoroutineScope(Dispatchers.Default)
lateinit var weakLaunchJob: Job
lateinit var weakAsyncJob: Job
scope.launchSynchronously {
val completeDeferred = Job()
coroutineScope {
weakLaunchJob = weakLaunch {
while (isActive) {
delay(100L)
}
}
weakAsyncJob = weakAsync {
while (isActive) {
delay(100L)
}
}
coroutineContext.job.invokeOnCompletion {
scope.launch {
delay(1000L)
completeDeferred.complete()
}
}
launch { delay(1000L); cancel() }
}
completeDeferred.join()
}
assert(!weakLaunchJob.isActive)
assert(!weakAsyncJob.isActive)
}
}

View File

@@ -20,11 +20,11 @@ allprojects {
releaseMode = (project.hasProperty('RELEASE_MODE') && project.property('RELEASE_MODE') == "true") || System.getenv('RELEASE_MODE') == "true"
mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerialization"
mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject"
mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject"
mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerialization.gradle"
mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject.gradle"
mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject.gradle"
defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings"
defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle"
publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle"
publishMavenPath = "${rootProject.projectDir.absolutePath}/maven.publish.gradle"

View File

@@ -6,18 +6,18 @@ kotlin.incremental.js=true
android.useAndroidX=true
android.enableJetifier=true
kotlin_version=1.4.31
kotlin_version=1.4.32
kotlin_coroutines_version=1.4.3
kotlin_serialisation_core_version=1.1.0
kotlin_exposed_version=0.29.1
kotlin_exposed_version=0.30.2
ktor_version=1.5.2
ktor_version=1.5.3
klockVersion=2.0.6
klockVersion=2.0.7
github_release_plugin_version=2.2.12
uuidVersion=0.2.3
uuidVersion=0.2.4
# ANDROID
@@ -39,10 +39,10 @@ crypto_js_version=4.0.0
# Dokka
dokka_version=1.4.20
dokka_version=1.4.30
# Project data
group=dev.inmo
version=0.4.29
android_code_version=33
version=0.4.36
android_code_version=40

View File

@@ -12,6 +12,22 @@ data class PaginationResult<T>(
fun <T> emptyPaginationResult() = PaginationResult<T>(0, 0, emptyList(), 0)
/**
* @return New [PaginationResult] with [data] without checking of data sizes equality
*/
fun <I, O> PaginationResult<I>.changeResultsUnchecked(
data: List<O>
): PaginationResult<O> = PaginationResult(page, pagesNumber, data, size)
/**
* @return New [PaginationResult] with [data] <b>with</b> checking of data sizes equality
*/
fun <I, O> PaginationResult<I>.changeResults(
data: List<O>
): PaginationResult<O> {
require(data.size == results.size)
return changeResultsUnchecked(data)
}
fun <T> List<T>.createPaginationResult(
pagination: Pagination,
commonObjectsNumber: Long

View File

@@ -7,14 +7,17 @@ const val defaultMediumPageSize = 5
const val defaultLargePageSize = 10
const val defaultExtraLargePageSize = 15
var defaultPaginationPageSize = defaultMediumPageSize
@Suppress("NOTHING_TO_INLINE", "FunctionName")
inline fun FirstPagePagination(size: Int = defaultMediumPageSize) =
inline fun FirstPagePagination(size: Int = defaultPaginationPageSize) =
SimplePagination(
page = 0,
size = size
)
val emptyPagination = Pagination(0, 0)
val firstPageWithOneElementPagination = FirstPagePagination(1)
@Suppress("NOTHING_TO_INLINE")
inline fun Pagination.nextPage() =

View File

@@ -21,8 +21,10 @@ inline fun PaginationResult<*>.nextPageIfNotEmpty() = if (results.isNotEmpty())
}
@Suppress("NOTHING_TO_INLINE")
inline fun PaginationResult<*>.thisPageIfNotEmpty(): Pagination? = if (results.isNotEmpty()) {
inline fun <T> PaginationResult<T>.thisPageIfNotEmpty(): PaginationResult<T>? = if (results.isNotEmpty()) {
this
} else {
null
}
inline fun <T> PaginationResult<T>.currentPageIfNotEmpty() = thisPageIfNotEmpty()

View File

@@ -0,0 +1,35 @@
package dev.inmo.micro_utils.pagination.utils
import dev.inmo.micro_utils.pagination.*
suspend fun <T> doForAll(
initialPagination: Pagination = FirstPagePagination(),
paginationMapper: (PaginationResult<T>) -> Pagination?,
block: suspend (Pagination) -> PaginationResult<T>
) {
doWithPagination(initialPagination) {
block(it).let(paginationMapper)
}
}
suspend fun <T> doForAllWithNextPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T>
) {
doForAll(
initialPagination,
{ it.nextPageIfNotEmpty() },
block
)
}
suspend fun <T> doAllWithCurrentPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T>
) {
doForAll(
initialPagination,
{ it.currentPageIfNotEmpty() },
block
)
}

View File

@@ -0,0 +1,35 @@
package dev.inmo.micro_utils.pagination.utils
import dev.inmo.micro_utils.pagination.*
suspend fun <T> getAll(
initialPagination: Pagination = FirstPagePagination(),
paginationMapper: (PaginationResult<T>) -> Pagination?,
block: suspend (Pagination) -> PaginationResult<T>
): List<T> {
val results = mutableListOf<T>()
doForAll(initialPagination, paginationMapper) {
block(it).also {
results.addAll(it.results)
}
}
return results.toList()
}
suspend fun <T> getAllWithNextPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T>
): List<T> = getAll(
initialPagination,
{ it.nextPageIfNotEmpty() },
block
)
suspend fun <T> getAllWithCurrentPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T>
): List<T> = getAll(
initialPagination,
{ it.currentPageIfNotEmpty() },
block
)

View File

@@ -36,6 +36,6 @@ class PaginatedIterable<T>(
@Suppress("NOTHING_TO_INLINE")
inline fun <T> makeIterable(
noinline countGetter: () -> Long,
pageSize: Int = defaultMediumPageSize,
pageSize: Int = defaultPaginationPageSize,
noinline paginationResultGetter: Pagination.() -> PaginationResult<T>
): Iterable<T> = PaginatedIterable(pageSize, countGetter, paginationResultGetter)

View File

@@ -18,6 +18,6 @@ val Pagination.asUrlQueryArrayParts
val Map<String, String?>.extractPagination: Pagination
get() = SimplePagination(
get(paginationPageKey) ?.toIntOrNull() ?: 0,
get(paginationSizeKey) ?.toIntOrNull() ?: defaultMediumPageSize
get(paginationSizeKey) ?.toIntOrNull() ?: defaultPaginationPageSize
)

View File

@@ -6,7 +6,7 @@ import io.ktor.http.Parameters
val Parameters.extractPagination: Pagination
get() = SimplePagination(
get("page") ?.toIntOrNull() ?: 0,
get("size") ?.toIntOrNull() ?: defaultMediumPageSize
get("size") ?.toIntOrNull() ?: defaultPaginationPageSize
)
val ApplicationCall.extractPagination: Pagination

18
repos/cache/build.gradle vendored Normal file
View File

@@ -0,0 +1,18 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api internalProject("micro_utils.repos.common")
api internalProject("micro_utils.repos.inmemory")
}
}
}
}

View File

@@ -0,0 +1,24 @@
package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
protected val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
protected val kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
protected val idGetter: (ObjectType) -> IdType
) : CRUDRepo<ObjectType, IdType, InputValueType> by parentRepo {
protected val onNewJob = parentRepo.newObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
protected val onUpdatedJob = parentRepo.updatedObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
protected val onRemoveJob = parentRepo.deletedObjectsIdsFlow.onEach { kvCache.unset(it) }.launchIn(scope)
override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also {
kvCache.set(id, it)
})
override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id)
}

View File

@@ -0,0 +1,41 @@
package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
interface KVCache<K, V> : KeyValueRepo<K, V>
open class SimpleKVCache<K, V>(
protected val cachedValuesCount: Int,
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) : KVCache<K, V>, KeyValueRepo<K, V> by kvParent {
protected open val cacheStack = ArrayList<K>(cachedValuesCount)
protected val syncMutex = Mutex()
protected suspend fun makeUnset(toUnset: List<K>) {
cacheStack.removeAll(toUnset)
kvParent.unset(toUnset)
}
override suspend fun set(toSet: Map<K, V>) {
syncMutex.withLock {
if (toSet.size > cachedValuesCount) {
cacheStack.clear()
kvParent.unset(getAllWithNextPaging { kvParent.keys(it) })
val keysToInclude = toSet.keys.drop(toSet.size - cachedValuesCount)
cacheStack.addAll(keysToInclude)
kvParent.set(keysToInclude.associateWith { toSet.getValue(it) })
} else {
makeUnset(cacheStack.take(toSet.size))
}
}
}
override suspend fun unset(toUnset: List<K>) {
syncMutex.withLock { makeUnset(toUnset) }
}
}

View File

@@ -0,0 +1,20 @@
package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
open class KeyValueCacheRepo<Key,Value>(
protected val parentRepo: KeyValueRepo<Key, Value>,
protected val kvCache: KVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : KeyValueRepo<Key,Value> by parentRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope)
override suspend fun get(k: Key): Value? = kvCache.get(k) ?: parentRepo.get(k) ?.also { kvCache.set(k, it) }
override suspend fun contains(key: Key): Boolean = kvCache.contains(key) || parentRepo.contains(key)
}

View File

@@ -0,0 +1,37 @@
package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.utils.paginate
import dev.inmo.micro_utils.pagination.utils.reverse
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
open class KeyValuesCacheRepo<Key,Value>(
protected val parentRepo: KeyValuesRepo<Key, Value>,
protected val kvCache: KVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : KeyValuesRepo<Key,Value> by parentRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)) }.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.minus(it.second) ?: return@onEach) }.launchIn(scope)
protected val onDataClearedJob = parentRepo.onDataCleared.onEach { kvCache.unset(it) }.launchIn(scope)
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
return kvCache.get(k) ?.paginate(
pagination.let { if (reversed) it.reverse(count(k)) else it }
) ?.let {
if (reversed) it.copy(results = it.results.reversed()) else it
} ?: parentRepo.get(k, pagination, reversed)
}
override suspend fun getAll(k: Key, reversed: Boolean): List<Value> {
return kvCache.get(k) ?.let {
if (reversed) it.reversed() else it
} ?: parentRepo.getAll(k, reversed)
}
override suspend fun contains(k: Key, v: Value): Boolean = kvCache.get(k) ?.contains(v) ?: parentRepo.contains(k, v)
override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k)
}

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.micro_utils.repos.cache"/>

View File

@@ -1,6 +1,7 @@
package dev.inmo.micro_utils.repos
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import kotlinx.coroutines.flow.Flow
interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
@@ -12,14 +13,12 @@ interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
suspend fun count(k: Key): Long
suspend fun count(): Long
suspend fun getAll(k: Key, reversed: Boolean = false): List<Value> = mutableListOf<Value>().also { list ->
doWithPagination {
get(k, it).also {
list.addAll(it.results)
}.nextPageIfNotEmpty()
}
if (reversed) {
list.reverse()
suspend fun getAll(k: Key, reversed: Boolean = false): List<Value> {
val results = getAllWithNextPaging { get(k, it) }
return if (reversed) {
results.reversed()
} else {
results
}
}

View File

@@ -1,6 +1,7 @@
package dev.inmo.micro_utils.repos
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.doAllWithCurrentPaging
import kotlinx.coroutines.flow.Flow
interface ReadStandardKeyValueRepo<Key, Value> : Repo {
@@ -41,7 +42,7 @@ suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.unsetWithV
interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value>, WriteStandardKeyValueRepo<Key, Value> {
override suspend fun unsetWithValues(toUnset: List<Value>) = toUnset.forEach { v ->
doWithPagination {
doAllWithCurrentPaging {
keys(v, it).also {
unset(it.results)
}

View File

@@ -1,31 +1,33 @@
package dev.inmo.micro_utils.repos.pagination
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
@Deprecated("Will be removed soon due to redundancy. Can be replaced with other doForAll extensions")
suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.doForAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
methodCaller: suspend REPO.(Pagination) -> PaginationResult<T>,
block: (List<T>) -> Unit
crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<T>,
crossinline block: (List<T>) -> Unit
) {
doWithPagination {
doForAllWithNextPaging {
methodCaller(it).also {
block(it.results)
}.nextPageIfNotEmpty()
}
}
}
@Deprecated("Will be removed soon due to redundancy. Can be replaced with other doForAll extensions")
suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.doForAll(
block: (List<T>) -> Unit
) = doForAll({ getByPagination(it) }, block)
crossinline block: (List<T>) -> Unit
) = doForAllWithNextPaging {
getByPagination(it).also { block(it.results) }
}
suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.getAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
methodCaller: suspend REPO.(Pagination) -> PaginationResult<T>
): List<T> {
val resultList = mutableListOf<T>()
doForAll(methodCaller) {
resultList.addAll(it)
}
return resultList
crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<T>
): List<T> = getAllWithNextPaging {
methodCaller(this, it)
}

View File

@@ -1,8 +1,10 @@
package dev.inmo.micro_utils.repos.pagination
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
@Deprecated("Will be removed soon due to redundancy")
suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.doForAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>,
@@ -15,17 +17,17 @@ suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REP
}
}
@Deprecated("Will be removed soon due to redundancy")
suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.doForAll(
block: (List<Pair<Key, Value>>) -> Unit
) = doForAll({ keys(it, false) }, block)
suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.getAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>
): List<Pair<Key, Value>> {
val resultList = mutableListOf<Pair<Key, Value>>()
doForAll(methodCaller) {
resultList.addAll(it)
}
return resultList
crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>
): List<Pair<Key, Value>> = getAllWithNextPaging {
val result = methodCaller(it)
result.changeResultsUnchecked(
result.results.mapNotNull { it to (get(it) ?: return@mapNotNull null) }
)
}

View File

@@ -1,8 +1,10 @@
package dev.inmo.micro_utils.repos.pagination
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
@Deprecated("Will be removed soon due to redundancy")
suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.doForAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>,
@@ -25,17 +27,19 @@ suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> RE
}
}
@Deprecated("Will be removed soon due to redundancy")
suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.doForAll(
block: (List<Pair<Key, List<Value>>>) -> Unit
) = doForAll({ keys(it, false) }, block)
suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.getAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>
): List<Pair<Key, List<Value>>> {
val resultList = mutableListOf<Pair<Key, List<Value>>>()
doForAll(methodCaller) {
resultList.addAll(it)
}
return resultList
crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>
): List<Pair<Key, List<Value>>> = getAllWithNextPaging {
val keysResult = methodCaller(it)
keysResult.changeResultsUnchecked(
keysResult.results.map { k ->
k to getAll(k)
}
)
}

View File

@@ -8,7 +8,8 @@ import kotlinx.coroutines.flow.*
abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType>(
helper: StandardSQLHelper
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>,
AbstractAndroidCRUDRepo<ObjectType, IdType>(helper) {
AbstractAndroidCRUDRepo<ObjectType, IdType>(helper),
StandardCRUDRepo<ObjectType, IdType, InputValueType> {
protected val newObjectsChannel = MutableSharedFlow<ObjectType>(64)
protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(64)
protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(64)

View File

@@ -1,5 +1,7 @@
package dev.inmo.micro_utils.repos.exposed
import dev.inmo.micro_utils.repos.StandardCRUDRepo
abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = 0,
tableName: String = ""
@@ -8,4 +10,5 @@ abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize,
tableName
),
ExposedCRUDRepo<ObjectType, IdType>
ExposedCRUDRepo<ObjectType, IdType>,
StandardCRUDRepo<ObjectType, IdType, InputValueType>

View File

@@ -0,0 +1,7 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -0,0 +1,74 @@
package dev.inmo.micro_utils.serialization.typed_serializer
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlin.reflect.KClass
open class TypedSerializer<T : Any>(
kClass: KClass<T>,
presetSerializers: Map<String, KSerializer<out T>> = emptyMap()
) : KSerializer<T> {
protected val serializers = presetSerializers.toMutableMap()
@InternalSerializationApi
override open val descriptor: SerialDescriptor = buildSerialDescriptor(
"TextSourceSerializer",
SerialKind.CONTEXTUAL
) {
element("type", String.serializer().descriptor)
element("value", ContextualSerializer(kClass).descriptor)
}
@InternalSerializationApi
override open fun deserialize(decoder: Decoder): T {
return decoder.decodeStructure(descriptor) {
var type: String? = null
lateinit var result: T
while (true) {
when (val index = decodeElementIndex(descriptor)) {
0 -> type = decodeStringElement(descriptor, 0)
1 -> {
require(type != null) { "Type is null, but it is expected that was inited already" }
result = decodeSerializableElement(
descriptor,
1,
serializers.getValue(type)
)
}
CompositeDecoder.DECODE_DONE -> break
else -> error("Unexpected index: $index")
}
}
result
}
}
@InternalSerializationApi
protected open fun <O: T> CompositeEncoder.encode(value: O) {
encodeSerializableElement(descriptor, 1, value::class.serializer() as KSerializer<O>, value)
}
@InternalSerializationApi
override open fun serialize(encoder: Encoder, value: T) {
encoder.encodeStructure(descriptor) {
val valueSerializer = value::class.serializer()
val type = serializers.keys.first { serializers[it] == valueSerializer }
encodeStringElement(descriptor, 0, type)
encode(value)
}
}
open fun <O: T> include(type: String, serializer: KSerializer<O>) {
serializers[type] = serializer
}
open fun exclude(type: String) {
serializers.remove(type)
}
}
inline fun <reified T : Any> TypedSerializer(
presetSerializers: Map<String, KSerializer<out T>> = emptyMap()
) = TypedSerializer(T::class, presetSerializers)

View File

@@ -0,0 +1,40 @@
package dev.inmo.micro_utils.serialization.typed_serializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
class TypedSerializerTests {
interface Example {
val number: Number
}
val serialFormat = Json { }
@Serializable
data class Example1(override val number: Long) : Example
@Serializable
data class Example2(override val number: Double) : Example
@Test
fun testThatSerializerWorksCorrectly() {
val serializer = TypedSerializer(
mapOf(
"long" to Example1.serializer(),
"double" to Example2.serializer()
)
)
val value1 = Example1(Random.nextLong())
val value2 = Example2(Random.nextDouble())
val list = listOf(value1, value2)
val serialized = serialFormat.encodeToString(ListSerializer(serializer), list)
val deserialized = serialFormat.decodeFromString(ListSerializer(serializer), serialized)
assertEquals(list, deserialized)
}
}

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.micro_utils.serialization.typed_serializer"/>

View File

@@ -11,6 +11,7 @@ String[] includes = [
":pagination:ktor:server",
":mime_types",
":repos:common",
":repos:cache",
":repos:exposed",
":repos:inmemory",
":repos:ktor:client",
@@ -25,6 +26,7 @@ String[] includes = [
":android:alerts:recyclerview",
":serialization:base64",
":serialization:encapsulator",
":serialization:typed_serializer",
":dokka"
]