Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin DSLs

Kotlin DSLs

Talk held at Android Makers, Paris (https://androidmakers.fr/)

D0ed2f28dd13ddfb192ebe198623924c?s=128

Wolfram Rittmeyer

April 24, 2018
Tweet

Transcript

  1. KOTLIN DSLs Wolfram Rittmeyer

  2. 2 What are DSLs

  3. 3 DSLs • Easily understandable • Make the intention obvious

    • Are specific to a problem
  4. 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
  5. 5 Samples

  6. 6 kotlintest should("verify names in map") { //... name should

    (startWith("test") or endWith("Name")) name shouldBe "testName" ageMap should contain("testName") }
  7. 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 } }
  8. 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) ) } } }
  9. 9 How is it done?

  10. 10 mockK view = mockk<CarDiscoveryView>(relaxed = true) // ... every

    { view.setRemoteVehicles(vehicles = capture(params)) } just runs
  11. 11 mockK just runs

  12. 12 mockK

  13. 13 mockK /** * Part of DSL. Answer placeholder for

    Unit returning functions. */ infix fun MockKStubScope<Unit>.just(runs: Runs) = answers(ConstantAnswer(Unit))
  14. 14 mockK /** * Part of DSL. Answer placeholder for

    Unit returning functions.
  15. 15 mockK just(runs)

  16. 16 mockK just runs

  17. 17 Concepts covered so far • Infix calls

  18. 18 mockK view = mockk<CarDiscoveryView>(relaxed = true) // ... every

    { view.setRemoteVehicles(vehicles = capture(params)) } just runs
  19. 19 mockK just runs

  20. 20 mockK runs

  21. 21 mockK /** * Part of DSL. Object to represent

    phrase "just Runs" */ object Runs typealias runs = Runs
  22. 22 Concepts covered so far • Infix calls • Empty

    object • Type aliases
  23. 23 kotlintest class CalenderListScreen : ShouldSpec() { init { should("display

    channelMap in list") { // ... } } }
  24. 24 kotlintest

  25. 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 }
  26. 26 kotlintest

  27. 27 kotlintest

  28. 28 kotlintest

  29. 29 Concepts covered so far • Infix calls • Empty

    object • Type aliases • Trailing lambdas
  30. 30 kotlintest class CalenderListScreen : ShouldSpec() { init { should("display

    channelMap in list") { // ... } } }
  31. 31 kotlintest class CalenderListScreen : StringSpec() { init { "should

    display channelMap in list" { // ... } } }
  32. 32 kotlintest

  33. 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 }
  34. 34 kotlintest

  35. 35 Concepts covered so far • Infix calls • Empty

    object • Type aliases • Trailing lambdas • Extension functions • Conventions
  36. 36 kotlinx.html val html = buildString { appendHTML().html { body

    { div { +"Some text inside the div" } } } }
  37. 37 kotlinx.html +"Some text inside the div"

  38. 38 kotlinx.html @kotlinx.html.HtmlTagMarker public interface Tag { // ... public

    open operator fun kotlin.String.unaryPlus(): kotlin.Unit { /* ... */ } // ... }
  39. 39 kotlinx.html

  40. 40 Concepts covered so far • Infix calls • Empty

    object • Type aliases • Trailing lambdas • Conventions • Extension functions • Operator overloading
  41. 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 } }
  42. 42 Koin

  43. 43 Koin fun applicationContext(init: Context.() -> Unit): Module = {

    Context(Scope.ROOT, StandAloneContext.koinContext as KoinContext).apply(init) }
  44. 44 Koin

  45. 45 Koin fun applicationContext(init: Context.() -> Unit): Module = {

    Context(Scope.ROOT, StandAloneContext.koinContext as KoinContext).apply(init) }
  46. 46 Koin fun applicationContext(init: Context.() -> Unit): Module = {

    val ctx = Context(Scope.ROOT, StandAloneContext.koinContext as KoinContext) ctx.init() }
  47. 47 Lambda vs. Lambda with receivers "heyho".also { str ->

    println(str) }
  48. 48 Lambda vs. Lambda with receivers "heyho".also { println(it) }

  49. 49 Lambda vs. Lambda with receivers "heyho".also { println(it) }

    "heyho".apply { println(this) }
  50. 50 Lambda vs. Lambda with receivers // slightly simplified public

    inline fun <T> T.also(block: (T) -> Unit): T { block(this) return this } // slightly simplified public inline fun <T> T.apply(block: T.() -> Unit): T { block() return this }
  51. 51 Lambda vs. Lambda with receivers

  52. 52 Lambda with receivers in DSLs

  53. 53 Lambda with receivers in DSLs fun applicationContext(init: Context.() ->

    Unit): Module = { val ctx = Context(Scope.ROOT, StandAloneContext.koinContext as KoinContext) ctx.init() }
  54. 54 Concepts covered so far • Infix calls • Empty

    object • Type aliases • Trailing lambdas • Conventions • Extension functions • Operator overloading • Lambda with Receivers
  55. 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) ) } } }
  56. 56 Anko

  57. 57 Anko inline fun ViewManager.textView( init: (@AnkoViewDslMarker android.widget.TextView).() -> Unit)

    : android.widget.TextView {
  58. 58 Anko

  59. 59 Anko @DslMarker @Target(AnnotationTarget.TYPE) annotation class AnkoViewDslMarker

  60. 60 Concepts • Infix calls • Empty object • Type

    aliases • Trailing lambdas • Conventions • Extension functions • Operator overloading • Lambda with Receivers • Scope limiting
  61. 61 Summary

  62. 62 Concepts covered so far • Infix calls • Empty

    object • Type aliases • Trailing lambdas • Conventions • Extension functions • Operator overloading • Lambda with Receivers • Scope limiting
  63. 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
  64. 64 Libs

  65. 65 General libs • DI / Service Locating: – Koin

    – Kodein • Testing – MockK – Spek – kotlintest
  66. 66 Android specific libs • Anko • Konduit

  67. 67 Libs • Spring Router Config • Spring Bean Config

    • Gradle Kotlin DSL • kotlinx.HTML • Exposed
  68. Q&A www.wolfram-rittmeyer.de twitter.com/RittmeyerW www.grokkingandroid.com