-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b98d5a6
commit f712f9a
Showing
19 changed files
with
269 additions
and
282 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Data4k | ||
|
||
<a href="https://mvnrepository.com/artifact/dev.forkhandles"><img alt="Download" src="https://img.shields.io/maven-central/v/dev.forkhandles/forkhandles-bom"></a> | ||
[![.github/workflows/build.yaml](https://github.com/fork-handles/forkhandles/actions/workflows/build.yaml/badge.svg)](https://github.com/fork-handles/forkhandles/actions/workflows/build.yaml) | ||
<a href="https://codecov.io/gh/fork-handles/forkhandles"><img src="https://codecov.io/gh/fork-handles/forkhandles/branch/trunk/graph/badge.svg"/></a> | ||
<a href="http//www.apache.org/licenses/LICENSE-2.0"><img alt="GitHub license" src="https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat"></a> | ||
<a href="https://codebeat.co/projects/github-com-fork-handles-forkhandles-trunk"><img alt="codebeat badge" src="https://codebeat.co/badges/5b369ed4-af27-46f4-ad9c-a307d900617e"></a> | ||
|
||
Library to make working with Data-Oriented programming in Kotlin easier, to extract values from dynamic data structures such as Maps. | ||
|
||
## Installation | ||
|
||
In Gradle, install the ForkHandles BOM and then this module in the dependency block: | ||
|
||
```kotlin | ||
implementation(platform("dev.forkhandles:forkhandles-bom:X.Y.Z")) | ||
implementation("dev.forkhandles:data4k") | ||
``` | ||
|
||
## Usage | ||
|
||
The library defines a DataContainer class and implementations for: | ||
|
||
- Map<String, Any?> | ||
- Jackson JSON Node | ||
|
||
To extract data from the underlying data, define wrappers which provides access via delegated-properties: | ||
|
||
```kotlin | ||
class MapBacked(propertySet: Map<String, Any?>) : MapDataContainer(propertySet) { | ||
val stringField by field<String>() | ||
val listSubClassField by list(::SubMap) | ||
val objectField by obj(::SubMap) | ||
} | ||
|
||
class SubMap(propertySet: Map<String, Any?>) : MapDataContainer(propertySet) { | ||
val stringField by field<String>() | ||
} | ||
|
||
val input = MapBacked( | ||
mapOf( | ||
"stringField" to "string", | ||
"listSubClassField" to listOf( | ||
mapOf("stringField" to "string1"), | ||
mapOf("stringField" to "string2"), | ||
), | ||
"listStringsField" to listOf("string1", "string2"), | ||
"objectField" to mapOf( | ||
"stringField" to "string" | ||
) | ||
) | ||
) | ||
|
||
// then just get the values from the underlying data using the type system. Errors will be thrown for missing/invalid properties | ||
val data: String = input.objectField.stringField | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
description = 'ForkHandles data-oriented programming library' | ||
|
||
dependencies { | ||
api("org.jetbrains.kotlin:kotlin-reflect:_") | ||
compileOnly("com.fasterxml.jackson.core:jackson-databind:_") | ||
testImplementation("com.fasterxml.jackson.core:jackson-databind:_") | ||
testImplementation("io.strikt:strikt-jvm:_") | ||
} |
23 changes: 23 additions & 0 deletions
23
data4k/src/main/kotlin/dev/forkhandles/data/DataContainer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package dev.forkhandles.data | ||
|
||
/** | ||
* Superclass for all container implementations. Defines the delegate property classes to extract data from the | ||
* underlying data structure. | ||
*/ | ||
@Suppress("UNCHECKED_CAST", "ClassName") | ||
abstract class DataContainer<CONTENT>( | ||
protected val data: CONTENT, | ||
existsFn: (CONTENT, String) -> Boolean, | ||
getFn: (CONTENT, String) -> Any? | ||
) { | ||
private val exists: DataContainer<CONTENT>.(String) -> Boolean = { existsFn(data, it) } | ||
private val get: DataContainer<CONTENT>.(String) -> Any? = { getFn(data, it) } | ||
|
||
inner class field<OUT> : DataProperty<DataContainer<CONTENT>, OUT>(exists, get) | ||
|
||
inner class list<IN : Any, OUT>(mapFn: (IN) -> OUT) : | ||
DataProperty<DataContainer<CONTENT>, List<OUT>>(exists, { (get(it) as List<IN>).map(mapFn) }) | ||
|
||
inner class obj<OUT : DataContainer<CONTENT>>(mapFn: (CONTENT) -> OUT) : | ||
DataProperty<DataContainer<CONTENT>, OUT>(exists, { mapFn(get(it) as CONTENT) }) | ||
} |
4 changes: 2 additions & 2 deletions
4
...n/kotlin/dev/forkhandles/lens/LensProp.kt → ...tlin/dev/forkhandles/data/DataProperty.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
data4k/src/main/kotlin/dev/forkhandles/data/JacksonDataContainer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package dev.forkhandles.data | ||
|
||
import com.fasterxml.jackson.databind.JsonNode | ||
import com.fasterxml.jackson.databind.node.* | ||
|
||
/** | ||
* Jackson JsonNode-based implementation of the DataContainer | ||
*/ | ||
abstract class JacksonDataContainer(input: JsonNode) : | ||
DataContainer<JsonNode>( | ||
input, | ||
{ content, it -> content.has(it) }, | ||
{ content, it -> content[it]?.let(Companion::nodeToValue) } | ||
) { | ||
|
||
companion object { | ||
private fun nodeToValue(input: JsonNode): Any? = when (input) { | ||
is BooleanNode -> input.booleanValue() | ||
is IntNode -> input.intValue() | ||
is LongNode -> input.longValue() | ||
is DecimalNode -> input.decimalValue() | ||
is DoubleNode -> input.doubleValue() | ||
is TextNode -> input.textValue() | ||
is ArrayNode -> input.map(::nodeToValue) | ||
is ObjectNode -> input | ||
is NullNode -> null | ||
else -> error("Invalid node type $input") | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
data4k/src/main/kotlin/dev/forkhandles/data/MapDataContainer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package dev.forkhandles.data | ||
|
||
import kotlin.collections.Map | ||
|
||
/** | ||
* Map-based implementation of the DataContainer | ||
*/ | ||
abstract class MapDataContainer(input: Map<String, Any?>) : | ||
DataContainer<Map<String, Any?>>(input, { content, it -> content.containsKey(it) }, { content, it -> content[it] }) | ||
|
||
|
70 changes: 70 additions & 0 deletions
70
data4k/src/test/kotlin/dev/forkhandles/lens/DataContainerContract.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package dev.forkhandles.lens | ||
|
||
import org.junit.jupiter.api.Test | ||
import strikt.api.expectThat | ||
import strikt.api.expectThrows | ||
import strikt.assertions.isEqualTo | ||
import strikt.assertions.message | ||
|
||
interface MainClassFields { | ||
val stringField: String | ||
val booleanField: Boolean | ||
val intField: Int | ||
val longField: Long | ||
val decimalField: Double | ||
val notAStringField: String | ||
val optionalField: String? | ||
val noSuchField: String | ||
val listSubClassField: List<SubClassFields> | ||
val listIntsField: List<Int> | ||
val listStringsField: List<String> | ||
val objectField: SubClassFields | ||
} | ||
|
||
interface SubClassFields { | ||
val stringField: String | ||
val noSuchField: String | ||
} | ||
|
||
abstract class DataContainerContract { | ||
|
||
abstract fun container(input: Map<String, Any?>): MainClassFields | ||
|
||
@Test | ||
fun `can get values from properties`() { | ||
val input = container( | ||
mapOf( | ||
"stringField" to "string", | ||
"optionalField" to "optional", | ||
"booleanField" to true, | ||
"intField" to 123, | ||
"longField" to Long.MAX_VALUE, | ||
"decimalField" to 1.1234, | ||
"notAStringField" to 123, | ||
"listIntsField" to listOf(1, 2, 3), | ||
"listSubClassField" to listOf( | ||
mapOf("stringField" to "string1"), | ||
mapOf("stringField" to "string2"), | ||
), | ||
"listStringsField" to listOf("string1", "string2"), | ||
"objectField" to mapOf( | ||
"stringField" to "string" | ||
) | ||
) | ||
) | ||
|
||
expectThat(input.stringField).isEqualTo("string") | ||
expectThat(input.optionalField).isEqualTo("optional") | ||
expectThat(input.booleanField).isEqualTo(true) | ||
expectThat(input.intField).isEqualTo(123) | ||
expectThat(input.longField).isEqualTo(Long.MAX_VALUE) | ||
expectThat(input.decimalField).isEqualTo(1.1234) | ||
expectThat(input.listIntsField).isEqualTo(listOf(1, 2, 3)) | ||
expectThat(input.listSubClassField.map { it.stringField }).isEqualTo(listOf("string1", "string2")) | ||
expectThat(input.listStringsField).isEqualTo(listOf("string1", "string2")) | ||
expectThat(input.objectField.stringField).isEqualTo("string") | ||
expectThrows<NoSuchElementException> { input.notAStringField }.message.isEqualTo("Value for field <notAStringField> is not a class kotlin.String but class kotlin.Int") | ||
expectThrows<NoSuchElementException> { input.noSuchField }.message.isEqualTo("Field <noSuchField> is missing") | ||
expectThrows<NoSuchElementException> { input.objectField.noSuchField }.message.isEqualTo("Field <noSuchField> is missing") | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
data4k/src/test/kotlin/dev/forkhandles/lens/JacksonDataContainerTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package dev.forkhandles.lens | ||
|
||
import com.fasterxml.jackson.databind.JsonNode | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import dev.forkhandles.data.JacksonDataContainer | ||
|
||
class JacksonDataContainerTest : DataContainerContract() { | ||
|
||
class SubNodeBacked(node: JsonNode) : JacksonDataContainer(node), SubClassFields { | ||
override val stringField by field<String>() | ||
override val noSuchField by field<String>() | ||
} | ||
|
||
class NodeBacked(node: JsonNode) : JacksonDataContainer(node), MainClassFields { | ||
override val stringField by field<String>() | ||
override val optionalField by field<String?>() | ||
override val booleanField by field<Boolean>() | ||
override val intField by field<Int>() | ||
override val longField by field<Long>() | ||
override val decimalField by field<Double>() | ||
override val notAStringField by field<String>() | ||
override val noSuchField by field<String>() | ||
override val listSubClassField by list(::SubNodeBacked) | ||
override val listStringsField by list(Any::toString) | ||
override val listIntsField by list<Any, Int> { it as Int } | ||
override val objectField by obj(::SubNodeBacked) | ||
} | ||
|
||
override fun container(input: Map<String, Any?>) = NodeBacked(ObjectMapper().valueToTree(input)) | ||
} |
28 changes: 28 additions & 0 deletions
28
data4k/src/test/kotlin/dev/forkhandles/lens/MapDataContainerTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package dev.forkhandles.lens | ||
|
||
import dev.forkhandles.data.MapDataContainer | ||
|
||
class MapDataContainerTest : DataContainerContract() { | ||
|
||
class SubMap(propertySet: Map<String, Any?>) : MapDataContainer(propertySet), SubClassFields { | ||
override val stringField by field<String>() | ||
override val noSuchField by field<String>() | ||
} | ||
|
||
class MapBacked(propertySet: Map<String, Any?>) : MapDataContainer(propertySet), MainClassFields { | ||
override val stringField by field<String>() | ||
override val optionalField by field<String?>() | ||
override val booleanField by field<Boolean>() | ||
override val intField by field<Int>() | ||
override val longField by field<Long>() | ||
override val decimalField by field<Double>() | ||
override val notAStringField by field<String>() | ||
override val noSuchField by field<String>() | ||
override val listSubClassField by list(::SubMap) | ||
override val listStringsField by list(Any::toString) | ||
override val listIntsField by list<Any, Int> { it as Int } | ||
override val objectField by obj(::SubMap) | ||
} | ||
|
||
override fun container(input: Map<String, Any?>): MainClassFields = MapBacked(input) | ||
} |
This file was deleted.
Oops, something went wrong.
19 changes: 0 additions & 19 deletions
19
lens4k/src/main/kotlin/dev/forkhandles/lens/AbstractWrapper.kt
This file was deleted.
Oops, something went wrong.
28 changes: 0 additions & 28 deletions
28
lens4k/src/main/kotlin/dev/forkhandles/lens/JacksonWrapper.kt
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.