kotlin dsl_使用Kotlin DSL的Cloud Foundry应用程序清单

本文介绍如何使用Kotlin语言创建领域特定语言(DSL),以简化Cloud Foundry应用清单的配置过程。通过Kotlin的特性,如Lambda表达式、扩展函数及运算符重载,实现了简洁而强大的DSL语法。

kotlin dsl

我为与Kotlin语言创建DSL提供了出色的支持,并为此投入了大量精力

此功能现在用于创建gradle构建文件在Spring Webflux中定义路由, 使用kotlinx.html库创建html模板。

在这里,我将演示创建基于kotlin的DSL来表示Cloud Foundry应用清单内容

当表示为yaml文件时,样本清单如下所示:

applications:
 - name: myapp
   memory: 512M
   instances: 1
   path: target/someapp.jar
   routes:
     - somehost.com
     - antother.com/path
   envs:
    ENV_NAME1: VALUE1
    ENV_NAME2: VALUE2

这是我要针对的DSL类型:

cf {
    name = "myapp"
    memory = 512(M)
    instances = 1
    path = "target/someapp.jar"
    routes {
        +"somehost.com"
        +"another.com/path"
    }
    envs {
        env["ENV_NAME1"] = "VALUE1"
        env["ENV_NAME2"] = "VALUE2"
    }
}

获得基本结构

让我从一个看起来像这样的简单结构开始:

cf {
    name = "myapp"
    instances = 1
    path = "target/someapp.jar"
}

并希望这种DSL映射到如下所示的结构:

data class CfManifest(
        var name: String = "",
        var instances: Int? = 0,
        var path: String? = null
)

它将转换为带有Lambda表达式的Kotlin函数:

fun cf(init: CfManifest.() -> Unit) {
 ...
}

参数如下所示:

() -> Unit

是一个不言自明的,不带任何参数且不返回任何内容的lambda表达式。

我花了一段时间才想到的部分是这个经过修改的lambda表达式,称为带有接收器的Lambda表达式:

CfManifest.() -> Unit

按照我的理解,它有两件事:

1.它在包装函数的范围内定义了接收器类型的扩展函数–在我的情况下
CfManifest类别

2. lambda表达式中的this现在是指接收器函数。

鉴于此, cf函数转换为:

fun cf(init: CfManifest.() -> Unit): CfManifest {
    val manifest = CfManifest()
    manifest.init()
    return manifest
}

可以简单地表示为:

fun cf(init: CfManifest.() -> Unit) = CfManifest().apply(init)

所以现在我打电话给:

cf {
    name = "myapp"
    instances = 1
    path = "target/someapp.jar"
}

转换为:

CFManifest().apply {
  this.name = "myapp"
  this.instances = 1
  this.path = "target/someapp.jar"
}

更多DSL

扩展基本结构:

cf {
    name = "myapp"
    memory = 512(M)
    instances = 1
    path = "target/someapp.jar"
    routes {
        +"somehost.com"
        +"another.com/path"
    }
    envs {
        env["ENV_NAME1"] = "VALUE1"
        env["ENV_NAME2"] = "VALUE2"
    }
}

路由和env依次成为CfManifest类上的方法,如下所示:

data class CfManifest(
        var name: String = "",
        var path: String? = null,
        var memory: MEM? = null,
        ...
        var routes: ROUTES? = null,
        var envs: ENVS = ENVS()
) {

    fun envs(block: ENVS.() -> Unit) {
        this.envs = ENVS().apply(block)
    }

    ...

    fun routes(block: ROUTES.() -> Unit) {
        this.routes = ROUTES().apply(block)
    }
}

data class ENVS(
        var env: MutableMap<String, String> = mutableMapOf()
)

data class ROUTES(
        private val routes: MutableList<String> = mutableListOf()
) {
    operator fun String.unaryPlus() {
        routes.add(this)
    }
}

查看routes方法如何将接收器类型为ROUTES的Lambda表达式接受,这使我可以定义如下表达式:

cf {
    ...
    routes {
        +"somehost.com"
        +"another.com/path"
    }
    ...
}

这里的另一个技巧是使用以下方法添加路线:

+"somehost.com"

使用Kotlin约定启用此功能,该约定将特定的方法名称转换为运算符 ,此处为unaryPlus方法。 对我来说很酷的事情是,该运算符仅在ROUTES实例的范围内可见!

利用Kotlin功能的DSL的另一个功能是指定内存的方式,它有两个部分-数字和修饰符2G,500M等。

通过DSL以稍微修改的方式将其指定为2(G)和500(M)。

它的实现方式是使用另一种Kotlin约定,如果一个类具有invoke方法,则实例可以通过以下方式调用它:

class ClassWithInvoke() {
    operator fun invoke(n: Int): String = "" + n
}
val c = ClassWithInvoke()
c(10)

因此,在CFManifest类的范围内将invoke方法实现为Int的扩展功能,就可以实现这种DSL:

data class CfManifest(
        var name: String = "",
        ...
) {
    ...
    operator fun Int.invoke(m: MemModifier): MEM = MEM(this, m)
}

就我而言,这纯粹是实验,对于Kotlin和Kotlin DSL来说我都是新手,因此很可能在此实现中有很多地方可以改进,欢迎任何反馈和建议。 您可以使用此示例代码在我的github回购玩这里

翻译自: https://www.javacodegeeks.com/2017/07/cloud-foundry-application-manifest-using-kotlin-dsl.html

kotlin dsl

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值