Isolate and deprecate Context plugin mutation logic.

This commit is contained in:
Alexander Nozik 2021-04-06 15:12:40 +03:00
parent 9fbc482549
commit f1f5f7a70c
18 changed files with 165 additions and 105 deletions

View File

@ -4,12 +4,16 @@
### Added
- LogManager plugin
- dataforge-context API dependency on SLF4j
- Context `withEnv` and `fetch` methods to manipulate plugins without changing plugins after creation.
### Changed
- Kotlin-logging moved from common to JVM and JS. Replaced by console for native.
- Package changed to `space.kscience`
- Scheme made observable
- Global context is a variable (the singleton is hidden and will be deprecated in future)
### Deprecated
- Direct use of PluginManager
### Removed
- Common dependency on Kotlin-logging

View File

@ -38,6 +38,7 @@ public final class space/kscience/dataforge/context/ClassLoaderPluginKt {
public class space/kscience/dataforge/context/Context : kotlinx/coroutines/CoroutineScope, space/kscience/dataforge/meta/MetaRepr, space/kscience/dataforge/misc/Named, space/kscience/dataforge/provider/Provider {
public static final field Companion Lspace/kscience/dataforge/context/Context$Companion;
public static final field PROPERTY_TARGET Ljava/lang/String;
public final fun buildContext (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context;
public fun close ()V
public fun content (Ljava/lang/String;)Ljava/util/Map;
public final fun content (Ljava/lang/String;Z)Ljava/util/Map;
@ -47,6 +48,7 @@ public class space/kscience/dataforge/context/Context : kotlinx/coroutines/Corou
public final fun getName ()Lspace/kscience/dataforge/names/Name;
public final fun getParent ()Lspace/kscience/dataforge/context/Context;
public final fun getPlugins ()Lspace/kscience/dataforge/context/PluginManager;
public final fun getProperties ()Lspace/kscience/dataforge/meta/Laminate;
public fun toMeta ()Lspace/kscience/dataforge/meta/Meta;
}
@ -58,24 +60,35 @@ public abstract interface class space/kscience/dataforge/context/ContextAware {
}
public final class space/kscience/dataforge/context/ContextBuilder {
public fun <init> ()V
public fun <init> (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;)V
public synthetic fun <init> (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun build ()Lspace/kscience/dataforge/context/Context;
public final fun getName ()Ljava/lang/String;
public final fun getName ()Lspace/kscience/dataforge/names/Name;
public final fun name (Ljava/lang/String;)V
public final fun plugin (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public final fun plugin (Lspace/kscience/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;)V
public final fun plugin (Lspace/kscience/dataforge/context/PluginFactory;Lspace/kscience/dataforge/meta/Meta;)V
public final fun plugin (Lspace/kscience/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Lspace/kscience/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Lspace/kscience/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public final fun properties (Lkotlin/jvm/functions/Function1;)V
public final fun setName (Ljava/lang/String;)V
public final fun setName (Lspace/kscience/dataforge/names/Name;)V
}
public final class space/kscience/dataforge/context/ContextKt {
public static final fun Context (Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context;
public static synthetic fun Context$default (Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/context/Context;
public final class space/kscience/dataforge/context/ContextBuilderKt {
}
public final class space/kscience/dataforge/context/DefaultLogManager : space/kscience/dataforge/context/AbstractPlugin, space/kscience/dataforge/context/LogManager {
public static final field Companion Lspace/kscience/dataforge/context/DefaultLogManager$Companion;
public fun <init> ()V
public fun getTag ()Lspace/kscience/dataforge/context/PluginTag;
public fun log (Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V
}
public final class space/kscience/dataforge/context/DefaultLogManager$Companion : space/kscience/dataforge/context/PluginFactory {
public fun getTag ()Lspace/kscience/dataforge/context/PluginTag;
public fun getType ()Lkotlin/reflect/KClass;
public synthetic fun invoke (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/context/Context;)Ljava/lang/Object;
public fun invoke (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/context/Context;)Lspace/kscience/dataforge/context/DefaultLogManager;
}
public abstract interface class space/kscience/dataforge/context/Factory {
@ -88,12 +101,13 @@ public final class space/kscience/dataforge/context/Factory$DefaultImpls {
public final class space/kscience/dataforge/context/Global : space/kscience/dataforge/context/Context {
public static final field INSTANCE Lspace/kscience/dataforge/context/Global;
public fun close ()V
public final fun context (Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context;
public static synthetic fun context$default (Lspace/kscience/dataforge/context/Global;Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/context/Context;
public final fun getContext (Ljava/lang/String;)Lspace/kscience/dataforge/context/Context;
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
public final fun getLogger ()Lspace/kscience/dataforge/context/LogManager;
public final fun getDefaultLogger ()Lspace/kscience/dataforge/context/LogManager;
}
public final class space/kscience/dataforge/context/GlobalKt {
public static final fun Context (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context;
public static synthetic fun Context$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/context/Context;
}
public abstract interface class space/kscience/dataforge/context/LogManager : space/kscience/dataforge/context/Logable, space/kscience/dataforge/context/Plugin {
@ -128,7 +142,7 @@ public final class space/kscience/dataforge/context/LogManagerKt {
public static final fun error (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)V
public static synthetic fun error$default (Lspace/kscience/dataforge/context/Logable;Ljava/lang/Throwable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
public static synthetic fun error$default (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
public static final fun getLogger (Lspace/kscience/dataforge/context/Context;)Lspace/kscience/dataforge/context/LogManager;
public static final fun getLogger (Lspace/kscience/dataforge/context/Context;)Lspace/kscience/dataforge/context/Logable;
public static final fun getLogger (Lspace/kscience/dataforge/context/ContextAware;)Lspace/kscience/dataforge/context/Logable;
public static final fun info (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)V
public static synthetic fun info$default (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V

View File

@ -11,6 +11,7 @@ import space.kscience.dataforge.misc.Named
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.provider.Provider
import kotlin.coroutines.CoroutineContext
import kotlin.jvm.Synchronized
/**
* The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top.
@ -31,12 +32,13 @@ public open class Context internal constructor(
/**
* Context properties. Working as substitute for environment variables
*/
private val properties: Laminate = if (parent == null) {
public val properties: Laminate = if (parent == null) {
Laminate(meta)
} else {
Laminate(meta, parent.properties)
}
/**
* A [PluginManager] for current context
*/
@ -68,10 +70,24 @@ public open class Context internal constructor(
}
}
private val childrenContexts = HashMap<Name, Context>()
/**
* Detach all plugins and terminate context
* Build and register a child context
*/
@Synchronized
public fun buildContext(block: ContextBuilder.() -> Unit): Context{
val newContext = ContextBuilder(this).apply(block).build()
childrenContexts[newContext.name] = newContext
return newContext
}
/**
* Detach all plugins, and close child contexts
*/
public open fun close() {
//recursively closed child context
childrenContexts.forEach { it.value.close() }
//detach all plugins
plugins.forEach { it.detach() }
}
@ -87,9 +103,6 @@ public open class Context internal constructor(
}
}
public fun Context(name: String, parent: Context = Global, block: ContextBuilder.() -> Unit = {}): Context =
Global.context(name, parent, block)
/**
* The interface for something that encapsulated in context
*

View File

@ -3,8 +3,11 @@ package space.kscience.dataforge.context
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaBuilder
import space.kscience.dataforge.meta.seal
import space.kscience.dataforge.meta.toMutableMeta
import space.kscience.dataforge.misc.DFBuilder
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.toName
import kotlin.collections.component1
import kotlin.collections.component2
@ -14,14 +17,22 @@ import kotlin.collections.set
* A convenience builder for context
*/
@DFBuilder
public class ContextBuilder(private val parent: Context = Global, public var name: String = "@anonymous") {
private val factories = HashMap<PluginFactory<*>, Meta>()
private var meta = MetaBuilder()
public class ContextBuilder internal constructor(
private val parent: Context,
public var name: Name? = null,
meta: Meta = Meta.EMPTY,
) {
internal val factories = HashMap<PluginFactory<*>, Meta>()
internal var meta = meta.toMutableMeta()
public fun properties(action: MetaBuilder.() -> Unit) {
meta.action()
}
public fun name(string: String){
this.name = string.toName()
}
@OptIn(DFExperimental::class)
private fun findPluginFactory(tag: PluginTag): PluginFactory<*> =
parent.gatherInSequence<PluginFactory<*>>(PluginFactory.TYPE).values
@ -32,6 +43,10 @@ public class ContextBuilder(private val parent: Context = Global, public var nam
factories[factory] = Meta(metaBuilder)
}
public fun plugin(factory: PluginFactory<*>, meta: Meta){
factories[factory] = meta
}
public fun plugin(factory: PluginFactory<*>, metaBuilder: MetaBuilder.() -> Unit = {}) {
factories[factory] = Meta(metaBuilder)
}
@ -41,10 +56,29 @@ public class ContextBuilder(private val parent: Context = Global, public var nam
}
public fun build(): Context {
return Context(name.toName(), parent, meta.seal()).apply {
val contextName = name ?: "@auto[${hashCode().toUInt().toString(16)}]".toName()
return Context(contextName, parent, meta.seal()).apply {
factories.forEach { (factory, meta) ->
plugins.load(factory, meta)
}
}
}
}
/**
* Check if current context contains all plugins required by the builder and return it it does or forks to a new context
* if it does not.
*/
public fun Context.withEnv(block: ContextBuilder.() -> Unit): Context {
fun Context.contains(factory: PluginFactory<*>, meta: Meta): Boolean {
val loaded = plugins[factory.tag] ?: return false
return loaded.meta == meta
}
val builder = ContextBuilder(this, name + "env", properties).apply(block)
val requiresFork = builder.factories.any { (factory, meta) ->
!contains(factory, meta)
} || ((properties as Meta) == builder.meta)
return if (requiresFork) builder.build() else this
}

View File

@ -1,7 +1,7 @@
package space.kscience.dataforge.context
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.Job
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.asName
import kotlin.coroutines.CoroutineContext
@ -13,43 +13,10 @@ internal expect val globalLoggerFactory: PluginFactory<out LogManager>
* A global root context. Closing [Global] terminates the framework.
*/
@ThreadLocal
public object Global : Context("GLOBAL".asName(), null, Meta.EMPTY) {
override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + SupervisorJob()
/**
* The default logging manager
*/
public val logger: LogManager by lazy { globalLoggerFactory.invoke(context = this).apply { attach(this@Global) } }
/**
* Closing all contexts
*
* @throws Exception
*/
override fun close() {
logger.info { "Shutting down GLOBAL" }
for (ctx in contextRegistry.values) {
ctx.close()
}
super.close()
private object GlobalContext : Context("GLOBAL".asName(), null, Meta.EMPTY) {
override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + Job()
}
private val contextRegistry = HashMap<String, Context>()
public val Global: Context get() = GlobalContext
/**
* Get previously built context
*
* @param name
* @return
*/
public fun getContext(name: String): Context? {
return contextRegistry[name]
}
public fun context(name: String, parent: Context = this, block: ContextBuilder.() -> Unit = {}): Context =
ContextBuilder(parent, name).apply(block).build().also {
contextRegistry[name] = it
}
}
public fun Context(block: ContextBuilder.() -> Unit = {}): Context = Global.buildContext(block)

View File

@ -1,15 +1,16 @@
package space.kscience.dataforge.context
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.misc.Named
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus
import kotlin.reflect.KClass
public interface Logable {
public fun interface Logable {
public fun log(name: Name, tag: String, body: () -> String)
}
public interface LogManager : Plugin, Logable {
public companion object {
public const val TRACE: String = "TRACE"
public const val INFO: String = "INFO"
@ -41,11 +42,30 @@ public fun Logable.error(throwable: Throwable?, name: Name = Name.EMPTY, body: (
}
}
public class DefaultLogManager : AbstractPlugin(), LogManager {
override fun log(name: Name, tag: String, body: () -> String) {
val message: String = body.safe
println("[${context.name}] $name: $message")
}
override val tag: PluginTag get() = Companion.tag
public companion object : PluginFactory<DefaultLogManager> {
override fun invoke(meta: Meta, context: Context): DefaultLogManager = DefaultLogManager()
override val tag: PluginTag = PluginTag(group = PluginTag.DATAFORGE_GROUP, name = "log.default")
override val type: KClass<out DefaultLogManager> = DefaultLogManager::class
}
}
/**
* Context log manager inherited from parent
*/
public val Context.logger: LogManager
get() = plugins.find(inherit = true) { it is LogManager } as? LogManager ?: Global.logger
public val Context.logger: Logable
get() = plugins.find(inherit = true) { it is LogManager } as? LogManager
?: globalLoggerFactory(context = Global).apply { attach(Global) }
/**
* The named proxy logger for a context member

View File

@ -63,5 +63,4 @@ public interface Plugin : Named, ContextAware, Provider, MetaRepr {
public companion object {
public const val TARGET: String = "plugin"
}
}

View File

@ -13,8 +13,6 @@ import kotlin.reflect.KClass
*/
public class PluginManager(override val context: Context) : ContextAware, Iterable<Plugin> {
//TODO refactor to read-only container
/**
* A set of loaded plugins
*/
@ -85,6 +83,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
* @param plugin
* @return
*/
@Deprecated("Use immutable contexts instead")
public fun <T : Plugin> load(plugin: T): T {
if (get(plugin::class, plugin.tag, recursive = false) != null) {
error("Plugin with tag ${plugin.tag} already exists in ${context.name}")
@ -93,7 +92,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
fetch(factory, meta, true)
}
Global.logger.info { "Loading plugin ${plugin.name} into ${context.name}" }
logger.info { "Loading plugin ${plugin.name} into ${context.name}" }
plugin.attach(context)
plugins.add(plugin)
return plugin
@ -103,15 +102,18 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
/**
* Load a plugin using its factory
*/
@Deprecated("Use immutable contexts instead")
public fun <T : Plugin> load(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY): T =
load(factory(meta, context))
@Deprecated("Use immutable contexts instead")
public fun <T : Plugin> load(factory: PluginFactory<T>, metaBuilder: MetaBuilder.() -> Unit): T =
load(factory, Meta(metaBuilder))
/**
* Remove a plugin from [PluginManager]
*/
@Deprecated("Use immutable contexts instead")
public fun remove(plugin: Plugin) {
if (plugins.contains(plugin)) {
Global.logger.info { "Removing plugin ${plugin.name} from ${context.name}" }
@ -123,6 +125,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
/**
* Get an existing plugin with given meta or load new one using provided factory
*/
@Deprecated("Use immutable contexts instead")
public fun <T : Plugin> fetch(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY, recursive: Boolean = true): T {
val loaded = get(factory.type, factory.tag, recursive)
return when {
@ -132,6 +135,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
}
}
@Deprecated("Use immutable contexts instead")
public fun <T : Plugin> fetch(
factory: PluginFactory<T>,
recursive: Boolean = true,
@ -141,3 +145,17 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
override fun iterator(): Iterator<Plugin> = plugins.iterator()
}
/**
* Fetch a plugin with given meta from the context. If the plugin (with given meta) is already registered, it is returned.
* Otherwise, new child context with the plugin is created. In the later case the context could be retrieved from the plugin.
*/
public inline fun <reified T : Plugin> Context.fetch(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY): T {
val existing = plugins[factory]
return if (existing != null && existing.meta == meta) existing
else {
buildContext {
plugin(factory, meta)
}.plugins[factory]!!
}
}

View File

@ -22,7 +22,9 @@ class ContextTest {
@Test
fun testPluginManager() {
val context = Global.context("test")
val context = Global.buildContext{
name("test")
}
context.plugins.load(DummyPlugin())
//Global.plugins.load(DummyPlugin())
val members = context.gather<Name>("test")

View File

@ -1,25 +1,4 @@
package space.kscience.dataforge.context
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name
import kotlin.reflect.KClass
public class NativeLogManager : AbstractPlugin(), LogManager {
override fun log(name: Name, tag: String, body: () -> String) {
val message: String = body.safe
println("[${context.name}] $name: $message")
}
override val tag: PluginTag get() = Companion.tag
public companion object : PluginFactory<NativeLogManager> {
override fun invoke(meta: Meta, context: Context): NativeLogManager = NativeLogManager()
override val tag: PluginTag = PluginTag(group = PluginTag.DATAFORGE_GROUP, name = "log.native")
override val type: KClass<out NativeLogManager> = NativeLogManager::class
}
}
internal actual val globalLoggerFactory: PluginFactory<out LogManager> = NativeLogManager
internal actual val globalLoggerFactory: PluginFactory<out LogManager> = DefaultLogManager

View File

@ -9,6 +9,7 @@ import space.kscience.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import kotlin.native.concurrent.ThreadLocal
import kotlin.reflect.KClass
public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
@ -62,7 +63,8 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
public companion object : PluginFactory<IOPlugin> {
public val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat, BinaryMetaFormat)
public val defaultEnvelopeFormats: List<EnvelopeFormatFactory> = listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat)
public val defaultEnvelopeFormats: List<EnvelopeFormatFactory> =
listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat)
override val tag: PluginTag = PluginTag("io", group = PluginTag.DATAFORGE_GROUP)
@ -71,4 +73,10 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
}
}
public val Context.io: IOPlugin get() = plugins.fetch(IOPlugin)
@ThreadLocal
internal val ioContext = Global.withEnv {
name("IO")
plugin(IOPlugin)
}
public val Context.io: IOPlugin get() = (if (this == Global) ioContext else this).fetch(IOPlugin)

View File

@ -161,7 +161,7 @@ public class TaggedEnvelopeFormat(
}
}
private val default by lazy { invoke() }
private val default by lazy { invoke(context = ioContext) }
override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial(input) }

View File

@ -197,7 +197,7 @@ public class TaglessEnvelopeFormat(
return TaglessEnvelopeFormat(context.io, meta)
}
private val default by lazy { invoke() }
private val default by lazy { invoke(context = ioContext) }
override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial(input) }

View File

@ -193,6 +193,7 @@ public abstract interface class space/kscience/dataforge/meta/Meta : space/kscie
public final class space/kscience/dataforge/meta/Meta$Companion {
public static final field TYPE Ljava/lang/String;
public static final field VALUE_KEY Ljava/lang/String;
public final fun equals (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;)Z
public final fun getEMPTY ()Lspace/kscience/dataforge/meta/Meta;
}

View File

@ -54,6 +54,8 @@ public interface Meta : MetaRepr, ItemProvider {
*/
public const val VALUE_KEY: String = "@value"
public fun equals(meta1: Meta, meta2: Meta): Boolean = meta1.items == meta2.items
public val EMPTY: Meta = object : MetaBase() {
override val items: Map<NameToken, MetaItem> = emptyMap()
}

View File

@ -32,7 +32,7 @@ public operator fun <M : TypedMeta<M>> M.get(key: NameToken): TypedMetaItem<M>?
public abstract class MetaBase : Meta {
override fun equals(other: Any?): Boolean = if (other is Meta) {
this.items == other.items
Meta.equals(this, other)
} else {
false
}

View File

@ -4,7 +4,6 @@ import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int
import space.kscience.dataforge.workspace.WorkspaceBuilder
import space.kscience.dataforge.workspace.target
import kotlin.test.Test
import kotlin.test.assertEquals
@ -16,7 +15,7 @@ class BuildersKtTest {
val workspace = WorkspaceBuilder(Global).apply {
println("I am working")
context("test")
context { name("test") }
target("testTarget") {
"a" put 12
@ -29,7 +28,7 @@ class BuildersKtTest {
val script = """
println("I am working")
context("test")
context{ name("test") }
target("testTarget"){
"a" put 12

View File

@ -50,8 +50,8 @@ public class WorkspaceBuilder(private val parentContext: Context = Global) : Tas
/**
* Define a context for the workspace
*/
public fun context(name: String = "workspace", block: ContextBuilder.() -> Unit = {}) {
this.context = ContextBuilder(parentContext, name).apply(block).build()
public fun context(block: ContextBuilder.() -> Unit = {}) {
this.context = parentContext.buildContext(block)
}
/**