- Как пришли к использованию и разработки своих DSL
- Посмотрим примеры используемых в экосистеме DSL - gradle, spek, spring
- Рассмотрим базис для конструирования DSL на примере kohttp
13. Spek
13
object SetFeature: Spek({
Feature("Set addition") {
val set by memoized { mutableSetOf<String>() }
Scenario("getting the first item") {
val item = "foo"
Given("a non-empty set") {
set.add(item)
}
lateinit var result: String
When("getting the first item") {
result = set.first()
}
Then("it should return the first item") {
assertEquals(item, result)
}
}
}
20. Tools. Lambda as last argument
20
fun measureTimeMillis(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}
val time = measureTimeMillis {
task()
}
22. Tools. Lambda with receiver
22
httpGet {
url("https://github.com/search")
header {
"username" to "rybalkinsd"
"security-policy" to json {
"max-age" to 2592000
}
cookie {
"user_session" to "toFycNV"
"expires" to "Fri, 31 Jul 2019 20:30:00 -0000"
}
}
}
23. Tools. Lambda with receiver
23
fun httpGet(
client: Call.Factory = defaultHttpClient,
init: HttpGetContext.() -> Unit): Response {
val context = HttpGetContext().apply(init)
return client.newCall(
context.makeRequest()
).execute()
}
24. Lambda with receiver. HttpContext
24
class HttpContext(private val method: Method = GET) {
private val paramContext = ParamContext()
private val headerContext = HeaderContext()
var scheme: String = "http"
var host: String = ""
var port: Int? = null
var path: String? = null
...
}
25. Tools. Infix function
25
httpGet {
param {
"id" to Id("42")
"sender" to "localhost"
}
}
class ParamContext {
private val params = mutableMapOf<>()
infix fun String.to(v: Any?) { params[this] = v }
}
26. Tools. Type alias
26
typealias Header = Pair<String, String>
fun Request.headers(): Sequence<Header> = Sequence { ... }
httpRequest.headers().forEach {
(name, value) -> log("$name: $value")
}
27. Tools. Inline classes
27
inline class Token(val v: String)
infix fun String.to(token: Token) {
specific action
}
val response = httpPost {
url("https://googleapis.com/upload/drive/v3/files")
header {
"Authorization" to Token(“secret-token”)
}
https://kotlinlang.org/docs/reference/inline-classes.html
Inline classes are available only since Kotlin 1.3 and currently are experimental
38. Problem 3. Context control
38
httpGet {
param {
"text" to "iphone"
param {
"lr" to 213
}
}
}
39. Problem 3. Context control
39
@DslMarker
annotation class HttpDslMarker
@HttpDslMarker
class ParamContext
httpGet {
param {
param { } <— COMPILATION ERROR
}
}
40. Outline
‣ DSL это круто, если и только если
помогает вашим пользователям
‣ Работайте над DevX. Пользовательский
фидбек - это единственное что важно при
построении DSL
‣ Уже наступило время делать async-first DSL
40