Compare commits

...

37 Commits
0.5.2 ... 0.5.8

Author SHA1 Message Date
eb959a3135 add regular build workflow 2021-06-11 22:33:08 +06:00
24033e0cac firstNotNull and LinkedSupervisor(Job|Scope) 2021-06-11 17:32:13 +06:00
71f9a505e0 start 0.5.8 2021-06-11 17:03:20 +06:00
979b8f017b Merge pull request #71 from InsanusMokrassar/0.5.7
0.5.7
2021-06-06 10:46:13 +06:00
af78f01682 remove progressbar alert dialog 2021-06-06 10:43:42 +06:00
0b16d5c826 upfix 2021-06-06 02:10:48 +06:00
597e14bc7e fix of alert dialog with progress bar background 2021-06-06 02:04:21 +06:00
04a95867e2 fixes 2021-06-06 01:46:33 +06:00
e0d5eb45b7 separate cache repos to read and read/write 2021-06-05 20:29:59 +06:00
b90cab318e start 0.5.7 2021-06-05 20:12:41 +06:00
3252b61abe Merge pull request #70 from InsanusMokrassar/0.5.6
0.5.6
2021-06-05 15:23:40 +06:00
2a2da21ff3 fillup safelyWithResult 2021-06-05 15:18:48 +06:00
04ef371337 update safelyWithResult 2021-06-05 15:18:09 +06:00
623e0cd369 improve launchSynchronously 2021-06-05 15:16:07 +06:00
1f466747f0 update exposed 2021-06-05 15:08:22 +06:00
2215462f99 start 0.5.6 2021-06-05 15:07:27 +06:00
ac4c0a2e4c Merge pull request #69 from InsanusMokrassar/0.5.5
0.5.5
2021-05-28 23:50:35 +06:00
f7496db5ac Update CHANGELOG.md 2021-05-28 19:34:30 +06:00
3028fe975d Update ktor 2021-05-28 19:33:02 +06:00
23a5034493 start 0.5.5 2021-05-28 19:32:04 +06:00
65e339f811 Merge pull request #68 from InsanusMokrassar/0.5.4
0.5.4
2021-05-26 22:33:53 +06:00
2020e48659 fixes of deprecates 2021-05-26 22:33:08 +06:00
9566d6f81f upfix of android version increasing 2021-05-26 22:22:43 +06:00
a00d734712 Merge pull request #67 from InsanusMokrassar/0.5.4
0.5.4
2021-05-26 22:07:28 +06:00
27a3e8706a update klock 2021-05-26 22:05:34 +06:00
e601efcfc0 Update gradle.properties 2021-05-26 22:03:30 +06:00
2bfad9f885 Merge pull request #66 from InsanusMokrassar/0.5.3
0.5.3
2021-05-26 18:06:39 +06:00
e78e984943 fillup changelog 2021-05-26 18:00:24 +06:00
242f4b02d0 return previous logic of work with safely 2021-05-26 17:59:45 +06:00
041be5a1d1 Update gradle.properties 2021-05-25 16:08:07 +06:00
976ce056c1 runCatching for safely and safelyWithoutExceptions 2021-05-25 00:18:46 +06:00
00c23c73a8 several small improvements 2021-05-24 20:26:48 +06:00
9dd1848337 note about safely logic 2021-05-24 12:31:32 +06:00
9b30efd9a2 optimize imports 2021-05-24 12:29:31 +06:00
5853f7cc49 coroutines updates 2021-05-24 12:27:54 +06:00
7b00a06f3e start 0.5.3 2021-05-23 00:03:43 +06:00
9ef9be0f37 Merge pull request #64 from InsanusMokrassar/0.5.2
0.5.2
2021-05-21 18:08:34 +06:00
20 changed files with 264 additions and 84 deletions

12
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
name: Regular build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build
run: ./gradlew build

View File

@@ -1,5 +1,56 @@
# Changelog
## 0.5.8
* `Common`:
* New extension `Iterable#firstNotNull`
* `Coroutines`
* New extension `Flow#firstNotNull`
* New extensions `CoroutineContext#LinkedSupervisorJob`, `CoroutineScope#LinkedSupervisorJob` and
`CoroutineScope#LinkedSupervisorScope`
## 0.5.7
* `Pagination`
* `Ktor`
* `Server`
* Fixes in extension `extractPagination`
* `Repos`
* `Cache`
* All standard cache repos have been separated to read and read/write repos
## 0.5.6
* `Versions`
* `Exposed`: `0.31.1` -> `0.32.1`
* `Coroutines`
* `JVM`
* `launchSynchronously` and subsequent functions got improved mechanism
* New method `safelyWithResult`
## 0.5.5
* `Versions`
* `Ktor`: `1.5.4` -> `1.6.0`
## 0.5.4
* `Versions`:
* `Klock`: `2.1.0` -> `2.1.2`
## 0.5.3
* `Versions`:
* `Kotlin`: `1.5.0` -> `1.5.10`
* `Coroutines`:
* Extensions `doInUI` and `doInDefault` were replaced in common and available on any supported platform
* Extension `doInIO` replaced into `jvm` and available on any `JVM` platform
* Old extension `safelyWithouException` without `onException` has been replaced by its copy with `onException` and
default value
* New value `defaultSafelyWithoutExceptionHandlerWithNull` which is used in all `*WithoutExceptions` by default
* Analogs of `launch` and `async` for `safely` and `safelyWithoutExceptions` were added
* Analogs of `runCatching` for `safely` and `safelyWithoutExceptions` were added
## 0.5.2
* `Ktor`:

View File

@@ -0,0 +1,3 @@
package dev.inmo.micro_utils.common
fun <T> Iterable<T?>.firstNotNull() = first { it != null }!!

View File

@@ -38,7 +38,7 @@ inline fun CoroutineScope.createSafeActionsActor(
suspend fun <T> Channel<suspend () -> Unit>.doWithSuspending(
action: ActorAction<T>
) = suspendCoroutine<T> {
offer {
trySend {
safely({ e -> it.resumeWithException(e) }) {
it.resume(action())
}

View File

@@ -0,0 +1,23 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
inline val UI
get() = Dispatchers.Main
inline val Default
get() = Dispatchers.Default
suspend inline fun <T> doIn(context: CoroutineContext, noinline block: suspend CoroutineScope.() -> T) = withContext(
context,
block
)
suspend inline fun <T> doInUI(noinline block: suspend CoroutineScope.() -> T) = doIn(
UI,
block
)
suspend inline fun <T> doInDefault(noinline block: suspend CoroutineScope.() -> T) = doIn(
Default,
block
)

View File

@@ -0,0 +1,6 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
suspend fun <T> Flow<T?>.firstNotNull() = first { it != null }!!

View File

@@ -86,6 +86,9 @@ suspend fun <T> safelyWithContextExceptionHandler(
* * [CoroutineContext.get] with [SafelyExceptionHandlerKey] as key
* * [defaultSafelyExceptionHandler]
*
* Remember, that [ExceptionHandler] from [CoroutineContext.get] will be used anyway if it is available. After it will
* be called [onException]
*
* @param [onException] Will be called when happen exception inside of [block]. By default will throw exception - this
* exception will be available for catching
*
@@ -105,24 +108,42 @@ suspend inline fun <T> safely(
}
}
suspend inline fun <T> runCatchingSafely(
noinline onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
noinline block: suspend CoroutineScope.() -> T
): Result<T> = runCatching {
safely(onException, block)
}
suspend inline fun <T> safelyWithResult(
noinline block: suspend CoroutineScope.() -> T
): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
/**
* Use this handler in cases you wish to include handling of exceptions by [defaultSafelyWithoutExceptionHandler] and
* returning null at one time
*
* @see safelyWithoutExceptions
* @see launchSafelyWithoutExceptions
* @see asyncSafelyWithoutExceptions
*/
val defaultSafelyWithoutExceptionHandlerWithNull: ExceptionHandler<Nothing?> = {
defaultSafelyWithoutExceptionHandler.invoke(it)
null
}
/**
* Shortcut for [safely] with exception handler, that as expected must return null in case of impossible creating of
* result from exception (instead of throwing it)
* result from exception (instead of throwing it, by default always returns null)
*/
suspend inline fun <T> safelyWithoutExceptions(
noinline onException: ExceptionHandler<T?>,
noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
noinline block: suspend CoroutineScope.() -> T
): T? = safely(onException, block)
/**
* Shortcut for [safely] without exception handler (instead of this you will always receive null as a result)
*/
suspend inline fun <T> safelyWithoutExceptions(
suspend inline fun <T> runCatchingSafelyWithoutExceptions(
noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
noinline block: suspend CoroutineScope.() -> T
): T? = safelyWithoutExceptions(
{
defaultSafelyWithoutExceptionHandler.invoke(it)
null
},
block
)
): Result<T?> = runCatching {
safelyWithoutExceptions(onException, block)
}

View File

@@ -0,0 +1,41 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
inline fun CoroutineScope.launchSafely(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
noinline onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
noinline block: suspend CoroutineScope.() -> Unit
) = launch(context, start) {
safely(onException, block)
}
inline fun CoroutineScope.launchSafelyWithoutExceptions(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
noinline onException: ExceptionHandler<Unit?> = defaultSafelyWithoutExceptionHandlerWithNull,
noinline block: suspend CoroutineScope.() -> Unit
) = launch(context, start) {
safelyWithoutExceptions(onException, block)
}
inline fun <T> CoroutineScope.asyncSafely(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
noinline onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
noinline block: suspend CoroutineScope.() -> T
) = async(context, start) {
safely(onException, block)
}
inline fun <T> CoroutineScope.asyncSafelyWithoutExceptions(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
noinline block: suspend CoroutineScope.() -> T
) = async(context, start) {
safelyWithoutExceptions(onException, block)
}

View File

@@ -0,0 +1,17 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
fun CoroutineContext.LinkedSupervisorJob(
additionalContext: CoroutineContext? = null
) = SupervisorJob(job).let { if (additionalContext != null) it + additionalContext else it }
fun CoroutineScope.LinkedSupervisorJob(
additionalContext: CoroutineContext? = null
) = coroutineContext.LinkedSupervisorJob(additionalContext)
fun CoroutineScope.LinkedSupervisorScope(
additionalContext: CoroutineContext? = null
) = CoroutineScope(
coroutineContext + LinkedSupervisorJob(additionalContext)
)

View File

@@ -0,0 +1,11 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
val IO
get() = Dispatchers.IO
suspend inline fun <T> doInIO(noinline block: suspend CoroutineScope.() -> T) = doIn(
IO,
block
)

View File

@@ -3,27 +3,21 @@ package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
fun <T> CoroutineScope.launchSynchronously(block: suspend CoroutineScope.() -> T): T {
val deferred = CompletableDeferred<T>()
val objectToSynchronize = java.lang.Object()
val launchCallback = {
var result: Result<T>? = null
val objectToSynchronize = Object()
synchronized(objectToSynchronize) {
launch {
safely(
{
deferred.completeExceptionally(it)
}
) {
deferred.complete(block())
}
result = safelyWithResult(block)
}.invokeOnCompletion {
synchronized(objectToSynchronize) {
objectToSynchronize.notifyAll()
}
}
while (result == null) {
objectToSynchronize.wait()
}
}
synchronized(objectToSynchronize) {
launchCallback()
objectToSynchronize.wait()
}
return deferred.getCompleted()
return result!!.getOrThrow()
}
fun <T> launchSynchronously(block: suspend CoroutineScope.() -> T): T = CoroutineScope(Dispatchers.Default).launchSynchronously(block)

View File

@@ -1,9 +1,8 @@
package dev.inmo.micro_utils.coroutines
import dev.inmo.micro_utils.coroutines.asDeferred
import dev.inmo.micro_utils.coroutines.launchSynchronously
import kotlinx.coroutines.*
import kotlin.test.*
import kotlin.test.Test
import kotlin.test.assertEquals
class DoWithFirstTests {
@Test

View File

@@ -1,18 +0,0 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
suspend inline fun <T> doInUI(noinline block: suspend CoroutineScope.() -> T) = withContext(
Dispatchers.Main,
block
)
suspend inline fun <T> doInDefault(noinline block: suspend CoroutineScope.() -> T) = withContext(
Dispatchers.Default,
block
)
suspend inline fun <T> doInIO(noinline block: suspend CoroutineScope.() -> T) = withContext(
Dispatchers.IO,
block
)

View File

@@ -7,14 +7,14 @@ android.useAndroidX=true
android.enableJetifier=true
org.gradle.jvmargs=-Xmx2g
kotlin_version=1.5.0
kotlin_version=1.5.10
kotlin_coroutines_version=1.5.0
kotlin_serialisation_core_version=1.2.1
kotlin_exposed_version=0.31.1
kotlin_exposed_version=0.32.1
ktor_version=1.5.4
ktor_version=1.6.0
klockVersion=2.1.0
klockVersion=2.1.2
github_release_plugin_version=2.2.12
@@ -45,5 +45,5 @@ dokka_version=1.4.32
# Project data
group=dev.inmo
version=0.5.2
android_code_version=43
version=0.5.8
android_code_version=49

View File

@@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.collect
import kotlinx.serialization.SerializationStrategy
private suspend fun DefaultWebSocketSession.checkReceivedAndCloseIfExists() {
if (incoming.poll() != null) {
if (incoming.tryReceive() != null) {
close()
throw CorrectCloseException
}

View File

@@ -5,8 +5,8 @@ import io.ktor.http.Parameters
val Parameters.extractPagination: Pagination
get() = SimplePagination(
get("page") ?.toIntOrNull() ?: 0,
get("size") ?.toIntOrNull() ?: defaultPaginationPageSize
get(paginationPageKey) ?.toIntOrNull() ?: 0,
get(paginationSizeKey) ?.toIntOrNull() ?: defaultPaginationPageSize
)
val ApplicationCall.extractPagination: Pagination

View File

@@ -6,19 +6,29 @@ 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>,
open class ReadCRUDCacheRepo<ObjectType, IdType>(
protected val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
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)
) : ReadCRUDRepo<ObjectType, IdType> by parentRepo {
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)
}
open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
idGetter: (ObjectType) -> IdType
) : ReadCRUDCacheRepo<ObjectType, IdType>(
parentRepo,
kvCache,
idGetter
), CRUDRepo<ObjectType, IdType, InputValueType>, WriteCRUDRepo<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)
}

View File

@@ -7,14 +7,19 @@ 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>,
open class ReadKeyValueCacheRepo<Key,Value>(
protected val parentRepo: ReadKeyValueRepo<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)
) : ReadKeyValueRepo<Key,Value> by parentRepo {
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)
}
open class KeyValueCacheRepo<Key,Value>(
parentRepo: KeyValueRepo<Key, Value>,
kvCache: KVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : ReadKeyValueCacheRepo<Key,Value>(parentRepo, kvCache), KeyValueRepo<Key,Value>, WriteKeyValueRepo<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)
}

View File

@@ -11,15 +11,10 @@ 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)
open class ReadKeyValuesCacheRepo<Key,Value>(
protected val parentRepo: ReadKeyValuesRepo<Key, Value>,
protected val kvCache: KVCache<Key, List<Value>>
) : ReadKeyValuesRepo<Key,Value> by parentRepo {
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 }
@@ -35,3 +30,13 @@ open class KeyValuesCacheRepo<Key,Value>(
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)
}
open class KeyValuesCacheRepo<Key,Value>(
parentRepo: KeyValuesRepo<Key, Value>,
kvCache: KVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : ReadKeyValuesCacheRepo<Key,Value>(parentRepo, kvCache), KeyValuesRepo<Key,Value>, WriteKeyValuesRepo<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)
}