Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
[ ![Download](https://api.bintray.com/packages/elementsinteractive/maven/ObjectStore/images/download.svg) ](https://bintray.com/elementsinteractive/maven/ObjectStore/_latestVersion)
# ObjectStore

[![kotlin](https://img.shields.io/badge/kotlin-1.3.31-blue.svg)]()

###### Convenient interface for persisting objects.
``` groovy
implementation "nl.elements.objectstore:objectstore:+"
Expand All @@ -19,6 +22,21 @@ fun example(store: ObjectStore) {
}
```

#### Define a model

```kotlin
class AppData : ObjectStoreModel(InMemoryStore()) {
var username by stringItem()
val lastname by nullableStringItem()
var age by intItem()
var hasSeenOnboarding by booleanItem()
val landedOnDashboard by booleanNullabeItem()
var lastSeen by longItem()
var rating by floatItem()
}
```


## Observing
Each `ObjectStore` is (Rx) observable and will emit whenever something changes in store.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class PreferencesStoreTest {
val key = "contains"
val name = "Danny"

store.set(key, name)
store[key] = name

assertTrue(store.contains(key))
}
Expand Down Expand Up @@ -92,7 +92,7 @@ class PreferencesStoreTest {
val key = "name"
val value = "Danny"

store.set(key, value)
store[key] = value

val actual = store.keys

Expand All @@ -102,7 +102,7 @@ class PreferencesStoreTest {
}
}

private fun <T : Any> ObjectStore.setAndGet(key: String, value: T): T {
private fun <T : Any> ObjectStore.setAndGet(key: String, value: T): T? {
set(key, value)
return this[key]
}
Expand Down
10 changes: 5 additions & 5 deletions library/src/main/java/nl/elements/objectstore/Converter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ interface Converter {
* Convert the value into bytes that will be used for IO.
*/

fun serialize(key: String, value: Any, output: OutputStream)
fun serialize(key: String, value: Any?, output: OutputStream)

/**
* Convert the bytes from IO into the desired object.
*/

fun <T : Any> deserialize(key: String, input: InputStream): T
fun <T : Any> deserialize(key: String, input: InputStream): T?

companion object {

val DEFAULT = object : Converter {

override fun serialize(key: String, value: Any, output: OutputStream) =
override fun serialize(key: String, value: Any?, output: OutputStream) =
ObjectOutputStream(output).writeObject(value)

@Suppress("UNCHECKED_CAST")
override fun <T : Any> deserialize(key: String, input: InputStream): T =
ObjectInputStream(input).readObject() as T
override fun <T : Any> deserialize(key: String, input: InputStream): T? =
ObjectInputStream(input).readObject() as T?

}

Expand Down
14 changes: 7 additions & 7 deletions library/src/main/java/nl/elements/objectstore/ObjectStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ interface ObjectStore : ReadableObjectStore, ObservableSource<ObjectStore.Event>
val converter: Converter
val transformer: Transformer

operator fun set(key: String, value: Any)
operator fun set(key: String, value: Any?)

fun remove(key: String)

fun OutputStream.write(key: String, value: Any) =
fun OutputStream.write(key: String, value: Any?) =
ByteArrayOutputStream()
.also { converter.serialize(key, value, it) }
.toByteArray()
.inputStream()
.let { transformer.write(key, it, this@write) }

fun <T : Any> InputStream.read(key: String): T =
fun <T : Any> InputStream.read(key: String): T? =
ByteArrayOutputStream()
.also { transformer.read(key, this@read, it) }
.toByteArray()
Expand Down Expand Up @@ -57,7 +57,7 @@ fun ObjectStore.toObservable(): Observable<ObjectStore.Event> = Observable.defer

fun ObjectStore.toReadableObjectStore(): ReadableObjectStore = this

fun ObjectStore.writeToBytes(key: String, value: Any): ByteArray =
fun ObjectStore.writeToBytes(key: String, value: Any?): ByteArray =
ByteArrayOutputStream()
.also { it.write(key, value) }
.toByteArray()
Expand Down Expand Up @@ -114,13 +114,13 @@ private fun ObjectStore.withNamespace(namespace: String, next: ObjectStore): Obj
override val keys: Set<String>
get() = store.keys.map { namespace + it }.toMutableSet().apply { addAll(next.keys) }

override fun <T : Any> get(key: String): T =
override fun <T : Any> get(key: String): T? =
key.removeNamespace()?.let(store::get) ?: next[key]

override fun contains(key: String): Boolean =
key.removeNamespace()?.let(store::contains) ?: next.contains(key)

override fun set(key: String, value: Any) =
override fun set(key: String, value: Any?) =
key.removeNamespace()?.let { store[it] = value } ?: next.set(key, value)

override fun remove(key: String) = key.removeNamespace()?.let(store::remove) ?: next.remove(key)
Expand Down Expand Up @@ -148,7 +148,7 @@ private fun unknownNamespaceObjectStore(): ObjectStore = object : ObjectStore {
override val transformer: Transformer = Transformer.DEFAULT
override val keys: Set<String> = emptySet()

override fun set(key: String, value: Any) = throw UnknownNamespaceException(key)
override fun set(key: String, value: Any?) = throw UnknownNamespaceException(key)

override fun remove(key: String) = throw UnknownNamespaceException(key)

Expand Down
86 changes: 86 additions & 0 deletions library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package nl.elements.objectstore

import nl.elements.objectstore.model.AnyItem
import nl.elements.objectstore.model.AnyNullableItem
import kotlin.properties.ReadWriteProperty

abstract class ObjectStoreModel(
internal val store: ObjectStore
) {
/**
* Clears all items in this store
*/
fun clear() {
store.keys.forEach(store::remove)
}

/**
* Delegate a string item
* @param default value
* @param key custom key (optional)
*/
protected fun stringItem(
default: String = "",
key: String? = null
): ReadWriteProperty<ObjectStoreModel, String> = AnyItem(default, key)

/**
* Delegate a nullable string item
* @param default value
* @param key custom key (optional)
*/
protected fun nullableStringItem(
default: String? = null,
key: String? = null
): ReadWriteProperty<ObjectStoreModel, String?> = AnyNullableItem(default, key)

/**
* Delegate a boolean item
* @param default value
* @param key custom key (optional)
*/
protected fun booleanItem(
default: Boolean = false,
key: String? = null
): ReadWriteProperty<ObjectStoreModel, Boolean> = AnyItem(default, key)

/**
* Delegate a nullable boolean item
* @param default value
* @param key custom key (optional)
*/
protected fun booleanNullabeItem(
default: Boolean? = null,
key: String? = null
): ReadWriteProperty<ObjectStoreModel, Boolean?> = AnyNullableItem(default, key)

/**
* Delegate an int item
* @param default value
* @param key custom key (optional)
*/
protected fun intItem(
default: Int = 0,
key: String? = null
): ReadWriteProperty<ObjectStoreModel, Int> = AnyItem(default, key)

/**
* Delegate a long item
* @param default value
* @param key custom key (optional)
*/
protected fun longItem(
default: Long = 0L,
key: String? = null
): ReadWriteProperty<ObjectStoreModel, Long> = AnyItem(default, key)

/**
* Delegate a float item
* @param default value
* @param key custom key (optional)
*/
protected fun floatItem(
default: Float = 0F,
key: String? = null
): ReadWriteProperty<ObjectStoreModel, Float> = AnyItem(default, key)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ interface ReadableObjectStore {

val keys: Set<String>

operator fun <T : Any> get(key: String): T
operator fun <T : Any> get(key: String): T?

operator fun contains(key: String): Boolean

Expand Down Expand Up @@ -39,7 +39,7 @@ private fun combine(l: ReadableObjectStore, r: ReadableObjectStore): ReadableObj
override val keys: Set<String>
get() = l.keys.toMutableSet().apply { addAll(r.keys) }

override fun <T : Any> get(key: String): T =
override fun <T : Any> get(key: String): T? =
when (key) {
in l -> l[key]
else -> r[key]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nl.elements.objectstore.model

import nl.elements.objectstore.ObjectStoreModel
import kotlin.properties.ReadWriteProperty

abstract class AbstractItem<T : Any?> : ReadWriteProperty<ObjectStoreModel, T>, ItemKey {
}

abstract class NullableAbstractItem<T : Any?> : ReadWriteProperty<ObjectStoreModel, T?>, ItemKey {
}
23 changes: 23 additions & 0 deletions library/src/main/java/nl/elements/objectstore/model/AnyItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package nl.elements.objectstore.model

import nl.elements.objectstore.ObjectStoreModel
import kotlin.reflect.KProperty

internal class AnyItem<T : Any>(
val default: T,
override val key: String?
) : AbstractItem<T>() {

override fun getValue(thisRef: ObjectStoreModel, property: KProperty<*>): T =
(key ?: property.name).let { key ->
if (thisRef.store.contains(key)) {
thisRef.store[key] ?: default
} else {
default
}
}

override fun setValue(thisRef: ObjectStoreModel, property: KProperty<*>, value: T) {
thisRef.store[key ?: property.name] = value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package nl.elements.objectstore.model

import nl.elements.objectstore.ObjectStoreModel
import kotlin.reflect.KProperty

internal class AnyNullableItem<T : Any>(
val default: T?,
override val key: String?
) : NullableAbstractItem<T>() {

override fun getValue(thisRef: ObjectStoreModel, property: KProperty<*>): T? =
(key ?: property.name).let { key ->
if (thisRef.store.contains(key)) {
thisRef.store[key] ?: default
} else {
default
}
}

override fun setValue(thisRef: ObjectStoreModel, property: KProperty<*>, value: T?) {
thisRef.store[key ?: property.name] = value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package nl.elements.objectstore.model

interface ItemKey {
val key: String?
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ class DatabaseStore(
"""
CREATE TABLE IF NOT EXISTS $table (
$_ID STRING PRIMARY KEY UNIQUE,
$VALUE BLOB NOT NULL
$VALUE BLOB
)
"""

database.rawQuery(query, null).close()
}

override fun set(key: String, value: Any) {
override fun set(key: String, value: Any?) {
val bytes = writeToBytes(key, value)
val values = ContentValues().apply {
put(_ID, key)
Expand All @@ -54,13 +54,13 @@ class DatabaseStore(
emit(Updated(key))
}

override fun <T : Any> get(key: String): T =
override fun <T : Any> get(key: String): T? =
queryById(key, VALUE).use { cursor ->
cursor
.takeIf { it.moveToFirst() }
?.value
?.inputStream()
?.read(key)!!
?.read(key)
}

override fun contains(key: String): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class DirectoryStore(
override val keys: Set<String>
get() = directory.list().toSet()

override fun set(key: String, value: Any) {
override fun set(key: String, value: Any?) {
fileOf(key)
.ensure()
.outputStream()
Expand All @@ -33,7 +33,7 @@ class DirectoryStore(
emit(Updated(key))
}

override fun <T : Any> get(key: String): T =
override fun <T : Any> get(key: String): T? =
fileOf(key)
.ensure()
.inputStream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ class MemoryStore(
override val keys: Set<String>
get() = synchronized { data.keys.toSet() }

override fun <T : Any> get(key: String): T =
override fun <T : Any> get(key: String): T? =
synchronized { data[key]!! }
.inputStream()
.read(key)

override fun set(key: String, value: Any) {
override fun set(key: String, value: Any?) {
val bytes = writeToBytes(key, value)

synchronized { data[key] = bytes }
Expand Down
Loading