3
DSLs
●
Easily understandable
●
Make the intention obvious
●
Are specific to a problem
Slide 4
Slide 4 text
4
DSLs
●
External DSLs
– Use any language to implement it
●
Internal DSLs
– Uses host language
– Uses normal language constructs
– Can use language constructs within DSL
Slide 5
Slide 5 text
5
Samples
Slide 6
Slide 6 text
6
kotlintest
should("verify names in map") {
//...
name should (startWith("test") or endWith("Name"))
name shouldBe "testName"
ageMap should contain("testName")
}
Slide 7
Slide 7 text
7
Koin
val appModule = applicationContext {
val db = SomeDatabase.getInstance(applicationContext)
bean { db.entryDao }
bean { SomeApi.create(BuildConfig.DEBUG) }
bean("MainScheduler") { AndroidSchedulers.mainThread() as Scheduler }
factory { AndroidNotifBuilder(applicationContext) as NotificationBuilder }
}
Slide 8
Slide 8 text
8
Anko
constraintLayout {
val title = textView {
id = R.id.txtTitle
textAppearance = R.style.TextAppearance_AppCompat_Large
textColor = R.color.titleText
}
applyConstraintSet {
title {
connect(
TOP to TOP of PARENT_ID margin dip(16),
START to START of PARENT_ID margin dip(16)
)
}
}
}
Slide 9
Slide 9 text
9
How is it done?
Slide 10
Slide 10 text
10
mockK
view = mockk(relaxed = true)
// ...
every { view.setRemoteVehicles(vehicles = capture(params)) } just runs
Slide 11
Slide 11 text
11
mockK
just runs
Slide 12
Slide 12 text
12
mockK
Slide 13
Slide 13 text
13
mockK
/**
* Part of DSL. Answer placeholder for Unit returning functions.
*/
infix fun MockKStubScope.just(runs: Runs) = answers(ConstantAnswer(Unit))
Slide 14
Slide 14 text
14
mockK
/**
* Part of DSL. Answer placeholder for Unit returning functions.
Slide 15
Slide 15 text
15
mockK
just(runs)
Slide 16
Slide 16 text
16
mockK
just runs
Slide 17
Slide 17 text
17
Concepts covered so far
●
Infix calls
Slide 18
Slide 18 text
18
mockK
view = mockk(relaxed = true)
// ...
every { view.setRemoteVehicles(vehicles = capture(params)) } just runs
Slide 19
Slide 19 text
19
mockK
just runs
Slide 20
Slide 20 text
20
mockK
runs
Slide 21
Slide 21 text
21
mockK
/**
* Part of DSL. Object to represent phrase "just Runs"
*/
object Runs
typealias runs = Runs
Slide 22
Slide 22 text
22
Concepts covered so far
●
Infix calls
●
Empty object
●
Type aliases
Slide 23
Slide 23 text
23
kotlintest
class CalenderListScreen : ShouldSpec() {
init {
should("display channelMap in list") {
// ...
}
}
}
Slide 24
Slide 24 text
24
kotlintest
Slide 25
Slide 25 text
25
kotlintest
fun should(name: String, test: () -> Unit): TestCase {
val testCase = TestCase(
suite = current,
name = "should $name",
test = test,
config = defaultTestCaseConfig)
current.addTestCase(testCase)
return testCase
}
Slide 26
Slide 26 text
26
kotlintest
Slide 27
Slide 27 text
27
kotlintest
Slide 28
Slide 28 text
28
kotlintest
Slide 29
Slide 29 text
29
Concepts covered so far
●
Infix calls
●
Empty object
●
Type aliases
●
Trailing lambdas
Slide 30
Slide 30 text
30
kotlintest
class CalenderListScreen : ShouldSpec() {
init {
should("display channelMap in list") {
// ...
}
}
}
Slide 31
Slide 31 text
31
kotlintest
class CalenderListScreen : StringSpec() {
init {
"should display channelMap in list" {
// ...
}
}
}
Slide 32
Slide 32 text
32
kotlintest
Slide 33
Slide 33 text
33
kotlintest
operator fun String.invoke(test: () -> Unit): TestCase {
val tc = TestCase(suite = rootTestSuite,
name = this,
test = test,
config = defaultTestCaseConfig)
rootTestSuite.addTestCase(tc)
return tc
}
Slide 34
Slide 34 text
34
kotlintest
Slide 35
Slide 35 text
35
Concepts covered so far
●
Infix calls
●
Empty object
●
Type aliases
●
Trailing lambdas
●
Extension functions
●
Conventions
Slide 36
Slide 36 text
36
kotlinx.html
val html = buildString {
appendHTML().html {
body {
div {
+"Some text inside the div"
}
}
}
}
Slide 37
Slide 37 text
37
kotlinx.html
+"Some text inside the div"
Slide 38
Slide 38 text
38
kotlinx.html
@kotlinx.html.HtmlTagMarker public interface Tag {
// ...
public open operator fun kotlin.String.unaryPlus(): kotlin.Unit { /* ... */ }
// ...
}
Slide 39
Slide 39 text
39
kotlinx.html
Slide 40
Slide 40 text
40
Concepts covered so far
●
Infix calls
●
Empty object
●
Type aliases
●
Trailing lambdas
●
Conventions
●
Extension functions
●
Operator overloading
Slide 41
Slide 41 text
41
Koin
val appModule = applicationContext {
val db = SomeDatabase.getInstance(applicationContext)
bean { db.entryDao }
bean { SomeApi.create(BuildConfig.DEBUG) }
bean("MainScheduler") { AndroidSchedulers.mainThread() as Scheduler }
factory { AndroidNotifBuilder(applicationContext) as NotificationBuilder }
}
Slide 42
Slide 42 text
42
Koin
Slide 43
Slide 43 text
43
Koin
fun applicationContext(init: Context.() -> Unit): Module =
{ Context(Scope.ROOT,
StandAloneContext.koinContext as KoinContext).apply(init) }
Slide 44
Slide 44 text
44
Koin
Slide 45
Slide 45 text
45
Koin
fun applicationContext(init: Context.() -> Unit): Module =
{ Context(Scope.ROOT,
StandAloneContext.koinContext as KoinContext).apply(init) }
Slide 46
Slide 46 text
46
Koin
fun applicationContext(init: Context.() -> Unit): Module =
{
val ctx = Context(Scope.ROOT,
StandAloneContext.koinContext as KoinContext)
ctx.init()
}
Slide 47
Slide 47 text
47
Lambda vs. Lambda with receivers
"heyho".also { str ->
println(str)
}
Slide 48
Slide 48 text
48
Lambda vs. Lambda with receivers
"heyho".also {
println(it)
}
Slide 49
Slide 49 text
49
Lambda vs. Lambda with receivers
"heyho".also {
println(it)
}
"heyho".apply {
println(this)
}
Slide 50
Slide 50 text
50
Lambda vs. Lambda with receivers
// slightly simplified
public inline fun T.also(block: (T) -> Unit): T {
block(this)
return this
}
// slightly simplified
public inline fun T.apply(block: T.() -> Unit): T {
block()
return this
}
Slide 51
Slide 51 text
51
Lambda vs. Lambda with receivers
Slide 52
Slide 52 text
52
Lambda with receivers in DSLs
Slide 53
Slide 53 text
53
Lambda with receivers in DSLs
fun applicationContext(init: Context.() -> Unit): Module =
{
val ctx = Context(Scope.ROOT,
StandAloneContext.koinContext as KoinContext)
ctx.init()
}
Slide 54
Slide 54 text
54
Concepts covered so far
●
Infix calls
●
Empty object
●
Type aliases
●
Trailing lambdas
●
Conventions
●
Extension functions
●
Operator overloading
●
Lambda with Receivers
Slide 55
Slide 55 text
55
Anko
constraintLayout {
val title = textView {
id = R.id.txtTitle
textAppearance = R.style.TextAppearance_AppCompat_Large
textColor = R.color.titleText
}
applyConstraintSet {
title {
connect(
TOP to TOP of PARENT_ID margin dip(16),
START to START of PARENT_ID margin dip(16)
)
}
}
}
Slide 56
Slide 56 text
56
Anko
Slide 57
Slide 57 text
57
Anko
inline fun ViewManager.textView(
init: (@AnkoViewDslMarker android.widget.TextView).() -> Unit)
: android.widget.TextView {
Slide 58
Slide 58 text
58
Anko
Slide 59
Slide 59 text
59
Anko
@DslMarker
@Target(AnnotationTarget.TYPE)
annotation class AnkoViewDslMarker
62
Concepts covered so far
●
Infix calls
●
Empty object
●
Type aliases
●
Trailing lambdas
●
Conventions
●
Extension functions
●
Operator overloading
●
Lambda with Receivers
●
Scope limiting
Slide 63
Slide 63 text
63
Designing DSLs
●
Consider deviating from naming conventions
●
As per usual: Don’t go overboard
●
An interesting discussion of approaches:
https:/
/github.com/zsmb13/VillageDSL
Slide 64
Slide 64 text
64
Libs
Slide 65
Slide 65 text
65
General libs
●
DI / Service Locating:
– Koin
– Kodein
●
Testing
– MockK
– Spek
– kotlintest
Slide 66
Slide 66 text
66
Android specific libs
●
Anko
●
Konduit
Slide 67
Slide 67 text
67
Libs
●
Spring Router Config
●
Spring Bean Config
●
Gradle Kotlin DSL
●
kotlinx.HTML
●
Exposed