mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-17 14:29:24 +00:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
1a41f37a9d | |||
07a65e0bb5 | |||
615f7f99c3 | |||
3de5558ed4 | |||
6bbe3a271f | |||
8bee29f683 | |||
c40f0fdcb9 | |||
30d1453a12 | |||
75fa88b00d | |||
0f817ad212 | |||
d7b46ae0d4 | |||
b6be14ecca | |||
33dbfc6f69 | |||
fcacdcd544 | |||
dd2fc5a86f | |||
0f8a6f6bde | |||
1efd94181d | |||
71ff0232aa | |||
63921cd984 | |||
051e03bed3 | |||
a051394f4f |
@@ -22,5 +22,6 @@ jobs:
|
||||
include:
|
||||
- stage: build
|
||||
script: ./gradlew build -s -x jvmTest -x jsIrTest -x jsIrBrowserTest -x jsIrNodeTest -x jsLegacyTest -x jsLegacyBrowserTest -x jsLegacyNodeTest
|
||||
- state: test
|
||||
script: ./gradlew allTests
|
||||
# Tests are temporarily disabled on public travis due to the problems of launching
|
||||
# - state: test
|
||||
# script: ./gradlew allTests
|
||||
|
36
CHANGELOG.md
36
CHANGELOG.md
@@ -1,5 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
## 0.4.4
|
||||
|
||||
* `Versions`:
|
||||
* `Klock`: `1.12.1` -> `2.0.0`
|
||||
* `Repos`:
|
||||
* Add interface `VersionsRepo`
|
||||
* Add default realization of `VersionsRepo` named `StandardVersionsRepo` which use `StandardVersionsRepoProxy`
|
||||
to get access to some end-store
|
||||
* Add default realization of `StandardVersionsRepoProxy` based on `KeyValue` repos
|
||||
* Add realizations of `StandardVersionsRepoProxy` for exposed and android (`SQL` and `SharedPreferences`)
|
||||
* `Commons`:
|
||||
* In Android fully reworked transactions functions
|
||||
* Now `DatabaseCoroutineContext` is a shortcut for `Dispatchers.IO`
|
||||
|
||||
## 0.4.3
|
||||
|
||||
* `Versions`:
|
||||
* `Kotlin`: `1.4.10` -> `1.4.20`
|
||||
* `Common`:
|
||||
* Two new extensions for Android:
|
||||
* `Resources#getSp`
|
||||
* `Resources#getDp`
|
||||
|
||||
## 0.4.2
|
||||
|
||||
* `Repos`:
|
||||
* Add `WriteOneToManyKeyValueRepo#set` function and extensions
|
||||
|
||||
## 0.4.1
|
||||
|
||||
* `Repos`:
|
||||
* Fixed error in `ExposedKeyValueRepo` related to negative size of shared flow
|
||||
* Fixed error in `ExposedKeyValueRepo` related to avoiding of table initiation
|
||||
|
||||
## 0.4.0
|
||||
|
||||
* `Repos`:
|
||||
@@ -224,4 +258,4 @@ All deprecations has been removed
|
||||
|
||||
## 0.1.0
|
||||
|
||||
Inited :)
|
||||
Inited :)
|
||||
|
@@ -8,10 +8,9 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
||||
classpath 'com.android.tools.build:gradle:4.0.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version"
|
||||
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
|
||||
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
|
||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
|
||||
|
@@ -0,0 +1,13 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package dev.inmo.micro_utils.common
|
||||
|
||||
import android.content.res.Resources
|
||||
|
||||
inline fun Resources.getSp(
|
||||
resId: Int
|
||||
) = getDimension(resId) / displayMetrics.scaledDensity
|
||||
|
||||
inline fun Resources.getDp(
|
||||
resId: Int
|
||||
) = getDimension(resId) * displayMetrics.density
|
@@ -7,16 +7,15 @@ android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
org.gradle.jvmargs=-Xmx3072m
|
||||
|
||||
kotlin_version=1.4.10
|
||||
kotlin_version=1.4.20
|
||||
kotlin_coroutines_version=1.4.1
|
||||
kotlin_serialisation_core_version=1.0.1
|
||||
kotlin_exposed_version=0.28.1
|
||||
|
||||
ktor_version=1.4.2
|
||||
|
||||
klockVersion=1.12.1
|
||||
klockVersion=2.0.0
|
||||
|
||||
gradle_bintray_plugin_version=1.8.5
|
||||
github_release_plugin_version=2.2.12
|
||||
|
||||
uuidVersion=0.2.2
|
||||
@@ -41,5 +40,5 @@ dokka_version=1.4.0
|
||||
# Project data
|
||||
|
||||
group=dev.inmo
|
||||
version=0.4.0
|
||||
android_code_version=4
|
||||
version=0.4.4
|
||||
android_code_version=8
|
||||
|
@@ -1,60 +0,0 @@
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
task javadocsJar(type: Jar) {
|
||||
classifier = 'javadoc'
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
project.publishing.publications.all {
|
||||
// rename artifacts
|
||||
groupId "${project.group}"
|
||||
if (it.name.contains('kotlinMultiplatform')) {
|
||||
artifactId = "${project.name}"
|
||||
} else {
|
||||
artifactId = "${project.name}-$name"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.all {
|
||||
artifact javadocsJar
|
||||
|
||||
pom {
|
||||
description = ""
|
||||
name = "${project.name}"
|
||||
url = "https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror"
|
||||
|
||||
scm {
|
||||
developerConnection = "scm:git:[fetch=]ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git[push=]ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git"
|
||||
url = "ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git"
|
||||
}
|
||||
|
||||
developers {
|
||||
|
||||
developer {
|
||||
id = "InsanusMokrassar"
|
||||
name = "Aleksei Ovsiannikov"
|
||||
email = "ovsyannikov.alexey95@gmail.com"
|
||||
}
|
||||
|
||||
|
||||
developer {
|
||||
id = "000Sanya"
|
||||
name = "Syrov Aleksandr"
|
||||
email = "000sanya.000sanya@gmail.com"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
licenses {
|
||||
|
||||
license {
|
||||
name = "Apache Software License 2.0"
|
||||
url = "https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror/src/master/LICENSE"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1 +1 @@
|
||||
{"bintrayConfig":{"repo":"MicroUtils","packageName":"${project.name}","packageVcs":"https://github.com/InsanusMokrassar/MicroUtils","autoPublish":true,"overridePublish":true},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror/src/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"","url":"https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror","vcsUrl":"ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}]},"type":"Multiplatform"}
|
||||
{"bintrayConfig":{"repo":"MicroUtils","packageName":"${project.name}","packageVcs":"https://github.com/InsanusMokrassar/MicroUtils","autoPublish":true,"overridePublish":true},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror/src/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"","url":"https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror","vcsUrl":"ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}]}}
|
125
publish.gradle
125
publish.gradle
@@ -1,69 +1,72 @@
|
||||
apply plugin: 'com.jfrog.bintray'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
apply from: "$publishMavenPath"
|
||||
|
||||
bintray {
|
||||
user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')
|
||||
key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
|
||||
filesSpec {
|
||||
from "${buildDir}/publications/"
|
||||
eachFile {
|
||||
String directorySubname = it.getFile().parentFile.name
|
||||
if (it.getName() == "module.json") {
|
||||
if (directorySubname == "kotlinMultiplatform") {
|
||||
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.module")
|
||||
} else {
|
||||
switch (directorySubname) {
|
||||
case "androidRelease":
|
||||
it.setPath("${project.name}-android/${project.version}/${project.name}-android-${project.version}.module")
|
||||
break
|
||||
case "androidDebug":
|
||||
it.setPath("${project.name}-android-debug/${project.version}/${project.name}-android-debug-${project.version}.module")
|
||||
break
|
||||
default:
|
||||
it.setPath("${project.name}-${directorySubname}/${project.version}/${project.name}-${directorySubname}-${project.version}.module")
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (directorySubname == "kotlinMultiplatform" && it.getName() == "pom-default.xml") {
|
||||
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.pom")
|
||||
} else {
|
||||
it.exclude()
|
||||
}
|
||||
}
|
||||
}
|
||||
into "${project.group}".replace(".", "/")
|
||||
}
|
||||
|
||||
publish = true
|
||||
override = true
|
||||
|
||||
pkg {
|
||||
repo = "MicroUtils"
|
||||
name = "${project.name}"
|
||||
vcsUrl = "https://github.com/InsanusMokrassar/MicroUtils"
|
||||
licenses = ["Apache-2.0"]
|
||||
version {
|
||||
name = "${project.version}"
|
||||
released = new Date()
|
||||
vcsTag = "${project.version}"
|
||||
gpg {
|
||||
sign = true
|
||||
passphrase = project.hasProperty('signing.gnupg.passphrase') ? project.property('signing.gnupg.passphrase') : System.getenv('signing.gnupg.passphrase')
|
||||
}
|
||||
}
|
||||
}
|
||||
task javadocsJar(type: Jar) {
|
||||
classifier = 'javadoc'
|
||||
}
|
||||
|
||||
bintrayUpload.doFirst {
|
||||
publications = publishing.publications.collect {
|
||||
afterEvaluate {
|
||||
project.publishing.publications.all {
|
||||
// rename artifacts
|
||||
groupId "${project.group}"
|
||||
if (it.name.contains('kotlinMultiplatform')) {
|
||||
null
|
||||
artifactId = "${project.name}"
|
||||
} else {
|
||||
it.name
|
||||
artifactId = "${project.name}-$name"
|
||||
}
|
||||
} - null
|
||||
}
|
||||
}
|
||||
|
||||
bintrayUpload.dependsOn publishToMavenLocal
|
||||
publishing {
|
||||
publications.all {
|
||||
artifact javadocsJar
|
||||
|
||||
pom {
|
||||
description = ""
|
||||
name = "${project.name}"
|
||||
url = "https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror"
|
||||
|
||||
scm {
|
||||
developerConnection = "scm:git:[fetch=]ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git[push=]ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git"
|
||||
url = "ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git"
|
||||
}
|
||||
|
||||
developers {
|
||||
|
||||
developer {
|
||||
id = "InsanusMokrassar"
|
||||
name = "Aleksei Ovsiannikov"
|
||||
email = "ovsyannikov.alexey95@gmail.com"
|
||||
}
|
||||
|
||||
|
||||
developer {
|
||||
id = "000Sanya"
|
||||
name = "Syrov Aleksandr"
|
||||
email = "000sanya.000sanya@gmail.com"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
licenses {
|
||||
|
||||
license {
|
||||
name = "Apache Software License 2.0"
|
||||
url = "https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror/src/master/LICENSE"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "bintray"
|
||||
url = uri("https://api.bintray.com/maven/${project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')}/MicroUtils/${project.name}/;publish=1;override=1")
|
||||
credentials {
|
||||
username = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')
|
||||
password = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -47,6 +47,11 @@ interface WriteOneToManyKeyValueRepo<Key, Value> : Repo {
|
||||
suspend fun remove(toRemove: Map<Key, List<Value>>)
|
||||
|
||||
suspend fun clear(k: Key)
|
||||
|
||||
suspend fun set(toSet: Map<Key, List<Value>>) {
|
||||
toSet.keys.forEach { key -> clear(key) }
|
||||
add(toSet)
|
||||
}
|
||||
}
|
||||
|
||||
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.add(
|
||||
@@ -65,6 +70,22 @@ suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.add(
|
||||
k: Key, vararg v: Value
|
||||
) = add(k, v.toList())
|
||||
|
||||
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.set(
|
||||
keysAndValues: List<Pair<Key, List<Value>>>
|
||||
) = set(keysAndValues.toMap())
|
||||
|
||||
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.set(
|
||||
vararg keysAndValues: Pair<Key, List<Value>>
|
||||
) = set(keysAndValues.toMap())
|
||||
|
||||
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.set(
|
||||
k: Key, v: List<Value>
|
||||
) = set(mapOf(k to v))
|
||||
|
||||
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.set(
|
||||
k: Key, vararg v: Value
|
||||
) = set(k, v.toList())
|
||||
|
||||
interface OneToManyKeyValueRepo<Key, Value> : ReadOneToManyKeyValueRepo<Key, Value>, WriteOneToManyKeyValueRepo<Key, Value>
|
||||
|
||||
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.remove(
|
||||
|
@@ -107,6 +107,12 @@ open class MapperWriteOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
|
||||
}.toMap()
|
||||
)
|
||||
|
||||
override suspend fun set(toSet: Map<FromKey, List<FromValue>>) {
|
||||
to.set(
|
||||
toSet.map { (k, vs) -> k.toOutKey() to vs.map { v -> v.toOutValue() } }.toMap()
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun clear(k: FromKey) = to.clear(k.toOutKey())
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,13 @@
|
||||
package dev.inmo.micro_utils.repos.versions
|
||||
|
||||
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
|
||||
import dev.inmo.micro_utils.repos.set
|
||||
|
||||
class KeyValueBasedVersionsRepoProxy<T>(
|
||||
private val keyValueStore: StandardKeyValueRepo<String, Int>,
|
||||
override val database: T
|
||||
) : StandardVersionsRepoProxy<T> {
|
||||
override suspend fun getTableVersion(tableName: String): Int? = keyValueStore.get(tableName)
|
||||
|
||||
override suspend fun updateTableVersion(tableName: String, version: Int) { keyValueStore.set(tableName, version) }
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package dev.inmo.micro_utils.repos.versions
|
||||
|
||||
import dev.inmo.micro_utils.repos.Repo
|
||||
|
||||
interface StandardVersionsRepoProxy<T> : Repo {
|
||||
val database: T
|
||||
|
||||
suspend fun getTableVersion(tableName: String): Int?
|
||||
suspend fun updateTableVersion(tableName: String, version: Int)
|
||||
}
|
||||
|
||||
class StandardVersionsRepo<T>(
|
||||
private val proxy: StandardVersionsRepoProxy<T>
|
||||
) : VersionsRepo<T> {
|
||||
override suspend fun setTableVersion(
|
||||
tableName: String,
|
||||
version: Int,
|
||||
onCreate: suspend T.() -> Unit,
|
||||
onUpdate: suspend T.(from: Int, to: Int) -> Unit
|
||||
) {
|
||||
var savedVersion = proxy.getTableVersion(tableName)
|
||||
if (savedVersion == null) {
|
||||
proxy.database.onCreate()
|
||||
proxy.updateTableVersion(tableName, version)
|
||||
} else {
|
||||
while (savedVersion != null && savedVersion < version) {
|
||||
val newVersion = savedVersion + 1
|
||||
|
||||
proxy.database.onUpdate(savedVersion, newVersion)
|
||||
|
||||
proxy.updateTableVersion(tableName, newVersion)
|
||||
savedVersion = newVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package dev.inmo.micro_utils.repos.versions
|
||||
|
||||
import dev.inmo.micro_utils.repos.Repo
|
||||
|
||||
/**
|
||||
* This interface has been created due to requirement to work with different versions of databases and make some
|
||||
* migrations between versions
|
||||
*
|
||||
* @param T It is a type of database, which will be used by this repo to retrieve current table version and update it
|
||||
*/
|
||||
interface VersionsRepo<T> : Repo {
|
||||
/**
|
||||
* By default, instance of this interface will check that version of table with name [tableName] is less than
|
||||
* [version] or is absent
|
||||
*
|
||||
* * In case if [tableName] didn't found, will be called [onCreate] and version of table will be set up to [version]
|
||||
* * In case if [tableName] have version less than parameter [version], it will increase version one-by-one
|
||||
* until database version will be equal to [version]
|
||||
*
|
||||
* @param version Current version of table
|
||||
* @param onCreate This callback will be called in case when table have no information about table
|
||||
* @param onUpdate This callback will be called after **iterative** changing of version. It is expected that parameter
|
||||
* "to" will always be greater than "from"
|
||||
*/
|
||||
suspend fun setTableVersion(
|
||||
tableName: String,
|
||||
version: Int,
|
||||
onCreate: suspend T.() -> Unit = {},
|
||||
onUpdate: suspend T.(from: Int, to: Int) -> Unit = { _, _ ->}
|
||||
)
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package dev.inmo.micro_utils.repos
|
||||
|
||||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
val DatabaseCoroutineContext: CoroutineContext = newSingleThreadContext("db-context")
|
||||
val DatabaseCoroutineContext: CoroutineContext = Dispatchers.IO
|
||||
|
@@ -2,26 +2,68 @@ package dev.inmo.micro_utils.repos
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import dev.inmo.micro_utils.coroutines.safely
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
private object ContextsPool {
|
||||
private val contexts = mutableListOf<CoroutineContext>()
|
||||
private val mutex = Mutex(locked = false)
|
||||
private val freeContexts = mutableListOf<CoroutineContext>()
|
||||
|
||||
suspend fun acquireContext(): CoroutineContext {
|
||||
return mutex.withLock {
|
||||
freeContexts.removeFirstOrNull() ?: Executors.newSingleThreadExecutor().asCoroutineDispatcher().also {
|
||||
contexts.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun freeContext(context: CoroutineContext) {
|
||||
return mutex.withLock {
|
||||
if (context in contexts && context !in freeContexts) {
|
||||
freeContexts.add(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <T> use(block: suspend (CoroutineContext) -> T): T = acquireContext().let {
|
||||
try {
|
||||
block(it)
|
||||
} finally {
|
||||
freeContext(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TransactionContext(
|
||||
val databaseContext: CoroutineContext
|
||||
): CoroutineContext.Element {
|
||||
override val key: CoroutineContext.Key<TransactionContext> = TransactionContext
|
||||
|
||||
companion object : CoroutineContext.Key<TransactionContext>
|
||||
}
|
||||
|
||||
suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T): T {
|
||||
return withContext(DatabaseCoroutineContext) {
|
||||
when {
|
||||
inTransaction() -> {
|
||||
block()
|
||||
}
|
||||
else -> {
|
||||
beginTransaction()
|
||||
safely(
|
||||
{
|
||||
endTransaction()
|
||||
throw it
|
||||
}
|
||||
) {
|
||||
block().also {
|
||||
setTransactionSuccessful()
|
||||
endTransaction()
|
||||
}
|
||||
return coroutineContext[TransactionContext] ?.let {
|
||||
withContext(it.databaseContext) {
|
||||
block()
|
||||
}
|
||||
} ?: ContextsPool.use { context ->
|
||||
withContext(TransactionContext(context) + context) {
|
||||
beginTransaction()
|
||||
safely(
|
||||
{
|
||||
endTransaction()
|
||||
throw it
|
||||
}
|
||||
) {
|
||||
block().also {
|
||||
setTransactionSuccessful()
|
||||
endTransaction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import android.database.DatabaseErrorHandler
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import dev.inmo.micro_utils.coroutines.safely
|
||||
import dev.inmo.micro_utils.repos.versions.*
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
@@ -36,6 +37,9 @@ class StandardSQLHelper(
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {}
|
||||
}
|
||||
val versionsRepo: VersionsRepo<SQLiteOpenHelper> by lazy {
|
||||
StandardVersionsRepo(AndroidSQLStandardVersionsRepoProxy(sqlOpenHelper))
|
||||
}
|
||||
|
||||
suspend fun <T> writableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.writableTransaction(block)
|
||||
|
||||
|
@@ -87,6 +87,29 @@ class OneToManyAndroidRepo<Key, Value>(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun set(toSet: Map<Key, List<Value>>) {
|
||||
val (clearedKeys, inserted) = helper.writableTransaction {
|
||||
toSet.mapNotNull { (k, _) ->
|
||||
if (delete(tableName, "$idColumnName=?", arrayOf(k.asId())) > 0) {
|
||||
k
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} to toSet.flatMap { (k, values) ->
|
||||
values.map { v ->
|
||||
insert(
|
||||
tableName,
|
||||
null,
|
||||
contentValuesOf(idColumnName to k.asId(), valueColumnName to v.asValue())
|
||||
)
|
||||
k to v
|
||||
}
|
||||
}
|
||||
}
|
||||
clearedKeys.forEach { _onDataCleared.emit(it) }
|
||||
inserted.forEach { newPair -> _onNewValue.emit(newPair) }
|
||||
}
|
||||
|
||||
override suspend fun contains(k: Key): Boolean = helper.readableTransaction {
|
||||
select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use {
|
||||
it.count > 0
|
||||
|
@@ -0,0 +1,63 @@
|
||||
package dev.inmo.micro_utils.repos.versions
|
||||
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import androidx.core.content.contentValuesOf
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Will create [VersionsRepo] based on [SQLiteOpenHelper] with table inside of [database]
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun versionsRepo(database: SQLiteOpenHelper): VersionsRepo<SQLiteOpenHelper> = StandardVersionsRepo(
|
||||
AndroidSQLStandardVersionsRepoProxy(database)
|
||||
)
|
||||
|
||||
class AndroidSQLStandardVersionsRepoProxy(
|
||||
override val database: SQLiteOpenHelper
|
||||
) : StandardVersionsRepoProxy<SQLiteOpenHelper> {
|
||||
private val tableName: String = "AndroidSQLStandardVersionsRepo"
|
||||
private val tableNameColumnName = "tableName"
|
||||
private val tableVersionColumnName = "version"
|
||||
|
||||
init {
|
||||
runBlocking(DatabaseCoroutineContext) {
|
||||
database.writableTransaction {
|
||||
createTable(
|
||||
tableName,
|
||||
tableNameColumnName to ColumnType.Text.NOT_NULLABLE,
|
||||
tableVersionColumnName to ColumnType.Numeric.INTEGER()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getTableVersion(table: String): Int? = database.writableTransaction {
|
||||
select(
|
||||
tableName,
|
||||
selection = "$tableNameColumnName=?",
|
||||
selectionArgs = arrayOf(table),
|
||||
limit = limitClause(1)
|
||||
).use {
|
||||
if (it.moveToFirst()) {
|
||||
it.getInt(tableVersionColumnName)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateTableVersion(table: String, version: Int) {
|
||||
database.writableTransaction {
|
||||
val updated = update(
|
||||
tableName,
|
||||
contentValuesOf(tableVersionColumnName to version),
|
||||
"$tableNameColumnName=?",
|
||||
arrayOf(table)
|
||||
) > 0
|
||||
if (!updated) {
|
||||
insert(tableName, null, contentValuesOf(tableNameColumnName to table, tableVersionColumnName to version))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package dev.inmo.micro_utils.repos.versions
|
||||
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import androidx.core.content.contentValuesOf
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import dev.inmo.micro_utils.repos.keyvalue.keyValueStore
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Will create [VersionsRepo] based on [T], but versions will be stored in [StandardKeyValueRepo]
|
||||
*
|
||||
* @receiver Will be used to create [KeyValueBasedVersionsRepoProxy] via [keyValueStore] and pass it to [StandardVersionsRepo]
|
||||
*
|
||||
* @see [KeyValueBasedVersionsRepoProxy]
|
||||
* @see [keyValueStore]
|
||||
*/
|
||||
inline fun <T> Context.versionsKeyValueRepo(
|
||||
database: T
|
||||
): VersionsRepo<T> = StandardVersionsRepo(
|
||||
KeyValueBasedVersionsRepoProxy(
|
||||
keyValueStore("SPVersionsRepo"),
|
||||
database
|
||||
)
|
||||
)
|
||||
/**
|
||||
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [StandardKeyValueRepo]
|
||||
*
|
||||
* @receiver Will be used to create [StandardKeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
|
||||
*
|
||||
* @see [keyValueStore]
|
||||
*/
|
||||
inline fun Context.versionsKeyValueRepoForSQL(
|
||||
database: SQLiteOpenHelper
|
||||
) = versionsKeyValueRepo(database)
|
||||
|
||||
/**
|
||||
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [StandardKeyValueRepo]
|
||||
*
|
||||
* @param context Will be used to create [StandardKeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
|
||||
*
|
||||
* @see [keyValueStore]
|
||||
*/
|
||||
inline fun versionsRepo(
|
||||
context: Context,
|
||||
database: SQLiteOpenHelper
|
||||
) = context.versionsKeyValueRepoForSQL(database)
|
@@ -2,6 +2,7 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue
|
||||
|
||||
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
|
||||
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
|
||||
import dev.inmo.micro_utils.repos.exposed.initTable
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.jetbrains.exposed.sql.*
|
||||
@@ -18,8 +19,8 @@ open class ExposedKeyValueRepo<Key, Value>(
|
||||
valueColumnAllocator,
|
||||
tableName
|
||||
) {
|
||||
private val _onNewValue = MutableSharedFlow<Pair<Key, Value>>(Channel.BUFFERED)
|
||||
private val _onValueRemoved = MutableSharedFlow<Key>(Channel.BUFFERED)
|
||||
private val _onNewValue = MutableSharedFlow<Pair<Key, Value>>()
|
||||
private val _onValueRemoved = MutableSharedFlow<Key>()
|
||||
|
||||
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
|
||||
override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
|
||||
|
@@ -2,8 +2,7 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue
|
||||
|
||||
import dev.inmo.micro_utils.pagination.*
|
||||
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
|
||||
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
|
||||
import dev.inmo.micro_utils.repos.exposed.ExposedRepo
|
||||
import dev.inmo.micro_utils.repos.exposed.*
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
@@ -17,6 +16,8 @@ open class ExposedReadKeyValueRepo<Key, Value>(
|
||||
protected val valueColumn: Column<Value> = valueColumnAllocator()
|
||||
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn)
|
||||
|
||||
init { initTable() }
|
||||
|
||||
override suspend fun get(k: Key): Value? = transaction(database) {
|
||||
select { keyColumn.eq(k) }.limit(1).firstOrNull() ?.getOrNull(valueColumn)
|
||||
}
|
||||
@@ -33,8 +34,8 @@ open class ExposedReadKeyValueRepo<Key, Value>(
|
||||
}
|
||||
}.createPaginationResult(pagination, count())
|
||||
|
||||
override suspend fun keys(value: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
|
||||
select { valueColumn.eq(value) }.let {
|
||||
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
|
||||
select { valueColumn.eq(v) }.let {
|
||||
it.count() to it.paginate(pagination, keyColumn to if (reversed) SortOrder.DESC else SortOrder.ASC).map {
|
||||
it[keyColumn]
|
||||
}
|
||||
|
@@ -0,0 +1,48 @@
|
||||
package dev.inmo.micro_utils.repos.exposed.versions
|
||||
|
||||
import dev.inmo.micro_utils.repos.exposed.ExposedRepo
|
||||
import dev.inmo.micro_utils.repos.exposed.initTable
|
||||
import dev.inmo.micro_utils.repos.versions.*
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
/**
|
||||
* Use this method to create [StandardVersionsRepo] based on [Database] with [ExposedStandardVersionsRepoProxy] as
|
||||
* [StandardVersionsRepoProxy]
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun versionsRepo(database: Database): VersionsRepo<Database> = StandardVersionsRepo(
|
||||
ExposedStandardVersionsRepoProxy(database)
|
||||
)
|
||||
|
||||
class ExposedStandardVersionsRepoProxy(
|
||||
override val database: Database
|
||||
) : StandardVersionsRepoProxy<Database>, Table("ExposedVersionsProxy"), ExposedRepo {
|
||||
private val tableNameColumn = text("tableName")
|
||||
private val tableVersionColumn = integer("tableName")
|
||||
|
||||
init {
|
||||
initTable()
|
||||
}
|
||||
|
||||
override suspend fun getTableVersion(tableName: String): Int? = transaction(database) {
|
||||
select { tableNameColumn.eq(tableName) }.limit(1).firstOrNull() ?.getOrNull(tableVersionColumn)
|
||||
}
|
||||
|
||||
override suspend fun updateTableVersion(tableName: String, version: Int) {
|
||||
transaction(database) {
|
||||
val updated = update(
|
||||
{ tableNameColumn.eq(tableName) }
|
||||
) {
|
||||
it[tableVersionColumn] = version
|
||||
} > 0
|
||||
if (!updated) {
|
||||
insert {
|
||||
it[tableNameColumn] = tableName
|
||||
it[tableVersionColumn] = version
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -42,12 +42,12 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
|
||||
override suspend fun add(toAdd: Map<Key, List<Value>>) = client.unipost(
|
||||
buildStandardUrl(
|
||||
baseUrl,
|
||||
clearRoute,
|
||||
addRoute,
|
||||
),
|
||||
BodyPair(keyValueMapSerializer, toAdd),
|
||||
Unit.serializer(),
|
||||
)
|
||||
override suspend fun clear(k: Key) = client.unipost(
|
||||
override suspend fun clear(k: Key) = client.unipost(
|
||||
buildStandardUrl(
|
||||
baseUrl,
|
||||
clearRoute,
|
||||
@@ -55,4 +55,13 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
|
||||
BodyPair(keySerializer, k),
|
||||
Unit.serializer(),
|
||||
)
|
||||
|
||||
override suspend fun set(toSet: Map<Key, List<Value>>) = client.unipost(
|
||||
buildStandardUrl(
|
||||
baseUrl,
|
||||
setRoute,
|
||||
),
|
||||
BodyPair(keyValueMapSerializer, toSet),
|
||||
Unit.serializer(),
|
||||
)
|
||||
}
|
@@ -13,4 +13,5 @@ const val onDataClearedRoute = "onDataCleared"
|
||||
|
||||
const val addRoute = "add"
|
||||
const val removeRoute = "remove"
|
||||
const val clearRoute = "clear"
|
||||
const val clearRoute = "clear"
|
||||
const val setRoute = "set"
|
@@ -61,4 +61,13 @@ fun <Key, Value> Route.configureOneToManyWriteKeyValueRepoRoutes(
|
||||
originalRepo.clear(key),
|
||||
)
|
||||
}
|
||||
|
||||
post(setRoute) {
|
||||
val obj = call.uniload(keyValueMapSerializer)
|
||||
|
||||
call.unianswer(
|
||||
Unit.serializer(),
|
||||
originalRepo.set(obj)
|
||||
)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user