Skip to content

Commit

Permalink
feat: add delegate functions
Browse files Browse the repository at this point in the history
  • Loading branch information
adrielcafe committed Aug 12, 2020
1 parent de12506 commit 52ade38
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 12 deletions.
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,13 @@ satchel.apply {

val notificationsEnabled = getOrDefault("notificationsEnabled", false)

val favoritePostIds = getOrElse("favoritePostIds") { emptySet<Int>() }
val favoritePostIds = getOrDefault("favoritePostIds") { emptySet<Int>() }

val registeredAt = getOrSet("registeredAt", currentTimestamp)

set("username", "John Doe")
val lastName = getOrSet("lastName") { "Doe" }

set("username", "john.doe")

setIfAbsent("lastName", lastName)

Expand All @@ -141,7 +143,26 @@ satchel.apply {

But unlike `SharedPreferences`, there's no `apply()` or `commit()`. Changes are **saved asynchronously** every time a write operation (`set()`, `remove()` and `clear()`) happens.

### Listening to events
### Delegates
It's possible to delegate the job of `get` and `set` the value of a specific key:
```kotlin
private var favoritePostIds by satchel.value(key = "favoritePostIds", defaultValue = emptySet<Int>())

// Will call set(key, value)
favoritePostIds = setOf(1, 2, 3)

// Will call getOrDefault(key, defaultValue)
showFavoritePosts(favoritePostIds)
```

If you doesn't specify a default value, it will return a nullable value:
```kotlin
private var username by satchel.value<String>("username")

username?.let(::showProfile)
```

### Events
You can be notified every time the storage changes, just call `addListener()` to register a listener in the specified `CoroutineScope`:
```kotlin
satchel.addListener(lifecycleScope) { event ->
Expand Down Expand Up @@ -284,7 +305,7 @@ val serializer = KryoSatchelSerializer
:warning: At the moment Kryo 5 only works on Android API 26 and later, [this issue](https://github.com/EsotericSoftware/kryo/issues/691) explains how to make it work in previous versions.

#### [ProtobufLiteSatchelSerializer](https://github.com/adrielcafe/satchel/blob/master/satchel-serializer-protobuf-lite/src/main/java/cafe/adriel/satchel/serializer/protobuf/lite/ProtobufLiteSatchelSerializer.kt)
Uses the [Protocol Buffers](https://github.com/protocolbuffers/protobuf) library for serialization/deserialization.
Uses the [Protocol Buffers Java Lite](https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md) library for serialization/deserialization.
```kotlin
val serializer = ProtobufLiteSatchelSerializer
```
Expand Down
32 changes: 31 additions & 1 deletion satchel-core/src/main/java/cafe/adriel/satchel/ktx/satchel.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,48 @@
package cafe.adriel.satchel.ktx

import cafe.adriel.satchel.SatchelStorage
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

inline fun <reified T : Any> SatchelStorage.value(key: String): ReadWriteProperty<Any, T?> =
object : ReadWriteProperty<Any, T?> {
override fun getValue(thisRef: Any, property: KProperty<*>): T? =
get(key)

override fun setValue(thisRef: Any, property: KProperty<*>, value: T?) =
when (value) {
null -> remove(key)
else -> set(key, value)
}
}

inline fun <reified T : Any> SatchelStorage.value(key: String, defaultValue: T): ReadWriteProperty<Any, T> =
object : ReadWriteProperty<Any, T> {
override fun getValue(thisRef: Any, property: KProperty<*>): T =
getOrDefault(key, defaultValue)

override fun setValue(thisRef: Any, property: KProperty<*>, value: T) =
set(key, value)
}

inline infix operator fun <reified T : Any> SatchelStorage.get(key: String): T? =
getAny(key) as? T

inline fun <reified T : Any> SatchelStorage.getOrDefault(key: String, defaultValue: T): T =
get(key) ?: defaultValue

inline fun <reified T : Any> SatchelStorage.getOrElse(key: String, defaultValue: () -> T): T =
inline fun <reified T : Any> SatchelStorage.getOrDefault(key: String, defaultValue: () -> T): T =
get(key) ?: defaultValue()

inline fun <reified T : Any> SatchelStorage.getOrSet(key: String, defaultValue: T): T =
get(key) ?: run {
set(key, defaultValue)
defaultValue
}

inline fun <reified T : Any> SatchelStorage.getOrSet(key: String, defaultValue: () -> T): T =
get(key) ?: run {
val value = defaultValue()
set(key, value)
value
}
33 changes: 26 additions & 7 deletions test/src/test/java/cafe/adriel/satchel/SatchelTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cafe.adriel.satchel
import cafe.adriel.satchel.encrypter.bypass.BypassSatchelEncrypter
import cafe.adriel.satchel.ktx.get
import cafe.adriel.satchel.ktx.getOrDefault
import cafe.adriel.satchel.ktx.getOrElse
import cafe.adriel.satchel.ktx.getOrSet
import cafe.adriel.satchel.serializer.raw.RawSatchelSerializer
import cafe.adriel.satchel.storer.SatchelStorer
Expand Down Expand Up @@ -170,34 +169,34 @@ class SatchelTest {
}

@Nested
inner class GetOrElse {
inner class GetOrDefault {

@Test
fun `when key exists then return the current value`() {
satchel["key"] = "value"

expectThat(satchel.getOrElse("key") { "default value" }) isEqualTo "value"
expectThat(satchel.getOrDefault("key", "default value")) isEqualTo "value"
}

@Test
fun `when key doesn't exist then return the default value`() {
expectThat(satchel.getOrElse("key") { "default value" }) isEqualTo "default value"
expectThat(satchel.getOrDefault("key", "default value")) isEqualTo "default value"
}
}

@Nested
inner class GetOrDefault {
inner class GetOrDefaultLambda {

@Test
fun `when key exists then return the current value`() {
satchel["key"] = "value"

expectThat(satchel.getOrDefault("key", "default value")) isEqualTo "value"
expectThat(satchel.getOrDefault("key") { "default value" }) isEqualTo "value"
}

@Test
fun `when key doesn't exist then return the default value`() {
expectThat(satchel.getOrDefault("key", "default value")) isEqualTo "default value"
expectThat(satchel.getOrDefault("key") { "default value" }) isEqualTo "default value"
}
}

Expand All @@ -221,6 +220,26 @@ class SatchelTest {
}
}

@Nested
inner class GetOrSetLambda {

@Test
fun `when key exists then return the current value`() {
satchel["key"] = "value"

expectThat(satchel.getOrSet("key") { "default value" }) isEqualTo "value"

verify(exactly = 0) { satchel["key"] = "default value" }
}

@Test
fun `when key doesn't exist then return the default value and set it`() {
expectThat(satchel.getOrSet("key") { "default value" }) isEqualTo "default value"

verify(exactly = 1) { satchel["key"] = "default value" }
}
}

@Nested
inner class Set {

Expand Down

0 comments on commit 52ade38

Please sign in to comment.