From 24d240ac0d03caf600d7c736bb972ac1b2fe4d6e Mon Sep 17 00:00:00 2001 From: Isuru Rajapakse Date: Tue, 2 May 2023 17:00:01 +1000 Subject: [PATCH] Update README.md --- README.md | 296 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 173 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index 51631d9..710fcce 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build](https://github.com/xxfast/KStore/actions/workflows/build.yml/badge.svg)](https://github.com/xxfast/KStore/actions/workflows/build.yml) [![Kotlin Alpha](https://kotl.in/badges/alpha.svg)](https://kotlinlang.org/docs/components-stability.html) -[![Kotlin](https://img.shields.io/badge/Kotlin-1.8.20-blue.svg?style=flat&logo=kotlin)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/Kotlin-1.8.21-blue.svg?style=flat&logo=kotlin)](https://kotlinlang.org) [![Maven Central](https://img.shields.io/maven-central/v/io.github.xxfast/kstore?color=blue)](https://search.maven.org/search?q=g:io.github.xxfast) ![badge-android](http://img.shields.io/badge/platform-android-6EDB8D.svg?style=flat) @@ -26,6 +26,27 @@ Inspired by [RxStore](https://github.com/Gridstone/RxStore) - 🚚 Migration support; moving shop? take your data with you - 🚉 Multiplatform! +## At a glace + +```kotlin +// Take any serializable model +@Serializable data class Pet(val name: String, val age: Int) + +// Create a store +val store: KStore = storeOf(filePath = "path/to/my_cats.json") + +// Get, set, update or delete values +val mylo: Pet? = store.get() +store.set(mylo) +store.update { pet: Pet? -> + pet?.copy(age = pet.age + 1) +} +store.delete() + +// Observe for updates +val pets: Flow = store.updates +``` + ## Adding to your project KStore is published on Maven Central @@ -51,45 +72,54 @@ sourceSets { ## Platform configurations KStore provides factory methods to create your platform specific store. There's two variants -### 1. kstore-file -This includes factory methods to create a store that read/writes to a file. -This is suitable for `android`, `ios`, `desktop` and `js { nodejs() }` targets where we have a file system - -Include the dependency in `androidMain`, `iosMain`, `desktopMain` or `jsMain` (only for `nodejs()`). -```kotlin -sourceSets { - // You may define this on commonMain if your js targets nodejs. - val commonMain by getting { - dependencies { - implementation("io.github.xxfast:kstore-file:") +
+ 1. kstore-file + + ### kstore-file + This includes factory methods to create a store that read/writes to a file. + This is suitable for `android`, `ios`, `desktop` and `js { nodejs() }` targets where we have a file system + + Include the dependency in `androidMain`, `iosMain`, `desktopMain` or `jsMain` (only for `nodejs()`). + ```kotlin + sourceSets { + // You may define this on commonMain if your js targets nodejs. + val commonMain by getting { + dependencies { + implementation("io.github.xxfast:kstore-file:") + } } } -} -``` + ``` -Then create a store -```kotlin -val store: KStore = storeOf(filePath = "path/to/my_cats.json") -``` -For full configurations, see [here](#configurations) + Then create a store + ```kotlin + val store: KStore = storeOf(filePath = "path/to/my_cats.json") + ``` + For full configurations, see [here](#configurations) +
-### 2. kstore-storage -This includes factory methods to create a store that read/writes to a key-value storage provider -This is suitable for `js { browser() }` target where we have storage providers (e.g:- localStorage, sessionStorage) +
+ 2. kstore-storage -Include the dependency in `jsMain` (only for `browser()`). -```kotlin -sourceSets { - // only for js { browser() } - val jsMain by getting { dependencies { implementation("io.github.xxfast:kstore-storage:") } } -} -``` -Then create a store -```kotlin -val store: KStore = storeOf(key = "my_cats") -``` + ### kstore-storage -## Usage + This includes factory methods to create a store that read/writes to a key-value storage provider + This is suitable for `js { browser() }` target where we have storage providers (e.g:- localStorage, sessionStorage) + + Include the dependency in `jsMain` (only for `browser()`). + ```kotlin + sourceSets { + // only for js { browser() } + val jsMain by getting { dependencies { implementation("io.github.xxfast:kstore-storage:") } } + } + ``` + Then create a store + ```kotlin + val store: KStore = storeOf(key = "my_cats") + ``` +
+ +## Full usage Given that you have a `@Serializable` model ```kotlin @Serializable data class Pet(val name: String, val age: Int) // Any serializable @@ -147,45 +177,50 @@ You can also reset a value back to its default (if set, see [here](#configuratio store.reset() ``` -### Create a list store +
+ Create a list store -KStore provides you with some convenient extensions to manage stores that contain lists. -`listStoreOf` is the same as `storeOf`, but defaults to empty list instead of `null` -```kotlin -val listStore: KStore> = listStoreOf("path/to/file") -``` + ### Create a list store -#### Get values + KStore provides you with some convenient extensions to manage stores that contain lists. + `listStoreOf` is the same as `storeOf`, but defaults to empty list instead of `null` + ```kotlin + val listStore: KStore> = listStoreOf("path/to/file") + ``` - + #### Get values -```kotlin -val pets: List = listStore.getOrEmpty() -val pet: Cat = store.get(0) -``` + -or observe values + ```kotlin + val pets: List = listStore.getOrEmpty() + val pet: Cat = store.get(0) + ``` -```kotlin -val pets: Flow> = listStore.updatesOrEmpty -``` + or observe values -#### Add or remove elements + ```kotlin + val pets: Flow> = listStore.updatesOrEmpty + ``` - + #### Add or remove elements -```kotlin -listStore.plus(cat) -listStore.minus(cat) -``` + -#### Map elements - + ```kotlin + listStore.plus(cat) + listStore.minus(cat) + ``` -```kotlin -listStore.map { cat -> cat.copy(cat.age = cat.age + 1) } -listStore.mapIndexed { index, cat -> cat.copy(cat.age = index) } -``` + #### Map elements + + + ```kotlin + listStore.map { cat -> cat.copy(cat.age = cat.age + 1) } + listStore.mapIndexed { index, cat -> cat.copy(cat.age = index) } + ``` + +
## Configurations Everything you want is in the factory method @@ -222,44 +257,55 @@ Getting a path to a file is different for each platform and you will need to def ```kotlin expect fun pathTo(id: String): String ``` +
+ On Android + + Getting a path on android involves invoking from `filesDir` from `Context`. + ```kotlin + actual fun pathTo(id: String): String = "${context.filesDir.path}/$id.json" + ``` +
+ +
+ On iOS & other Apple platforms + + To get a path on iOS, you can use `NSHomeDirectory`. + ```kotlin + actual fun pathTo(id: String): String = "${NSHomeDirectory()}/$id.json" + ``` +
+ +
+ On Desktop (JVM) + + This depends on where you want to save your files, but generally you should save your files in a user data directory. + Recommending to use [harawata's appdirs](https://github.com/harawata/appdirs) to get the platform specific app dir + ```kotlin + actual fun pathTo(id: String): String { + // implementation("net.harawata:appdirs:1.2.1") + val appDir: String = AppDirsFactory.getInstance().getUserDataDir(PACKAGE_NAME, VERSION, ORGANISATION) + return "$appDir/$id.json" + } + ``` +
-#### On Android - -Getting a path on android involves invoking from `filesDir` from `Context`. -```kotlin -actual fun pathTo(id: String): String = "${context.filesDir.path}/$id.json" -``` - -#### On iOS & other Apple platforms - -To get a path on iOS, you can use `NSHomeDirectory`. -```kotlin -actual fun pathTo(id: String): String = "${NSHomeDirectory()}/$id.json" -``` - -#### On Desktop +
+ On Browser -This depends on where you want to save your files, but generally you should save your files in a user data directory. -Recommending to use [harawata's appdirs](https://github.com/harawata/appdirs) to get the platform specific app dir -```kotlin -actual fun pathTo(id: String): String { - // implementation("net.harawata:appdirs:1.2.1") - val appDir: String = AppDirsFactory.getInstance().getUserDataDir(PACKAGE_NAME, VERSION, ORGANISATION) - return "$appDir/$id.json" -} -``` + This is straight-forward on a browser, since we are storing on localStorage/sessionStorage, we just need a key name + ```kotlin + actual fun pathTo(name: String): String = name + ``` +
-#### On Browser -This is straight-forward on a browser, since we are storing on localStorage/sessionStorage, we just need a key name -```kotlin -actual fun pathTo(name: String): String = name -``` +
+ On NodeJS -#### On NodeJS -```kotlin -TODO() -``` + ```kotlin + TODO() + ``` +
### 🚚 Migrating stores You can use the existing fields to derive the new fields without needing to write your own migrations @@ -269,36 +315,40 @@ You can use the existing fields to derive the new fields without needing to writ @Serializable data class CatV2(val name: String, val lives: Int = 9, val age: Int = 9 - lives) ``` -#### Binary incompatible changes -If the new models are [binary incompatible](https://github.com/Kotlin/binary-compatibility-validator#what-makes-an-incompatible-change-to-the-public-binary-api) you will need to specify how to migrate the models from version to version - -```kotlin -@Serializable data class CatV1(val name: String, val lives: Int = 9, val cuteness: Int) -@Serializable data class CatV2(val name: String, val lives: Int = 9, val age: Int = 9 - lives, val kawaiiness: Long) -@Serializable data class CatV3(val name: String, val lives: Int = 9, val age: Int = 9 - lives, val isCute: Boolean) - -private val storeV3: KStore = storeOf(filePath = filePath, version = 3) { version, jsonElement -> - when (version) { - 1 -> jsonElement?.jsonObject?.let { - val name = it["name"]!!.jsonPrimitive.content - val lives = it["lives"]!!.jsonPrimitive.int - val age = it["age"]?.jsonPrimitive?.int ?: (9 - lives) - val isCute = it["cuteness"]!!.jsonPrimitive.int.toLong() > 1 - CatV3(name, lives, age, isCute) - } - - 2 -> jsonElement?.jsonObject?.let { - val name = it["name"]!!.jsonPrimitive.content - val lives = it["lives"]!!.jsonPrimitive.int - val age = it["age"]?.jsonPrimitive?.int ?: (9 - lives) - val isCute = it["kawaiiness"]!!.jsonPrimitive.long > 1 - CatV3(name, lives, age, isCute) +
+ What about binary incompatible changes? + + #### Binary incompatible changes + If the new models are [binary incompatible](https://github.com/Kotlin/binary-compatibility-validator#what-makes-an-incompatible-change-to-the-public-binary-api) you will need to specify how to migrate the models from version to version + + ```kotlin + @Serializable data class CatV1(val name: String, val lives: Int = 9, val cuteness: Int) + @Serializable data class CatV2(val name: String, val lives: Int = 9, val age: Int = 9 - lives, val kawaiiness: Long) + @Serializable data class CatV3(val name: String, val lives: Int = 9, val age: Int = 9 - lives, val isCute: Boolean) + + private val storeV3: KStore = storeOf(filePath = filePath, version = 3) { version, jsonElement -> + when (version) { + 1 -> jsonElement?.jsonObject?.let { + val name = it["name"]!!.jsonPrimitive.content + val lives = it["lives"]!!.jsonPrimitive.int + val age = it["age"]?.jsonPrimitive?.int ?: (9 - lives) + val isCute = it["cuteness"]!!.jsonPrimitive.int.toLong() > 1 + CatV3(name, lives, age, isCute) + } + + 2 -> jsonElement?.jsonObject?.let { + val name = it["name"]!!.jsonPrimitive.content + val lives = it["lives"]!!.jsonPrimitive.int + val age = it["age"]?.jsonPrimitive?.int ?: (9 - lives) + val isCute = it["kawaiiness"]!!.jsonPrimitive.long > 1 + CatV3(name, lives, age, isCute) + } + + else -> null } - - else -> null } -} -``` + ``` +
## Licence