From 59fb0c392cad46ec57abbbd96479c066e32537c6 Mon Sep 17 00:00:00 2001 From: Jeffrey de Looff Date: Fri, 16 Aug 2019 14:34:48 +0200 Subject: [PATCH 1/8] Adds initial set-up for model delegation --- .../elements/objectstore/ObjectStoreModel.kt | 25 +++++++++++++++ .../objectstore/model/AbstractItem.kt | 8 +++++ .../nl/elements/objectstore/model/ItemKey.kt | 5 +++ .../elements/objectstore/model/StringItem.kt | 24 ++++++++++++++ .../objectstore/ObjectStoreModelTest.kt | 31 +++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100644 library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt create mode 100644 library/src/main/java/nl/elements/objectstore/model/AbstractItem.kt create mode 100644 library/src/main/java/nl/elements/objectstore/model/ItemKey.kt create mode 100644 library/src/main/java/nl/elements/objectstore/model/StringItem.kt create mode 100644 library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt diff --git a/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt b/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt new file mode 100644 index 0000000..4d86f0c --- /dev/null +++ b/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt @@ -0,0 +1,25 @@ +package nl.elements.objectstore + +import nl.elements.objectstore.model.StringItem +import kotlin.properties.ReadWriteProperty + +abstract class ObjectStoreModel( + internal val store: ObjectStore +) { + /** + * Clear all items in this store + */ + fun clear() { + store.keys.forEach(store::remove) + } + + /** + * Delegate an item with type string + * @param default default string value + * @param key custom key + */ + protected fun stringPref( + default: String = "", + key: String? = null + ): ReadWriteProperty = StringItem(default, key) +} diff --git a/library/src/main/java/nl/elements/objectstore/model/AbstractItem.kt b/library/src/main/java/nl/elements/objectstore/model/AbstractItem.kt new file mode 100644 index 0000000..5d5a604 --- /dev/null +++ b/library/src/main/java/nl/elements/objectstore/model/AbstractItem.kt @@ -0,0 +1,8 @@ +package nl.elements.objectstore.model + +import nl.elements.objectstore.ObjectStoreModel +import kotlin.properties.ReadWriteProperty + +abstract class AbstractItem : ReadWriteProperty, ItemKey { + abstract override val key: String? +} diff --git a/library/src/main/java/nl/elements/objectstore/model/ItemKey.kt b/library/src/main/java/nl/elements/objectstore/model/ItemKey.kt new file mode 100644 index 0000000..6758257 --- /dev/null +++ b/library/src/main/java/nl/elements/objectstore/model/ItemKey.kt @@ -0,0 +1,5 @@ +package nl.elements.objectstore.model + +interface ItemKey { + val key: String? +} diff --git a/library/src/main/java/nl/elements/objectstore/model/StringItem.kt b/library/src/main/java/nl/elements/objectstore/model/StringItem.kt new file mode 100644 index 0000000..570e6f5 --- /dev/null +++ b/library/src/main/java/nl/elements/objectstore/model/StringItem.kt @@ -0,0 +1,24 @@ +package nl.elements.objectstore.model + +import nl.elements.objectstore.ObjectStoreModel +import kotlin.reflect.KProperty + +internal class StringItem( + val default: String, + override val key: String? +) : AbstractItem() { + + override fun getValue(model: ObjectStoreModel, property: KProperty<*>): String { + val realKey = key ?: property.name + + return if (model.store.contains(realKey)) { + model.store.get(realKey) + } else { + default + } + } + + override fun setValue(model: ObjectStoreModel, property: KProperty<*>, value: String) { + model.store[key ?: property.name] = value + } +} diff --git a/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt b/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt new file mode 100644 index 0000000..afb3592 --- /dev/null +++ b/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt @@ -0,0 +1,31 @@ +package nl.elements.objectstore + +import nl.elements.objectstore.stores.MemoryStore +import org.junit.Test +import kotlin.test.assertEquals + +class ObjectStoreModelTest { + + @Test + fun `Test Store Model with MemoryStore`() { + val model = InMemoryModel() + + val newValue = "Test" + + assertEquals("", model.name) + + model.name = newValue + + assertEquals(newValue, model.name) + assertEquals(DEFAULT_VALUE, model.nameWithDefault) + } + + companion object { + class InMemoryModel : ObjectStoreModel(MemoryStore()) { + var name by stringPref() + var nameWithDefault by stringPref(DEFAULT_VALUE) + } + + const val DEFAULT_VALUE = "default" + } +} From d177055ff49c982e983044ca90f0abfb16f6e469 Mon Sep 17 00:00:00 2001 From: Jeffrey de Looff Date: Fri, 16 Aug 2019 14:38:15 +0200 Subject: [PATCH 2/8] Updates comment --- .../src/main/java/nl/elements/objectstore/ObjectStoreModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt b/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt index 4d86f0c..ceec16d 100644 --- a/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt +++ b/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt @@ -15,7 +15,7 @@ abstract class ObjectStoreModel( /** * Delegate an item with type string - * @param default default string value + * @param default value * @param key custom key */ protected fun stringPref( From b9d9b777c594b5d8f17c42f42c21715537a78ce7 Mon Sep 17 00:00:00 2001 From: Jeffrey de Looff Date: Fri, 16 Aug 2019 15:01:04 +0200 Subject: [PATCH 3/8] Adds nullable support --- .../java/nl/elements/objectstore/Converter.kt | 10 ++++---- .../nl/elements/objectstore/ObjectStore.kt | 14 +++++------ .../elements/objectstore/ObjectStoreModel.kt | 17 ++++++++++--- .../objectstore/ReadableObjectStore.kt | 4 ++-- .../elements/objectstore/model/StringItem.kt | 10 ++++---- .../objectstore/model/StringNullableItem.kt | 24 +++++++++++++++++++ .../objectstore/stores/DatabaseStore.kt | 4 ++-- .../objectstore/stores/DirectoryStore.kt | 4 ++-- .../objectstore/stores/MemoryStore.kt | 4 ++-- .../objectstore/stores/PreferencesStore.kt | 4 ++-- .../objectstore/ObjectStoreModelTest.kt | 10 ++++++-- 11 files changed, 73 insertions(+), 32 deletions(-) create mode 100644 library/src/main/java/nl/elements/objectstore/model/StringNullableItem.kt diff --git a/library/src/main/java/nl/elements/objectstore/Converter.kt b/library/src/main/java/nl/elements/objectstore/Converter.kt index 95737e1..9f243ec 100644 --- a/library/src/main/java/nl/elements/objectstore/Converter.kt +++ b/library/src/main/java/nl/elements/objectstore/Converter.kt @@ -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 deserialize(key: String, input: InputStream): T + fun 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 deserialize(key: String, input: InputStream): T = - ObjectInputStream(input).readObject() as T + override fun deserialize(key: String, input: InputStream): T? = + ObjectInputStream(input).readObject() as T? } diff --git a/library/src/main/java/nl/elements/objectstore/ObjectStore.kt b/library/src/main/java/nl/elements/objectstore/ObjectStore.kt index 8003ec5..a762109 100644 --- a/library/src/main/java/nl/elements/objectstore/ObjectStore.kt +++ b/library/src/main/java/nl/elements/objectstore/ObjectStore.kt @@ -18,18 +18,18 @@ interface ObjectStore : ReadableObjectStore, ObservableSource 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 InputStream.read(key: String): T = + fun InputStream.read(key: String): T? = ByteArrayOutputStream() .also { transformer.read(key, this@read, it) } .toByteArray() @@ -57,7 +57,7 @@ fun ObjectStore.toObservable(): Observable = 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() @@ -114,13 +114,13 @@ private fun ObjectStore.withNamespace(namespace: String, next: ObjectStore): Obj override val keys: Set get() = store.keys.map { namespace + it }.toMutableSet().apply { addAll(next.keys) } - override fun get(key: String): T = + override fun 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) @@ -148,7 +148,7 @@ private fun unknownNamespaceObjectStore(): ObjectStore = object : ObjectStore { override val transformer: Transformer = Transformer.DEFAULT override val keys: Set = 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) diff --git a/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt b/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt index ceec16d..bd899d4 100644 --- a/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt +++ b/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt @@ -1,6 +1,7 @@ package nl.elements.objectstore import nl.elements.objectstore.model.StringItem +import nl.elements.objectstore.model.StringNullableItem import kotlin.properties.ReadWriteProperty abstract class ObjectStoreModel( @@ -14,12 +15,22 @@ abstract class ObjectStoreModel( } /** - * Delegate an item with type string + * Delegate an item with string * @param default value - * @param key custom key + * @param key custom key (optional) */ - protected fun stringPref( + protected fun stringItem( default: String = "", key: String? = null ): ReadWriteProperty = StringItem(default, key) + + /** + * Delegate an item with nullable string + * @param default value + * @param key custom key (optional) + */ + protected fun nullableStringItem( + default: String? = null, + key: String? = null + ): ReadWriteProperty = StringNullableItem(default, key) } diff --git a/library/src/main/java/nl/elements/objectstore/ReadableObjectStore.kt b/library/src/main/java/nl/elements/objectstore/ReadableObjectStore.kt index f5870bb..4c1a152 100644 --- a/library/src/main/java/nl/elements/objectstore/ReadableObjectStore.kt +++ b/library/src/main/java/nl/elements/objectstore/ReadableObjectStore.kt @@ -4,7 +4,7 @@ interface ReadableObjectStore { val keys: Set - operator fun get(key: String): T + operator fun get(key: String): T? operator fun contains(key: String): Boolean @@ -39,7 +39,7 @@ private fun combine(l: ReadableObjectStore, r: ReadableObjectStore): ReadableObj override val keys: Set get() = l.keys.toMutableSet().apply { addAll(r.keys) } - override fun get(key: String): T = + override fun get(key: String): T? = when (key) { in l -> l[key] else -> r[key] diff --git a/library/src/main/java/nl/elements/objectstore/model/StringItem.kt b/library/src/main/java/nl/elements/objectstore/model/StringItem.kt index 570e6f5..7eee7ec 100644 --- a/library/src/main/java/nl/elements/objectstore/model/StringItem.kt +++ b/library/src/main/java/nl/elements/objectstore/model/StringItem.kt @@ -8,17 +8,17 @@ internal class StringItem( override val key: String? ) : AbstractItem() { - override fun getValue(model: ObjectStoreModel, property: KProperty<*>): String { + override fun getValue(thisRef: ObjectStoreModel, property: KProperty<*>): String { val realKey = key ?: property.name - return if (model.store.contains(realKey)) { - model.store.get(realKey) + return if (thisRef.store.contains(realKey)) { + thisRef.store.get(realKey) ?: default } else { default } } - override fun setValue(model: ObjectStoreModel, property: KProperty<*>, value: String) { - model.store[key ?: property.name] = value + override fun setValue(thisRef: ObjectStoreModel, property: KProperty<*>, value: String) { + thisRef.store[key ?: property.name] = value } } diff --git a/library/src/main/java/nl/elements/objectstore/model/StringNullableItem.kt b/library/src/main/java/nl/elements/objectstore/model/StringNullableItem.kt new file mode 100644 index 0000000..3a0f5a4 --- /dev/null +++ b/library/src/main/java/nl/elements/objectstore/model/StringNullableItem.kt @@ -0,0 +1,24 @@ +package nl.elements.objectstore.model + +import nl.elements.objectstore.ObjectStoreModel +import kotlin.reflect.KProperty + +internal class StringNullableItem( + val default: String?, + override val key: String? +) : AbstractItem() { + + override fun getValue(thisRef: ObjectStoreModel, property: KProperty<*>): String? { + val realKey = key ?: property.name + + return if (thisRef.store.contains(realKey)) { + thisRef.store.get(realKey) + } else { + default + } + } + + override fun setValue(thisRef: ObjectStoreModel, property: KProperty<*>, value: String?) { + thisRef.store[key ?: property.name] = value + } +} diff --git a/library/src/main/java/nl/elements/objectstore/stores/DatabaseStore.kt b/library/src/main/java/nl/elements/objectstore/stores/DatabaseStore.kt index 2840922..9b0983a 100644 --- a/library/src/main/java/nl/elements/objectstore/stores/DatabaseStore.kt +++ b/library/src/main/java/nl/elements/objectstore/stores/DatabaseStore.kt @@ -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) diff --git a/library/src/main/java/nl/elements/objectstore/stores/DirectoryStore.kt b/library/src/main/java/nl/elements/objectstore/stores/DirectoryStore.kt index d0fc1cc..5049cda 100644 --- a/library/src/main/java/nl/elements/objectstore/stores/DirectoryStore.kt +++ b/library/src/main/java/nl/elements/objectstore/stores/DirectoryStore.kt @@ -24,7 +24,7 @@ class DirectoryStore( override val keys: Set get() = directory.list().toSet() - override fun set(key: String, value: Any) { + override fun set(key: String, value: Any?) { fileOf(key) .ensure() .outputStream() @@ -33,7 +33,7 @@ class DirectoryStore( emit(Updated(key)) } - override fun get(key: String): T = + override fun get(key: String): T? = fileOf(key) .ensure() .inputStream() diff --git a/library/src/main/java/nl/elements/objectstore/stores/MemoryStore.kt b/library/src/main/java/nl/elements/objectstore/stores/MemoryStore.kt index b0f57e8..31196bd 100644 --- a/library/src/main/java/nl/elements/objectstore/stores/MemoryStore.kt +++ b/library/src/main/java/nl/elements/objectstore/stores/MemoryStore.kt @@ -17,12 +17,12 @@ class MemoryStore( override val keys: Set get() = synchronized { data.keys.toSet() } - override fun get(key: String): T = + override fun 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 } diff --git a/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt b/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt index 5045807..859c197 100644 --- a/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt +++ b/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt @@ -24,7 +24,7 @@ class PreferencesStore( override val keys: Set get() = preferences.all.keys - override fun set(key: String, value: Any) { + override fun set(key: String, value: Any?) { writeToBytes(key, value) .let { Base64.encodeToString(it, 0) } .let { preferences.edit().putString(key, it).apply() } @@ -32,7 +32,7 @@ class PreferencesStore( emit(Updated(key)) } - override fun get(key: String): T = + override fun get(key: String): T? = preferences .getString(key, null)!! .let { Base64.decode(it, 0) } diff --git a/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt b/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt index afb3592..2aca660 100644 --- a/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt +++ b/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt @@ -18,12 +18,18 @@ class ObjectStoreModelTest { assertEquals(newValue, model.name) assertEquals(DEFAULT_VALUE, model.nameWithDefault) + + assertEquals(null, model.nameNull) + assertEquals(DEFAULT_VALUE, model.nameNullWithDefault) } companion object { class InMemoryModel : ObjectStoreModel(MemoryStore()) { - var name by stringPref() - var nameWithDefault by stringPref(DEFAULT_VALUE) + var name by stringItem() + var nameWithDefault by stringItem(DEFAULT_VALUE) + + var nameNull by nullableStringItem() + var nameNullWithDefault by nullableStringItem(DEFAULT_VALUE) } const val DEFAULT_VALUE = "default" From 25b6de256913733f609008511f648d3e6912b52e Mon Sep 17 00:00:00 2001 From: Jeffrey de Looff Date: Fri, 16 Aug 2019 15:33:54 +0200 Subject: [PATCH 4/8] Adds nullable to setAndGet --- .../java/nl/elements/objectstore/PreferencesStoreTest.kt | 2 +- .../src/test/java/nl/elements/objectstore/InMemoryStoreTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/androidTest/java/nl/elements/objectstore/PreferencesStoreTest.kt b/library/src/androidTest/java/nl/elements/objectstore/PreferencesStoreTest.kt index e5a0aa8..6971c80 100644 --- a/library/src/androidTest/java/nl/elements/objectstore/PreferencesStoreTest.kt +++ b/library/src/androidTest/java/nl/elements/objectstore/PreferencesStoreTest.kt @@ -102,7 +102,7 @@ class PreferencesStoreTest { } } -private fun ObjectStore.setAndGet(key: String, value: T): T { +private fun ObjectStore.setAndGet(key: String, value: T): T? { set(key, value) return this[key] } diff --git a/library/src/test/java/nl/elements/objectstore/InMemoryStoreTest.kt b/library/src/test/java/nl/elements/objectstore/InMemoryStoreTest.kt index 1b05383..8be35db 100644 --- a/library/src/test/java/nl/elements/objectstore/InMemoryStoreTest.kt +++ b/library/src/test/java/nl/elements/objectstore/InMemoryStoreTest.kt @@ -95,7 +95,7 @@ class InMemoryStoreTest { } } -private fun ObjectStore.setAndGet(key: String, value: T): T { +private fun ObjectStore.setAndGet(key: String, value: T): T? { set(key, value) return this[key] } From e3af743f0e4474942425646ea745c8a6ad758f8e Mon Sep 17 00:00:00 2001 From: Jeffrey de Looff Date: Thu, 22 Aug 2019 12:00:52 +0200 Subject: [PATCH 5/8] Adds additional items to the model --- .../objectstore/PreferencesStoreTest.kt | 4 +- .../elements/objectstore/ObjectStoreModel.kt | 64 +++++++++++++++++-- .../objectstore/model/AbstractItem.kt | 4 +- .../model/{StringItem.kt => AnyItem.kt} | 23 ++++--- ...ringNullableItem.kt => AnyNullableItem.kt} | 23 ++++--- .../objectstore/stores/PreferencesStore.kt | 3 +- .../elements/objectstore/InMemoryStoreTest.kt | 4 +- .../objectstore/ObjectStoreModelTest.kt | 63 +++++++++++++++--- 8 files changed, 141 insertions(+), 47 deletions(-) rename library/src/main/java/nl/elements/objectstore/model/{StringItem.kt => AnyItem.kt} (51%) rename library/src/main/java/nl/elements/objectstore/model/{StringNullableItem.kt => AnyNullableItem.kt} (51%) diff --git a/library/src/androidTest/java/nl/elements/objectstore/PreferencesStoreTest.kt b/library/src/androidTest/java/nl/elements/objectstore/PreferencesStoreTest.kt index 6971c80..7866c18 100644 --- a/library/src/androidTest/java/nl/elements/objectstore/PreferencesStoreTest.kt +++ b/library/src/androidTest/java/nl/elements/objectstore/PreferencesStoreTest.kt @@ -28,7 +28,7 @@ class PreferencesStoreTest { val key = "contains" val name = "Danny" - store.set(key, name) + store[key] = name assertTrue(store.contains(key)) } @@ -92,7 +92,7 @@ class PreferencesStoreTest { val key = "name" val value = "Danny" - store.set(key, value) + store[key] = value val actual = store.keys diff --git a/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt b/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt index bd899d4..1e58439 100644 --- a/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt +++ b/library/src/main/java/nl/elements/objectstore/ObjectStoreModel.kt @@ -1,36 +1,86 @@ package nl.elements.objectstore -import nl.elements.objectstore.model.StringItem -import nl.elements.objectstore.model.StringNullableItem +import nl.elements.objectstore.model.AnyItem +import nl.elements.objectstore.model.AnyNullableItem import kotlin.properties.ReadWriteProperty abstract class ObjectStoreModel( internal val store: ObjectStore ) { /** - * Clear all items in this store + * Clears all items in this store */ fun clear() { store.keys.forEach(store::remove) } /** - * Delegate an item with string + * Delegate a string item * @param default value * @param key custom key (optional) */ protected fun stringItem( default: String = "", key: String? = null - ): ReadWriteProperty = StringItem(default, key) + ): ReadWriteProperty = AnyItem(default, key) /** - * Delegate an item with nullable string + * Delegate a nullable string item * @param default value * @param key custom key (optional) */ protected fun nullableStringItem( default: String? = null, key: String? = null - ): ReadWriteProperty = StringNullableItem(default, key) + ): ReadWriteProperty = 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 = 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 = 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 = 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 = 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 = AnyItem(default, key) } diff --git a/library/src/main/java/nl/elements/objectstore/model/AbstractItem.kt b/library/src/main/java/nl/elements/objectstore/model/AbstractItem.kt index 5d5a604..9c0cfdd 100644 --- a/library/src/main/java/nl/elements/objectstore/model/AbstractItem.kt +++ b/library/src/main/java/nl/elements/objectstore/model/AbstractItem.kt @@ -4,5 +4,7 @@ import nl.elements.objectstore.ObjectStoreModel import kotlin.properties.ReadWriteProperty abstract class AbstractItem : ReadWriteProperty, ItemKey { - abstract override val key: String? +} + +abstract class NullableAbstractItem : ReadWriteProperty, ItemKey { } diff --git a/library/src/main/java/nl/elements/objectstore/model/StringItem.kt b/library/src/main/java/nl/elements/objectstore/model/AnyItem.kt similarity index 51% rename from library/src/main/java/nl/elements/objectstore/model/StringItem.kt rename to library/src/main/java/nl/elements/objectstore/model/AnyItem.kt index 7eee7ec..5730e88 100644 --- a/library/src/main/java/nl/elements/objectstore/model/StringItem.kt +++ b/library/src/main/java/nl/elements/objectstore/model/AnyItem.kt @@ -3,22 +3,21 @@ package nl.elements.objectstore.model import nl.elements.objectstore.ObjectStoreModel import kotlin.reflect.KProperty -internal class StringItem( - val default: String, +internal class AnyItem( + val default: T, override val key: String? -) : AbstractItem() { +) : AbstractItem() { - override fun getValue(thisRef: ObjectStoreModel, property: KProperty<*>): String { - val realKey = key ?: property.name - - return if (thisRef.store.contains(realKey)) { - thisRef.store.get(realKey) ?: default - } else { - default + 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: String) { + override fun setValue(thisRef: ObjectStoreModel, property: KProperty<*>, value: T) { thisRef.store[key ?: property.name] = value } } diff --git a/library/src/main/java/nl/elements/objectstore/model/StringNullableItem.kt b/library/src/main/java/nl/elements/objectstore/model/AnyNullableItem.kt similarity index 51% rename from library/src/main/java/nl/elements/objectstore/model/StringNullableItem.kt rename to library/src/main/java/nl/elements/objectstore/model/AnyNullableItem.kt index 3a0f5a4..798a2d4 100644 --- a/library/src/main/java/nl/elements/objectstore/model/StringNullableItem.kt +++ b/library/src/main/java/nl/elements/objectstore/model/AnyNullableItem.kt @@ -3,22 +3,21 @@ package nl.elements.objectstore.model import nl.elements.objectstore.ObjectStoreModel import kotlin.reflect.KProperty -internal class StringNullableItem( - val default: String?, +internal class AnyNullableItem( + val default: T?, override val key: String? -) : AbstractItem() { +) : NullableAbstractItem() { - override fun getValue(thisRef: ObjectStoreModel, property: KProperty<*>): String? { - val realKey = key ?: property.name - - return if (thisRef.store.contains(realKey)) { - thisRef.store.get(realKey) - } else { - default + 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: String?) { + override fun setValue(thisRef: ObjectStoreModel, property: KProperty<*>, value: T?) { thisRef.store[key ?: property.name] = value } } diff --git a/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt b/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt index 859c197..8c57977 100644 --- a/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt +++ b/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt @@ -42,8 +42,9 @@ class PreferencesStore( override fun contains(key: String): Boolean = preferences.contains(key) override fun remove(key: String) { - if (preferences.edit().remove(key).commit()) + if (preferences.edit().remove(key).commit()) { emit(Removed(key)) + } } fun toPreferences(): SharedPreferences = StorePreferences(this, preferences) diff --git a/library/src/test/java/nl/elements/objectstore/InMemoryStoreTest.kt b/library/src/test/java/nl/elements/objectstore/InMemoryStoreTest.kt index 8be35db..9f54ad4 100644 --- a/library/src/test/java/nl/elements/objectstore/InMemoryStoreTest.kt +++ b/library/src/test/java/nl/elements/objectstore/InMemoryStoreTest.kt @@ -21,7 +21,7 @@ class InMemoryStoreTest { val key = "key" val value = "Danny" - store.set(key, value) + store[key] = value assertTrue(store.contains(key)) } @@ -85,7 +85,7 @@ class InMemoryStoreTest { val key = "name" val value = "Danny" - store.set(key, value) + store[key] = value val actual = store.keys diff --git a/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt b/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt index 2aca660..dba2d4b 100644 --- a/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt +++ b/library/src/test/java/nl/elements/objectstore/ObjectStoreModelTest.kt @@ -3,6 +3,9 @@ package nl.elements.objectstore import nl.elements.objectstore.stores.MemoryStore import org.junit.Test import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNull +import kotlin.test.assertTrue class ObjectStoreModelTest { @@ -10,28 +13,68 @@ class ObjectStoreModelTest { fun `Test Store Model with MemoryStore`() { val model = InMemoryModel() - val newValue = "Test" - assertEquals("", model.name) + assertEquals(DEFAULT_STRING_VALUE, model.nameDefault) + + assertNull(model.nameNull) + assertEquals(DEFAULT_STRING_VALUE, model.nameNullDefault) + + assertFalse(model.isOpen) + assertFalse(model.isOpenDefault) + + assertNull(model.isClosedNull) + assertEquals(DEFAULT_BOOLEAN_VALUE, model.isClosedNullDefault) + + assertEquals(0, model.int) + assertEquals(DEFAULT_INT_VALUE, model.intDefault) + + assertEquals(0, model.long) + assertEquals(DEFAULT_LONG_VALUE, model.longDefault) + + assertEquals(0F, model.float) + assertEquals(DEFAULT_FLOAT_VALUE, model.floatDefault) + } - model.name = newValue + @Test + fun `Test Store Model with MemoryStore to change values`() { + val model = InMemoryModel() - assertEquals(newValue, model.name) - assertEquals(DEFAULT_VALUE, model.nameWithDefault) + model.isOpen = true + model.name = "Test" - assertEquals(null, model.nameNull) - assertEquals(DEFAULT_VALUE, model.nameNullWithDefault) + assertTrue(model.isOpen) + assertEquals("Test", model.name) } + companion object { class InMemoryModel : ObjectStoreModel(MemoryStore()) { var name by stringItem() - var nameWithDefault by stringItem(DEFAULT_VALUE) + var nameDefault by stringItem(DEFAULT_STRING_VALUE) var nameNull by nullableStringItem() - var nameNullWithDefault by nullableStringItem(DEFAULT_VALUE) + var nameNullDefault by nullableStringItem(DEFAULT_STRING_VALUE) + + var isOpen by booleanItem() + var isOpenDefault by booleanItem(false) + + var isClosedNull by booleanNullabeItem() + var isClosedNullDefault by booleanNullabeItem(DEFAULT_BOOLEAN_VALUE) + + var int by intItem() + var intDefault by intItem(DEFAULT_INT_VALUE) + + var long by longItem() + var longDefault by longItem(DEFAULT_LONG_VALUE) + + var float by floatItem() + var floatDefault by floatItem(DEFAULT_FLOAT_VALUE) } - const val DEFAULT_VALUE = "default" + const val DEFAULT_STRING_VALUE = "default" + const val DEFAULT_BOOLEAN_VALUE = true + const val DEFAULT_INT_VALUE = 42 + const val DEFAULT_LONG_VALUE = 42L + const val DEFAULT_FLOAT_VALUE = 42F } } From df37c7406359139ffa4536d56f90b7a3a56134a6 Mon Sep 17 00:00:00 2001 From: Jeffrey de Looff Date: Thu, 22 Aug 2019 13:48:51 +0200 Subject: [PATCH 6/8] Adds model example in README --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 640f37e..7bb1f09 100644 --- a/README.md +++ b/README.md @@ -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:+" @@ -19,6 +22,19 @@ fun example(store: ObjectStore) { } ``` +#### Define a model + +```kotlin +class AppData : ObjectStoreModel(InMemoryStore()) { + var username by stringItem() + var age by intItem() + var hasSeenOnboarding by booleanItem() + var lastSeen by longItem() + var rating by floatItem() +} +``` + + ## Observing Each `ObjectStore` is (Rx) observable and will emit whenever something changes in store. From 37b455e0824731d91e2507a16a01448d5511e3c1 Mon Sep 17 00:00:00 2001 From: Jeffrey de Looff Date: Mon, 9 Sep 2019 13:40:21 +0200 Subject: [PATCH 7/8] Expands README example --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7bb1f09..3d65275 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,10 @@ fun example(store: ObjectStore) { ```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() } From c5b51af768c091f7ebea6051ee1c1288af28fffe Mon Sep 17 00:00:00 2001 From: Jeffrey de Looff Date: Fri, 15 Nov 2019 14:19:50 +0100 Subject: [PATCH 8/8] Fixes nullability in preferences store --- .../elements/objectstore/stores/DatabaseStore.kt | 4 ++-- .../objectstore/stores/PreferencesStore.kt | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/library/src/main/java/nl/elements/objectstore/stores/DatabaseStore.kt b/library/src/main/java/nl/elements/objectstore/stores/DatabaseStore.kt index 9b0983a..27948ca 100644 --- a/library/src/main/java/nl/elements/objectstore/stores/DatabaseStore.kt +++ b/library/src/main/java/nl/elements/objectstore/stores/DatabaseStore.kt @@ -54,13 +54,13 @@ class DatabaseStore( emit(Updated(key)) } - override fun get(key: String): T = + override fun 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 = diff --git a/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt b/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt index 8c57977..8ef0e5e 100644 --- a/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt +++ b/library/src/main/java/nl/elements/objectstore/stores/PreferencesStore.kt @@ -62,17 +62,17 @@ private class StorePreferences( override fun contains(key: String?): Boolean = key?.let { store.contains(it) } ?: false - override fun getBoolean(key: String?, defValue: Boolean): Boolean = get(key, defValue) + override fun getBoolean(key: String?, defValue: Boolean): Boolean = get(key, defValue) ?: defValue - override fun getInt(key: String?, defValue: Int): Int = get(key, defValue) + override fun getInt(key: String?, defValue: Int): Int = get(key, defValue) ?: defValue - override fun getLong(key: String?, defValue: Long): Long = get(key, defValue) + override fun getLong(key: String?, defValue: Long): Long = get(key, defValue) ?: defValue - override fun getFloat(key: String?, defValue: Float): Float = get(key, defValue) + override fun getFloat(key: String?, defValue: Float): Float = get(key, defValue) ?: defValue - override fun getString(key: String?, defValue: String): String? = get(key, defValue) + override fun getString(key: String, defValue: String?): String? = get(key, defValue) - override fun getStringSet(key: String?, defValues: MutableSet): MutableSet? = get(key, defValues) + override fun getStringSet(key: String, defValues: MutableSet?): MutableSet? = get(key, defValues) override fun getAll(): MutableMap = store @@ -101,7 +101,7 @@ private class StorePreferences( } } - private fun get(key: String?, defValue: T): T = key?.let { store.get(it) } ?: defValue + private fun get(key: String?, defValue: T?): T? = key?.let { store.get(it) } ?: defValue }