Descriptor updates and utilities
This commit is contained in:
parent
28a6914747
commit
e432b07201
18
.github/workflows/build.yml
vendored
18
.github/workflows/build.yml
vendored
@ -1,6 +1,9 @@
|
|||||||
name: Gradle build
|
name: Gradle build
|
||||||
|
|
||||||
on: [ push ]
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ dev, master ]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -8,23 +11,22 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ macOS-latest, windows-latest ]
|
os: [ macOS-latest, windows-latest ]
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 40
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repo
|
- name: Checkout the repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Set up JDK 11
|
- name: Set up JDK 11
|
||||||
uses: DeLaGuardo/setup-graalvm@4.0
|
uses: DeLaGuardo/setup-graalvm@4.0
|
||||||
with:
|
with:
|
||||||
graalvm: 21.1.0
|
graalvm: 21.2.0
|
||||||
java: java11
|
java: java11
|
||||||
arch: amd64
|
arch: amd64
|
||||||
- name: Add msys to path
|
|
||||||
if: matrix.os == 'windows-latest'
|
|
||||||
run: SETX PATH "%PATH%;C:\msys64\mingw64\bin"
|
|
||||||
- name: Cache gradle
|
- name: Cache gradle
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.gradle/caches
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
|
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-gradle-
|
${{ runner.os }}-gradle-
|
||||||
@ -36,4 +38,4 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-gradle-
|
${{ runner.os }}-gradle-
|
||||||
- name: Build
|
- name: Build
|
||||||
run: ./gradlew build --no-daemon --stacktrace
|
run: ./gradlew build --build-cache --no-daemon --stacktrace
|
||||||
|
28
.github/workflows/pages.yml
vendored
28
.github/workflows/pages.yml
vendored
@ -2,39 +2,27 @@ name: Dokka publication
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [ master ]
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
timeout-minutes: 40
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repo
|
- uses: actions/checkout@v2
|
||||||
uses: actions/checkout@v2
|
- uses: DeLaGuardo/setup-graalvm@4.0
|
||||||
- name: Set up JDK 11
|
|
||||||
uses: DeLaGuardo/setup-graalvm@4.0
|
|
||||||
with:
|
with:
|
||||||
graalvm: 21.1.0
|
graalvm: 21.2.0
|
||||||
java: java11
|
java: java11
|
||||||
arch: amd64
|
arch: amd64
|
||||||
- name: Cache gradle
|
- uses: actions/cache@v2
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
with:
|
||||||
path: ~/.gradle/caches
|
path: ~/.gradle/caches
|
||||||
key: ubuntu-20.04-gradle-${{ hashFiles('*.gradle.kts') }}
|
|
||||||
restore-keys: |
|
|
||||||
ubuntu-20.04-gradle-
|
|
||||||
- name: Cache konan
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ~/.konan
|
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
|
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-gradle-
|
${{ runner.os }}-gradle-
|
||||||
- name: Build
|
- run: ./gradlew dokkaHtmlMultiModule --build-cache --no-daemon --no-parallel --stacktrace
|
||||||
run: ./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace
|
- uses: JamesIves/github-pages-deploy-action@4.1.0
|
||||||
- name: Deploy to GitHub Pages
|
|
||||||
uses: JamesIves/github-pages-deploy-action@4.1.0
|
|
||||||
with:
|
with:
|
||||||
branch: gh-pages
|
branch: gh-pages
|
||||||
folder: build/dokka/htmlMultiModule
|
folder: build/dokka/htmlMultiModule
|
||||||
|
32
.github/workflows/publish.yml
vendored
32
.github/workflows/publish.yml
vendored
@ -3,8 +3,7 @@ name: Gradle publish
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
types:
|
types: [ created ]
|
||||||
- created
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
@ -20,16 +19,15 @@ jobs:
|
|||||||
- name: Set up JDK 11
|
- name: Set up JDK 11
|
||||||
uses: DeLaGuardo/setup-graalvm@4.0
|
uses: DeLaGuardo/setup-graalvm@4.0
|
||||||
with:
|
with:
|
||||||
graalvm: 21.1.0
|
graalvm: 21.2.0
|
||||||
java: java11
|
java: java11
|
||||||
arch: amd64
|
arch: amd64
|
||||||
- name: Add msys to path
|
|
||||||
if: matrix.os == 'windows-latest'
|
|
||||||
run: SETX PATH "%PATH%;C:\msys64\mingw64\bin"
|
|
||||||
- name: Cache gradle
|
- name: Cache gradle
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.gradle/caches
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
|
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-gradle-
|
${{ runner.os }}-gradle-
|
||||||
@ -42,20 +40,14 @@ jobs:
|
|||||||
${{ runner.os }}-gradle-
|
${{ runner.os }}-gradle-
|
||||||
- name: Publish Windows Artifacts
|
- name: Publish Windows Artifacts
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
|
shell: cmd
|
||||||
run: >
|
run: >
|
||||||
./gradlew release --no-daemon
|
./gradlew release --no-daemon --build-cache -Ppublishing.enabled=true
|
||||||
-Ppublishing.enabled=true
|
-Ppublishing.space.user=${{ secrets.SPACE_APP_ID }}
|
||||||
-Ppublishing.github.user=${{ secrets.PUBLISHING_GITHUB_USER }}
|
-Ppublishing.space.token=${{ secrets.SPACE_APP_SECRET }}
|
||||||
-Ppublishing.github.token=${{ secrets.PUBLISHING_GITHUB_TOKEN }}
|
|
||||||
-Ppublishing.space.user=${{ secrets.PUBLISHING_SPACE_USER }}
|
|
||||||
-Ppublishing.space.token=${{ secrets.PUBLISHING_SPACE_TOKEN }}
|
|
||||||
- name: Publish Mac Artifacts
|
- name: Publish Mac Artifacts
|
||||||
if: matrix.os == 'macOS-latest'
|
if: matrix.os == 'macOS-latest'
|
||||||
run: >
|
run: >
|
||||||
./gradlew release --no-daemon
|
./gradlew release --no-daemon --build-cache -Ppublishing.enabled=true -Ppublishing.platform=macosX64
|
||||||
-Ppublishing.enabled=true
|
-Ppublishing.space.user=${{ secrets.SPACE_APP_ID }}
|
||||||
-Ppublishing.platform=macosX64
|
-Ppublishing.space.token=${{ secrets.SPACE_APP_SECRET }}
|
||||||
-Ppublishing.github.user=${{ secrets.PUBLISHING_GITHUB_USER }}
|
|
||||||
-Ppublishing.github.token=${{ secrets.PUBLISHING_GITHUB_TOKEN }}
|
|
||||||
-Ppublishing.space.user=${{ secrets.PUBLISHING_SPACE_USER }}
|
|
||||||
-Ppublishing.space.token=${{ secrets.PUBLISHING_SPACE_TOKEN }}
|
|
||||||
|
@ -14,10 +14,8 @@
|
|||||||
- **API breaking** `String.toName()` is replaced by `Name.parse()`
|
- **API breaking** `String.toName()` is replaced by `Name.parse()`
|
||||||
- **API breaking** Configurable`config` changed to `meta`
|
- **API breaking** Configurable`config` changed to `meta`
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
- Direct use of `Config`
|
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
- `Config`
|
||||||
- Public PluginManager mutability
|
- Public PluginManager mutability
|
||||||
- Tables and tables-exposed moved to the separate project `tables.kt`
|
- Tables and tables-exposed moved to the separate project `tables.kt`
|
||||||
- BinaryMetaFormat. Use CBOR encoding instead
|
- BinaryMetaFormat. Use CBOR encoding instead
|
||||||
|
@ -4,7 +4,7 @@ plugins {
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.5.0-dev-7"
|
version = "0.5.0-dev-9"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
@ -91,9 +91,9 @@ public fun JsonPrimitive.toValue(descriptor: MetaDescriptor?): Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turn this [JsonArray] into a [ListValue] with recursion or return null if it contains objects
|
* Turn this [JsonElement] into a [ListValue] with recursion or return null if it contains objects
|
||||||
*/
|
*/
|
||||||
public fun JsonElement.toValueOrNull(descriptor: MetaDescriptor?): Value? = when (this) {
|
private fun JsonElement.toValueOrNull(descriptor: MetaDescriptor?): Value? = when (this) {
|
||||||
is JsonPrimitive -> toValue(descriptor)
|
is JsonPrimitive -> toValue(descriptor)
|
||||||
is JsonObject -> get(Meta.VALUE_KEY)?.toValueOrNull(descriptor)
|
is JsonObject -> get(Meta.VALUE_KEY)?.toValueOrNull(descriptor)
|
||||||
is JsonArray -> {
|
is JsonArray -> {
|
||||||
|
@ -3,6 +3,7 @@ package space.kscience.dataforge.meta
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.dataforge.values.Value
|
import space.kscience.dataforge.values.Value
|
||||||
|
import space.kscience.dataforge.values.asValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The meta implementation which is guaranteed to be immutable.
|
* The meta implementation which is guaranteed to be immutable.
|
||||||
@ -32,6 +33,15 @@ public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(
|
|||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
public fun Meta(value: Value): SealedMeta = SealedMeta(value, emptyMap())
|
public fun Meta(value: Value): SealedMeta = SealedMeta(value, emptyMap())
|
||||||
|
|
||||||
|
@Suppress("FunctionName")
|
||||||
|
public fun Meta(value: Number): SealedMeta = Meta(value.asValue())
|
||||||
|
|
||||||
|
@Suppress("FunctionName")
|
||||||
|
public fun Meta(value: String): SealedMeta = Meta(value.asValue())
|
||||||
|
|
||||||
|
@Suppress("FunctionName")
|
||||||
|
public fun Meta(value: Boolean): SealedMeta = Meta(value.asValue())
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
public inline fun Meta(builder: MutableMeta.() -> Unit): SealedMeta =
|
public inline fun Meta(builder: MutableMeta.() -> Unit): SealedMeta =
|
||||||
MutableMeta(builder).seal()
|
MutableMeta(builder).seal()
|
||||||
|
@ -41,7 +41,7 @@ public interface Specification<out T : Any> : ReadOnlySpecification<T> {
|
|||||||
/**
|
/**
|
||||||
* Update a [MutableMeta] using given specification
|
* Update a [MutableMeta] using given specification
|
||||||
*/
|
*/
|
||||||
public fun <T : Any> MutableMeta.update(
|
public fun <T : Any> MutableMeta.updateWith(
|
||||||
spec: Specification<T>,
|
spec: Specification<T>,
|
||||||
action: T.() -> Unit
|
action: T.() -> Unit
|
||||||
): T = spec.write(this).apply(action)
|
): T = spec.write(this).apply(action)
|
||||||
@ -50,7 +50,7 @@ public fun <T : Any> MutableMeta.update(
|
|||||||
/**
|
/**
|
||||||
* Update configuration using given specification
|
* Update configuration using given specification
|
||||||
*/
|
*/
|
||||||
public fun <T : Any> Configurable.update(
|
public fun <T : Any> Configurable.updateWith(
|
||||||
spec: Specification<T>,
|
spec: Specification<T>,
|
||||||
action: T.() -> Unit,
|
action: T.() -> Unit,
|
||||||
): T = spec.write(meta).apply(action)
|
): T = spec.write(meta).apply(action)
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
//package space.kscience.dataforge.meta.descriptors
|
|
||||||
//
|
|
||||||
//import space.kscience.dataforge.meta.*
|
|
||||||
//import space.kscience.dataforge.misc.DFBuilder
|
|
||||||
//import space.kscience.dataforge.names.*
|
|
||||||
//import space.kscience.dataforge.values.Value
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * A common parent for [ValueDescriptor] and [NodeDescriptor]. Describes a single [TypedMetaItem] or a group of same-name-siblings.
|
|
||||||
// */
|
|
||||||
//public sealed interface ItemDescriptor: MetaRepr {
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * True if same name siblings with this name are allowed
|
|
||||||
// */
|
|
||||||
// public val multiple: Boolean
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The item description text
|
|
||||||
// */
|
|
||||||
// public val info: String?
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * True if the item is required
|
|
||||||
// */
|
|
||||||
// public val required: Boolean
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Additional attributes of an item. For example validation and widget parameters
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// public val attributes: Meta?
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * An index field by which this node is identified in case of same name siblings construct
|
|
||||||
// */
|
|
||||||
// public val indexKey: String
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Compute and cache the default [Meta] value described by this descriptor
|
|
||||||
// */
|
|
||||||
// public val defaultValue: Meta?
|
|
||||||
//
|
|
||||||
// public companion object {
|
|
||||||
// public const val DEFAULT_INDEX_KEY: String = "@index"
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * The builder for [ItemDescriptor]
|
|
||||||
// */
|
|
||||||
//@DFBuilder
|
|
||||||
//public sealed class ItemDescriptorBuilder(final override val config: MutableMeta) : Configurable, ItemDescriptor {
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * True if same name siblings with this name are allowed
|
|
||||||
// */
|
|
||||||
// override var multiple: Boolean by config.boolean(false)
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The item description text
|
|
||||||
// */
|
|
||||||
// override var info: String? by config.string()
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * True if the item is required
|
|
||||||
// */
|
|
||||||
// abstract override var required: Boolean
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Additional attributes of an item. For example validation and widget parameters
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// override var attributes: MutableMeta? by config.node()
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * An index field by which this node is identified in case of same name siblings construct
|
|
||||||
// */
|
|
||||||
// override var indexKey: String by config.string(DEFAULT_INDEX_KEY)
|
|
||||||
//
|
|
||||||
// public abstract fun build(): ItemDescriptor
|
|
||||||
//
|
|
||||||
// override fun toMeta(): Meta = config
|
|
||||||
//
|
|
||||||
// public companion object {
|
|
||||||
// public const val DEFAULT_INDEX_KEY: String = "@index"
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Configure attributes of the descriptor, creating an attributes node if needed.
|
|
||||||
// */
|
|
||||||
//public inline fun ItemDescriptorBuilder.attributes(block: MutableMeta.() -> Unit) {
|
|
||||||
// (attributes ?: MutableMeta().also { this.attributes = it }).apply(block)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Check if given item suits the descriptor
|
|
||||||
// */
|
|
||||||
//public fun ItemDescriptor.validateItem(item: MetaItem?): Boolean {
|
|
||||||
// if (item == null) return !required
|
|
||||||
// return when (this) {
|
|
||||||
// is ValueDescriptor -> isAllowedValue(item.value ?: return false)
|
|
||||||
// is NodeDescriptor -> items.all { (key, d) ->
|
|
||||||
// d.validateItem(item.node[key])
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Get a descriptor item associated with given name or null if item for given name not provided
|
|
||||||
// */
|
|
||||||
//public operator fun ItemDescriptor.get(name: Name): ItemDescriptor? {
|
|
||||||
// if (name.isEmpty()) return this
|
|
||||||
// return when (this) {
|
|
||||||
// is ValueDescriptor -> null // empty name already checked
|
|
||||||
// is NodeDescriptor -> items[name.firstOrNull()!!.toString()]?.get(name.cutFirst())
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public operator fun ItemDescriptor.get(name: String): ItemDescriptor? = get(name.toName())
|
|
||||||
//
|
|
@ -8,13 +8,34 @@ import space.kscience.dataforge.names.*
|
|||||||
import space.kscience.dataforge.values.Value
|
import space.kscience.dataforge.values.Value
|
||||||
import space.kscience.dataforge.values.ValueType
|
import space.kscience.dataforge.values.ValueType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The restriction on Meta node content
|
||||||
|
*/
|
||||||
|
public enum class MetaNodeRequirements {
|
||||||
|
/**
|
||||||
|
* no restrictions
|
||||||
|
*/
|
||||||
|
NONE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The node itself must exist
|
||||||
|
*/
|
||||||
|
EXIST,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value must be present
|
||||||
|
*/
|
||||||
|
HAS_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The descriptor for a meta
|
* The descriptor for a meta
|
||||||
* @param info description text
|
* @param info description text
|
||||||
* @param children child descriptors for this node
|
* @param children child descriptors for this node
|
||||||
* @param multiple True if same name siblings with this name are allowed
|
* @param multiple True if same name siblings with this name are allowed
|
||||||
* @param required True if the item is required
|
* @param required The requirements for node content
|
||||||
* @param type list of allowed types for [Meta.value], null if all values are allowed. If the type is [ValueType.NULL], no value is allowed for this node.
|
* @param valueTypes list of allowed types for [Meta.value], null if all values are allowed.
|
||||||
|
* Empty list means that no value should be present in this node.
|
||||||
* @param indexKey An index field by which this node is identified in case of same name siblings construct
|
* @param indexKey An index field by which this node is identified in case of same name siblings construct
|
||||||
* @param defaultValue the default [Meta.value] for the node
|
* @param defaultValue the default [Meta.value] for the node
|
||||||
* @param attributes additional attributes of this descriptor. For example validation and widget parameters
|
* @param attributes additional attributes of this descriptor. For example validation and widget parameters
|
||||||
@ -24,8 +45,8 @@ public data class MetaDescriptor(
|
|||||||
public val info: String? = null,
|
public val info: String? = null,
|
||||||
public val children: Map<String, MetaDescriptor> = emptyMap(),
|
public val children: Map<String, MetaDescriptor> = emptyMap(),
|
||||||
public val multiple: Boolean = false,
|
public val multiple: Boolean = false,
|
||||||
public val required: Boolean = false,
|
public val required: MetaNodeRequirements = MetaNodeRequirements.NONE,
|
||||||
public val type: List<ValueType>? = null,
|
public val valueTypes: List<ValueType>? = null,
|
||||||
public val indexKey: String = Meta.INDEX_KEY,
|
public val indexKey: String = Meta.INDEX_KEY,
|
||||||
public val defaultValue: Value? = null,
|
public val defaultValue: Value? = null,
|
||||||
public val attributes: Meta = Meta.EMPTY,
|
public val attributes: Meta = Meta.EMPTY,
|
||||||
@ -58,9 +79,22 @@ public val MetaDescriptor.defaultNode: Meta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun MetaDescriptor.validate(value: Value?): Boolean = if (value == null) {
|
||||||
|
required != MetaNodeRequirements.HAS_VALUE
|
||||||
|
} else {
|
||||||
|
(valueTypes == null || value.type in valueTypes) && (allowedValues?.let { value in it } ?: true)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if given item suits the descriptor
|
* Check if given item suits the descriptor
|
||||||
*/
|
*/
|
||||||
public fun MetaDescriptor.validate(item: Meta?): Boolean = (item != null || !required) &&
|
public fun MetaDescriptor.validate(item: Meta?): Boolean {
|
||||||
allowedValues?.let { item?.value in it } ?: true &&
|
if (item == null) return required == MetaNodeRequirements.NONE
|
||||||
children.all { (key, childDescriptor) -> childDescriptor.validate(item?.get(key)) }
|
if (!validate(item.value)) return false
|
||||||
|
|
||||||
|
children.forEach { (key, childDescriptor) ->
|
||||||
|
if (!childDescriptor.validate(item[key])) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package space.kscience.dataforge.meta.descriptors
|
package space.kscience.dataforge.meta.descriptors
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
|
||||||
import space.kscience.dataforge.meta.get
|
|
||||||
import space.kscience.dataforge.meta.set
|
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.cutFirst
|
import space.kscience.dataforge.names.cutFirst
|
||||||
import space.kscience.dataforge.names.first
|
import space.kscience.dataforge.names.first
|
||||||
@ -11,21 +8,13 @@ import space.kscience.dataforge.names.length
|
|||||||
import space.kscience.dataforge.values.Value
|
import space.kscience.dataforge.values.Value
|
||||||
import space.kscience.dataforge.values.ValueType
|
import space.kscience.dataforge.values.ValueType
|
||||||
import space.kscience.dataforge.values.asValue
|
import space.kscience.dataforge.values.asValue
|
||||||
import kotlin.collections.List
|
|
||||||
import kotlin.collections.MutableMap
|
|
||||||
import kotlin.collections.emptyList
|
|
||||||
import kotlin.collections.getOrPut
|
|
||||||
import kotlin.collections.hashMapOf
|
|
||||||
import kotlin.collections.listOf
|
|
||||||
import kotlin.collections.map
|
|
||||||
import kotlin.collections.mapValues
|
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
public class MetaDescriptorBuilder {
|
public class MetaDescriptorBuilder internal constructor() {
|
||||||
public var info: String? = null
|
public var info: String? = null
|
||||||
public var children: MutableMap<String, MetaDescriptorBuilder> = hashMapOf()
|
public var children: MutableMap<String, MetaDescriptorBuilder> = hashMapOf()
|
||||||
public var multiple: Boolean = false
|
public var multiple: Boolean = false
|
||||||
public var required: Boolean = false
|
public var required: MetaNodeRequirements = MetaNodeRequirements.NONE
|
||||||
|
|
||||||
public var type: List<ValueType>? = null
|
public var type: List<ValueType>? = null
|
||||||
|
|
||||||
@ -42,6 +31,22 @@ public class MetaDescriptorBuilder {
|
|||||||
|
|
||||||
public var attributes: MutableMeta = MutableMeta()
|
public var attributes: MutableMeta = MutableMeta()
|
||||||
|
|
||||||
|
public inline fun attributes(block: MutableMeta.() -> Unit) {
|
||||||
|
attributes.apply(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun item(name: Name, descriptor: MetaDescriptor) {
|
||||||
|
when (name.length) {
|
||||||
|
0 -> {}
|
||||||
|
1 -> {
|
||||||
|
children[name.first().body] = descriptor.toBuilder()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
children.getOrPut(name.first().body) { MetaDescriptorBuilder() }.item(name.cutFirst(), descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public fun item(name: Name, block: MetaDescriptorBuilder.() -> Unit) {
|
public fun item(name: Name, block: MetaDescriptorBuilder.() -> Unit) {
|
||||||
when (name.length) {
|
when (name.length) {
|
||||||
0 -> apply(block)
|
0 -> apply(block)
|
||||||
@ -71,7 +76,7 @@ public class MetaDescriptorBuilder {
|
|||||||
children = children.mapValues { it.value.build() },
|
children = children.mapValues { it.value.build() },
|
||||||
multiple = multiple,
|
multiple = multiple,
|
||||||
required = required,
|
required = required,
|
||||||
type = type,
|
valueTypes = type,
|
||||||
indexKey = indexKey,
|
indexKey = indexKey,
|
||||||
defaultValue = default,
|
defaultValue = default,
|
||||||
attributes = attributes
|
attributes = attributes
|
||||||
@ -127,10 +132,27 @@ public inline fun <reified E : Enum<E>> MetaDescriptorBuilder.enum(
|
|||||||
key: Name,
|
key: Name,
|
||||||
default: E?,
|
default: E?,
|
||||||
crossinline modifier: MetaDescriptorBuilder.() -> Unit = {},
|
crossinline modifier: MetaDescriptorBuilder.() -> Unit = {},
|
||||||
): Unit = value(key,ValueType.STRING) {
|
): Unit = value(key, ValueType.STRING) {
|
||||||
default?.let {
|
default?.let {
|
||||||
this.default = default.asValue()
|
this.default = default.asValue()
|
||||||
}
|
}
|
||||||
allowedValues = enumValues<E>().map { it.asValue() }
|
allowedValues = enumValues<E>().map { it.asValue() }
|
||||||
modifier()
|
modifier()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun MetaDescriptor.toBuilder(): MetaDescriptorBuilder = MetaDescriptorBuilder().apply {
|
||||||
|
info = this@toBuilder.info
|
||||||
|
children = this@toBuilder.children.mapValuesTo(LinkedHashMap()) { it.value.toBuilder() }
|
||||||
|
multiple = this@toBuilder.multiple
|
||||||
|
required = this@toBuilder.required
|
||||||
|
type = this@toBuilder.valueTypes
|
||||||
|
indexKey = this@toBuilder.indexKey
|
||||||
|
default = defaultValue
|
||||||
|
attributes = this@toBuilder.attributes.toMutableMeta()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a deep copy of this descriptor applying given transformation [block]
|
||||||
|
*/
|
||||||
|
public fun MetaDescriptor.copy(block: MetaDescriptorBuilder.() -> Unit = {}): MetaDescriptor =
|
||||||
|
toBuilder().apply(block).build()
|
@ -1,222 +0,0 @@
|
|||||||
//package space.kscience.dataforge.meta.descriptors
|
|
||||||
//
|
|
||||||
//import space.kscience.dataforge.meta.*
|
|
||||||
//import space.kscience.dataforge.misc.DFBuilder
|
|
||||||
//import space.kscience.dataforge.names.*
|
|
||||||
//
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * A [Meta] that is constructed from [NodeDescriptor]
|
|
||||||
// */
|
|
||||||
//private class DescriptorMeta(val descriptor: NodeDescriptor) : AbstractMeta() {
|
|
||||||
// override val items: Map<NameToken, MetaItem>
|
|
||||||
// get() = buildMap {
|
|
||||||
// descriptor.items.forEach { (token, descriptorItem) ->
|
|
||||||
// val item = descriptorItem.defaultValue
|
|
||||||
// if (item != null) {
|
|
||||||
// put(NameToken(token), item)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Descriptor for meta node. Could contain additional information for viewing
|
|
||||||
// * and editing.
|
|
||||||
// *
|
|
||||||
// * @author Alexander Nozik
|
|
||||||
// */
|
|
||||||
//@DFBuilder
|
|
||||||
//public sealed interface NodeDescriptor : ItemDescriptor {
|
|
||||||
// /**
|
|
||||||
// * True if the node is required
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// override val required: Boolean
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The default for this node. Null if there is no default.
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// public val default: Meta?
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The map of children item descriptors (both nodes and values)
|
|
||||||
// */
|
|
||||||
// public val items: Map<String, ItemDescriptor>
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The map of children node descriptors
|
|
||||||
// */
|
|
||||||
// public val nodes: Map<String, NodeDescriptor>
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The list of children value descriptors
|
|
||||||
// */
|
|
||||||
// public val values: Map<String, ValueDescriptor>
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Generate a laminate representing default item set generated by this descriptor
|
|
||||||
// */
|
|
||||||
// public val defaultMeta: Laminate
|
|
||||||
//
|
|
||||||
// public companion object {
|
|
||||||
//
|
|
||||||
// internal val ITEM_KEY: Name = "item".asName()
|
|
||||||
// internal val IS_NODE_KEY: Name = "@isNode".asName()
|
|
||||||
//
|
|
||||||
// //TODO infer descriptor from spec
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//@DFBuilder
|
|
||||||
//public class NodeDescriptorBuilder(config: MutableMeta = MutableMeta()) : ItemDescriptorBuilder(config), NodeDescriptor {
|
|
||||||
// init {
|
|
||||||
// config[IS_NODE_KEY] = true
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * True if the node is required
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// override var required: Boolean by config.boolean { default == null }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The default for this node. Null if there is no default.
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// override var default: MutableMeta? by config.node()
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The map of children item descriptors (both nodes and values)
|
|
||||||
// */
|
|
||||||
// override val items: Map<String, ItemDescriptor>
|
|
||||||
// get() = config.getIndexed(ITEM_KEY).entries.associate { (name, item) ->
|
|
||||||
// if (name == null) error("Child item index should not be null")
|
|
||||||
// val node = item.node ?: error("Node descriptor must be a node")
|
|
||||||
// if (node[IS_NODE_KEY].boolean == true) {
|
|
||||||
// name to NodeDescriptorBuilder(node as MutableMeta)
|
|
||||||
// } else {
|
|
||||||
// name to ValueDescriptorBuilder(node as MutableMeta)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The map of children node descriptors
|
|
||||||
// */
|
|
||||||
// @Suppress("UNCHECKED_CAST")
|
|
||||||
// override val nodes: Map<String, NodeDescriptor>
|
|
||||||
// get() = config.getIndexed(ITEM_KEY).entries.filter {
|
|
||||||
// it.value.node[IS_NODE_KEY].boolean == true
|
|
||||||
// }.associate { (name, item) ->
|
|
||||||
// if (name == null) error("Child node index should not be null")
|
|
||||||
// val node = item.node ?: error("Node descriptor must be a node")
|
|
||||||
// name to NodeDescriptorBuilder(node as MutableMeta)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The list of children value descriptors
|
|
||||||
// */
|
|
||||||
// override val values: Map<String, ValueDescriptor>
|
|
||||||
// get() = config.getIndexed(ITEM_KEY).entries.filter {
|
|
||||||
// it.value.node[IS_NODE_KEY].boolean != true
|
|
||||||
// }.associate { (name, item) ->
|
|
||||||
// if (name == null) error("Child value index should not be null")
|
|
||||||
// val node = item.node ?: error("Node descriptor must be a node")
|
|
||||||
// name to ValueDescriptorBuilder(node as MutableMeta)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private fun buildNode(name: Name): NodeDescriptorBuilder {
|
|
||||||
// return when (name.length) {
|
|
||||||
// 0 -> this
|
|
||||||
// 1 -> {
|
|
||||||
// val token = NameToken(ITEM_KEY.toString(), name.toString())
|
|
||||||
// val config: MutableMeta = config[token].node ?: MutableMeta().also {
|
|
||||||
// it[IS_NODE_KEY] = true
|
|
||||||
// config[token] = it
|
|
||||||
// }
|
|
||||||
// NodeDescriptorBuilder(config)
|
|
||||||
// }
|
|
||||||
// else -> buildNode(name.firstOrNull()?.asName()!!).buildNode(name.cutFirst())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Define a child item descriptor for this node
|
|
||||||
// */
|
|
||||||
// private fun newItem(key: String, descriptor: ItemDescriptor) {
|
|
||||||
// if (items.keys.contains(key)) error("The key $key already exists in descriptor")
|
|
||||||
// val token = ITEM_KEY.withIndex(key)
|
|
||||||
// config[token] = descriptor.toMeta()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public fun item(name: Name, descriptor: ItemDescriptor) {
|
|
||||||
// buildNode(name.cutLast()).newItem(name.lastOrNull().toString(), descriptor)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public fun item(name: String, descriptor: ItemDescriptor) {
|
|
||||||
// item(name.toName(), descriptor)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Create and configure a child node descriptor
|
|
||||||
// */
|
|
||||||
// public fun node(name: Name, block: NodeDescriptorBuilder.() -> Unit) {
|
|
||||||
// item(name, NodeDescriptorBuilder().apply(block))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public fun node(name: String, block: NodeDescriptorBuilder.() -> Unit) {
|
|
||||||
// node(name.toName(), block)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Create and configure child value descriptor
|
|
||||||
// */
|
|
||||||
// public fun value(name: Name, block: ValueDescriptorBuilder.() -> Unit) {
|
|
||||||
// require(name.length >= 1) { "Name length for value descriptor must be non-empty" }
|
|
||||||
// item(name, ValueDescriptorBuilder().apply(block))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public fun value(name: String, block: ValueDescriptorBuilder.() -> Unit) {
|
|
||||||
// value(name.toName(), block)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Generate a laminate representing default item set generated by this descriptor
|
|
||||||
// */
|
|
||||||
// override val defaultMeta: Laminate by lazy { Laminate(default, DescriptorMeta(this)) }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Build a default [MetaItemNode] from this node descriptor
|
|
||||||
// */
|
|
||||||
// override val defaultValue: MetaItem get() = MetaItemNode(defaultMeta)
|
|
||||||
//
|
|
||||||
// override fun build(): NodeDescriptor = NodeDescriptorBuilder(config.copy())
|
|
||||||
//
|
|
||||||
// public companion object {
|
|
||||||
//
|
|
||||||
// internal val ITEM_KEY: Name = "item".asName()
|
|
||||||
// internal val IS_NODE_KEY: Name = "@isNode".asName()
|
|
||||||
//
|
|
||||||
// //TODO infer descriptor from spec
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public inline fun NodeDescriptor(block: NodeDescriptorBuilder.() -> Unit): NodeDescriptor =
|
|
||||||
// NodeDescriptorBuilder().apply(block)
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Merge two node descriptors into one using first one as primary
|
|
||||||
// */
|
|
||||||
//public operator fun NodeDescriptor.plus(other: NodeDescriptor): NodeDescriptor {
|
|
||||||
// return NodeDescriptorBuilder().apply {
|
|
||||||
// config.update(other.toMeta())
|
|
||||||
// config.update(this@plus.toMeta())
|
|
||||||
// }
|
|
||||||
//}
|
|
@ -1,139 +0,0 @@
|
|||||||
//package space.kscience.dataforge.meta.descriptors
|
|
||||||
//
|
|
||||||
//import space.kscience.dataforge.meta.*
|
|
||||||
//import space.kscience.dataforge.misc.DFBuilder
|
|
||||||
//import space.kscience.dataforge.values.*
|
|
||||||
//
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * A descriptor for meta value
|
|
||||||
// *
|
|
||||||
// * Descriptor can have non-atomic path. It is resolved when descriptor is added to the node
|
|
||||||
// *
|
|
||||||
// * @author Alexander Nozik
|
|
||||||
// */
|
|
||||||
//@DFBuilder
|
|
||||||
//public sealed interface ValueDescriptor : ItemDescriptor {
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * True if the value is required
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// override val required: Boolean
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The default for this value. Null if there is no default.
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// public val default: Value?
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * A list of allowed ValueTypes. Empty if any value type allowed
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// public val type: List<ValueType>?
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Check if given value is allowed for here. The type should be allowed and
|
|
||||||
// * if it is value should be within allowed values
|
|
||||||
// *
|
|
||||||
// * @param value
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// public fun isAllowedValue(value: Value): Boolean =
|
|
||||||
// (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true)
|
|
||||||
// && (allowedValues.isEmpty() || allowedValues.contains(value))
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * A list of allowed values with descriptions. If empty than any value is
|
|
||||||
// * allowed.
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// public val allowedValues: List<Value>
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * A builder fir [ValueDescriptor]
|
|
||||||
// */
|
|
||||||
//@DFBuilder
|
|
||||||
//public class ValueDescriptorBuilder(
|
|
||||||
// config: MutableMeta = MutableMeta()
|
|
||||||
//) : ItemDescriptorBuilder(config), ValueDescriptor {
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * True if the value is required
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// override var required: Boolean by config.boolean { default == null }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The default for this value. Null if there is no default.
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// override var default: Value? by config.value()
|
|
||||||
//
|
|
||||||
// public fun default(v: Any) {
|
|
||||||
// this.default = Value.of(v)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * A list of allowed ValueTypes. Empty if any value type allowed
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// override var type: List<ValueType>? by config.listValue { ValueType.valueOf(it.string) }
|
|
||||||
//
|
|
||||||
// public fun type(vararg t: ValueType) {
|
|
||||||
// this.type = listOf(*t)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Check if given value is allowed for here. The type should be allowed and
|
|
||||||
// * if it is value should be within allowed values
|
|
||||||
// *
|
|
||||||
// * @param value
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// override fun isAllowedValue(value: Value): Boolean {
|
|
||||||
// return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true)
|
|
||||||
// && (allowedValues.isEmpty() || allowedValues.contains(value))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * A list of allowed values with descriptions. If empty than any value is
|
|
||||||
// * allowed.
|
|
||||||
// *
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// override var allowedValues: List<Value> by config.item().convert(
|
|
||||||
// reader = {
|
|
||||||
// val value = it.value
|
|
||||||
// when {
|
|
||||||
// value?.list != null -> value.list
|
|
||||||
// type?.let { type -> type.size == 1 && type[0] === ValueType.BOOLEAN } ?: false -> listOf(True, False)
|
|
||||||
// else -> emptyList()
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// writer = {
|
|
||||||
// MetaItemValue(it.asValue())
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Allow given list of value and forbid others
|
|
||||||
// */
|
|
||||||
// public fun allow(vararg v: Any) {
|
|
||||||
// this.allowedValues = v.map { Value.of(it) }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override val defaultValue: MetaItem? get() = default?.asMetaItem()
|
|
||||||
//
|
|
||||||
// override fun build(): ValueDescriptor = ValueDescriptorBuilder(config.copy())
|
|
||||||
//}
|
|
@ -48,6 +48,11 @@ public class Name(public val tokens: List<NameToken>) {
|
|||||||
|
|
||||||
public val EMPTY: Name = Name(emptyList())
|
public val EMPTY: Name = Name(emptyList())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a list of strings to a [Name] interpreting all arguments as token bodies without indices
|
||||||
|
*/
|
||||||
|
public fun of(vararg strings: String): Name = Name(strings.map { NameToken(it) })
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a [String] to name parsing it and extracting name tokens and index syntax.
|
* Convert a [String] to name parsing it and extracting name tokens and index syntax.
|
||||||
* This operation is rather heavy so it should be used with care in high performance code.
|
* This operation is rather heavy so it should be used with care in high performance code.
|
||||||
|
@ -62,7 +62,7 @@ class SpecificationTest {
|
|||||||
fun testChildUpdate() {
|
fun testChildUpdate() {
|
||||||
val config = MutableMeta()
|
val config = MutableMeta()
|
||||||
val child = config.getOrCreate("child")
|
val child = config.getOrCreate("child")
|
||||||
child.update(TestScheme) {
|
child.updateWith(TestScheme) {
|
||||||
a = 22
|
a = 22
|
||||||
b = "test"
|
b = "test"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user