Compare commits

...

11 Commits
0.4.7 ... 0.4.9

10 changed files with 145 additions and 52 deletions

View File

@@ -1,5 +1,25 @@
# Changelog
## 0.4.9
* `Versions`:
* `Ktor`: `1.4.2` -> `1.4.3`
* `Coroutines`:
* `launchSynchronously` has been added in JVM
* `Repo`
* `Common`
* In repos different usages of `BroadcastChannel`s has been replaced with `MutableSharedFlow`
* `Exposed`
* `asObject` open fun has been added in CRUD realization
## 0.4.8
* `Versions`:
* `Coroutines`: `1.4.1` -> `1.4.2`
* `UUID`: `0.2.2` -> `0.2.3`
* `Pagination`
* Add `PaginatedIterable` and `PaginatedIterator`
## 0.4.7
* `Ktor`

View File

@@ -0,0 +1,28 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
fun <T> launchSynchronously(scope: CoroutineScope = CoroutineScope(Dispatchers.Default), block: suspend CoroutineScope.() -> T): T {
var throwable: Throwable? = null
var result: T? = null
val objectToSynchronize = java.lang.Object()
val launchCallback = {
scope.launch {
safely(
{
throwable = it
}
) {
result = block()
}
synchronized(objectToSynchronize) {
objectToSynchronize.notifyAll()
}
}
}
synchronized(objectToSynchronize) {
launchCallback()
objectToSynchronize.wait()
}
throw throwable ?: return result!!
}

View File

@@ -0,0 +1,13 @@
package dev.inmo.micro_utils.coroutines
import kotlin.test.Test
import kotlin.test.assertEquals
class LaunchSynchronouslyTest {
@Test
fun testRunInCoroutine() {
(0 .. 10000).forEach {
assertEquals(it, launchSynchronously { it })
}
}
}

View File

@@ -1,3 +1,5 @@
apply plugin: 'com.getkeepsafe.dexcount'
android {
compileSdkVersion "$android_compileSdkVersion".toInteger()
buildToolsVersion "$android_buildToolsVersion"

View File

@@ -5,20 +5,19 @@ kotlin.incremental=true
kotlin.incremental.js=true
android.useAndroidX=true
android.enableJetifier=true
org.gradle.jvmargs=-Xmx2048m
kotlin_version=1.4.20
kotlin_coroutines_version=1.4.1
kotlin_coroutines_version=1.4.2
kotlin_serialisation_core_version=1.0.1
kotlin_exposed_version=0.28.1
ktor_version=1.4.2
ktor_version=1.4.3
klockVersion=2.0.0
github_release_plugin_version=2.2.12
uuidVersion=0.2.2
uuidVersion=0.2.3
# ANDROID
@@ -29,17 +28,17 @@ appcompat_version=1.2.0
android_minSdkVersion=19
android_compileSdkVersion=30
android_buildToolsVersion=30.0.2
dexcount_version=2.0.0-RC1
dexcount_version=2.0.0
junit_version=4.12
test_ext_junit_version=1.1.2
espresso_core=3.3.0
# Dokka
dokka_version=1.4.0
dokka_version=1.4.10.2
# Project data
group=dev.inmo
version=0.4.7
android_code_version=11
version=0.4.9
android_code_version=13

View File

@@ -0,0 +1,41 @@
package dev.inmo.micro_utils.pagination.utils
import dev.inmo.micro_utils.pagination.*
class PaginatedIterator<T>(
pageSize: Int,
private val countGetter: () -> Long,
private val paginationResultGetter: Pagination.() -> PaginationResult<T>
) : Iterator<T> {
private var pagination = FirstPagePagination(pageSize)
private val currentStack = mutableListOf<T>()
override fun hasNext(): Boolean = currentStack.isNotEmpty() || (countGetter() < pagination.lastIndexExclusive)
override fun next(): T {
if (currentStack.isEmpty()) {
val resultPagination = paginationResultGetter.invoke(pagination)
currentStack.addAll(resultPagination.results)
require(currentStack.isNotEmpty()) { "There is no elements left" }
pagination = resultPagination.nextPage()
}
return currentStack.removeFirst()
}
}
class PaginatedIterable<T>(
private val pageSize: Int,
private val countGetter: () -> Long,
private val paginationResultGetter: Pagination.() -> PaginationResult<T>
) : Iterable<T> {
override fun iterator(): Iterator<T> = PaginatedIterator(pageSize, countGetter, paginationResultGetter)
}
/**
* Will make iterable using incoming [countGetter] and [paginationResultGetter]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <T> makeIterable(
noinline countGetter: () -> Long,
pageSize: Int = defaultMediumPageSize,
noinline paginationResultGetter: Pagination.() -> PaginationResult<T>
): Iterable<T> = PaginatedIterable(pageSize, countGetter, paginationResultGetter)

View File

@@ -3,20 +3,18 @@ package dev.inmo.micro_utils.repos.crud
import android.content.ContentValues
import dev.inmo.micro_utils.common.mapNotNullA
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.*
abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType>(
helper: StandardSQLHelper
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>,
AbstractAndroidCRUDRepo<ObjectType, IdType>(helper) {
protected val newObjectsChannel = BroadcastChannel<ObjectType>(64)
protected val updateObjectsChannel = BroadcastChannel<ObjectType>(64)
protected val deleteObjectsIdsChannel = BroadcastChannel<IdType>(64)
override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asFlow()
override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asFlow()
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asFlow()
protected val newObjectsChannel = MutableSharedFlow<ObjectType>(64)
protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(64)
protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(64)
override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asSharedFlow()
override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asSharedFlow()
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asSharedFlow()
protected abstract suspend fun InputValueType.asContentValues(id: IdType? = null): ContentValues
@@ -42,7 +40,7 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
}
}.also {
it.forEach {
newObjectsChannel.send(it)
newObjectsChannel.emit(it)
}
}
}
@@ -59,7 +57,7 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
}
}
deleted.forEach {
deleteObjectsIdsChannel.send(it)
deleteObjectsIdsChannel.emit(it)
}
}
@@ -76,7 +74,7 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
}
}
return getById(id) ?.also {
updateObjectsChannel.send(it)
updateObjectsChannel.emit(it)
}
}
@@ -95,7 +93,7 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
getById(it.first)
}.also {
it.forEach {
updateObjectsChannel.send(it)
updateObjectsChannel.emit(it)
}
}
}

View File

@@ -8,10 +8,7 @@ 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.StandardKeyValueRepo
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.*
private val cache = HashMap<String, KeyValueStore<*>>()
@@ -37,11 +34,11 @@ class KeyValueStore<T : Any> internal constructor (
null
}
private val onNewValueChannel = BroadcastChannel<Pair<String, T>>(Channel.BUFFERED)
private val onValueRemovedChannel = BroadcastChannel<String>(Channel.BUFFERED)
private val onNewValueChannel = MutableSharedFlow<Pair<String, T>>()
private val onValueRemovedChannel = MutableSharedFlow<String>()
override val onNewValue: Flow<Pair<String, T>> = onNewValueChannel.asFlow()
override val onValueRemoved: Flow<String> = onValueRemovedChannel.asFlow()
override val onNewValue: Flow<Pair<String, T>> = onNewValueChannel.asSharedFlow()
override val onValueRemoved: Flow<String> = onValueRemovedChannel.asSharedFlow()
init {
cachedData ?.let {
@@ -131,7 +128,7 @@ class KeyValueStore<T : Any> internal constructor (
}
}
toSet.forEach { (k, v) ->
onNewValueChannel.send(k to v)
onNewValueChannel.emit(k to v)
}
}
@@ -139,6 +136,6 @@ class KeyValueStore<T : Any> internal constructor (
sharedPreferences.edit {
toUnset.forEach { remove(it) }
}
toUnset.forEach { onValueRemovedChannel.send(it) }
toUnset.forEach { onValueRemovedChannel.emit(it) }
}
}

View File

@@ -32,11 +32,11 @@ class AndroidSQLStandardVersionsRepoProxy(
}
}
override suspend fun getTableVersion(table: String): Int? = database.writableTransaction {
override suspend fun getTableVersion(tableName: String): Int? = database.writableTransaction {
select(
tableName,
this@AndroidSQLStandardVersionsRepoProxy.tableName,
selection = "$tableNameColumnName=?",
selectionArgs = arrayOf(table),
selectionArgs = arrayOf(tableName),
limit = limitClause(1)
).use {
if (it.moveToFirst()) {
@@ -47,16 +47,16 @@ class AndroidSQLStandardVersionsRepoProxy(
}
}
override suspend fun updateTableVersion(table: String, version: Int) {
override suspend fun updateTableVersion(tableName: String, version: Int) {
database.writableTransaction {
val updated = update(
tableName,
this@AndroidSQLStandardVersionsRepoProxy.tableName,
contentValuesOf(tableVersionColumnName to version),
"$tableNameColumnName=?",
arrayOf(table)
arrayOf(tableName)
) > 0
if (!updated) {
insert(tableName, null, contentValuesOf(tableNameColumnName to table, tableVersionColumnName to version))
insert(this@AndroidSQLStandardVersionsRepoProxy.tableName, null, contentValuesOf(tableNameColumnName to tableName, tableVersionColumnName to version))
}
}
}

View File

@@ -2,7 +2,6 @@ package dev.inmo.micro_utils.repos.exposed
import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement
@@ -25,8 +24,10 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asSharedFlow()
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asSharedFlow()
@Deprecated("Will be removed in near major update. Override open fun with the same name instead")
abstract val InsertStatement<Number>.asObject: ObjectType
abstract val selectByIds: SqlExpressionBuilder.(List<out IdType>) -> Op<Boolean>
protected open fun InsertStatement<Number>.asObject(value: InputValueType): ObjectType = asObject
abstract val selectByIds: SqlExpressionBuilder.(List<IdType>) -> Op<Boolean>
protected abstract fun insert(value: InputValueType, it: InsertStatement<Number>)
protected abstract fun update(id: IdType, value: InputValueType, it: UpdateStatement)
@@ -34,7 +35,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
protected open suspend fun onBeforeCreate(value: List<InputValueType>) {}
private fun createWithoutNotification(value: InputValueType): ObjectType {
return transaction(database) {
insert { insert(value, it) }.asObject
insert { insert(value, it) }.asObject(value)
}
}
@@ -42,10 +43,8 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
onBeforeCreate(values)
return transaction(db = database) {
values.map { value -> createWithoutNotification(value) }
}.also {
it.forEach {
newObjectsChannel.emit(it)
}
}.onEach {
newObjectsChannel.emit(it)
}
}
@@ -83,13 +82,9 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
return (
transaction(db = database) {
values.map { (id, value) -> updateWithoutNotification(id, value) }
}.filter {
it != null
} as List<ObjectType>
).also {
it.forEach {
updateObjectsChannel.emit(it)
}
}.filterNotNull()
).onEach {
updateObjectsChannel.emit(it)
}
}
protected open suspend fun onBeforeDelete(ids: List<IdType>) {}